過去的幾個月我一直在周游以指導(dǎo)人們?nèi)绾螌?a target="_blank">嵌入式設(shè)備進(jìn)行漏洞利用,單單幻燈片已經(jīng)不足以承載足夠的信息,所以我將所有的都寫下來以便與知識的消化。接下來的內(nèi)容是 第一部分,介紹了一些嵌入式設(shè)備端的軟件。
我決定首先介紹軟件 因為很多漏洞都發(fā)生在軟件端,從二進(jìn)制可執(zhí)行程序到驅(qū)動程序。第二部分會介紹硬件層,教授 JTAG 是如何工作的,如何利用修改硬件繞過密碼保護(hù)或提取可入侵目標(biāo)設(shè)備的機(jī)密信息。
目錄
1 使用Binwalk提取固件
2 學(xué)習(xí)目標(biāo)設(shè)備的匯編
3 GPL
4 漏洞利用
5 DVRFv0.3 socket_bof 解決方案
6 引用參考
1 使用Binwalk提取固件
當(dāng)你能夠得到你有的嵌入式設(shè)備的二進(jìn)制固件的時候,或許你想看看里面有什么。幸運的是有個開源的工具 Binwalk,可分析目標(biāo)的二進(jìn)制文件中的 Magicbytes,看這里。
為了更加直觀的表現(xiàn),這里使用Binwalk提取DVRFv0.3
通過 binwalk -e filename提取二進(jìn)制文件的內(nèi)容:
binwalk_dvrf3.png
binwalk 顯示已知的結(jié)構(gòu)以及在二進(jìn)制文件中的偏移量
Offset_binwalk_arrow.png
在vim中使用 xxd 顯示TRX和gzip偏移量,確實和Binwalk提供的相匹配(使用vim打開DVRFv0.3,在命令模式下輸入%!xxd)
gzip_trx_offset_arrows.png
Binwalk交叉參考出TRX結(jié)構(gòu)
trx_magic_bytes_git.png
Binwalk交叉參考出gzip結(jié)構(gòu)的
gzip_github.png
根據(jù)Binwalk發(fā)現(xiàn)的內(nèi)容,使用vim和xxd交叉參考出SquashFS結(jié)構(gòu)的偏移量
squashfs_dvrf_arrows.png
binwalk 交叉參考出SquashFS結(jié)構(gòu)
2學(xué)習(xí)目標(biāo)設(shè)備的匯編
如果對目標(biāo)設(shè)備使用的匯編不熟悉,可以使用C語言和反匯編器快速學(xué)習(xí)。我認(rèn)為,下面的內(nèi)容是學(xué)習(xí)一門新匯編最先要去看的部分。
參數(shù)傳遞
函數(shù)的進(jìn)入和返回
堆棧的使用
函數(shù)的調(diào)用
條件分支
參數(shù)傳遞
有個簡單的C語言程序, 傳遞2個int 參數(shù)給另一個函數(shù)并且返回二者之和。
當(dāng)你編譯完成C程序后你想對生成的可執(zhí)行文件進(jìn)行反匯編
Note : 使用自己熟悉的反匯編工具, 這邊使用的是Radare2
雖然我們可以看到圖形話的視圖,不過也可以按g 然后按 a 查看函數(shù) pass_args_to_me
為了理解了當(dāng)傳遞的參數(shù)數(shù)量多于參數(shù)寄存器數(shù)量的情況。比如在MIPS中寄存器參數(shù)使用$a0 - $a3 ,所以修改上面的代碼,增加傳遞的參數(shù)使得參數(shù)數(shù)量大于4
將編譯后的可執(zhí)行文件使用radare2進(jìn)行反匯編,查看生成的匯編
可以看到當(dāng)參數(shù)個數(shù)大于4時,多余的參數(shù)會被壓入棧中
函數(shù)的進(jìn)入 調(diào)用 返回
需要注意的是:在 MIPS和ARM處理器中的返回地址寄存器。當(dāng)一個跳轉(zhuǎn)鏈接指令在MIPS中執(zhí)行的時候,返回地址寄存器的地址是當(dāng)前指令指針+8 bytes,8bytes的偏移是因為pipeling,因為PC+4 會在跳轉(zhuǎn)發(fā)生前執(zhí)行。讓我們編譯一個在返回main函數(shù)前調(diào)用2個或更多函數(shù)的程序。
記住 發(fā)生函數(shù)調(diào)用(JAL)會把 $PC+8 保存到$ra寄存器中,但是如果被調(diào)用函數(shù)還會調(diào)用其他函數(shù)時,$ra寄存器會被覆蓋,調(diào)用者的地址會丟失。為了防止這種情況,返回地址首先被保存到函數(shù)入口的棧上.所以我們可以看到所有的函數(shù)會將返回地址保存到棧上,除了 函數(shù)call_two,因為call_two()沒有調(diào)用其他函數(shù)。
只是對函數(shù)入口進(jìn)行分析我們就可以判斷這個函數(shù)是否調(diào)用了其他函數(shù)。當(dāng)試圖找內(nèi)存堆棧溢出漏洞的時候這個技巧很有用
條件分支
分析一個新架構(gòu)的時候,最重要的事情之一是處理器怎么處理條件分支。像之前一樣我們使用c和radare2進(jìn)行分析。
下面的程序會傳入一個命令行參數(shù),類型為int, 并判斷是否小于5
查看編譯器會產(chǎn)生怎樣的匯編來滿足條件
可以看到當(dāng)比較結(jié)果為小于的時候,使用了 slti。 學(xué)習(xí)一種新的匯編語言的時候,由于大量的比較運算符和類型,條件判斷會花費大部分的時間,參考c語言中的表達(dá), 確保你分析了所有生成條件分支的方法。例如 :在MIPS架構(gòu)中,有時既可以使用有符號立即數(shù),又可以使用無符號立即數(shù),這可能會被濫用。
現(xiàn)在你看了上面一些例子,你掌握那些技巧后, 就可以在只有編譯器和反匯編器的情況下學(xué)習(xí)任何處理器的架構(gòu)和匯編。不這樣的話,那就只能不幸的用更艱苦的方式學(xué)習(xí),查看處理器的開發(fā)手冊,甚至涉及自己的匯編器,模擬器,反匯編器。
3GPL
如果你審計的設(shè)備使用了開源軟件,那么軟件應(yīng)該是遵循GPL授權(quán)的。那么如果開發(fā)者使用代碼并編譯的話,源碼必須公開,不公開就違反了GPL協(xié)議。
很多路由器和小型設(shè)備使用Linxu(或者FressRTOS), Busybox,和其他授權(quán)GPL協(xié)議開源軟件,所以開始反匯編之前可以在Google搜索一小段供應(yīng)商的或者產(chǎn)品的源代碼。下面是一些我搜索到的示例源代碼庫.
4漏洞利用
這一部分假定讀者有利用內(nèi)存漏洞的基礎(chǔ)知識。如果沒有,可以查看底部的 SmashtheStack, SmashtheStack是我開始學(xué)習(xí)x86漏洞利用的地方。
如果你在審計的是MIPS架構(gòu)的嵌入式Linux設(shè)備, 那么很有可能在分析目標(biāo)二進(jìn)制文件的時候看到的是一下內(nèi)容
如你所見,棧區(qū)和堆區(qū)被標(biāo)記為可執(zhí)行,所以不必?fù)?dān)心NX(Not execute), 雖然棧上是可執(zhí)行的,但是為了讓代碼執(zhí)行,ROP(return-oriented Programming)也是必要的。你也會發(fā)現(xiàn) ASLR在大部分設(shè)備上都不會生效,所以不必要尋找首先泄露漏洞的信息
譯者注 :NX bit 是 一些CPU內(nèi)存管路單元的特性,允許特定內(nèi)存頁可執(zhí)行或不可執(zhí)行。more of NX
模擬
一旦使用Binwalk提取固件,為了分析崩潰你會想模擬運行二進(jìn)制文件。我個人使用的靜態(tài)編譯版本的QEMU,可以使用 chroot,在提取的固件環(huán)境中加載程序。于是漏洞利用者可以使用與目標(biāo)設(shè)備同一套的libc庫,改變得僅僅只是libc的地址。而有時使用QEMU
模擬一陣套系統(tǒng)也是必要的,因為主機(jī)也會不支持二進(jìn)制文件使用的IO操作而導(dǎo)致崩潰。
如果你使用的是基于debian的Linux發(fā)行版, 你可以通過 apt-get安裝QEMU
sudo apt-get install qemu-user-static qemu-system-*
安裝完成QEMU,需要將qemu的可執(zhí)行文件拷貝到提取的固件的根目錄處。例如我們在你DVRFv0.3中使用MIPS little Endian的模擬器。
cp `which qemu-mipsel-static` ./
這邊我們使用可以被攻破的二進(jìn)制文件
/pwnable/lntro/stack_bof_01 ,并為之寫一段利用代碼。然后將payload作為程序參數(shù) ,看看會發(fā)生什么。
二進(jìn)制的源代碼:
我們有一個簡單的棧溢出漏洞了,目的是執(zhí)行 dat_shell函數(shù)。但我們分析ELF文件的時候可以看到如下:
Entry point address: 0x00400630
因為Payload中不能有NULL字符,于是得依靠部分覆蓋來執(zhí)行,因為是小端格式,我們可以覆蓋最低的3個字節(jié),最高位置NULL, 在大端機(jī)不適用。
為了演示模擬環(huán)境的功能, 我會編寫payload并展示怎么找到模擬環(huán)境中加載庫的地址。
gdb遠(yuǎn)程調(diào)試附加進(jìn)程
可以看到 CP置為了 A8gA ,可以算出偏移量為 204, 即$RA在208字節(jié)上,在這邊我們只會覆蓋4字節(jié)中的3字節(jié)。
再次嘗試,使$RA寄存器為 0x42424242
我們想要跳過修改$gp的指令,它會導(dǎo)致程序崩潰, 我以我們跳轉(zhuǎn)到 0x0040095c
我們也可以打斷點來確認(rèn)是否跳轉(zhuǎn)到了函數(shù)正確的偏移地址
所以構(gòu)建ROP 鏈的時候你所要做的就是替換libc的地址(可以通過 cat /proc/[pid]/maps獲得).你需要的是libc的基址。如果構(gòu)造的ROP鏈在QEMU中可以正常運行那么99%可以在真實設(shè)備上運行。
5DVRFv0.3 socket_bof 解決方案
當(dāng)設(shè)計DVRF項目的實驗的時候,我想納入我所見過的大部分的常見漏洞類型。最常見的是棧溢出漏洞,如果不熟悉匯編的話會有點挑戰(zhàn)。
下面的漏洞利用代碼花了大概8個小時,因為自己人在學(xué)MIPS匯編 ,這段代碼是在QEMU上完成編寫的。
因為棧上可執(zhí)行,且?guī)煳募]有地址偏移,所以我們可以對ROP鏈進(jìn)行硬編碼,但是ROP的本質(zhì)是將$SP的值想想一個可以調(diào)用的寄存器。我認(rèn)為硬編碼棧地址不夠可靠,我更加喜歡使用偏移量來代替,下面是socket_bof的內(nèi)存映射,
地址0x2ab3e000是 libc可執(zhí)行塊的基址, 當(dāng)測試實際設(shè)備的時候,在QEMU上編寫的exploit代碼中,這是唯一需要更改的地方。
整個ROP 鏈 都是使用Radare2的 /R功能來完成的,比如,我想要查找 mmove t9, a1 來作為 ROP 的最后一小部分,我們可以按照如下方式來尋找:
Note : 一開始我是準(zhǔn)備自己編寫shellcode的,不過了解到了一個項目 Bowcaster已經(jīng)給出了。所以這邊展示流程,我根據(jù)下面的C語言代碼來進(jìn)行模塊化.
如果我們查看 Bowcaster Reverse_TCP 的shellcode,會發(fā)現(xiàn)上面的C代碼和Bowcaster的Shellocde是一致的
首先設(shè)置 Socket ( syscall Value 4183)
連接socket (syscall value 4170)
調(diào)用dup2 (syscall value 4063)
執(zhí)行 sh (syscall value 4011)
我們可以通過radare2 反匯編C語言程序來驗證syscall。
我們可以看到 C中調(diào)用socket()函數(shù)就是 syscall 4183,其他的系統(tǒng)調(diào)用號也可以這樣來進(jìn)行查看。
注意shellcode在QEMU的用戶模式下不是100%成功的, 表現(xiàn)在于你會看到一個TCP的反向連接,但是不會彈出shell而是一段錯誤信息。而這段Shellocde在實際設(shè)備上能夠正常運行。
更加簡單的方式去分析運行中的是使用qira(QEMU Interactive Runtime Analyse),下面的圖片展示了Qira是如何在不需要斷點的情況下分析二進(jìn)制文件
qira_shellcode.jpg
基于web的Qira 輸出顯示了所有的指令和系統(tǒng)調(diào)用
所以為了編寫漏洞利用代碼而重新造輪子是沒有必要的, 而設(shè)計自己的shellcode 和shellcode的編碼器則是進(jìn)行漏洞利用的很好的鍛煉方式。當(dāng)決定自己設(shè)計之前確保使用過所有可用的工具。如果已經(jīng)存在的shellcode 正好適用目標(biāo)設(shè)備,那么拿來用沒有什么錯,但是確認(rèn)要對網(wǎng)上找來的shellcode進(jìn)行代碼審計。
6引用參考
審核編輯:湯梓紅
評論
查看更多