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