0x01 核心原理
BeaconEye
的核心原理是通过扫描 CobaltStrike
中的内存特征,并进行 BeaconConfig
扫描解析出对应的 Beacon
信息,项目地址是https://github.com/CCob/BeaconEye
0x02 Beacon
CobaltStrike
的 shellcode
,实际都是通过反射加载的方式加载 Beacon.dll
,而 Beacon.dll
中存在 BeaconConfig
配置信息(主要定义通信目标/通信方式等),在 CobaltStrike
中对应的 Resource
是 sleeve/beacon.dll
0x03 Beacon Config Generate
BeaconConfig
的生成在 BeaconPayload
类的 exportBeaconStage
函数中这上面指向的
Settings
结构体就是 BeaconConfig
,比如 var1
,它代表实际通信的端口最终
CobaltStrike
会将 Settings
转化为 bytes
数组,然后使用固定的密钥进行 Xor
,并对剩余空白字段填入随机字符最后将生成的
beacon.dll
嵌入到最终的 PE
文件中
0x04 Beacon Struct
Settings
的 Add
系列函数,如 AddShort
,并不是简单的将 Short
类型直接追加到 bytes
数组中,而是追加了一个结构体第一个字段是
index
,第二个是 type
(short/int/...),第三个是 length
,第四个则是关键的 value
值,因此根据这个结构即可解析在内存或在文件中的 BeaconConfig
0x05 BeaconEye
接下来让我们看一下 BeaconEye
的 yara
规则32位的
BeaconConfig
规则长这个样子,如果你认真阅读了前文一定会觉得很疑惑,因为按照 Java
当中的结构,它应该分为四个部分
[ ID ] [ DATA TYPE ID ] [ LENGTH OF VALUE ] [ VALUE ]
但是实际的 yara
规则却没有办法对上 java
中的 BeaconConfig
结构,说明 Beacon.dll
在装载的过程中,并没有直接将上述数据 memcpy
分配到堆中,接下来让我们通过对 beacon.dll
进行逆向通过
dllmain
跟进,发现有一个关键函数,里面首先解密了先前 BeaconConfig
的加密数据,然后遍历 BeaconConfig
。首先是在拿到了 Type
之后,直接往堆中分配的内存写入 WORD
长度的 Type
,然后根据 Type
进行判断, case1
对应 Short
, case2
对应 Int
, case3
对应 Data
,所以实际上最终的 BeaconConfig
的结构是
DWORD DWORD
[ DATA TYPE] [ VALUE ]
因此最终的 yara
规则可以解读如下
??
代表通配符,实际匹配的就是 beacon.dll
当中真正的 config
结构体,到这一步,后面的结构体还原就是顺水推舟了
0x06 Bypass
目前网上公开的 Bypass
方式,是没有办法 Bypass
的,原因在于网上的方式更多是修改 Xor
的密钥,但是因为 BeaconEye
是扫描内存,所以 Xor
的密钥并不重要(始终会还原)。因此如果需要在使用 CobaltStrike
但是仍然规避检测的话,只能选择重写 Beacon
,并将结构体特征修改。抑或是通过其他的一些方式使 HIDS/EDR
无法检测,比如使得它们无法 Scan/Dump
内存等
本文始发于微信公众号(redteam101):BeaconEye分析以及Bypass思考
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论