Previously, I wrote a blog post to answer the question: why is notepad.exe connecting to the internet? This post was written in response to a generation of defenders zeroing in on the notepad.exe malware epidemic that was plaguing them. Many offensive actions require spawning a new process to inject something into. In the Metasploit Framework (and ancient versions of Cobalt Strike), notepad.exe was the default process to spawn for these actions.
Today, rundll32.exe is the process Cobalt Strike will spawn when it needs a one-off process to inject something into. I’ve had many people write and ask: “Raphael, why rundll32.exe?” Others ask, “how do I switch from rundll32.exe to something else?” This blog post aims to answer these questions.
Several of Cobalt Strike’s user-driven attacks automatically migrate the payload stager to a new process and then run it. I do this for two reasons:
First, the user-driven attack might land code execution in an x64 process. We can’t run an x86 payload in an x64 process. The solution here is to migrate. Cobalt Strike’s Java Applet attacks and the Microsoft Office macro attacks both migrate to rundll32.exe (by default).
Cobalt Strike’s user-driven attacks migrate for another good reason. What happens if the user closes the application we used to get code execution? If our payload ran within that application, our access would go away with it. If our payload lives elsewhere, our access is safe. This is another reason Cobalt Strike’s attacks migrate.
So, why rundll32.exe? Why not something else? Honestly, it doesn’t matter what I pick. Anything I pick is now the default. Because people rarely change defaults, it will show up enough that someone will notice. The right thing here, for all parties, is to know how to change the defaults. Fortunately, this isn’t too hard to do.
Cobalt Strike does not provide a way to override the default macro attack. Fortunately, its choice of rundll32.exe is a string inside of the macro that you can edit. If this choice does not work for you, change this to another process. Many times, I have edited Cobalt Strike’s VBA macro to spawn Internet Explorer and inject my stager into it. I found this was necessary for security postures that restricted which applications could make outbound connections.
The Java Applet is also easy to fix. If you’re using the Java Signed Applet attack with Cobalt Strike, chances are you’re familiar with the Applet Kit. This is the source code to Cobalt Strike’s Java Applet attack and the scripts necessary to build it. You’ve probably downloaded this kit to sign Cobalt Strike’s Applet with your code signing certificate. If you want the Java Applet to migrate elsewhere, edit src/injector.c, change rundll32.exe to something else, and rebuild the Applet Kit. This will require that you have the mingw-w64 package installed.
Executable and DLL Artifacts
Cobalt Strike’s options to export an x64 DLL to deliver an x86 Beacon also migrate to rundll32.exe. I do this for good reason. I can’t host the x86 Beacon inside of an x64 process! Again, the answer here is to migrate and I migrate to a default: rundll32.exe.
Cobalt Strike also generates executables that respond to commands from the Windows Service Control Manager. Cobalt Strike uses these executables with its psexec command and it lets you export them as well. These service executables automatically migrate your payload or stager. Why? I do this to make the service easier to cleanup. In the case of psexec, I can’t get rid of the executable until it stops running. If the service executable didn’t migrate, Cobalt Strike’s psexec command would have to wait until your session stopped to clean up the executable it put on target. That’s no good! This is why the service executables migrate.
Fortunately, changing the rundll32.exe indicator is pretty easy to do as well. Cobalt Strike allows users to change it process to generate executables and DLLs. This is possible through the Artifact Kit. The Artifact Kit is source code to Cobalt Strike’s executable/DLL templates and it’s a script to override Cobalt Strike’s internal process to patch shellcode into these templates.
If you edit src-common/patch.c, you can change the migrate process from rundll32.exe to something else. Rebuild the artifact kit, load its script into your Cobalt Strike client, and from that point on—you’re free of rundll32.exe in your service executable and x64 DLL artifacts.
rundll32.exe rears its ugly head in other places too. A favorite workflow in Cobalt Strike is the ability to right-click a session, select Spawn, and send a session to another listener. This command spawns a process and injects a payload stager for the chosen listener into it. I spawn a process because stagers do crash from time to time. Injecting the stager into another process protects your access from that crash.
When Beacon spawns an executable for session passing, which one does it spawn? Why our friend, rundll32.exe. Of course!
You may ask, how do I change this? There are a few answers to this question. The first answer is to reconsider your use of the spawn command. The spawn command creates a child process off of your Beacon process. This child process makes outbound network connections. If a hunt team is watching process creates and network connections, your access will stand out like a sore thumb. I recommend using the inject command to pass sessions instead.
That aside, let’s say you want to continue to use the spawn command. Your choice! Here’s how to move away from rundll32.exe: First, you may change which command Beacon spawns with the built-in spawnto command. This command will change the spawn process for that Beacon instance to something else.
You may also change the default for all of your Beacon sessions with a Malleable C2 profile. Malleable C2 is Cobalt Strike’s technology to allow you to change indicators and behaviors in the Beacon payload. It’s quite handy if you want to make Beacon look like other malware or blend-in to look like something totally innocuous. Malleable C2 has an option, spawnto, that changes this default to something else.
Post Exploitation Jobs
Let’s cover the last place rundll32.exe likes to show itself, post-exploitation jobs. Beacon is a very small payload. It’s single threaded. It’s designed to do a few very simple things. It calls home, it executes a few base things, and it monitors jobs.
Post-exploitation features such as hashdump, mimikatz, screenshots, keystroke loggers, and others run as jobs. In Beacon parlance, a job is a post-exploitation task that lives in another process. This design serves a few purposes. First, it makes it possible for you to inject a capability (e.g., the screenshot tool) into a process of your choosing. This allows you to get results from the right place without migrating your access. That’s nice! Second, some post-exploitation tasks absolutely must run from a process that matches the operating system’s architecture. This scheme allows an x86 Beacon to seamlessly run x64 post-exploitation jobs without any bother to the operator. Things just work! Third, this scheme protects your access. If, for some reason (heaven forbid!) a post-exploitation task were to crash, this scheme isolates your access from that failure.
Anyways, you have the option to inject some jobs into a process of your choosing. Others jobs just kick off a process, inject the capability, let it run, get results, and tear the process down. These jobs that kick off a process happen to spawn, our old friend, rundll32.exe.
You may ask, how do I change this? Fortunately, there’s not a lot of new advice to offer here. Post-exploitation jobs use the same spawnto process that the spawn command uses. If you edit your Malleable C2 profile to ask that Beacon spawn another placeholder, your post-exploitation jobs will use this placeholder as well.
There is one caveat here. The spawnto command only affects which x86 process the x86 Beacon kicks off. If x86 Beacon has to kick off an x64 process, it doesn’t change this. I do not have a means to change the x64 spawnto process, yet. I’ll take care of this.
And, for the sake of completeness: the spawnto command does not affect which x86 process the x64 Beacon kicks off, when it needs to run an x86 job. If x64 Beacon has to kick off an x86 process, it will use rundll32.exe. I do not have a means to change this yet either. Again, this is one of those todo items.
You’re now empowered!
This post was a lovely stroll through the migrate and spawning behavior of Cobalt Strike 3.0 and later. There are three take-aways for this post:
1. Cobalt Strike migrates stagers and tasks to other processes. It does this a lot. Usually for good reasons!
2. The default process Cobalt Strike migrates to is rundll32.exe.
3. You have the power to change this behavior in most cases.