1. 项目概述:一次从CTF到实战的流量解密之旅
最近在复盘一道CTF题目时,遇到了一个典型的加密Webshell流量分析场景,目标是要从一堆看似杂乱无章的TCP流里,找到攻击者留下的“Flag”。这道题的核心,是解密一个由“哥斯拉”4.0客户端生成的加密通信流量。对于很多刚接触安全分析或者CTF流量分析的朋友来说,看到Wireshark里那些被加密的HTTP POST数据包,常常会感到无从下手。其实,只要掌握了正确的密钥和加密方式,我们完全可以用Wireshark内置的功能,像剥洋葱一样,一层层还原出攻击者的原始操作指令。这不仅仅是解一道题,更是一次深入理解加密Webshell通信原理、掌握实战流量解密技巧的绝佳机会。无论你是CTF爱好者、安全运维人员,还是对Webshell检测技术感兴趣的研究者,通过这次手把手的演练,你都能获得一套可直接复用于真实网络取证分析的方法论。
2. 核心原理:哥斯拉4.0的流量加密机制拆解
要解密流量,首先得知道它是怎么被加密的。哥斯拉(Godzilla)作为一款经典的Webshell管理工具,其4.0版本在通信安全上做了显著加强,其加密机制是解密成功与否的关键。
2.1 动态密钥与加密流程
哥斯拉4.0默认采用AES加密算法,但其核心秘密在于“动态密钥”。它并非使用一个硬编码的密码,而是由客户端和服务器端(即Webshell)通过一个种子(key)和一段固定的盐(salt),利用MD5哈希动态生成每次会话的加密密钥和初始向量(IV)。具体流程如下:
- 密钥协商:客户端在发起第一个请求时,会携带一个
pass参数,这个pass值就是密钥种子。Webshell端收到后,会使用同样的算法在本地生成相同的密钥。 - 密钥派生:密钥和IV的生成公式通常为:
KEY = md5(pass + salt)[:16],IV = md5(pass + salt)[16:]。这里的salt是一个在客户端和Webshell脚本中预先定义好的固定字符串。通过MD5生成32位哈希值,前16位作为AES-128的密钥,后16位作为CBC模式的IV。 - 数据封装:需要传输的原始指令(如
whoami)或命令执行结果,会先经过一次base64编码,然后再用上面生成的KEY和IV进行AES-128-CBC加密。加密后的二进制数据,通常会再次进行base64编码或直接以二进制形式放入HTTP POST的Body中传输。
这种设计使得即使你抓到了流量包,如果不知道pass和salt,也无法直接解密。在CTF题目中,pass和salt往往隐藏在Webshell的源码、客户端的配置文件,或者通过其他隐写手段给出。
2.2 流量特征识别
尽管内容加密,但哥斯拉的流量仍有一些外部特征可循,帮助我们快速定位:
- HTTP POST请求:几乎所有操作都通过POST请求完成。
- URL路径与参数:可能包含特定的路径,如
/admin.php、/index.php,并带有pass等参数。 - Cookie:哥斯拉可能会设置一个特定的Cookie,如
PHPSESSID的值可能有固定模式。 - User-Agent:有时会使用默认或特征明显的User-Agent字符串。
在Wireshark中,我们可以使用过滤表达式,例如http.request.method == POST && http contains “pass”,来快速缩小目标数据包的范围。
3. 实战准备:Wireshark环境与关键数据提取
工欲善其事,必先利其器。在开始解密前,我们需要确保Wireshark配置得当,并精准定位到需要解密的核心流量。
3.1 Wireshark配置与插件检查
首先,确保你使用的是较新版本的Wireshark(3.6以上版本功能更完善)。对于解密操作,一般无需额外插件,但我们需要熟悉两个关键功能的位置:
- “编辑” -> “首选项” -> “协议”:这里是配置各种协议解密规则的地方,我们稍后会用到。
- “统计” -> “会话”:这是分析TCP/UDP流,还原完整通信过程的神器。
一个常被忽略但极其有用的设置是调整TCP重组选项(在“首选项”->“协议”->“TCP”中),确保“Allow subdissector to reassemble TCP streams”被勾选,这能让HTTP等应用层协议更完整地解析数据流。
3.2 定位与提取加密载荷
假设我们已经通过过滤找到了疑似哥斯拉流量的POST请求包。
- 跟随TCP流:右键点击该数据包,选择“追踪流” -> “TCP流”。这时会弹出一个新窗口,以纯文本形式展示该TCP连接的所有通信数据。通常,你会看到客户端发送的一坨
base64编码的密文(或乱码),以及服务器返回的同样被加密的响应。 - 保存原始数据:在“追踪TCP流”的窗口底部,将显示格式改为“原始数据”。然后复制整个显示的内容(包括客户端发送和服务器返回的数据),保存到一个文本文件中,例如
raw_stream.txt。这里有一个关键点:我们通常需要分别解密请求和响应。因此,更稳妥的做法是,在窗口左上角选择“仅显示发送的数据”或“仅显示接收的数据”,分别复制客户端发送的密文和服务器返回的密文,保存为两个文件,如client_raw.bin和server_raw.bin。注意,如果数据在TCP流窗口里显示为base64字符串,则需要先将其解码为二进制文件。可以使用Python或在线工具进行转换。 - 确定加密模式与参数:这是最关键的一步。你需要从题目描述、Webshell源码或客户端配置中明确:
- 加密算法:通常是
AES。 - 密钥(KEY):16字节的字符串。
- 初始向量(IV):16字节的字符串。
- 加密模式:通常是
CBC。 - 填充方式:通常是
PKCS7。 - 数据编码:加密后是
base64还是hex或直接是二进制。
- 加密算法:通常是
注意:在“追踪TCP流”窗口看到的数据,可能已经经过了一次
base64解码(Wireshark自动做的)。所以,你复制出来的“原始数据”可能是解密前的二进制乱码,也可能是base64字符串。务必结合数据包详情栏中HTTP协议层Line-based text data的内容进行对比判断。最可靠的方法是,直接从数据包详情中,找到HTTP->Hypertext Transfer Protocol-> 展开[truncated]...或者直接查看底层的TCP段数据,右键选择“复制” -> “...as a Hex Stream”,将其保存为十六进制文本,再借助脚本转换为二进制文件。这能确保你拿到最原始的传输载荷。
4. 核心操作:在Wireshark中配置并执行解密
拿到密钥和加密参数后,我们有两种主要解密方式:一是使用外部脚本解密提取出的数据;二是直接在Wireshark中配置解密规则,让Wireshark实时解密并解析流量,后者更直观、高效。
4.1 方法一:使用Python脚本进行离线解密
这是一种非常灵活且可控的方法。假设我们已经从流量中提取出了客户端发送的密文二进制文件client_encrypted.bin,并且已知:
- KEY:
0123456789abcdef(16字节示例) - IV:
fedcba9876543210(16字节示例) - 算法: AES-128-CBC
- 填充: PKCS7
我们可以编写一个Python脚本进行解密:
from Crypto.Cipher import AES from Crypto.Util.Padding import unpad import base64 # 读取加密的二进制数据 with open('client_encrypted.bin', 'rb') as f: ciphertext = f.read() # 定义密钥和IV key = b'0123456789abcdef' # 替换为实际KEY iv = b'fedcba9876543210' # 替换为实际IV # 创建AES解密器 cipher = AES.new(key, AES.MODE_CBC, iv) # 解密并去除填充 try: decrypted_padded = cipher.decrypt(ciphertext) decrypted_data = unpad(decrypted_padded, AES.block_size) # 哥斯拉通常会将原始指令先base64再加密,所以解密后可能需要再次base64解码 original_command = base64.b64decode(decrypted_data).decode('utf-8', errors='ignore') print("解密后的原始指令或数据:") print(original_command) except Exception as e: print(f"解密失败: {e}") # 如果失败,直接输出解密后的字节,可能本身就是可读的 print(decrypted_padded.decode('utf-8', errors='ignore'))运行脚本后,你很可能就能看到攻击者执行的系统命令,如whoami、ipconfig或cat /flag等。
4.2 方法二:在Wireshark中配置SSL/TLS解密规则(模拟)
Wireshark没有直接的“AES-CBC解密”协议选项,但我们可以巧妙地利用其SSL/TLS解密功能来模拟这个过程。因为SSL/TLS在记录层也使用对称加密(如AES-CBC),我们可以将哥斯拉的通信“伪装”成SSL/TLS流量来解密。
操作步骤如下:
生成SSL密钥日志文件:Wireshark支持通过一个记录
(Pre)-Master-Secret的日志文件来解密SSL/TLS流量。对于哥斯拉,我们需要创建一个特定格式的文本文件(例如godzilla_keys.log)。但这里我们不是记录真正的TLS密钥,而是“告诉”Wireshark一个固定的对称密钥。 实际上,更直接的方法是使用Wireshark的“协议首选项”来配置一个静态的RSA私钥(虽然不对,但目的是触发解密流程)。然而,对于自定义的AES-CBC,更通用的方法是使用udpdump格式的密钥文件。创建一个文本文件,内容如下:# Godzilla AES-128-CBC Static Key KEY 0123456789abcdef fedcba9876543210第一行是注释,第二行格式为
KEY <16字节Hex密钥> <16字节Hex IV>。将密钥和IV转换成十六进制字符串。例如,如果KEY是字符串this_is_godzilla,其Hex表示为746869735f69735f676f647a696c6c61。在Wireshark中配置:
- 打开Wireshark,进入“编辑” -> “首选项”。
- 在左侧找到并展开“协议”。
- 在协议列表中找到并点击“TLS”(旧版本可能是SSL)。
- 在右侧的“TLS”配置页面中,找到“(Pre)-Master-Secret log filename”选项。
- 点击“浏览”,选择你刚才创建的
godzilla_keys.log文件。
设置解密端口与协议(关键步骤):
- 仍然在“TLS”首选项页面,点击下方的“编辑...”按钮,配置“RSA keys list”。
- 我们需要添加一个条目,但目的不是提供RSA密钥,而是“欺骗”Wireshark尝试对特定端口的TCP流应用解密。可以任意指定一个IP、端口和协议(如http),并选择一个无关的PEM文件。这一步有时可以省略,核心是上一步的密钥日志文件。
- 更有效的方法:由于哥斯拉流量是HTTP协议承载的,Wireshark可能不会对其应用TLS解密。一个变通方案是,在抓包或分析时,修改数据包的解析方式。右键点击目标TCP数据包 -> “解码为...” -> 在“当前”列中,将该TCP流或端口号的协议从“HTTP”临时改为“TLS”。这样Wireshark就会尝试用我们配置的密钥去解密这个端口的流量。
查看解密结果:配置完成后,回到主界面,重新加载或者直接查看数据包。如果配置成功,原本显示为
TCP或HTTP的应用数据部分,可能会被解析为TLSv1.2 Application Data,并且其下方会展开一个[Decrypted SSL data]的标签页,里面就是解密后的明文HTTP请求!你可以看到原始的POST参数和内容。如果没有自动解析,可以尝试在数据包详情栏的TCP或SSL协议上右键,选择“解密SSL数据...”,手动选择密钥文件。
实操心得:方法二的成功率取决于Wireshark版本和具体流量特征,有时会比较“玄学”。如果方法二不成功,强烈推荐使用方法一(脚本解密)结合“追踪TCP流”,这是最可靠、最直接的方式。将解密后的明文,替换到TCP流窗口的原始数据中对比查看,能清晰还原整个攻击交互过程。
5. 案例复盘:CTF题目解密全流程演练
让我们结合一个虚构但典型的CTF场景,完整走一遍流程。题目描述:提供一个名为godzilla_traffic.pcapng的流量包,提示flag隐藏在攻击者执行的命令中。已知Webshell的连接密码pass为Godzilla2023,盐salt为Godzilla。
5.1 第一步:流量筛查与定位
- 用Wireshark打开数据包文件。
- 在过滤栏输入:
http.request.method == POST, 回车。 - 在列表中找到一条POST请求,目标路径可能是
/upload.php。查看其HTTP头部,发现一个Cookie:PHPSESSID=godzilla_session_123,这很可疑。 - 右键该数据包 -> “追踪流” -> “TCP流”。在弹出窗口中,可以看到客户端发送的数据是一长串
base64编码的字符串(如U2FsdGVkX1...开头),服务器返回的也是类似编码。这确认了是加密流量。
5.2 第二步:密钥计算与数据提取
计算KEY和IV:
- 已知
pass = Godzilla2023,salt = Godzilla。 - 连接字符串:
Godzilla2023Godzilla。 - 计算其MD5哈希(32位十六进制):可以使用在线工具或命令行。假设计算得到:
e99a18c428cb38d5f260853678922e03。 - 因此,
KEY = e99a18c428cb38d5(前16位),IV = f260853678922e03(后16位)。
- 已知
提取密文:
- 在TCP流窗口,确保显示格式为“原始数据”。
- 在左上角选择“仅显示发送的数据”(客户端->服务器)。
- 复制全部内容,保存到文件
client_b64.txt。 - 使用Python或在线工具,将
client_b64.txt中的base64字符串解码为二进制文件client_encrypted.bin。
import base64 with open('client_b64.txt', 'r') as f: b64_str = f.read().strip().replace('\n', '') with open('client_encrypted.bin', 'wb') as f: f.write(base64.b64decode(b64_str))
5.3 第三步:执行解密获得Flag
使用准备好的Python解密脚本,将key和iv的十六进制字符串转换为字节,并读取client_encrypted.bin进行解密。
from Crypto.Cipher import AES from Crypto.Util.Padding import unpad import base64 key_hex = 'e99a18c428cb38d5' iv_hex = 'f260853678922e03' key = bytes.fromhex(key_hex) iv = bytes.fromhex(iv_hex) with open('client_encrypted.bin', 'rb') as f: ciphertext = f.read() cipher = AES.new(key, AES.MODE_CBC, iv) decrypted_padded = cipher.decrypt(ciphertext) decrypted_data = unpad(decrypted_padded, AES.block_size) # 哥斯拉4.0默认会对原始数据先做一次base64编码 try: original_data = base64.b64decode(decrypted_data).decode('utf-8') except: original_data = decrypted_data.decode('utf-8', errors='ignore') print("解密后的数据:") print(original_data)运行脚本后,输出可能是一段序列化的PHP数组或JSON,其中包含了攻击指令。例如,你可能会看到:
{"func":"exec","args":"cat /var/www/html/flag.txt"}或者直接就是命令执行的结果:
flag{th1s_1s_4_g0dz1ll4_fl4g}至此,Flag成功获取。
6. 疑难排查与进阶技巧
在实际操作中,你可能会遇到各种问题。这里总结一些常见的坑和解决思路。
6.1 常见问题速查表
| 问题现象 | 可能原因 | 排查思路与解决方案 |
|---|---|---|
| 解密后是乱码 | 1. KEY或IV错误。 2. 加密模式不是CBC(可能是ECB)。 3. 填充方式不对(可能是ZeroPadding)。 4. 提取的密文不正确(多/少了字节)。 | 1. 反复核对pass和salt,确认生成KEY/IV的算法(有的版本是md5(md5(pass)+salt))。2. 尝试其他模式(如ECB,此时IV为空)。 3. 尝试不进行 unpad操作,直接查看解密数据末尾的字节。4. 确保从数据包中提取的是完整的TCP载荷,检查是否有分片。 |
解密脚本报Padding is incorrect错误 | 1. 密钥错误导致解密出的填充字节无效。 2. 密文在传输或保存过程中被损坏。 | 1. 这是密钥错误的最典型表现。请百分之百确认密钥。 2. 尝试使用 unpad的style参数(如PKCS7、iso7816),或手动处理填充。3. 重新从数据包中提取密文,确保使用“复制为Hex流”的方式避免编码问题。 |
| Wireshark配置TLS解密后无变化 | 1. 密钥日志文件格式错误。 2. Wireshark未对目标流量应用TLS解析。 3. 端口或IP配置不对。 | 1. 检查密钥文件格式,确保是KEY <hex_key> <hex_iv>格式,且Hex字符串长度正确(KEY和IV各32字符)。2. 尝试对目标TCP流“解码为”TLS协议。 3. 放弃Wireshark内置解密,坚定使用外部脚本方案。 |
| 找不到HTTP POST请求 | 1. 流量可能使用了其他端口(非80/443)。 2. 可能不是HTTP协议,或加密在应用层之下。 | 1. 使用过滤条件tcp.port == 8080(假设端口) 或直接浏览所有TCP会话。2. 使用 tcp contains "POST"或tcp contains "pass="进行模糊搜索。3. 查看“统计”->“会话”,找出发送数据量较大的TCP连接进行排查。 |
6.2 进阶技巧:自动化与流量特征提取
- 编写自动化解密脚本:可以将上述Python解密步骤封装成一个函数,并集成从pcap文件中自动提取指定TCP流载荷的功能。使用
scapy或pyshark库可以方便地读取pcap文件,定位到特定条件的TCP流,提取载荷,然后调用解密函数,一气呵成。 - 构建哥斯拉流量指纹:除了已知的
pass参数,哥斯拉的HTTP请求头、Cookie名称、POST数据的结构(即使加密,长度和模式也有特征)都可以作为入侵检测系统(IDS)的规则。例如,可以关注POST数据长度固定为16字节倍数、特定User-Agent(如某些版本带Godzilla字样)、以及请求间隔规律等行为特征。 - 应对变种与自定义加密:哥斯拉支持自定义加密器。在更复杂的场景中,攻击者可能使用
XOR、RC4或自定义的加密算法。这时,关键就在于逆向分析Webshell的payload代码,找到其加密函数的实现逻辑,然后用Python或Go语言重写解密函数。思路永远是:定位流量 -> 获取密钥/算法 -> 还原逻辑 -> 编写解密程序。
解密哥斯拉流量的过程,本质上是一场与攻击者之间的密码学对抗和信息战。通过这道CTF题的深入实践,我们不仅掌握了一个工具的使用技巧,更重要的