今日靶场
公交车司机 (Misc/工控流量分析)

1. 题目背景与分析
- 题目名称:公交车司机
- 题目描述:有一名黑客入侵了公交车交通系统,我们需要分析流量中的蛛丝马迹,找到 FLAG。
- 文件名称:
modbus.pcapng
通过 Wireshark 的统计 (Statistics) -> 协议分级 (Protocol Hierarchy)功能观察,发现该流量包中 均为 Modbus/TCP 协议流量,总共包含 150 个数据包。
结合题目公交车交通系统的背景,判定这是一道典型的工控安全流量分析题。黑客通过 Modbus 协议向 PLC(可编程逻辑控制器)发送了指令,Flag 极有可能隐藏在黑客写入的数据中。
2. 解题步骤
步骤一:流量宏观观察
使用 Wireshark 打开流量包,在主界面过滤器中输入 modbus 进行筛选。
观察发现,所有的 Query 请求功能码均为 Func: 16: Write Multiple Registers(写多个保持寄存器),且每一条报文的长度均为固定的 69 字节。这表明黑客正在连续、有规律地向工控设备写入特定数据。
步骤二:数据提取
由于数据包较多(150个),逐个手动查看效率较低。我们利用 Wireshark 的导出功能将流量包转换为结构化的 JSON 文件:
- 点击
文件 (File) -> 导出分组解析结果 (Export Packet Dissections) -> 作为 JSON... (As JSON...)。
随后编写 Python 脚本,批量提取每个 Modbus 报文中的寄存器写入值(modbus.regval_uint16 字段):
Python
import json
# 读取导出的 JSON 流量数据
with open('modbus.json', 'r', encoding='utf-8') as f:
packets = json.load(f)
extracted_values = []
for p in packets:
layers = p.get('_source', {}).get('layers', {})
mb = layers.get('modbus', {})
# 提取寄存器写入值
for k, v in mb.items():
if isinstance(v, dict) and 'modbus.regval_uint16' in v:
extracted_values.append(int(v['modbus.regval_uint16']))
break
步骤三:数据分析与解码
对提取出的寄存器数值进行分析发现,前 66 个数据包的值较小,属于干扰或探测流量。
从第 67 个数据包开始,写入的数值对应的 ASCII 码呈现明显的十六进制字符特征(如 54 对应字符 '6',99 对应字符 'c')。
将第 67 包之后的所有数值转换成对应的字符并拼接,得到如下十六进制字符串:
Plaintext
666c61677b31343234303135392d376633362d346333362d623436612d3734613661366465643834617d
使用 Python 或 CyberChef 将该 Hex 字符串转换为明文字符串:
Python
hex_str = "666c61677b31343234303135392d376633362d346333362d623436612d3734613661366465643834617d"
flag = bytes.fromhex(hex_str).decode('utf-8')
print(flag)
3. 最终 FLAG
Plaintext
flag{14240159-7f36-4c36-b46a-74a6a6ded84a}
老乡鸡sign

解题过程
拿到图片后,先查看文件结构。PNG 正常结束于 IEND,但文件尾部仍然存在多余数据,说明图片后面可能拼接了其他文件。
手动解析 PNG chunk,发现 IEND 之后还有 358 字节数据:
import struct
path = "老乡鸡.png"
data = open(path, "rb").read()
offset = 8
while offset < len(data):
length = struct.unpack(">I", data[offset:offset + 4])[0]
chunk_type = data[offset + 4:offset + 8]
offset += 12 + length
if chunk_type == b"IEND":
break
print("IEND end:", offset)
print("trailing bytes:", len(data) - offset)
查看尾部数据时可以看到明显的 ZIP 目录结构特征,例如 504B0102 和 504B0506,并且还出现了 .txt 文件名特征。但是文件头缺失了 504B0304,所以普通解压工具无法直接解压。
尾部数据开头类似:
33 00 01 00 63 00 ... ... 50 4B 01 02 ... ... 50 4B 05 06 ...
因此将 IEND 后面的数据取出,并在前面补上 ZIP 本地文件头 PK\x03\x04,也就是十六进制的 504B0304。
import struct
src = "老乡鸡.png"
data = open(src, "rb").read()
offset = 8
while offset < len(data):
length = struct.unpack(">I", data[offset:offset + 4])[0]
chunk_type = data[offset + 4:offset + 8]
offset += 12 + length
if chunk_type == b"IEND":
break
tail = data[offset:]
open("recovered_tail_fixed.zip", "wb").write(b"PK\x03\x04" + tail)
修复后可以识别为 ZIP,里面有一个文件。文件名使用 GBK 解码后为:
老乡鸡秘诀.txt
继续查看 ZIP 信息,发现压缩方式为 99,这是 WinZip AES 加密。
import zipfile
with zipfile.ZipFile("recovered_tail_fixed.zip") as z:
for info in z.infolist():
print(info.filename)
print(info.compress_type)
print(info.flag_bits)
图片元数据中存在异常时间戳提示:
exiftool 老乡鸡.png
可以看到时间相关字段异常,对应时间戳:
1065054630
用该时间戳作为密码解压 AES ZIP。
import pyzipper
password = b"1065054630"
with pyzipper.AESZipFile("recovered_tail_fixed.zip") as z:
name = z.namelist()[0]
z.setpassword(password)
data = z.read(name)
print(data.decode("utf-8"))
得到内容:
hocctf {老乡鸡_中式快餐_老母鸡汤_香飘全国}
干净卫生,比家里还放心
Flag
hocctf {老乡鸡_中式快餐_老母鸡汤_香飘全国}
评论区
评论加载中...