Home Switchblade To Swiss Army Knife: Expanding The Toolset With Python

Switchblade To Swiss Army Knife: Expanding The Toolset With Python


    A while ago I posted about Switchblade. This was a C2 technique that utilized mutual TLS to authenticate beacons that were compromised and separate them out from other traffic. I won't rehash it too much but it was a cool (in my opinion) way to use Nginx to authenticate beacons with an unusual method and would be considered zero trust since the beacon authenticated to the server and the server authenticated to the beacon.

    Since then, some stuff has changed and some stuff has been updated. Find the details here. Mainly, things could be done easier if the beacon did away with just a simple shell. So the beacon was changed to perform a GET request to the C2 which would have some response options. You can download a file or upload a file, you can inject a DLL into a process, or you can run a command such as cmd.exe or notepad.exe or calc.exe. Limitless potential!

    Let's review the code that's driving this.

The Code

DLL Injection 

    DLL injection is somewhat confusing to a lot of people. They can be used to do a lot of things, but the main purpose is to manipulate programs to change how they execute. For example, KeyFarce is a DLL to dump the unlocked contents of a KeePass database. The code used in the beacon for DLL injection was taken from Grayhat Python and slightly adapted for our needs.

    The only change that we made to the original was that we start a new process and get it's PID instead of injecting into our own main process. The idea behind this is that if injecting the DLL causes the process to crash, at least our beacon won't crash.

    Some may be wondering what these flags set at the start of the function are. More can be found in this Microsoft documentation, but the summary is that PAGE_READWRITE allows us to write to this section of "pages" and pages can be thought of as a virtual section of memory. Then PROCESS_ALL_ACCESS gives you rights to the whole process. Finally, the way the VIRTUAL_MEM flag is set allows us to reserve and commit a space of virtual memory in one go.

    This function is called with:

    inject;{dll name}; null

Command Execution

        Executing commands on another host is the most basic principle of a beacon. We need this functionality to enumerate the domain, determine our IP, move files around, add users, etc.

     Subprocess was chosen over Os.System because it executes commands in the context of a new process which makes some detection more challenging. Os.System just forks a new child process for each command making it much easier to trace back the parent calling. This can be seen if you look in your EDR at the execution chain comparing Os.System to subprocess. Os.System can be traced back to Python calling it while subprocess only shows that the command was run.

    The original intent with using subprocess was to use the DETACHED_PROCESS flag which makes our new process not inherit the parents console. The idea was that this would then start a new process which would not have a parent. This can be seen below.

    The parent is defined as Non-existent process. Cool right? Using the OS library cannot accomplish anything similar to this as far as I'm aware. The DETACHED_PROCESS flag causes issues though when issuing commands like "ipaddress" or "whoami" so the final version we see here has that removed. 

    Below is an example of our beacon running "pwd." The EDR was able to determine that the process "cmd.exe" had called it with the "/C" flag set, and if we scroll over we can see there is a parent process identifying our beacon.

      "pwd" however isn't an inbuilt tool on Windows. This was downloaded and added to the path. Running something like "whoami" produces very different results.

    Unlike with the previous command, there is no parent calling process immediately linked, despite both examples being run the same way. The EDR does give us a parent PID, but we have to query that separately to find out what the parent truly was. To a quick glance, both of the "whoami" being run just look like a standard query being run by the user when one of them was actually run by our beacon.

    This command is called with:

    cmd;{command to run}; null

File Download

    Downloading files to a compromised end point is critical. We can add functionality by downloading different DLLs like KeyFarce or we can update the beacon with new features or signatures.

    Here, we just do a GET to the URL and then write the contents. We need to include the file extension for when we save it.

    This command is called with:

    download;{url};{file extension}

Uploading Files

   Exfiltrating information is also a critical function to have. Let's say you use KeyFarce to dump the KeyPass contents, how are you planning on retrieving that?

    With this function, we get a file path and an upload URL, read it in binary format since that allows requests to determine the Content-length header, and then send it off to the URL.

    This command is called with:

     upload;{url};{file to upload]

The Brains

    Finally, the brains of the beacon.

    The beacon does a GET to the C2 URL and splits the response on semicolons. There are four options pre built in. Cmd, inject, download, and upload. Each command calls their respective function that we reviewed above. For testing purposes, we are posting to httpbin.org/post which helps illustrate how the data is sent.

    The mTLS is performed in the GET request every time it is used. We just need the client cert, key, and the CA cert file loaded either locally on Linux or into the cert chain on Windows.

Why GET Requests?

    A series of GET requests may not be the most graceful way of retrieving commands from our controller, but it does hide itself well among the typical internet traffic you might see. We have changed the user-agent as well to appear more like normal internet traffic, stealing the one FireFox uses. The overall beacon design is very flexible as well. If you don't care about getting responses back, you can just set up a simple web page for commands to be retrieved from and put Nginx in front of it to keep prying eyes away.

Final Thoughts

    There are some issues that are caused by our use of ctypes here. Microsoft identifies this as malicious right away. Then, a lot of EDR vendors will pickup on the CreateRemoteThread flag that we use heavily in the DLL injection portions. VirtualAllocEx will also stand out to a lot of EDR tools but mainly when you allocate memory with read/write/execute permissions and that's never a good idea. What we've done here is allocate the memory with read/write and then we create a new  thread with an entry point to the LoadLibrary function that then points to our DLL. Despite all this, this goes undetected by a fair amount of vendors on Virustotal which isn't too surprising to see. SentinelOne, Microsoft, and Elastic are top of the field when it comes to identifying new and emerging threats.

    Making your own C2 is a lot of fun and there's a ton of learning that comes with it along the way. You have to address how to hide commands being run, how to exfiltrate information, and how deal with commands being fetched. I had played with Empire and Metasploit previously but had not delved into how they handle multiple compromised endpoints too much, so this was a fun opportunity to solve this problem on my own. There were roadblocks with features that should have been implemented, mainly process injection. It would have been nice to get this implemented but if you're going to use ctypes that much, you may as well just use C. It wasn't showcased here either, but handling multiple compromised endpoints is as simple as changing the URL each beacon checks into before compiling into an EXE.


This post is licensed under CC BY 4.0 by the author.