Quick Note on PoshC2 In-Memory IOCs

Quick Note on PoshC2 In-Memory IOCs

I’ve been looking at the public version of PoshC2 and how easy it can be to detect, yet it is still exceptionally successful at bypassing a significant number of so-called EDRs and AVs – even more so if the execution method is fairly good – GadgettoJS is still undetected by a lot of AV solutions.

I should probably highlight I am absolutely not a malware analyst, but if I can find these obvious IOCs in two or so hours, hopefully others will be able to do a more comprehensive job and reduce the false positive rate. None of this stuff in this blog is earth shattering, but it highlights just how difficult detection can be.

The Powershell implant makes use of the System.Management.Automation(.ni).dll, which should have fairly low instances of being invoked without the parent process being Powershell.exe or Powershell_ise.exe etc. Tools such as Carbon Black, Tanium etc can very quickly pull out every instance of this DLL being loaded into abnormal processes across the environment. The C# implant also makes use of mscoree.dll.

An example can be seen in the below screenshot.

There are also a number of further in-memory IOCs that PoshC2 has.

Tools such as PE-Sieve (shoutout to @FranticTyping at F-Secure Countercept for the steer to this) can very easily examine particular processes for common injection and hollowing methods. This tool can also generate you some JSON outputs that you can then ingest and manipulate further if needed. It can also output plenty of Olly / IDA compatible outputs, as well as trying to pull out the raw shellcode itself. It seems very powerful, and I’m fairly certain I’ve scratched about 3% of it’s functionality.

Examining a standard PoshC2 executable (or any injected process) in Process Explorer shows the AppDomain of dropper_cs, which you will also notice is present in the /opt/PoshC2_Project/payloads directory of the C2 server. Ben Turner and Doug McLeod expand on AppDomains in their BSides talk here and here.

This PID maps directly to the C# implant shown below.

Interestingly, PE-Sieve also highlights that amsi.dll has been hooked by the suspicious process in the scan report JSON, as well as in the command line output. This refers to the AmsiScanBuffer bypass built into the C# implant.

PE-Sieve can also generate you TAG files that can be read into PE-BEAR, as shown in the below screenshot.

Opening the executable or DLL in PE-Bear (with the TAG file in the same directory), gives you a handy quick route to where the amendments have been made.

You can also potentially investigate binaries running on your Windows hosts that were compiled using GCC – whilst FAR from a reliable detection, it may provide additional weight to any initial investigations of potentially suspicious processes.

The age old ‘This program cannot be run in DOS mode’ IOC is also present (Cobalt Strike MalleableC2 has the strrep entry to replace this string).

It is also possible to see PDB strings from compilation time, sadly the PDB files themselves are not present 😉

PE-Sieve will also attempt to pull the shellcode (using the shellc option) which outputs to an SHC file. You can then see many of the common IOC strings present in that SHC file again. I did try to re-inject the SHC shellcode after renaming it to .bin as a test, but no dice! There is also the classic VoidFunc dll entry point that has been common since PoshC2 began.

A good detection could be along the lines of, rundll32 on a DLL (don’t just filter on file extension though) with an entry point of VoidFunc, with suspicious child processes that subsequently make network connections. Clearly some spoofing techniques will to defeat this detection, but it will raise the bar slightly.

PE-Sieve also has a DLL that supports API interaction, Hollows Hunter is able to interact with this to examine all running processes, rather than just particular processes (PE-Sieve can only target one PID at a time).

Normal injection is easily flagged within Hollows Hunter.

As are RTLCreateUserThread injections (rather than CreateRemoteThread) within the PS implant in PoshC2. There could be some really interesting work around Donut from TheWover, XPN’s work on evading Get-InjectedThread (although PE-Sieve is much more clever) and Gargoyle from F-Secure.

Both the C# and PS implants do not benefit from the MalleableC2 configurations of use_rwx = false, and have plenty of RWX permissions within their host process.

You may also notice that due to patch2 in poshc2/server/Payloads.py (I think, but am not sure as haven’t dug into the source that much!), if you are able to carve out a PoshC2 DLL (or an entire suspicious process dump using PE-Sieve), you will see that the MZ headers are not present. There might be a way of seeing this in a more efficient way, but using PE-Sieve to create a full dump (/minidmp option) the PID, and then searching through the large DMP for the IOC of ‘This program cannot run in DOS mode‘ it is also possible to see that the MZ headers have been scrubbed from the reflectively loaded DLL (thanks to @FranticTyping for the steer).

PBind also has the default pipename IOC of msukpipereader, or in the current version, jaccdpqnvbrrxlaf which is set at the bottom of /opt/PoshC2/resources/payload-templates\pbind.ps1.

There is probably something to be said for seeing multiple requests for /branch-locator (default comms URLs) going out the proxy too. Should be fairly achievable to create alerts based on telemetry on these URLs. If there is an existing rule, please let me know on Twitter.

Nothing ground breaking, but the IOCs are there if you are looking for them :).

Comments are closed.