Why do I always use 32-bit payloads? - Cobalt Strike Research and Development
fortra logo

Why do I always use 32-bit payloads?

Yesterday, one of my customers asked about x64 payloads in Cobalt Strike. Specifically, he wanted to know why Cobalt Strike doesn’t expose them. I’ve already replied to the question, but I think it makes an interesting blog post.

Cobalt Strike’s listener management feature pretends that 64-bit payloads don’t exist. Beacon is a 32-bit payload with no 64-bit equivalent. This raises a question. Can I use Cobalt Strike to attack 64-bit Windows systems? Of course, the answer is yes.

Attacking 64-bit Applications on 64-bit Windows

If my attack will land in a 64-bit process, my approach is to make the attack smart enough to “do the right thing” and stage a 32-bit payload. Here are some examples of this:

Cobalt Strike’s Java Attacks inject shellcode into memory through a JNI DLL. The attacks ship with both 32-bit and 64-bit JNI DLLs. This allows the attacks to work from a 32-bit or 64-bit JVM. It doesn’t matter. The DLL is smart enough to spawn a 32-bit process and inject the 32-bit payload stager–even from a 64-bit process.

Cobalt Strike’s PsExec with PowerShell dialogs let you state whether the target system is 32-bit or 64-bit. When the target is a 64-bit system, Cobalt Strike will set the proper Metasploit Framework option to use the 32-bit version of PowerShell to stage the desired payload.

If you’re using psh_web_delivery to stage a payload (a new favorite module), change the one-liner to include the path to 32-bit PowerShell when you’re targeting a 64-bit system. It’ll work.

c:\windows\syswow64\WindowsPowerShell\v1.0\powershell.exe [blah]

While it’s not technically an attack that delivers a payload, Cobalt Strike’s Browser Pivoting feature is smart enough to detect a 32-bit or 64-bit Internet Explorer process. It will inject the right DLL to enable the activity.

Attacking 32-bit Applications on 64-bit Windows

On the flip side, if the application I’m targeting is a 32-bit application, I have to deliver a 32-bit payload to it anyways. 32-bit applications in a 64-bit world are more common than you may initially think.

The default Microsoft Office package is 32-bit. A 64-bit package is available, but Microsoft makes the user jump through a lot of hoops to grab it. Why? According to Microsoft, the 64-bit package offers little benefit to the user at the expense of breaking compatibility with existing add-ons.

office64bit

The Microsoft Office 64-bit Download screen. You must skillfully find and click two small links, away from the normal Download page to even get here.

Similarly, modern versions of Windows include both 32-bit and 64-bit versions of Internet Explorer. The 32-bit version of Internet Explorer is the default for Windows 7. It looks like it’s this way for Windows 8 too. Defaults are one thing, but ground truth reality on your customer’s network is what matters most. Cobalt Strike’s System Profiler will detect if a 32-bit or 64-bit Internet Explorer is in use and make that information available to you.

sysprofiler64bit

Cobalt Strike’s System Profiler run against 64-bit Internet Explorer on 64-bit Windows 7. Notice the *64 next to these applications.

Exploits that target 64-bit Applications

All of that said, you may still want a 64-bit payload to deliver when you attack a 64-bit application. Let’s take a look at the list of Metasploit Framework Windows exploits that support 64-bit targets:

# pwd
/opt/metasploit/msf3/modules/exploits/windows
# grep -r ARCH_X86_64 .
./smb/psexec_psh.rb:          [ 'Windows x64', { 'Arch' => ARCH_X86_64 } ]
./postgres/postgres_payload.rb:        [ 'Windows x86_64',    { 'Arch' => ARCH_X86_64 } ],
./local/ms13_005_hwnd_broadcast.rb:        [ 'Windows x64', { 'Arch' => ARCH_X86_64 } ]
./local/wmi.rb:            [ 'Windows x64', { 'Arch' => ARCH_X86_64 } ]
./local/ms10_092_schelevator.rb:      'Arch'          => [ ARCH_X86, ARCH_X86_64 ],
./local/always_install_elevated.rb:      'Arch'          => [ ARCH_X86, ARCH_X86_64 ],
./local/ikeext_service.rb:          [ 'Windows x64', { 'Arch' => ARCH_X86_64 } ]
./misc/psh_web_delivery.rb:          [ 'Windows x64', { 'Arch' => ARCH_X86_64 } ]
./http/oracle_endeca_exec.rb:      'Arch'        => [ ARCH_X86_64, ARCH_X86 ],
./winrm/winrm_script_exec.rb:      'Arch'          => [ ARCH_X86, ARCH_X86_64 ],

Most of these attacks can deliver a 32-bit payload on a 64-bit system with the right options set (e.g., EXE::Custom, RUN_WOW64, etc.)

Post Exploitation Considerations

There is one place where you need a 64-bit payload though. Some post-exploitation tasks will fail if you attempt them from a 32-bit payload on a 64-bit system. For example, when you run mimikatz from Meterpreter, the current process’s architecture must match the native architecture. That said, you have the option to migrate a 32-bit Meterpreter to a 64-bit process and it’ll magically work.

Now, let’s get back to the original question: why do I always use 32-bit payloads? I use 32-bit payloads because they work in most situations. I’m either targeting a 32-bit application or I’m using an attack that’s smart enough to adjust accordingly. There are few attacks in the Metasploit Framework that exclusively require a 64-bit payload. I can migrate to a 64-bit process if I need to. Right now, I don’t see a benefit to adding 64-bit listeners into Cobalt Strike’s workflow. What are your thoughts?