mqtt2做题记录
ldz师傅分享了一道有趣的题目,在做题过程中本人收获颇丰,接触到了许多新的知识点,记录一下做题(学习过程)
前置知识学习
在做题中接触的新知识较多,理解这些知识是搞懂题目逻辑的关键点,这里在篇首回顾一遍流程中学习的内容
go语言的逆向
go语言生成的可执行程序一打开有点想c语言静态编译的样子,程序入口为main_main这里注意一般来说,main_任意名字代表这个函数/变量是程序自定义的,而形如runtime_writeBarrier()类似的函数代表是go中runtime库对应的函数,另外题目中出现的github_com_eclipse_paho_2emqtt_2egolang__ptr_ClientOptions_AddBroker()这样的函数也是外部包导入的函数。
go的字符串与c++有很大区别,是二进制安全的,它不以\0作为终止符,一个字符串对象在内存中分为两部分,一部分为如下结构,占两个机器字用于索引数据:
1 |
|
而它的另一部分才存放真正的数据,它的大小由字符串长度决定,在逆向中重点关注的是如上结构,因此说一个string占两个机器字,其他结构也按这种约定。
go语言一大特性就是用户层面实现的协程(Goroutine),本质是轻量化的线程,能够轻松实现高并发,利用go函数启动一个Goroutine独立与主线程异步运行。对于协程的调度模型有非常多的内容,与本体无直接关联,具体实现可以参考:https://www.zhihu.com/question/20862617
MQTT协议
MQTT(Message Queuing Telemetry Transport)是一种轻量级、基于发布-订阅模式的消息传输协议,适用于资源受限的设备和低带宽、高延迟或不稳定的网络环境。它在物联网应用中广受欢迎,能够实现传感器、执行器和其它设备之间的高效通信。
MQTT Broker
MQTT Broker 是负责处理客户端请求的关键组件,包括建立连接、断开连接、订阅和取消订阅等操作,同时还负责消息的转发。通常题目中会连接一个mqtt的brocker,绑定到一个开启mqtt服务的端口上,同时对于返回的ClientOptions进行连接函数绑定,消息处理函数绑定,新建一个mqtt客户端。
MQTT Client
任何运行 MQTT 客户端库的应用或设备都是 MQTT 客户端。例如,使用 MQTT 的即时通讯应用是客户端,使用 MQTT 上报数据的各种传感器是客户端,各种 MQTT 测试工具也是客户端。
MQTT 的工作流程:
- 客户端使用 TCP/IP 协议与 Broker 建立连接,可以选择使用 TLS/SSL 加密来实现安全通信。客户端提供认证信息,并指定会话类型(Clean Session 或 Persistent Session)。
- 客户端既可以向特定主题发布消息,也可以订阅主题以接收消息。当客户端发布消息时,它会将消息发送给 MQTT Broker;而当客户端订阅消息时,它会接收与订阅主题相关的消息。**同时注意在用户端可以将特定主题绑定特定的massage_handler函数,实现接收到主题消息回调。**一个客户端也可以绑定一个连接函数,代表连接上brocker执行的动作,一般为subscribe一些主题
- MQTT Broker 接收发布的消息,并将这些消息转发给订阅了对应主题的客户端。它根据 QoS 等级确保消息可靠传递,并根据会话类型为断开连接的客户端存储消息。
题目
终于到题目了,这个题目其实就是利用go中的github_com_eclipse_paho_2emqtt_2egolang外部库连接到本地的brocker,创建了客户端,同时绑定了连接函数自动subscribe CTF主题,消息回调函数message_handler(具有命令执行漏洞)。并创建一个Goroutine每隔一段时间在CTF/send主题下publihs一个泄露ping码的信息。注意对于这种题目的pwn方法是:我们需要自己写一个MQTT Client,通过利用这个有漏洞的MQTT Client从而getflag。
思路还是比较清晰的:创建题目中对应broker服务器的连接,根据题目subscribe的主题publish相应内容的信息从而触发有漏洞消息回调函数getflag。
题目文件中对于mqtt client的初始化:
注意这里直接在**
ClientOptions**结构体中声明massage_handler代表该客户端只要收到自己subscribe的主题publish的消息就会触发
massage_handler函数中逻辑比较简单:解析json消息格式,提取auth,cmd,arg字段。先检查auth内容是否长度为6且等于特定加密算法后的pin值前6位,然后判断cmd中字段是否等于get_version或者set_vin:get_version打开/mnt/version文件输出内容;set_vin将arg内容拼接到echo -n %s > /mnt/VIN中。注意这里对于参数进行了比较严苛的子字符串检测常用的显示函数如cat,tail以及flag都被ban了。这里用‘’阶段一下cat以及flag,同时利用;注入命令,就能把flag内容带出来了。
exp.py
1 | import random |