测试环境:Windows 10 21H2 32
位,开启全局DEP
。这里来看如何绕过DEP
和ASLR
。
没得说,肯定是先看需要多少字节能够Crash
程序。简单的步骤就不说了,讲一下我遇到的一些小坑。
参考exploit-db
:https://www.exploit-db.com/exploits/14191。这里选择字节长度为`50000`。
1 | #!/usr/bin/python3 |
程序Crash
的时候,发现把SEH
链给覆盖了:
所以,这里应该是基于SEH
的栈溢出,需要绕过DEP
和ASLR
。
在寻找Offset
的时候,遇到一个小坑。这里可以用msf-pattern_create -l 50000
生成,或者windbg
加载mona
之后,使用!py mona pc 50000
生成,生成没有问题,主要是在确定具体的Offset
的时候,出现了一点小问题。
注意看windbg
中,通过mona
查找到的具体Offset
:
看一下msf-pattern_offset
中查找到的具体Offset
:
最后的正确Offset为43474
。个人感觉是因为pattern
太长了,里面的字符串出现了重复,而windbg
中mona
没有搜索完,只搜索到第一个就停止了,所以,后续查找Offset
的时候,最好两边一起验证下,如果有多个值符合条件,每个值最好也试一下。
Offset
偏移是正确的,注意标红的框,这里有个小细节,42424242
之后被覆盖了ffffffff
,这个ffffffff
应该是SEH
链的尾部。后续在查找坏字符的时候,会发现04030201
没找到,原因是因为被ffffffff
覆盖了。基于这一点,后续在写利用代码的时候,可以在Offset
之后先跟一段\x90
,然后再接shellcode
,以防shellcode
被覆盖导致不能执行。
注意,在查找坏字符的时候,需要定位坏字符串的位置,这里可以利用windbg
中的搜索功能,需要先用!teb
获取当前内存的空间大小及地址,然后再查找:
1 | 0:000> !teb |
搜索的命令为:
1 | s -a 000e0000 00150000 BBBB |
显然,第一个坏字符为09
,后续发现0a
也是坏字符:
最终坏字符为\x00\x09\x0a
。
讲一点小知识:对于开启DEP
且基于SEH
的栈溢出,不能使用之前讨论的未开启DEP
的SEH
栈溢出利用方法。因为开启DEP
之后,栈空间是不可执行的,而之前SEH
利用方式中组合P/P/R
和JMP
的方式,在P/P/R
执行完之后,后续跳转到JMP
指令上,因为该指令在栈上面导致无法执行。正确的方法是直接用能跳转到ROP Chain
上的指令覆盖SEH Handler
。
考虑到Bypass ASLR
,在构建ROP Chain
的时候,如果程序运行时没能泄漏出某个函数的地址,则需要选择没有开启ASLR
的DLL
,不然可以先计算出基址,然后ROP Gadget
都选择相对地址构建ROP Chain
即可。这里选择没开启ASLR
的DLL
:
在用mona
自动生成ROP Chain
的时候,发现生成不成功,如下所示:
1 | *** [ Python ] *** |
注意看,里面说没有找到设置EDX
的ROP Gadgets
。这里在构造设置EDX
的ROP Gadgets
的时候,因为牵涉到EBX
,需要将设置EBX
值的ROP Gadgets
放到设置EDX
的ROP Gadgets
后面去。来看看我构造的设置EDX
的ROP Gadget
:
1 | #[---INFO:gadgets_to_set_edx:---] |
因为RETN 0x10
指令的存在,这里需要把设置ECX
的其中一条ROP Gadget
放到设置EDX
的ROP Gadgets
中间。
修改之后的ROP Gadgets
如下:
1 | def create_rop_chain(): |
小知识点:RETN
操作:先EIP=ESP
,然后ESP=ESP+4
;RETN N
操作:先EIP=ESP
,然后ESP=ESP+4+N
。
ROP Gadgets
构造完成之后,现在就是如何让SEH Handler
执行的时候,能够跳转到ROP Gadgets
的第一条指令,这里一定要跳转到ROP Gadgets
的第一条指令,前面不能加\x90
进行填充,这里和shellcode
之前的\x90
填充不一样,这里因为有RETN
指令,如果用\x90
填充,会把\x90\x90\x90\x90
设置为EIP
的值,而这会指向一个无效的地址。shellcode
之前的\x90
填充,因为使用的JMP ESP
指令,就算跳转到\x90
所在区域,并不会把\x90\x90\x90\x90
设置为EIP
的值,EIP
的值指向\x90\x90\x90\x90
,只会不断的跳过这些\x90
空指令,直到遇到shellcode
第一条指令。
暂时设置SEH Handler
的值为\xcc\xcc\xcc\xcc
,然后重新运行,当溢出发生的时候,来看一下此刻ROP Chain
和ESP
之间的距离:
1 | (1c78.1948): Access violation - code c0000005 (first chance) |
可以看到两者之间的距离在18aach
,现在需要找到能够跳转大于等于18aach
的ROP Gadget
来替换SEH Handler
中的值。使用如下命令:
1 | !py mona stackpivot -n -m MSA2Mfilter03.dll -distance 101036 -cpb '\x00\x09\x0a' |
最终的结果会保存到stackpivot.txt
文件里面。来看一下文件里面的部分内容:
这里选择的0x1001f35f
地址所在的ROP Gadget
。但是这里跳转了19000h
,会跳到ROP Chain
里面去,我们需要在ROP Chain
前面增加一些\x90
,让ESP
正好能够指向ROP Chain
的第一条指令。具体的值需要不断的调试才能确定,主要是因为添加的\x90
也会占用空间,会让ESP
与ROP Chain
之间的距离变长。最终得到的\x90
长度为57424
。这里我还考虑了一下,这个值会不会让最终的PAYLOAD
指到栈空间以外的地方。验证了一下:
空间足够大,没问题。
最终的利用代码如下:
1 | #!/usr/bin/python3 |
成功反弹shell
: