Ledger is a Cobalt Strike aggressor script that tracks every operational change made during an engagement. Every service you enable, firewall rule you punch through, account you create, or registry key you touch gets logged with a risk score, operator attribution, and cleanup status. At the end of the engagement you have a complete audit trail of what was changed and what still needs to be cleaned up.
Red team engagements leave artifacts. Services get enabled, accounts get created, firewall rules get opened, registry keys get modified. Without a structured log these are easy to forget — especially across long engagements or when multiple operators are working simultaneously.
Ledger gives you a running journal with:
- Risk scoring per entry and per host
- Cleanup tracking so nothing gets left behind
- Per-operator attribution via the Cobalt Strike event log
- Export to JSON or plain text for after-action reports
- Automatic dead-beacon warnings when uncleaned changes are still pending
- Copy
ledger.cnato your Cobalt Strike scripts directory. - In the menu bar: Cobalt Strike → Script Manager → Load → select
ledger.cna. - A Ledger menu will appear in the menu bar.
- The
dirtyalias is now available in every Beacon console.
All commands run from a Beacon console using the dirty alias.
dirty <CATEGORY> <command> [comment] [score]
comment and score are optional. If omitted, sensible defaults are used based on the category.
dirty RPC "sql-enablerpc lon-db-1 lon-db-2"
dirty DEFENDER "add-exclusion C:\windows\temp" "Payload staging" 8
dirty GROUP "net group 'Domain Admins' svc_backup /add /domain"
dirty USER "net user backdoor P@ssw0rd! /add"
dirty SCHEDULED_TASK "schtasks /create /tn updater /tr C:\temp\beacon.exe /sc onlogon"
dirty exec <CATEGORY> <command> [comment] [score]
Logs the change and immediately executes the command on the beacon — one step instead of two.
dirty exec RPC "sql-enablerpc lon-db-1 lon-db-2"
dirty exec USER "net user backdoor P@ssw0rd! /add"
dirty exec DEFENDER "sc config WinDefend start= disabled"
dirty exec FIREWALL "netsh advfirewall set allprofiles state off"
Limitations of dirty exec
dirty exec uses brun internally, which executes a binary directly without a cmd.exe /c wrapper. This means:
- Works for real executables:
net,sc,reg,nltest,whoami,ipconfig, etc. - Does not work for Beacon built-ins:
sleep,cd,ls,pwd,download,upload,execute-assembly,inject, etc. For those, run the Beacon command normally and usedirty <CATEGORY> <command>separately to log it. - Does not work for shell built-ins that require
cmd.exe:dir,echo,set,type, pipes (|), redirects (>), etc. Log those manually withdirty CATEGORY "shell <command>". - Custom BOF aliases loaded from other
.cnafiles will not dispatch correctly throughdirty exec.
dirty show
dirty show DEFENDER
dirty show DESKTOP-ABC123
Shows all logged entries. The optional filter matches against command, comment, category, hostname, and username.
dirty score
Shows a per-host risk summary with aggregate score, pending cleanup count, and a visual bar. Hosts are sorted highest score first.
dirty clean <id>
dirty clean all
dirty clean host <hostname>
Hostname matching is case-insensitive and supports partial matches — DESKTOP matches DESKTOP-FGN4LPG.
dirty export text
dirty export json
dirty export text DEFENDER
dirty export json DESKTOP-ABC
Exports are written to the directory containing ledger.cna and include a host score summary at the top followed by the full change log. The Ledger menu bar buttons trigger unfiltered exports directly.
dirty help
| Category | Default Score | Default Comment |
|---|---|---|
| REGISTRY | 2 | Registry modified |
| FIREWALL | 3 | Firewall rule modified |
| SERVICE | 3 | Service configuration changed |
| RPC | 4 | Changes in RPC made |
| WINRM | 4 | WinRM enabled |
| SMB | 4 | SMB signing changed |
| DCOM | 4 | DCOM enabled |
| RDP | 5 | RDP enabled |
| SCHEDULED_TASK | 5 | Scheduled task created |
| DEFENDER | 6 | Defender configuration changed |
| GROUP | 6 | Group membership modified |
| USER | 7 | User account modified |
| ACL | 8 | ACL modified |
| SPN | 8 | SPN modified |
| DOMAIN_ADMIN | 9 | Domain admin group modified |
| TRUST | 9 | Trust relationship modified |
Any unlisted category is accepted with a default score of 3.
Per entry
| Label | Score |
|---|---|
| LOW | < 5 |
| MEDIUM | 5 – 7 |
| HIGH | 8+ |
Aggregate (per host)
| Label | Score |
|---|---|
| LOW | < 5 |
| MEDIUM | 5 – 9 |
| HIGH | 10 – 14 |
| CRITICAL | 15+ |
When a beacon goes dead with uncleaned ledger entries still pending, Ledger fires a red warning to the Cobalt Strike Event Log visible to all connected operators:
The warning fires once per beacon and resets automatically if the beacon recovers and checks in again. Linked (child) beacons and beacons explicitly killed by an operator are excluded. Warning timing scales with the beacon's sleep setting — a 60s sleep triggers after ~2 minutes, a 5m sleep after ~10 minutes.
LEDGER EXPORT -- 2026-05-21 19:50:34
HOST SCORE SUMMARY
================================================================================
Host Risk Score Pending Bar
--------------------------------------------------------------------------------
DESKTOP-RLP4KBJ (sally *) CRITICAL +21 2 pend [##########]
DESKTOP-FGN4LPG (Admin) CRITICAL +18 0 pend [#########]
================================================================================
CHANGE LOG
================================================================================
[000002] 2026-05-21 19:22:56 | neo | GROUP | MEDIUM +6 | CLEANED
Host : DESKTOP-FGN4LPG
User : Admin
Process : HTTP Listener_x64.exe (PID 4716)
Command : net group 'Domain Admins' svc_backup /add /domain
Comment : Added svc_backup to Domain Admins for lateral movement
{
"exported": "2026-05-21 19:53:19",
"host_summary": [
{
"host": "DESKTOP-FGN4LPG (Admin)",
"risk": "CRITICAL",
"score": 18,
"pending": 0
},
{
"host": "DESKTOP-RLP4KBJ (sally *)",
"risk": "CRITICAL",
"score": 21,
"pending": 2
}
],
"entries": [
{
"id": "000004",
"timestamp": "2026-05-21 19:31:36",
"operator": "neo",
"beacon_id": "1607712302",
"computer": "DESKTOP-RLP4KBJ",
"beacon_user": "sally *",
"beacon_process": "HTTP Listener_x64.exe (PID 11540)",
"functionality": "USER",
"command": "net user svc_backup P@ssw0rd123! /add /domain",
"comment": "Created service account for persistence",
"score": 7,
"risk": "MEDIUM",
"cleaned": false
}Every logged and cleaned entry is broadcast to the Cobalt Strike Event Log via elog, so all connected operators see each other's changes in real time. The journal lives in teamserver memory for the duration of the session.
- The journal is in-memory only and does not persist across teamserver restarts. Export before shutting down.
- Exports are written to the directory containing
ledger.cna. - Score overrides are supported:
dirty USER "net user ..." "my comment" 9— the final argument sets an explicit score. - Loading the script twice (e.g. once via Script Manager and once manually) will register duplicate event handlers. Check Script Manager if you see doubled output.