这次文章将较详细介绍下
Cobalt
Strike
4.1
的
agressor
端登录
teamserver
端的通讯和
beacon
端与
teamserver
端通讯,包括元数据、心跳包、执行任务等数据传输。帖子写到一半
IDEA
授权就掉了,所以就拖到现在才写完,
附项目地址:
https://github.com/mai1zhi2/CobaltstrikeSource/
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
方法:
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:
beacon端与teamserver端心跳包通信:
心跳包和发送元数据包的流程是相一致的,都是先复制加密后的数据再
base64
编码最后拼接
cookie
,只是没有再走
sub_1000671C()
函数获取元数据了,只是走
sub_100024F4()
发送数据,数据的内容也是与元数据相一致的。
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
: