User Guide - Part 5 (Report and Logging)

Report and Logging

Logging

Cobalt Strike logs all of its activity on the team server. These logs are located in the logs/ folder in the same directory you started your team server from. All Beacon activity is logged here with a date and timestamp.

Reports

Cobalt Strike has several report options to help make sense of your data and convey a story to your clients. You may configure the title, description, and hosts displayed in most reports.

Go to the Reporting menu and choose one of the reports to generate. Cobalt Strike will export your report as an MS Word or PDF document.

reports_export

Activity Report

The activity report provides a timeline of red team activities. Each of your post-exploitation activities are documented here.

Hosts Report

The hosts report summarizes information collected by Cobalt Strike on a host-by-host basis. Services, credentials, and sessions are listed here as well.

Indicators of Compromise

This report resembles an Indicators of Compromise appendix from a threat intelligence report. Content includes a generated analysis of your Malleable C2 profile, which domain you used, and MD5 hashes for files you’ve uploaded.

Sessions Report

This report documents indicators and activity on a session-by-session basis. This report includes: the communication path each session used to reach you, MD5 hashes of files put on disk during that session, miscellaneous indicators (e.g., service names), and a timeline of post-exploitation activity. This report is a fantastic tool to help a network defense team understand all of red’s activity and match their sensors to your activity.

Social Engineering

The social engineering report documents each round of spear phishing emails, who clicked, and what was collected from each user that clicked. This report also shows applications discovered by the system profiler.

Tactics, Techniques, and Procedures

This report maps your Cobalt Strike actions to tactics within MITRE’s ATT&CK Matrix. The ATT&CK matrix describes each tactic with detection and mitigation strategies. You may learn more about MITRE’s ATT&CK at: https://attack.mitre.org/

Custom Logo in Reports

Cobalt Strike reports display a Cobalt Strike logo at the top of the first page. You may replace this with an image of your choosing. Go to Cobalt StrikePreferencesReporting .

reports_custom-logo-adding1

Your custom image should be 1192x257px set to 300dpi. The 300dpi setting is necessary for the reporting engine to render your image at the right size.

You may also set an accent color. This accent color is the color of the thick line below your image on the first page of the report. Links inside reports use the accent color too.

reports_custom-logo-adding

Custom Reports

Cobalt Strike uses a domain specific language to define its reports. You may load your own reports through the Report Preferences dialog. To learn more about this feature, consult the Custom Reports chapter of the Aggressor Script documentation.

Appendix

Keyboard Shortcuts

The following keyboard shortcuts are available.

Shortcut Where Action
Ctrl+A console select all text
Ctrl+F console open find tool to search the console
Ctrl+K console clear the console
Ctrl+Minus console decrease font size
Ctrl+Plus console increase font size
Ctrl+0 console reset font size
Down console show next command in command history
Escape console clear edit box
Page Down console scroll down half a screen
Page Up console scroll up half a screen
Tab console complete the current command (in some console types)
Up console show previous command in command history
Ctrl+B everywhere send current tab to the bottom of the Cobalt Strike window
Ctrl+D everywhere close current tab
Ctrl+Shift+D everywhere close all tabs except the current tab
Ctrl+E everywhere empty the bottom of the Cobalt Strike window (undo Ctrl+B)
Ctrl+I everywhere choose a session to interact with
Ctrl+Left everywhere switch to previous tab
Ctrl+O everywhere open preferences
Ctrl+R everywhere Rename the current tab
Ctrl+Right everywhere switch to next tab
Ctrl+T everywhere take screenshot of current tab (result is sent to team server)
Ctrl+Shift+T everywhere take screenshot of Cobalt Strike (result is sent to team server)
Ctrl+W everywhere open current tab in its own window
Ctrl+C graph arrange sessions in a circle
Ctrl+H graph arrange sessions in a hierarchy
Ctrl+Minus graph zoom out
Ctrl+P graph save a picture of the graph display
Ctrl+Plus graph zoom in
Ctrl+S graph arrange sessions in a stack
Ctrl+0 graph reset to default zoom-level
Ctrl+F tables open find tool to filter table content
Ctrl+A targets select all hosts
Escape targets clear selected hosts

Beacon Command Behavior and OPSEC Considerations

A good operator knows their tools and has an idea of how the tool is accomplishing its objectives on their behalf. This document surveys Beacon’s commands and provides background on which commands inject into remote processes, which commands spawn jobs, and which commands rely on cmd.exe or powershell.exe.

API-only

These commands are built into Beacon and rely on Win32 APIs to meet their objectives.

cd
cp
connect
download
drives
exit
getprivs
getuid
inline-execute
jobkill
kill
link
ls
make_token
mkdir
mv
ps
pwd
rev2self
rm
rportfwd
rportfwd_local
setenv
socks
steal_token
unlink
upload

House-keeping Commands

The following commands are built into Beacon and exist to configure Beacon or perform house-keeping actions. Some of these commands (e.g., clear, downloads, help, mode, note) do not generate a task for Beacon to execute.

argue
blockdlls
cancel
checkin
clear
downloads
help
jobs
mode dns
mode dns-txt
mode dns6
note
powershell-import
ppid
sleep
socks stop
spawnto

Inline Execute (BOF)

The following commands are implemented as internal Beacon Object Files. A Beacon Object File is a compiled C program, written to a certain convention, that executes within a Beacon session. The capability is cleaned up after it finishes running.

dllload
elevate svc-exe
elevate uac-token-duplication
getsystem
jump psexec
jump psexec64
jump psexec_psh
kerberos_ccache_use
kerberos_ticket_purge
kerberos_ticket_use
net domain
reg query
reg queryv
remote-exec psexec
remote-exec wmi
runasadmin uac-cmstplua
runasadmin uac-token-duplication
timestomp

The network interface resolution within the portscan and covertvpn dialogs uses a Beacon Object File too.

OPSEC Advice

Beacon Object Files use RWX memory by default. Set the startrwx/userwx hints in Malleable C2’s process-inject block to change the initial or final memory permissions.

Post-Exploitation Jobs (Fork&Run)

Many Beacon post-exploitation features spawn a process and inject a capability into that process. Some people call this pattern fork&run. Beacon does this for a number of reasons: (i) this protects the agent if the capability crashes. (ii) historically, this scheme makes it seamless for an x86 Beacon to launch x64 post-exploitation tasks. This was critical as Beacon didn’t have an x64 build until 2016. (iii) Some features can target a specific remote process. This allows the post-ex action to occur within different contexts without the need to migrate or spawn a payload in that other context. And (iv) this design decision keeps a lot of clutter (threads, suspicious content) generated by your post-ex action out of your Beacon process space. Here are the features that use this pattern:

Fork&Run Only

covertvpn
execute-assembly
powerpick

Target Explicit Process Only

browserpivot
psinject

Fork&Run or Target Explicit Process

chromedump
dcsync
desktop
hashdump
keylogger
logonpasswords
mimikatz
net *
portscan
printscreen
pth
screenshot
screenwatch
ssh
ssh-key

OPSEC Advice

Use the spawnto command to change the process Beacon will launch for its post-exploitation jobs. The default is rundll32.exe (you probably don’t want that). The ppid command will change the parent process these jobs are run under as well. The blockdlls command will stop userland hooking for some security products. Malleable C2’s process-inject block gives a lot of control over the process injection process. Malleable C2’s post-ex block has several OPSEC options for these post-ex DLLs themselves. For features that have an explicit injection option, consider injecting into your current Beacon process. Cobalt Strike detects and acts on self-injection different from remote injection.

Explicit injection will not cleanup any memory after the post-exploitation job has completed. The recommendation is to inject into a process that can be safely terminated by you to cleanup in-memory artifacts.

Process Execution

These commands spawn a new process:

execute
run
runas
runu

OPSEC Advice

The ppid command will change the parent process of commands run by execute. The ppid command does not affect runas or runu.

Process Execution (cmd.exe)

The shell command depends on cmd.exe. Use run to run a command and get output without cmd.exe

The pth command relies on cmd.exe to pass a token to Beacon via a named pipe. The command pattern to pass this token is an indicator some host-based security products look for. Read [How to Pass-the-Hash with Mimikatz] for instructions on how to do this manually.

Process Execution (powershell.exe)

The following commands launch powershell.exe to perform some task on your behalf.

jump
winrm
jump winrm64
powershell
remote-exec winrm

OPSEC Advice

Use the ppid command to change the parent process powershell.exe is run under. Use the POWERSHELL_COMMAND Aggressor Script hook to change the format of the PowerShell command and its arguments. The jump winrm , jump winrm64 , and powershell [when a script is imported] commands deal with PowerShell content that is too large to fit in a single command-line. To get around this, these features host a script on a self-contained web server within your Beacon session. Use the POWERSHELL_DOWNLOAD_CRADLE Aggressor Script hook to shape the download cradle used to download these scripts.

Process Injection (Remote)

The post-exploitation job commands (previously mentioned) rely on process injection too. The other commands that inject into a remote process are:

dllinject
dllload
inject
shinject

OPSEC Advice

Malleable C2’s process-inject block block gives a lot of control over the process injection process. When beacon exits an injected process it will not clean itself from memory and will no longer be masked when the stage.sleep_mask is set to true. With the 4.5 release most of the heap memory will be cleared and released. Recommendation is to not exit beacon if you do not want to leave memory artifacts unmasked during your engagement. When your engagement is done it is recommended to reboot all of the targeted systems to remove any lingering in-memory artifacts.

Process Injection (Spawn&Inject)

These commands spawn a temporary process and inject a payload or shellcode into it:

elevate uac-token-duplication
shspawn
spawn
spawnas
spawnu
spunnel
spunnel_local

OPSEC Advice

Use the spawnto command to set the temporary process to use. The ppid command sets a parent process for most of these commands. The blockdlls command will block userland hooks from some security products. Malleable C2’s process-inject block gives a lot of control over the process injection process. Malleable C2’s post-ex block provides options to adjust Beacon’s in-memory evasion options.

Service Creation

The following internal Beacon commands create a service (either on the current host or a remote target) to run a command. These commands use Win32 APIs to create and manipulate services.

elevate svc-exe
jump psexec
jump psexec64
jump psexec_psh
remote-exec psexec

OPSEC Advice

These commands use a service name that consists of random letters and numbers by default. The Aggressor Script PSEXEC_SERVICE hook allows you to change this behavior. Each of these commands (excepting jump psexec_psh and remote-exec psexec) generate a service EXE and upload it to the target. Cobalt Strike’s built-in service EXE spawns rundll32.exe [with no arguments], injects a payload into it, and exits. This is done to allow immediate cleanup of the executable. Use the Artifact Kit to change the content and behaviors of the generated EXE.

Unicode Support

Unicode is a map of characters in the world’s languages to a fixed number or code-point. This document covers Cobalt Strike’s support for Unicode text.

Encodings

Unicode is a map of characters to numbers (code-points), but it is not an encoding. An encoding is a consistent way to assign meaning to individual or byte sequences by mapping them to code-points within this map.

Internally, Java applications, store and manipulate characters with the UTF-16 encoding. UTF-16 is an encoding that uses two bytes to represent common characters. Rarer characters are represented with four bytes. Cobalt Strike is a Java application and internally, Cobalt Strike is capable of storage, manipulation, and display of text in the world’s various writing systems. There’s no real technical barrier to this in the core Java platform.

In the Windows world, things are a little different. The options in Windows to represent characters date all the way back to the DOS days. DOS programs work with ASCII text and those beautiful box drawing characters. A common encoding to map numbers 0-127 to US ASCII and 128-255 to those beautiful box drawing characters has a name. It’s codepage 437. There are several variations of codepage 437 that mix the beautiful box drawing characters with characters from specific languages. This collection of encodings is known as an OEM encoding. Today, each Windows instance has a global OEM encoding setting. This setting dictates how to interpret the output of bytes written to a console by a program. To interpret the output of cmd.exe properly, it’s important to know the target’s OEM encoding.

The fun continues though. The box drawing characters are needed by DOS programs, but not necessarily Windows programs. So, with that, Windows has the concept of an ANSI encoding. It’s a global setting, like the OEM encoding. The ANSI encoding dictates how ANSI Win32 APIs will map a sequence of bytes to code-points. The ANSI encoding for a language forgoes the beautiful box drawing characters for characters useful in the language that encoding is designed for. An encoding is not necessarily confined to mapping one byte to one character. A variable-length encoding may represent the most common characters as a single byte and then represent others as some multi-byte sequence.

ANSI encodings are not the full story though. The Windows APIs often have both ANSI and Unicode variants. An ANSI variant of an API accepts and interprets a text argument as described above. A Unicode Win32 API expects text arguments that are encoded with UTF-16.

In Windows, there are multiple encoding situations possible. There’s OEM encoding which can represent some text in the target’s configured language. There’s ANSI encoding which can represent more text, primarily in the target’s configured language. And, there’s UTF-16 which can contain any code-point. There’s also UTF-8 which is a variable-length encoding that’s space efficient for ASCII text, but can contain any code-point too.

Beacon

Cobalt Strike’s Beacon reports the target’s ANSI and OEM encodings as part of its session metadata. Cobalt Strike uses these values to encode text input, as needed, to the target’s encoding. Cobalt Strike also uses these values to decode text output, as needed, with the target’s encoding.

In general, the translation of text to and from the target’s encoding is transparent to you. If you work on a target, configured to one language, things will work as you expect.

Different behaviors, between commands, will show up when you work with mixed language environments. For example, if output contains characters from Cyrillic, Chinese, and Latin alphabets, some commands will get it right. Others won’t.

Most commands in Beacon use the target’s ANSI encoding to encode input and decode output. The target’s configured ANSI encoding may only map characters to code-points for a handful of writing systems. If the ANSI encoding of the current target does not map Cyrillic characters, make_token will not do the right thing with a username or password that uses Cyrillic characters.

Some command, in Beacon, use UTF-8 for input and output. These commands will, generally, do what you expect with mixed language content. This is because UTF-8 text can map characters to any Unicode codepoint.

The following table documents which Beacon commands use something other than the ANSI encoding to decode input and output:

Command Input Encoding Output Encoding
hashdump UTF-8
mimikatz UTF-8 UTF-8
powerpick UTF-8 UTF-8
powershell UTF-16 OEM
psinject UTF-8 UTF-8
shell ANSI OEM

NOTE:

For those that know mimikatz well, you’ll note that mimikatz uses Unicode Win32 APIs internally and UTF-16 characters. Where does UTF-8 come from? Cobalt Strike’s interface to mimikatz sends input as UTF-8 and converts output to UTF-8.

SSH Sessions

Cobalt Strike’s SSH sessions use UTF-8 encoding for input and output.

Logging

Cobalt Strike’s logs are UTF-8 encoded text.

Fonts

Your font may have limitations displaying characters from some writing systems. To change the Cobalt Strike fonts:

Go to Cobalt Strike → Preferences → Cobalt Strike to change the GUI Font value. This will change the font Cobalt Strike uses in its dialogs, tables, and the rest of the interface.

Go to Cobalt Strike → Preferences → Console to change the Font used by Cobalt Strike’s consoles.

Cobalt Strike → Preferences → Graph has a Font option to change the font used by Cobalt Strike’s pivot graph.

Report-Only Functions

These functions apply to Cobalt Strike’s custom report capability only.

agApplications

Pull information from the applications model.

Arguments

$1 - the model to pull this information from.

Returns

An array of dictionary objects that describes each entry in the applications model.

Example

printAll(agApplications($model));

agC2info

Pull information from the c2info model.

Arguments

$1 - the model to pull this information from.

Returns

An array of dictionary objects that describes each entry in the c2info model.

Example

printAll(agC2Info($model));

agCredentials

Pull information from the credentials model

Arguments

$1 - the model to pull this information from.

Returns

An array of dictionary objects that describes each entry in the credentials model.

Example

printAll(agCredentials($model));

agServices

Pull information from the services model

Arguments

$1 - the model to pull this information from.

Returns

An array of dictionary objects that describes each entry in the services model.

Example

printAll(agServices($model));

agSessions

Pull information from the sessions model

Arguments

$1 - the model to pull this information from.

Returns

An array of dictionary objects that describes each entry in the sessions model.

Example

printAll(agSessions($model));

agTargets

Pull information from the targets model.

Arguments

$1 - the model to pull this information from.

to pull this information from.

Returns

An array of dictionary objects that describes each entry in the targets model.

Example

printAll(agTargets($model));

agTokens

Pull information from the phishing tokens model.

Arguments

$1 - the model to pull this information from.

Returns

An array of dictionary objects that describes each entry in the phishing tokens model.

Example

printAll(agTokens($model));

attack_describe

Maps a MITRE ATT&CK tactic ID to its longer description.

Returns

The full description of the tactic

Example

println(attack_describe("T1134"));

attack_detect

Maps a MITRE ATT&CK tactic ID to its detection strategy

Returns

The detection strategy for this tactic.

Example

println(attack_detect("T1134"));

attack_mitigate

Maps a MITRE ATT&CK tactic ID to its mitigation strategy

Returns

The mitigation strategy for this tactic.

Example

println(attack_mitigate("T1134"));

attack_name

Maps a MITRE ATT&CK tactic ID to its short name.

Returns

The name or short description of the tactic.

Example

println(attack_name("T1134"));

attack_tactics

An array of MITRE ATT&CK tactics known to Cobalt Strike.

https://attack.mitre.org

Returns

An array of tactic IDs (e.g., T1001, T1002, etc.).

Example

printAll(attack_tactics());

attack_url

Maps a MITRE ATT&CK tactic ID to the URL where you can learn more.

Returns

The URL associated with this tactic.

Example

println(attack_url("T1134"));

bookmark

Define a bookmark [PDF document only]

Arguments

$1 - The bookmark to define [must be the same as [&h1] or [&h2]title].

$2 - (Optional) Define a child bookmark [must be the same as [&h1] or [&h2] title].

Example

# build out a document structure
h1("First");
h2("Child #1");
h2("Child #2");
 
# define bookmarks for it
bookmark("First");
bookmark("First", "Child #1");
bookmark("First", "Child #2");

br

Print a line-break.

Example

br();

describe

Set a description for a report.

Arguments

$1 - The report to set a default description for.

$2 - The default description

Example

describe("Foo Report", "This report is about my foo");
 
report "Foo Report" {
   # yada yada yada...
}

h1

Prints a title heading.

Arguments

$1 - the heading to print.

Example

h1("I am the title");

h2

Prints a sub-title heading.

Arguments

$1 - the text to print.

Example

h2("I am the sub-title");

h3

Prints a sub-sub-title heading.

Arguments

$1 - the text to print.

Example

h3("I am not important.");

h4

Prints a sub-sub-sub-title heading.

Arguments

$1 - the text to print.

Example

h4("I am really not important.");

kvtable

Prints a table with key/value pairs.

Arguments

$1 - a dictionary with key/value pairs to print.

Example

# use an ordered-hash to preserve order
$table = ohash();
$table["#1"] = "first";
$table["#2"] = "second";
$table["#3"] = "third";
 
kvtable($table);

landscape

Changes the orientation of this document to landscape.

Example

landscape();

layout

Prints a table with no borders and no column headers.

Arguments

$1 - an array with column names

$2 - an array with width values for each column

$3 - an array with a dictionary object for each row. The dictionary should have keys that correspond to each column.

Example

@cols    = @("First", "Second", "Third");
@widths  = @("2in", "2in", "auto");
@rows    = @(
   %(First => "a", Second => "b", Third => "c"),
   %(First => "1", Second => "2", Third => "3"));
 
layout(@cols, @widths, @rows);

list_unordered

Prints an unordered list

Arguments

$1 - an array with individual bullet points.

Example

@list = @("apple", "bat", "cat");
list_unordered(@list);

nobreak

Group report elements together without a line break.

Arguments

$1 - the function with report elements to group together.

Example

# keep this stuff on the same page...
nobreak({
   h2("I am the sub-title");
   p("I am the initial information");
})

output

Print elements against a grey backdrop. Line-breaks are preserved.

Arguments

$1 - the function with report elements to group as output.

Example

output({
   p("This is line 1
   and this is line 2.");
});

p

Prints a paragraph of text.

Arguments

$1 - the text to print.

Example

p("I am some text!");

p_formatted

Prints a paragraph of text with some format preservation.

Arguments

$1 - the text to print.

** The Format Markup **

  1. This function preserves newlines

  2. You may specify bulleted lists:

* I am item 1
* I am item 2
* etc.
  1. You may specify a heading
===I am a heading===

Example

p_formatted("===Hello World===\n\nThis is some text.\nI am on a new line\nAnd, I am:\n* Cool\n* Awesome\n* A bulleted list");

table

Prints a table

Arguments

$1 - an array with column names

$2 - an array with width values for each column

$3 - an array with a dictionary object for each row. The dictionary should have keys that correspond to each column.

Example

@cols    = @("First", "Second", "Third");
@widths  = @("2in", "2in", "auto");
@rows    = @(
   %(First => "a", Second => "b", Third => "c"),
   %(First => "1", Second => "2", Third => "3"));
 
table(@cols, @widths, @rows);

ts

Prints a time/date stamp in italics.

Example

ts();