专栏名称: HACK学习呀
HACK学习,专注于互联网安全与黑客精神;渗透测试,社会工程学,Python黑客编程,资源分享,Web渗透培训,电脑技巧,渗透技巧等,为广大网络安全爱好者一个交流分享学习的平台!
目录
相关文章推荐
HACK学习呀  ·  交易实战 | ... ·  3 天前  
51好读  ›  专栏  ›  HACK学习呀

免杀干货 | 魔改CobaltStrike,让杀软和天眼流泪

HACK学习呀  · 公众号  · 黑客  · 2021-04-22 14:29

正文



如何魔改CobaltStrike


1
概述

这次文章将较详细介绍下 Cobalt Strike 4.1 agressor 端登录 teamserver 端的通讯和 beacon 端与 teamserver 端通讯,包括元数据、心跳包、执行任务等数据传输。帖子写到一半 IDEA 授权就掉了,所以就拖到现在才写完, 附项目地址: https://github.com/mai1zhi2/CobaltstrikeSource/


2
aggressor端登录teamserver端的通讯分析

2.1 agressor 客户端发送密码登录 teamserver 端进行验证:

运行 Teamserver 端,首先会创建 SecureServerSocket 对象,然后循环调用 acceptAndAuthenticate() 接受认证信息:

点击 connet 按钮后:

Aggressor 端首先在 dialogAction() 方法获得相应的登录信息:

然后传入 host port 创建安全套接字:

SecureSocket 的构造方法中设置了相关的 socket 属性 , 调用 createSocket() 传入要连接的 ip 地址和 port:

agressor 端调用完 createSocket() 后, teamserver acceptAndAuthenticate() 会接受到信息,其中跟入 authenticate(Socket var1, String var2, String var3) 方法负责处理接受到的信息:

跟入该方法,当 var4 没读取到足够的字符就一直阻塞:

Aggressor 端构建完 SecureSocket 对象后,调用其 authenticate(string var1) 方法,并传入登录的密码,该方法主要通过密码构造相应的数据包,其数据包先传入 48879 这个数值(标志),然后再传密码的长度,接着传入密码的内容,最后以 0x41 进行填充:

数据包 var3 内容:

var3.flush() 调用向 server 端发送数据, teamserver authenticate(Socket var1, String var2, String var3) 方法就会停止阻塞,接受到相应的数据并对密码进行判断:

密码相等 则发送 51966 标志数给 aggressor 端:

然后返回执行 PostAuthentication 对象实现的 clientAuthenticated():

新建线程,等待后续接受 aggressor 端所发送的信息:

aggressor 端接受到后进行判断:

至此 teamserver 端校验 aggressor 端密码完毕。


2.2 aggressor.authenticate 消息类型传输:

接着 Aggressor 端再通过 SecureSocket 对象构造出 TeamSocket 对象:

传入 TeamSocket 对象来创建 TeamQueue 对象,其中 TeamReader TeamWriter 均为 TeamQueue 的内部类,均实现 Runnable 接口负责与 TeamServer 通信:

然后调用 TeamQueue call() 方法,把消息类型 aggressor.authenticate 、用户名、密码、版本信息均传入,再把请求通过 TeamWriter 属性发送给 teamserver 端:

TeamServer 端收到 aggressor aggressor.authenticate 类型请求数据包:

跟入 process() ,先比较数据包的类型,再判断版本号,接着校验密码和是否重复登录,最后向 aggressor 回复 success:

最后新建名为 write for nickname 的线程向各个客户端回复消息:

Aggressor 端收到 Success 回复:


2.3 aggressor. metadata 消息类型传输:

Aggressor 端继续向 teamserver 端发送: aggressor.metadata 类型请求 :

TeamServer 端收到 aggressor.metadata 请求后,调用 process() 方法 :

继续跟入 process() ,该函数主要用 hashmap 存放相关的数据,最后向 aggressor 端返回了 aggressor.matedata 类型和 hashmap 数据 :

aggressor 客户端接收 teamserver 端返回的 aggressor.matedata 类型和 hashmap 数据 :

跟入 processread() 函数,函数主要获取消息包中数据类型和数据 :

再跟入 result() ,可见该函数负责处理从 teamserver 端返回的 aggressor.authenticate aggressor.metadata 类型的信息 :

传入 metadata 数据 hashmap var2 来构造 AggressorClient 对象,跟入该对象的构造方法:

继续跟入 setup() 方法,该方法主要是注册各类 Bridge 对象,储存 teamserver 端返回的 metadata ,加载插件框,以及发送 aggressor.ready teamserver 告知其准备完。

最后调用该对象的 showTime() ,至此显示出主界面,后续同步相关数据 :


下面图是 360 空间测绘的,顺手帮他们改了图中的小错误:


至此,我们知道了 cs agressor 端登录 teamserver 端通讯方式,所以可以进行相应简单的修改来规避 cs 登录爆破:

teamserver 端避免使用默认的端口号 50050

如果修改 48879 这个标识数,需要修改的地方有三个,涉及到修改 aggressor 端:

SecureSocket authenticate 方法:

AsymmetricCrypto decrypt() 方法:

SecureServerSocket authenticate 方法:



3
beacon端与teamserver端第一次数据包通信(元数据)

我们先生成不分段的后门 beacon.exe ,因为不分段的 beacon 更好调,其通信过程都一样的。

当启动 teamserver 后,会把 cobaltstrike.beacon_keys 反序列化为 Keyparis 对象:

然后把该对象传入 AsymmetricCrypto 构造函数中,并把 publickey privatekey 保存为 AsymmetricCrypto 对象中相应的属性:

所反序列化出来的公私钥:


公钥会在后门生成时放进去,但不是在上述的函数中,详见之前发的后门生成的帖子。而私钥则是为后续通信做加解密处理准备的。


下面我们继续操作:先直接上 x32dbg createthread() 下断,查看线程调转的地方再相应下断,然后执行到反射 dll ,再等所导出的解析函数解析完 dll 后,再通过导入表搜相关的通信函数,因为这次文章主要是协议分析,相关中间的相关功能先省略掉,有必要再从栈回溯查看相关的函数调用,然后我们看到相关的通信函数: winnet HttpOpenRequestA HttpSendRequestA InternetCloseHandle InternetopenA 等,还有 ws2_32 socket send recv accept 等,通过观察 ws2_32 用的是 select 网络模型,则先排除掉:

所以从 winnet 的相关 api 下断入手,

程序先在 InternetOpen() 函数断下,传入相关的 agent 信息:

接着 InternetConnect() 函数,传入相关的 url:192.168.202.1 port:80

httpopenRequestA() 函数, get 请求, uri /pixel.gif

httpSendRequest() 发送请求,注意 cookie 的值经过编码:

Wireshark 所捕捉到的数据包:

可见 cookie 对应的数据经过编码或加密的,我们往上翻找到相应的函数 sub_1000783E() ,跟进该函数,可见函数有 16 个不同的 case ,当发送第一个数据包(元数据)时先后走 case7 case3 case7 只是复制操作:

Case3 是自实现的 base64


根据所编码的内容 &unk_100398A8 继续找相应的赋值和加密操作 sub_1000671C() ,在该函数中,收集受害者主机的代码页、时间戳、程序 id 等信息并生成 hash 值:

生成 hash:

最后调用 sub_1000969e() 函数,对收集的信息进行 rsa 加密:

用来加密的 RSA 公钥:

RSA 加密前所收集的数据内容:

RSA 加密后的数据内容:

至此, beacon.exe 端第一次发包(元数据)分析完,流程总结为以下:

收集数据 ->RSA 加密 ->base64 编码


下面开始分析 Teamserver 端接受 beacon.exe 元数据的请求:

跟入 _server() ,该函数经过 Useragent 等判断后,先把 url method 等相关参数传入 webservice.serve() 进行构造相关响应头并返回,其中 MalleableHook 实现了相关的 webservice 接口,跟入 MalleableHook.serve()

在该方法中调用了 GetHandler.serve() 并把相应参数传入,继续跟入,该函数中先解析出相应的 URI method 等:

我们先跟入 BeaconHTTP.this.c2profile.recover() 函数,在该函数中先将 cookie 数据拿出来:

然后再进行 base64 解码:

解码完成后,会判断解码后数据的长度,接着传入被加密的数据到 process_beacon_metadata() 进行解析,继续跟入:

跟入 decrypt() ,首先把反序列化处 RSA 私钥,然后对原数据解密,接着判断标志数是否为 48879 ,如果不相等则解密失败,然后再判断数据长度,如果大于 117 则失败:

回到 process_beacon_metadata() ,解密完后先读取前 16 字节备用,然后用 16-20 字节判断 windowscharts

ip windowscharts listen 地址、截取后的元数据传入到 BeaconEntry() 的构造方法进行解析:

跟入该方法,方法中先跳过前 20 字节,然后获取到 id pid port 、系统位数、受害者 ip 等信息:

构造完后返回到 process_beacon_metadata() 函数,再传入元数据中 hash 值给 registerkey() 该函数是注册 AESkey HmacSHA256key 的,对传入的 hash 值进行一次 sha256 ,然后前一半作为 AESkey ,后一半作为 hmacSHA256key

最后构造 response 数据包返回给 beacon.exe:


4
beacon端与teamserver端心跳包通信:

心跳包和发送元数据包的流程是相一致的,都是先复制加密后的数据再 base64 编码最后拼接 cookie ,只是没有再走 sub_1000671C() 函数获取元数据了,只是走 sub_100024F4() 发送数据,数据的内容也是与元数据相一致的。


5
beacon端与teamserver端执行所需任务的通信

下面介绍 Beacon.exe 接受 teamserver 端的任务数据传输:

5.1 teamserver 端怎么发送任务给 beacon 的?

当每次心跳结束后, teamserver 端都会把相应的任务加入响应包中发送给 Beacon.exe

首先会根据 beaconEntry id 号获取到任务队列中的 data socks

然后把 beaconEntry id 号和 data 数据传入 encrypt() 函数进行加密,跟入 encrypt() 函数,函数中先通过 beaconEntry id 号拿到 AESkey hmackey

获取当前系统时间 /1000 data 长度、 data 数据并将其写入 var3 ,然后进行 aes 加密,密钥为 beaconenrty 中保存好的 :

对其加密后的数据进行 hmac

hmac 16 字节拼到加密后的数据后,并返回:

最后发送给 beacon.exe







请到「今天看啥」查看全文