TrendMicro CTF 2017 - RE300
Sun, Jun 25, 2017
Writeups #ctf #tmctf #trendmicro #2017 #reversing
This was the third reversing challenge in TrendMicro CTF 2017. You can download it here (md5: 38256f02d260bca145e90e06e474175a).
To extract it, as per the same instructions as the original challenge:
$ openssl enc -d -aes-256-cbc -k aOw0ZxbUtoj9BdHYtxjn -in files15.enc -out files15.zip
# or if you have problems with the first command
$ openssl enc -d -aes-256-cbc -k aOw0ZxbUtoj9BdHYtxjn -in files15.enc -out files15.zip -md md5
# then,
$ unzip files15.zip
Unfortunately the challenge is now shut down, so I cannot quite remember the original description or hint but I do remember it had to do with Powershell. If I find it posted somewhere else later, I’ll update this!
Anyway, we are given a single file — G0AL.BAT
, containing:
@echo off
set j=Thank_You_For_Joining_TMCTF2017
set k=Tested on Win7SP1 32-bit OS
set l=2eub2XQk9DHSsncxyWSLcTCLdgyLdhyLRgiLfiCLNjhPGHXzWQHR/+Fgi2wkJItFPItUKHgB6otKGItaIAHr4zRJizSLAe4x/zHA/KyEwHQHwc8NAcfr9Dt8JCh14YtaJAHrZosMS4taHAHriwSLAeiJRCQcYcOyCCnUieWJwmiOTg7sUuif////iUUEu37Y4nOHHCRS6I7///+
set m=moAsSAMOY%02maCkAOwBsdf%smadfdf9z///+qamogG8AZABSDGsaSSwhmzWMOYsA+/masdgmoKYqWTAGEAaQBuAC4ARAB//IAbA==
set n=LABNAmaodSDGASJOIHGI76msdm%ls:1qwerATSAYUDBGOSSnsAMIOLM//sogs+AuAFasgqQQYYHAFAZ2%:~QBtAGIAbABdABEA+
set o=JRQhobGwgQWgzMi5kaHVzZXIw24hcJAqJ5lb/VQSJwlC7qKJNvIccJFLoX////2hyb1ggaGRNaWNoVHJlbjHbiFwkConjaCF9WCBoZ2FpbmhzTWVBaCFJdEloaGVyZWhsbG9UaEZ7SGVoVE1DVDHJiEwkHonhMdJSU1FS/9AxwFD/VQg=
set p=GUAZQBwACgAOQAwACkAOwBGOALAHIAeQB7AGYAdQBuAGMAdABpAG8AbgAgAGcAZABlAGwAZQBnAGEAdABlAHsAUABhAHIAYQBtACAAKABbAFAAYQByAGEAbQBlAHQAZQByACgAUABvAHMAaQBGOALAGkAbwBuADGOALAMAAsAEGOALAYQBuAGQAYQBGOALAG8AcgB5ADGOALAJABUAHIAdQBlACkAXQAgAFsAVAB5AHAAZQBbAFGOALAXQAgACQAUABhAHIAYQBtAGUAdABlAHIAcwAsAFsAUABhAHIAYQBtAGUAdABlAHIAKABQAG8AcwBpAHQAaQBvAG4APQAxACkAXQAgAFsAVAB5AHAAZQBdACAAJABSAGUAdAB1AHIAbgBUAHkAcABlADGOALAWwBWAG8AaQBkAFGOALAKQA7ACQAVAB5AHAAZQBCAHUAaQBsAGQAZQByADGOALAWwBBAHAAcABEAG8AbQBhAGkAbgBdADoAOgBDAHUAcgByAGUAbgBGOALAEQAbwBtAGEAaQBuAC4ARABlAGYAaQBuAGUARAB5AG4AYQBtAGkAYwBBAHMAcwBlAGGOALAYgBsAHkAKAAoAE4AZQB3ACGOALATwBiAGoAZQBjAHQAIABTAHkAcwBGOALAGUAbQAuAFIAZQBmAGwAZQBjAHQAaQBvAG4ALgBBAHMAcwBlAGGOALAYgBsAHkATgBhAGGOALAZQAoACIAUgBlAGYAbABlAGMAdABlAGQARABlAGwAZQBnAGEAdABlACIAKQApACwAWwBTAHkAcwBGOALAGUAbQAuAFIAZQBmAGwAZQBjAHQAaQBvAG4ALgBFAGGOALAaQBGOALAC4AQQBzAHMAZQBtAGIAbAB5AEIAdQBpAGwAZABlAHIAQQBjAGMAZQBzAHMAXQA6ADoAUgB1AG4AKQAuAEQAZQBmAGkAbgBlAEQAeQBuAGEAbQBpAGMATQBvAGQAdQBsAGUAKAAiAEkAbgBNAGUAbQBvAHIAeQBNAG8AZAB1AGwAZQAiACwAJABmAGEAbABzAGUAKQAuAEQAZQBmAGkAbgBlAFQAeQBwAGUAKAAiAFgAWABYACIALAAiAEMAbABhAHMAcwAsAFAAdQBiAGwAaQBjACwAUwBlAGEAbABlAGQALABBAG4Acw^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%0gBSAHUAbgBGOALAGkAbQBlAC4ASQBuAHQAZQByAG8AcABTAGUAcgB2AGkAYwBlAHMALgBIAGEAbgBkAGwAZQBSAGUAZgBdACgATgBlAHcALQBPAGIAagBlAGMAdAAgAFMAeQBzAHQAZQBtAC4AUgB1AG4AdABpAGGOALAZQAuAEkAbgBGOALAGUAcgBvAHAAUwBlAHIAdgBpAGMAZQBzAC4ASABhAG4AZABsAGUAUgBlAGYAKAAoAE4AZQB3ACGOALATwBiAGoAZQBjAHQAIABJAG4AdABQAHQAcgApACwAJABVAG4AcwBhAGYAZQBOAGEAdABpAHYAZQBNAGUAdABoAG8AZABzAC4ARwBlAHQATQBlAHQAaABvAGQAKAAiAEcAZQBGOALAEGOALAbwBkAHUAbABlAEgAYQBuAGQAbABlACIAKQAuAEkAbgB2AG8AawBlACgAJABuAHUAbABsACwAQAAoACQATQBvAGQAdQBsAGUAKQApACkAKQAsACQAUAByAG8AYwBlAGQAdQByAGUAKQApADsAfQBbAEIAeQBGOALAGUAWwBdAFGOALAJABzAGMAMwAyACAAPQAgAFsAUwB5AHMAdABlAGGOALALgBDAG8AbgB2AGUAcgBGOALAFGOALAOgA6AEYAcgBvAGGOALAQgBhAHMAZQA2ADQAUwBGOALAHIAaQBuAGcAKAAkAGUAbgB2ADoAbAArACQAZQBuAHYAOgBPACkAOwAkAGEAPQBHAGUAdAAtAEQAYQBGOALAGUAOwBpAGYAKAAkAGEALgBNAG8AbgBGOALAGgAIAAtAGcAZQAgADIAKQB7AGUAeABpAHQAOwB9AFsAVQBpAG4AdAAzADIAWwBdAFGOALAIAAkAG8AcAA9ADAAOwAkAHIAPQAoAFsAUwB5AHMAdABlAGGOALALgBSAHUAbgBGOALAGkAbQBlAC4ASQBuAHQAZQByAG8AcABTAGUAcgB2AGkAYwBlAHMALgBNAGEAcgBzAGgAYQBsAFGOALAOgA6AEcAZQBGOALAEQAZQBsAGUAZwBhAHQAZQBGAG8AcgBGAHUAbgBjAHQAaQBvAG4AUABvAGkAbgBGOALAGUAcgAoACgAZwBwAHIAbwBjACAAawBlAHIAbgBlAGwAMwAyAC4AZABsAGwAIABWAGkAcgBGOALAHUAYQBsAFAAcgBvAHQAZQBjAHQAKQAsACgAZwBkAGUAbABlAGcAYQBGOALAGUAIABAACgAWwBCAHkAdABlAFsAXQBdACwAWwBVAEkAbgBGOALADMAMgBdACwAWwBVAEkAbgBGOALADMAMgBdACwAWwBVAEkAbgBGOALADMAMgBbAFGOALAXQApACAAKABbAEkAbgBGOALAFAAdAByAFGOALAKQApACkAKQAuAEkAbgB2AG8AawBlACgAJABzAGMAMwAyACwAJABzAGMAMwAyAC4ATABlAG4AZwBGOALAGgALAAwAHgANAAwACwAJABvAHAAKQA7AGkAZgAoACQAcgAgACGOALAZQBxACAAMAApAHsAJABwAHIAPQAoAFsAUwB5AHMAdABlAGGOALALgBSAHUAbgBGOALAGkAbQBlAC4ASQBuAHQAZQByAG8AcABTAGUAcgB2AGkAYwBlAHMALgBNAGEAcgBzAGgAYQBsAFGOALAOgA6AEcAZQBGOALAEQAZQBsAGUAZwBhAHQAZQBGAG8AcgBGAHUAbgBjAHQAaQBvAG4AUABvAGkAbgBGOALAGUAcgAoACgAZwBwAHIAbwBjACAAawBlAHIAbgBlAGwAMwAyAC4AZABsAGwAIABWAGkAcgBGOALAHUAYQBsAEEAbABsAG8AYwApACwAKABnAGQAZQBsAGUAZwBhAHQAZQAgAEAAKABbAEkAbgBGOALAFAAdAByAFGOALALABbAFUASQBuAHQAMwAyAFGOALALABbAFUASQBuAHQAMwAyAFGOALALABbAFUASQBuAHQAMwAyAFGOALAKQAgACgAWwBVAEkAbgBGOALADMAMgBdACkAKQApACkALgBJAG4AdgBvAGsAZQAoADAALAAkAHMAYwAzADIALgBMAGUAbgBnAHQAaAAsADAAeAAzADAAMAAwACwAMAB4ADQAMAApADsAaQBmACgAJABwAHIAIAAtAG4AZQAgADAAKQB7ACQAbQBlAGGOALAcwBlAHQAPQAoAFsAUwB5AHMAdABlAGGOALALgBSAHUAbgBGOALAGkAbQBlAC4ASQBuAHQAZQByAG8AcABTAGUAcgB2AGkAYwBlAHMALgBNAGEAcgBzAGgAYQ^BsAFGOALAOgA6AEcAZQBGOALAEQAZQBsAGUAZwBhAHQAZQBGAG8AcgBGAHUAbgBjAHQAaQBvAG4AUABvAGkAbgBGOALAGUAcgAoACgAZwBwAHIAbwBjACAAbQBzAHYAYwByAHQALgBkAGwAbAAgAGGOALAZQBtAHMAZQBGOALACkALAAoAGcAZABlAGwAZQBnAGEAdABlACAAQAAoAFsAVQBJAG4AdAAzADIAXQAsAFsAVQBJAG4AdAAzADIAXQAsAFsAVQBJAG4AdAAzADIAXQApACAAKABbAEkAbgBGOALAFAAdAByAFGOALAKQApACkAKQA7AGYAbwByACAAKAAkAGkAPQAwADsAJABpACAALQBsAGUAIAAoACQAcwBjADMAMgAuAEwAZQBuAGcAdABoACGOALAMQApADsAJABpACsAKwApACAAewAkAGGOALAZQBtAHMAZQBGOALAC4ASQBuAHYAbwBrAGUAKAAoACQAcAByACsAJABpACkALAAgACQAcwBjADMAMgBbACQAaQBdACwAIAAxACkAfQA7ACgAWwBTAHkAcwBGOALAGUAbQAuAFIAdQBuAHQAaQBtAGUALgBJAG4AdABlAHIAbwBwAFMAZQByAHYAaQBjAGUAcwAuAEGOALAYQByAHMAaABhAGwAXQA6ADoARwBlAHQARABlAGwAZQBnAGEAdABlAEYAbwByAEYAdQBuAGMAdABpAG8AbgBQAG8AaQBuAHQAZQByACgAKABnAHAAcgBvAGMAIABrAGUAcgBuAGUAbAAzADIALgBkAGwAbAAgAEMAcgBlAGEAdABlAFQAaAByAGUAYQBkACkALAAoAGcAZABlAGwAZQBnAGEAdABlACAAQAAoAFsASQBuAHQAUABGOALAHIAXQAsAFsAVQBJAG4AdAAzADIAXQAsAFsAVQBJAG4AdAAzADIAXQAsAFsAVQBJAG4AdAAzADIAXQAsAFsAVQBJAG4AdAAzADIAXQAsAFsASQBuAHQAUABGOALAHIAXQApACAAKABbAEkAbgBGOALAFAAdAByAFGOALAKQApACkAKQAuAEkAbgB2AG8AawBlACgAMAAsADAALAAkAHAAcgAsACQAcAByACwAMAAsADAAKQA7AHGOALAfQBlAGwAcwBlAHsAKABbAFMAeQBzAHQAZQBtAC4AUgB1AG4AdABpAGGOALAZQAuAEkAbgBGOALAGUAcgBvAHAAUwBlAHIAdgBpAGMAZQBzAC4ATQBhAHIAcwBoAGEAbABdADoAOgBHAGUAdABEAGUAbABlAGcAYQBGOALAGUARgBvAHIARgB1AG4AYwBGOALAGkAbwBuAFAAbwBpAG4AdABlAHIAKAAoAGcAcAByAG8AYwAgAGsAZQByAG4AZQBsADMAMgAuAGQAbABsACAAQwByAGUAYQBGOALAGUAVABoAHIAZQBhAGQAKQAsACgAZwBkAGUAbABlAGcAYQBGOALAGUAIABAACgAWwBJAG4AdABQAHQAcgBdACwAWwBVAEkAbgBGOALADMAMgBdACwAWwBCAHkAdABlAFsAXQBdACwAWwBCAHkAdABlAFsAXQBdACwAWwBVAEkAbgBGOALADMAMgBdACwAWwBJAG4AdABQAHQAcgBdACkAIAAoAFsASQBuAHQAUABGOALAHIAXQApACkAKQApAC4ASQBuAHYAbwBrAGUAKAAwACwAMAAsACQAcwBjADMAMgAsACQAcwBjADMAMgAsADAALAAwACkAOwB9AHMAbABlAGUAcAAoADEAMgAwADAAKQA7AHGOALAYwBhAHQAYwBoAHsAfQBlAHgAaQBGOALADsA"
cmd /c "powershell -command "$a=get-date;$t=[system.Text.Encoding]::UTF8.GetBytes('fzEvD');for ($i=0;$i -le ($t.Length-1);$i++){$t[$i]=$t[$i]-$a.hour} $d=[system.Text.Encoding]::UTF8.GetString($t);[Environment]::SetEnvironmentVariable('q', $d, 'User');"
cmd /c "powershell -enc %q%%p:GOAL=0% > NUL
echo %j%
echo %q%
set j=
set k=
set l=
set m=
set n=
set o=
set p=
The first thing that is important to realize is that this is a batch file, not a powershell file. Because of this, we must analyze what is going on first as a batch file (even though there is Powershell code executed inside of it).
First, a bunch of variables are being set to things that kinda look like incomplete base64. Also, the variable p
is being set with a long string and has a weird straggling "
at the end — this will make sense later. Next, a powershell script is run that has the following condensed into one line:
$date = Get-Date;
$str = [System.Text.Encoding]::UTF8.GetBytes('fzEvD');
for($i = 0; $i -le ($str.Length - 1); $i++) {
$str[$i] = $str[$i] - $date.hour;
}
$shiftedStr = [System.Text.Encoding]::UTF8.GetString($t);
[Environment]::SetEnvironmentVariable('q', $shiftedStr, 'User');
This script will take the string fzEvD
and shift the characters down by whatever hour it is currently — 0 to 23. It then takes this new string, and sets it to the user environment variable q
. Then, the batch script performs some string transformations and executes a base64-encoded powershell script.
Examining the string transformations,
cmd /c "powershell -enc %q%%p:GOAL=0% > NUL
This command only has a "
at the beginning because it is closed off by the "
at the end of p
— this is what I was referring to earlier. Next, %q%
is simply going to insert the value of q
which was set by the powershell script. After this, %p:GOAL=0%
is going to insert the value of p
but it is going to replace every instance of the string GOAL
with 0
— something seemed very repetitive in that “base64”! Lastly, the "
finishes off the script and is all piped into NUL
, making the output hidden if it fails! This is deceptive because there is an echo at the beginning saying Thank_You_For_Joining_TMCTF2017
, making you think you just got the flag! BAMBOOZLERS! Now, we need to figure out what that base64 encoded script is doing. To do that, we need to figure out what hour of the day this was intended to be run at to get the correct beginning portion of the base64 script. I put together a small python script that would do this for me by checking every possible hour and performing the shift. Then it would base64 decode the entire value with the shifted string at the front and print it out — if it would even decode at all.
Here was my script:
# the base "p" that we have with all "GOAL" replaced with "0" --- trimmed for readability
base = 'GUAZQBwACgAOQAwACkAOwB0AH...'
# each hour value possible
for k in xrange(24):
# shift all the characters in the front portion
front = ''.join(chr(ord(c) - k) for c in 'fzEvD')
try:
# insert it into the string and remove all these random
# NUL bytes I discovered after debugging what was being printed out
out = base64.b64decode('{}{}'.format(front, base)).replace('\x00', '')
# we want it to spell out the start of the code being "sleep(90);"
# this will make sure we have it de-mangled
if out.startswith('sleep'):
print k
except:
pass
After running this, we find that the k
hour value we want is 3
! Once we base64-decoded the string and stripped it of NUL
bytes, we get this script:
sleep(90);try{function gdelegate{Param ([Parameter(Position=0,Mandatory=$True)] [Type[]] $Parameters,[Parameter(Position=1)] [Type] $ReturnType=[Void]);$TypeBuilder=[AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName("ReflectedDelegate")),[System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule("InMemoryModule",$false).DefineType("XXX","Class,Public,Sealed,AnsiClass,AutoClass",[System.MulticastDelegate]);$TypeBuilder.DefineConstructor("RTSpecialName,HideBySig,Public",[System.Reflection.CallingConventions]::Standard,$Parameters).SetImplementationFlags("Runtime,Managed");$TypeBuilder.DefineMethod("Invoke","Public,HideBySig,NewSlot,Virtual",$ReturnType,$Parameters).SetImplementationFlags("Runtime,Managed");return $TypeBuilder.CreateType();}function gproc{Param ([Parameter(Position=0,Mandatory=$True)] [String] $Module,[Parameter(Position=1,Mandatory=$True)] [String] $Procedure);$SystemAssembly=[AppDomain]::CurrentDomain.GetAssemblies()|Where-Object{$_.GlobalAssemblyCache -And $_.Location.Split("\\")[-1].Equals("System.dll")};$UnsafeNativeMethods=$SystemAssembly.GetType("Microsoft.Win32.UnsafeNativeMethods");return $UnsafeNativeMethods.GetMethod("GetProcAddress").Invoke($null,@([Syste\x02Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr),$UnsafeNativeMethods.GetMethod("GetModuleHandle").Invoke($null,@($Module)))),$Procedure));}[Byte[]]$sc32 = [System.Convert]::FromBase64String($env:l+$env:O);$a=Get-Date;if($a.Month -ge 2){exit;}[Uint32[]] $op=0;$r=([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((gproc kernel32.dll VirtualProtect),(gdelegate @([Byte[]],[UInt32],[UInt32],[UInt32[]]) ([IntPtr])))).Invoke($sc32,$sc32.Length,0x40,$op);if($r -eq 0){$pr=([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((gproc kernel32.dll VirtualAlloc),(gdelegate @([IntPtr],[UInt32],[UInt32],[UInt32]) ([UInt32])))).Invoke(0,$sc32.Length,0x3000,0x40);if($pr -ne 0){$memset=([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((gproc msvcrt.dll memset),(gdelegate @([UInt32],[UInt32],[UInt32]) ([IntPtr]))));for ($i=0;$i -le ($sc32.Length-1);$i++) {$memset.Invoke(($pr+$i), $sc32[$i], 1)};([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((gproc kernel32.dll CreateThread),(gdelegate @([IntPtr],[UInt32],[UInt32],[UInt32],[UInt32],[IntPtr]) ([IntPtr])))).Invoke(0,0,$pr,$pr,0,0);}}else{([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((gproc kernel32.dll CreateThread),(gdelegate @([IntPtr],[UInt32],[Byte[]],[Byte[]],[UInt32],[IntPtr]) ([IntPtr])))).Invoke(0,0,$sc32,$sc32,0,0);}sleep(1200);}catch{}exit;
…..wow. Okay, that is a mess. Let’s clean that up, shall we?
sleep(90);
try {
function gdelegate {
Param(
[Parameter(Position=0,Mandatory=$True)]
[Type[]] $Parameters,
[Parameter(Position=1)]
[Type] $ReturnType=[Void]
);
$TypeBuilder = [AppDomain]::CurrentDomain.DefineDynamicAssembly(
(New-Object System.Reflection.AssemblyName("ReflectedDelegate")),
[System.Reflection.Emit.AssemblyBuilderAccess]::Run
).DefineDynamicModule("InMemoryModule", $false).DefineType(
"XXX", "Class,Public,Sealed,AnsiClass,AutoClass",
[System.MulticastDelegate]
);
$TypeBuilder.DefineConstructor(
"RTSpecialName,HideBySig,Public",
[System.Reflection.CallingConventions]::Standard,
$Parameters
).SetImplementationFlags("Runtime,Managed");
$TypeBuilder.DefineMethod(
"Invoke", "Public,HideBySig,NewSlot,Virtual",
$ReturnType, $Parameters
).SetImplementationFlags("Runtime,Managed");
return $TypeBuilder.CreateType();
}
function gproc {
Param(
[Parameter(Position=0, Mandatory=$True)]
[String] $Module,
[Parameter(Position=1, Mandatory=$True)]
[String] $Procedure
);
$SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() | Where-Object{$_.GlobalAssemblyCache -And $_.Location.Split("\")[-1].Equals("System.dll")};
$UnsafeNativeMethods = $SystemAssembly.GetType("Microsoft.Win32.UnsafeNativeMethods");
return $UnsafeNativeMethods.GetMethod("GetProcAddress").Invoke(
$null,
@([SysteRuntime.InteropServices.HandleRef](
New-Object System.Runtime.InteropServices.HandleRef(
(New-Object IntPtr),
$UnsafeNativeMethods.GetMethod("GetModuleHandle").Invoke($null, @($Module))
)
), $Procedure)
);
}
[Byte[]] $sc32 = [System.Convert]::FromBase64String($env:l + $env:O);
[Uint32[]] $op = 0;
$r = ([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
(gproc kernel32.dll VirtualProtect),
(gdelegate @([Byte[]], [UInt32], [UInt32], [UInt32[]]) ([IntPtr]))
)
).Invoke($sc32, $sc32.Length, 0x40, $op);
if($r -eq 0) {
$pr = ([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
(gproc kernel32.dll VirtualAlloc),
(gdelegate @([IntPtr], [UInt32], [UInt32],[UInt32]) ([UInt32]))
)
).Invoke(0, $sc32.Length, 0x3000, 0x40);
if($pr -ne 0) {
$memset=([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
(gproc msvcrt.dll memset),
(gdelegate @([UInt32],[UInt32],[UInt32]) ([IntPtr]))
)
);
for ($i = 0; $i -le ($sc32.Length - 1); $i++) {
$memset.Invoke(($pr + $i), $sc32[$i], 1)};
([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
(gproc kernel32.dll CreateThread),
(gdelegate @([IntPtr], [UInt32], [UInt32], [UInt32], [UInt32], [IntPtr]) ([IntPtr]))
)).Invoke(0, 0, $pr, $pr, 0, 0);
}
} else {
([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
(gproc kernel32.dll CreateThread),
(gdelegate @([IntPtr],[UInt32],[Byte[]],[Byte[]],[UInt32],[IntPtr]) ([IntPtr]))
)).Invoke(0, 0, $sc32, $sc32, 0, 0);
}
sleep(1200);
} catch{}
exit;
Note: I appologize for this formatting if some of it isn’t valid powershell, mine is a bit weak so I can’t vouch for its accuracy but this was the easiest to read.
I started looking at what this was doing and I pretty quickly identified that it was spawning a process with Windows API calls from powershell that would execute some kind of payload. How? You’ll notice that these functions basically define the C functions that are being accessed by powershell and which DLLs to use for them. Then, this line stands out and it is really the entry point for this script
[Byte[]] $sc32 = [System.Convert]::FromBase64String($env:l + $env:O);
This is going to take the environment variables l
and O
and convert them from base64 into a byte array called $sc32
….recall that there is a string in the original G0AL.BAT
that said Tested on Win7SP1 32-bit OS
— this was intended for a 32-bit OS, so $sc32
probably stands for shellcode 32-bit. Right, so this is gonna be what we wanna know what’s happening with. So next we have,
[Uint32[]] $op = 0;
$r = ([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
(gproc kernel32.dll VirtualProtect),
(gdelegate @([Byte[]], [UInt32], [UInt32], [UInt32[]]) ([IntPtr]))
)
).Invoke($sc32, $sc32.Length, 0x40, $op);
This function is defined in the Windows API Documentation as shown:
BOOL WINAPI VirtualProtect(
_In_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flNewProtect,
_Out_ PDWORD lpflOldProtect
);
As you can see, this is getting the function VirtualProtect
from kernel32.dll
, defining its parameters as byte[]
, uint32
, uint32
, uint32[]
, and its return type as an int*
, and then calling the function with the shellcode, the length of the shellcode, 0x40
which indicates PAGE_EXECUTE_READWRITE
and then 0
or NULL
as the final parameter — not sure why this is there as it only takes 3 parameters. Then we have this block of code:
if($r -eq 0) {
$pr = ([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
(gproc kernel32.dll VirtualAlloc),
(gdelegate @([IntPtr], [UInt32], [UInt32],[UInt32]) ([UInt32]))
)
).Invoke(0, $sc32.Length, 0x3000, 0x40);
if($pr -ne 0) {
$memset=([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
(gproc msvcrt.dll memset),
(gdelegate @([UInt32],[UInt32],[UInt32]) ([IntPtr]))
)
);
for ($i = 0; $i -le ($sc32.Length - 1); $i++) {
$memset.Invoke(($pr + $i), $sc32[$i], 1)};
([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
(gproc kernel32.dll CreateThread),
(gdelegate @([IntPtr], [UInt32], [UInt32], [UInt32], [UInt32], [IntPtr]) ([IntPtr]))
)).Invoke(0, 0, $pr, $pr, 0, 0);
}
} else {
([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
(gproc kernel32.dll CreateThread),
(gdelegate @([IntPtr],[UInt32],[Byte[]],[Byte[]],[UInt32],[IntPtr]) ([IntPtr]))
)).Invoke(0, 0, $sc32, $sc32, 0, 0);
}
Really, all this does is say “Hey, if I was unable to just reserve the size needed for the shellcode and make it read/write/execute”. Then it will memset
each byte of the VirtualAlloc
'd memory and create a thread for each execution of VirtualAlloc
every time it does memset
. If it was able to reserve the size needed for the shellcode, it would simply do CreateThread
for the shellcode.
Now, we need to know what that shellcode is in order to figure out what’s going on in this code. If we combine l
and O
together, we get the base64 string:
2eub2XQk9DHSsncxyWSLcTCLdgyLdhyLRgiLfiCLNjhPGHXzWQHR/+Fgi2wkJItFPItUKHgB6otKGItaIAHr4zRJizSLAe4x/zHA/KyEwHQHwc8NAcfr9Dt8JCh14YtaJAHrZosMS4taHAHriwSLAeiJRCQcYcOyCCnUieWJwmiOTg7sUuif////iUUEu37Y4nOHHCRS6I7///+JRQhobGwgQWgzMi5kaHVzZXIw24hcJAqJ5lb/VQSJwlC7qKJNvIccJFLoX////2hyb1ggaGRNaWNoVHJlbjHbiFwkConjaCF9WCBoZ2FpbmhzTWVBaCFJdEloaGVyZWhsbG9UaEZ7SGVoVE1DVDHJiEwkHonhMdJSU1FS/9AxwFD/VQg=
Which decodes to…
In [1]: import base64
In [2]: base64.b64decode('2eub2XQk9DHSsncxyWSLcTCLdgyLdhyLRgiLfiCLNjhPGHXzWQHR/+Fgi2wkJItFPItUKHgB6otKGItaIAHr4zRJizSLAe4x/zHA/KyEwHQHwc8NAcfr9Dt8JCh14YtaJAHrZosMS4taHAHriwSLAeiJRCQcYcOyCCnUieWJwmiOTg7
...: sUuif////iUUEu37Y4nOHHCRS6I7///+JRQhobGwgQWgzMi5kaHVzZXIw24hcJAqJ5lb/VQSJwlC7qKJNvIccJFLoX////2hyb1ggaGRNaWNoVHJlbjHbiFwkConjaCF9WCBoZ2FpbmhzTWVBaCFJdEloaGVyZWhsbG9UaEZ7SGVoVE1DVDHJiEwkHonhMdJS
...: U1FS/9AxwFD/VQg=')
Out[2]: '\xd9\xeb\x9b\xd9t$\xf41\xd2\xb2w1\xc9d\x8bq0\x8bv\x0c\x8bv\x1c\x8bF\x08\x8b~ \x8b68O\x18u\xf3Y\x01\xd1\xff\xe1`\x8bl$$\x8bE<\x8bT(x\x01\xea\x8bJ\x18\x8bZ \x01\xeb\xe34I\x8b4\x8b\x01\xee1\xff1\xc0\xfc\xac\x84\xc0t\x07\xc1\xcf\r\x01\xc7\xeb\xf4;|$(u\xe1\x8bZ$\x01\xebf\x8b\x0cK\x8bZ\x1c\x01\xeb\x8b\x04\x8b\x01\xe8\x89D$\x1ca\xc3\xb2\x08)\xd4\x89\xe5\x89\xc2h\x8eN\x0e\xecR\xe8\x9f\xff\xff\xff\x89E\x04\xbb~\xd8\xe2s\x87\x1c$R\xe8\x8e\xff\xff\xff\x89E\x08hll Ah32.dhuser0\xdb\x88\\$\n\x89\xe6V\xffU\x04\x89\xc2P\xbb\xa8\xa2M\xbc\x87\x1c$R\xe8_\xff\xff\xffhroX hdMichTren1\xdb\x88\\$\n\x89\xe3h!}X hgainhsMeAh!ItIhherehlloThF{HehTMCT1\xc9\x88L$\x1e\x89\xe11\xd2RSQR\xff\xd01\xc0P\xffU\x08'
I looked up this initial byte sequence — D9 EB 9B D9
— and found a snort rule on Github!
# alert ip $EXTERNAL_NET any -> $HOME_NET any (msg:"INDICATOR-SHELLCODE Metasploit payload windows_messagebox"; content:"|D9 EB 9B D9 74 24 F4 31 D2 B2 77 31 C9 64 8B 71 30 8B 76 0C 8B 76 1C 8B 46 08 8B 7E 20 8B 36 38 4F 18 75 F3 59 01 D1 FF E1 60 8B 6C 24 24 8B 45|"; fast_pattern:only; classtype:shellcode-detect; sid:30472; rev:1;)
So this is a Metasploit payload for a windows message box, huh? I dumped it into a file and was hoping to see something I could disassemble, but it didn’t look like it was going to be that easy :/
$ file shellcode
shellcode: data
So then I decided to do it the hard way, by hand!
$ strings shellcode
;|$(u
hll Ah32.dhuser0
hroX hdMichTren1
h!}X hgainhsMeAh!ItIhherehlloThF{HehTMCT1
RSQR
Let’s split this out a bit…h!}X hgainhsMeAh!ItIhherehlloThF{HehTMCT1
looks like all the h
are gonna be split between words. So, I started piecing it together that every time an h
ocurred, it was time for a new split in the string. There was an h
after every 4 characters! Now we have something like this:
!}X
gain
sMeA
!ItI
here
lloT
F{He
TMCT
1
I think that 1
and X
are junk indicating the front and end of the word or something, so now we can see that this is sorta the flag, but backwards. If we concatenate to the end of the string in order from bottom to top, we get the flag: TMCTF{HelloThere!ItIsMeAgain!}
I definitely need to brush up on my powershell…
~Joel