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.
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 Strike → Preferences → Reporting .
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.
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.
Returns
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 **
-
This function preserves newlines
-
You may specify bulleted lists:
* I am item 1
* I am item 2
* etc.
- 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();