Apollo 2.0 — New Year, New Features
At the beginning of 2020, I took my first real foray into programming. Inspired by Cody Thomas’s Mythic, I undertook to, what was unbeknownst to me at the time, a massive undertaking in writing my own agent. In truth, one that I wasn’t truly prepared for. I had written a few .NET assemblies here and there, but a .NET developer I was not.
Fast forward one year and four .NET fundamentals courses later, I’m both excited and proud to release Apollo 2.0. This update fulfills the promise of peer-to-peer communications over SMB and TCP, provides SOCKS5 tunneling capabilities, and is architected to allow for dynamic command loading, all of which has been field tested in our very own Adversary Tactics: Red Team Operations lab environment.
What’s ChangedA lot. A lot changed.
The 2.0 release is a complete rewrite of the entire Apollo code base. It didn’t just get a brand new set of paint — it’s an entirely new house. Doing away with the 1.X code base allowed me to refactor every component of the agent, providing:
- Granular Task Management
- Code Clarity for Profiles and Taskings
- Faster Processing Speeds
- Scalable, Unit-Testable Code
- Dynamic API Resolution
- Encrypted File Cache
- Strictly .NET Code
Every component of Apollo has been updated, but the following new features have been implemented:
- Dynamic Command Loading
- SOCKS5 Proxy Tunneling
- Peer to Peer Communication over SMB and TCP
- In-Process Assembly Execution
- Unmanaged Code Execution for Arbitrary Portable Executables (PEs)
Dynamic Command Loading
Apollo now supports command loading at runtime of the agent. In Apollo 1.X, due to the plumbing of the agent, there was no way for Apollo to intelligently “know” or discern how to route taskings if they weren’t compiled with the agent at build.
In the 2.0 release, this is no longer the case. Command loading, tracking, and execution is managed separately from the agent core, and as such new commands can be sent from Mythic and loaded into the agent at runtime. You could create a thin agent with only the “load” command, for example, and dynamically load all other capabilities once execution is achieved on target. Or, say you wish to modify how a task works in the Tasks library. You can now make those changes and deliver an updated version of the task to the agent without having to recompile the agent itself. Even if a new version of Apollo comes out with additional commands, all that’s required is a server-side update. Mythic can then bundle and send the new commands to Apollo to load and execute.Task Loading and Delegation in Action
Lastly, it came to my attention that some commands really are just the same thing execution wise. You spin up a process, you inject some shellcode, and you get the output. Nothing truly special about it. To facilitate that, several commands are typed as “script only,” meaning they don’t have any C# related files on disk by that task name. These tasks simply wrap a more atomic task, and send that atomic task very specific data in its execution. As seen above, I loaded the command “execute_pe,” but this command is a dependency for mimikatz, printspoofer, and more. Even though I just loaded one command into the agent, these commands that specified it as a dependency are now loaded in the agent.
SOCKS5 Proxy Tunneling
Apollo now supports full SOCKS5 proxying into target environments. Previously, SOCKS5 in Apollo 1.X was short-lived, buggy, and disconnected frequently. Now, after taking the time to study different .NET structures and patterns, SOCKS proxying is fast, reliable, and most importantly, consistent. Apollo’s SOCKS functionality has been verified to work across a variety of technologies, such as:
- Remote Desktop Protocol
- Web Browsing (including streaming)
Peer to Peer Communication
Apollo can now reach isolated network segments using peer to peer communications. When Apollo is built, the user has the option to choose from one of several command-and-control profiles to communicate with Mythic or a Mythic peer. Users can disconnect and reconnect these nodes to meet their desired egress path in a network.Nested SMB Communications
In-Process Assembly Execution
Thanks to the help of Thiago Mayllart, it’s now possible to execute assemblies in-process and stream its output back to the user in a separate, disposable Application Domain. This module is based off the Melkor project by Ruben Boone with modifications.In-Process Execution of Seatbelt — No Sacrificial Process Required
Unmanaged Code Execution for Arbitrary PEs
Lastly, arbitrary x64 PE files can be executed in sacrificial processes, in the same way the “execute_assembly” command functioned. Rather than .NET assemblies though, this command takes statically compiled (/MT) unmanaged 64-bit executables. This module was based heavily on the work of Nettitude’s RunPE project.Compiling x64 Binary with /MT for Use in execute_pe
Binaries like mimikatz, printspoofer, spoolsample, and more can all execute through the “execute_pe” command. Theoretically, this loader could be injected into any process to run (think mimikatz in a user’s explorer session), but this remains untested.Executing mimikatz with execute_pe
Development Road Map
Apollo 2.0 delivers on the promises of Apollo 1.X and more, but there’s plenty left to do.
First and foremost, I’d like to implement unit testing. Tasks, C2 profiles, and all fundamental classes Apollo relies on are their own discrete projects. The code base is massive (roughly ~500k lines in 6 months), and unit tests will ensure that each update will not break functionality. Moreover, unit tests will ensure tasks remain bug free and cover all edge cases (where as currently it’s a much more ad-hoc approach).
Additional, Rotating C2 Profiles
Tasks and C2 Profiles are their own separate projects under the Apollo parent solution. As a result, it’s relatively fast and straight forward to develop a new C2 profile for a variety of protocols. At the time of writing, there’s an outstanding pull request for a DNS C2 profile, but in the future web sockets, Slack, and other external channels can all be developed relatively quickly. Lastly, the plumbing exists to egress through a variety of channels, as well as change or add to the existing list of C2 profiles loaded in the agent; however, this was too ambitious for this current sprint as I wanted to get functional parity with Apollo 1.X.
Evasion and Payload Wrappers
The last item on the roadmap is evasive payload wrappers for Apollo and its assorted modules.
At a base level, it should be relatively straight forward to create assembly loaders that patch Event Tracing for Windows, disable AMSI, etc., before loading Apollo. Every sacrificial job in Apollo also leverages the .NET runtime though, and as such would benefit from the same evasive feature set. Integrating these payload wrappers into the build process for “execute_pe,” “execute_assembly,” “powerpick” and otherwise would permit Apollo to execute without introspection from several Windows built-ins.
A more challenging evasion technique though is obfuscation. Apollo task dispatching leverages reflection to invoke taskings from Mythic. When evasion comes into play, class names will get changed and garbled, leading to Mythic tasking a command the agent has garbled internally. I have an idea of how to re-architect this component, but it’s simply not a priority at this time.
Why Did It Take So Long?
Apollo 1.X had many shortcomings. While it demonstrated how great Mythic could be, the code base itself was cluttered, unintuitive, and unmaintainable. There was no place to begin fixing the agent, as the issues were so deeply embedded that the only possible solution was to do away with it and try again. Not only is the prospect of trashing your entire code base demotivating, I lacked the proper knowledge on how to architect a sustainable project.
Fast forward 8 months later, I was able to recapture my passion for this project thanks to the likes of Cody Thomas and Thiago Mayllart. With the help of Pluralsight courses and a steady supply of supreme pizzas Apollo 2.0’s bones were laid in a few short weeks. Follow that with a couple months of plumbing and polish in the midnight hours, and the result is what you see today.
Apollo 2.0 delivers on the promises when it first released. Mythic finally has an agent that takes advantage of all the features it advertises: SOCKS5 pivoting, dynamic command loading, peer to peer communications, and more. As much as I hate to admit it, this has been a labor of love, and I hope it shows as such.