一道整数相关的题~
分析
保护全开:
分析程序,有一个结构体(check域应该是4字节,定义错了):
程序主体也是选单,只有增查删可用:
增
1 | unsigned __int64 add() |
其中readN实现为:
1 | unsigned __int64 __fastcall readN(char *srcBuf, int len) |
查
1 | unsigned __int64 view() |
删
1 | __int64 cancel() |
如上,本题的不太明显的漏洞,在输入长度为0时,可以栈溢出,但是保护全开一切随机,此时还没有什么有用的信息,观察栈,发现在buf之上有一个指针,即newPro
,可是没有任何信息,并不知道应该覆盖什么,只能使用部分覆盖控制堆,由于readN
以\x00
结束,所以部分覆盖只能覆盖为\x00
,由于canary最低位一定是\x00
所以当第一次泄露出堆以后,下次就可以完全覆盖,结束符放到canary里面去而不会造成任何影响。
利用
由于edit未实现,能写的地方只有栈,但是view可以用来读,那么控制Prolist里的指针可以做到任意位置读,于是就可以先读到canary,然后使用rop技术,而读canary需要先知道栈的位置,栈和堆目前没明显关系,但是libc里可以找到栈地址,堆里可以找到libc地址,那么通用思路就是泄露堆地址->泄露libc地址->泄露栈地址->泄露canary->rop
:
- 先使用fastbin泄露出堆地址,因为free两个fastchunk则后一个chunk的fd指向前一个chunk,即一个堆地址,可以使用部分覆盖来做到指向一个fastchun,由于我们的输入会以
\x00
结束,所以要利用的堆的地址应该是以\x00
结束的 - 有了堆地址,后续就不用部分覆盖了,现在还需要泄露libc地址由于不能直接malloc一个smallchunk,需要构造一个满足约束的chunk然后释放它,这里的约束有两个:满足free的约束,满足程序自身校验的约束,另外整个程序的chunk应满足(length+&chunk)之后的一些地址仍然可访问,防止内存访问异常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
180x5654ef5a6060: 0x0000000000000300 0x0000000000000031 -->chunk1
0x5654ef5a6070: 0x000000610000000f 0x0000000000000000
0x5654ef5a6080: 0x0000000100000000 0x00000000000000d1 -->fake
0x5654ef5a6090: 0x0000000000000064 0x0000000000000071
0x5654ef5a60a0: 0x0000000100000050 0x0000000000000000
0x5654ef5a60b0: 0x0000000000000000 0x0000000000000000
0x5654ef5a60c0: 0x0000000000000000 0x0000000000000000
0x5654ef5a60d0: 0x0000000000000000 0x0000000000000000
0x5654ef5a60e0: 0x0000000000000000 0x0000000000000000
0x5654ef5a60f0: 0x0000010000000000 0x0000010000000100
0x5654ef5a6100: 0x0000000000000100 0x0000000000000071 -->chunk2
0x5654ef5a6110: 0x6161616100000050 0x6161616161616161
0x5654ef5a6120: 0x6161616161616161 0x6161616161616161
0x5654ef5a6130: 0x6161616161616161 0x6161616161616161
0x5654ef5a6140: 0x6161616161616161 0x6161616161616161
0x5654ef5a6150: 0x6161616161616161 0x0000000000000031 -->fake
0x5654ef5a6160: 0x0000010000000000 0x0000010000000100
0x5654ef5a6170: 0x0000000000000100 0x0000000000020e91 -->top chunk - 此时可以得到libc的地址,有libc通过__environ得到stack的地址
- 由stack得到canary
- 使用普通的rop即可getshell
利用代码如下:
1 | #!/usr/bin/env python |
结果
参考
[0]https://github.com/briansp8210/CTF-writeup/blob/master/HITB-GSEC-CTF-2017/SENTOSA/README.md