MythicAgents / Apollo

A .NET Framework 4.0 Windows Agent
BSD 3-Clause "New" or "Revised" License
434 stars 90 forks source link

Added inline_assembly Task (Run .NET assemblies in disposable AppDomains) #56

Closed thiagomayllart closed 2 years ago

thiagomayllart commented 2 years ago

Hi! I've added a new file DarkMelkor.cs in the Utils folder, while also modifying the AssemblyManager.cs file to add the inline_assembly function.

Some months ago, @b33f (FuzzySecurity) developed Melkor: https://github.com/FuzzySecurity/Sharp-Suite/. This tool uses CryptProtectData and CryptUnprotectData functions to encrypt and decrypt .NET assemblies in memory, which is a good way to avoid memory scans flagging registered assemblies in the Apollo agent. Melkor also leverages the capability to run these loaded assemblies in disposable AppDomains, which is a good alternative to fork&run tasks in case process injection (at least through some techniques) is not an option.

The only issue with Melkor is the fact that it is not capable of running these Assemblies in case you are doing this in an injected process. Because of that, some days ago I've modified Melkor into this version: https://github.com/thiagomayllart/DarkMelkor.

This version still encrypts and decrypts the .NET assemblies through the crypt32 functions. However, the way it runs is based in this article: https://www.accenture.com/us-en/blogs/cyber-defense/clrvoyance-loading-managed-code-into-unmanaged-processes.

Bryan Alexander and Josh Stone found a way to "bypass" the way the AppDomains segregate the .NET assemblies. They found that it is possible to create two Cross App Domain Delegates: one of them pointing to a function that can be resolved by our disposable AppDomain (generally a function in the mscorlib) and another one pointing to the malicious function (which, in the context of the Apollo agent, will be the one actually invoking the loaded bytes of our registered assembly).

By patching the initial bytes of the function (the one that can be resolved) with the jmp instructions (mov rax, &delegate; jmp rax) to the malicious delegate, it is possible to callback this function in a way that, instead of running the non-malicious one, it will end up jumping to the address of the other one, thus, loading the malicious code.

I've also modified the other functions that handle .NET assemblies in AssemblyManager.cs in order to decrypt the registered assemblies. This might help to avoid some memory scans even when using the fork&run tasks.

Some things to mention:

Since Apollo is compiled for .NET 4.0, I have not added the correct position to patch in case of .NET 4.5 and above. In case that's necessary, i've added these in here: https://github.com/thiagomayllart/DarkMelkor/blob/main/DarkMelkor/DarkMelkor/DarkMelkor/hDarkMelkor.cs (line 126). It seems that in .NET 4.5, the patch position when you are doing this in an injected process is different from when you are doing it by running the compiled Apollo.exe directly. I have not verified these positions in >.NET 4.5.

Currently, the way DarkMelkor modifies the memory regions to RWX is by using the VirtualProtect function. It also leverages RtlZeroMemory by declaring an extern. These problems might be solved by adding ntprotectvirtualmemory and the capability to resolve syscalls dynamically (without dinvoke/without reading ntdll from disk), which solves the problem of having an EDR hooking these functions. I intend to add these in a further request.

Credit goes specially to @b33f (Fuzzy Security), Bryan Alexander and Josh Stone, who actually researched the mentioned techniques, I've just assembled them.

djhohnstein commented 2 years ago

** Also please target the dev branch for your merge.

thiagomayllart commented 2 years ago

Something necessary to mention is another limitation of this functionality: if the .NET assembly tasked to execute contains an Environment.Exit call, it will likely terminate the agent. It is necessary to remove the call from the code to prevent this behavior.

thiagomayllart commented 2 years ago

Updated to this pull request: https://github.com/MythicAgents/Apollo/pull/60