路由器漏洞挖掘之 DIR-805L 越權檔案讀取漏洞分析
接下來的文章都會實戰復現一些關於路由器的 web /二進位制漏洞,可能會寫的比較細,希望能給大家帶來啟發。
前言
本文在復現 DIR-805L 任意檔案讀取漏洞時,將會比較詳細的分析一下用於 cgi 處理的 phpcgi_main
函式其中的一些功能。在逆向 cgibin
二進位制檔案時,常常會遇到一些用於解析 http 請求的函式,在分析時經常對這些函式的用法不太清楚。總是雲裡霧裡的,所以這裡對這些函式做一個比較細緻的總結,希望對同樣在學這塊的朋友們一點啟發吧,也算是達到拋磚引玉的目的吧。
- 筆者能力有限,寫不出高水平的文章,只能把基礎的要點寫詳細一些,目的為了供一些和我一樣的新手入門學習,文中若有解釋錯誤懇請師傅們指出。
前提準備
去 dlink 的官網下載韌體:
ftp://ftp2.dlink.com/PRODUCTS/DIR-850L/REVA/DIR-850L_REVA_FIRMWARE_1.14.B07_WW.ZIP
使用 firmadyne 來模擬韌體:
./sources/extractor/extractor.py -b DIR-850L -sql 127.0.0.1 -np -nk "DIR-850L_REVA_FIRMWARE_1.14.B07_WW.ZIP" images ./scripts/getArch.sh ./images/8.tar.gz ./scripts/tar2db.py -i 8 -f ./images/8.tar.gz sudo ./scripts/makeImage.sh 8 ./scripts/inferNetwork.sh 8 ./scratch/8/run.sh
- 這裡我使用遠端的 vps 來搭建 firmadyne 的環境,路由器的 IP 是
192.168.0.1
執行韌體之後在本地連上遠端的 ssh 隧道,代理到本地的 9000 埠:
ssh [email protected] -D 9000
sockets 代理 127.0.0.1:9000
,這裡我們就能在本地訪問到 vps 上模擬的韌體
訪問 192.168.0.1
即可訪問到路由器的登入介面。
- 如果用 Burp 訪問的話,在 User options 中開啟 socks proxy 選項就行了。
越權檔案讀取
先給相應的 payload
:
post_uri: http://192.168.0.1/getcfg.php data: SERVICES=DEVICE.ACCOUNT&attack=ture%0aAUTHORIZED_GROUP=1
漏洞分析
getcfg.php 原始碼分析
在 /htdocs/web/getcfg.php
檔案中,有一處檔案讀取的操作,通過讀取 POST 傳入的引數 SERVICES
的值,最後拼接到 $file
變數中,呼叫 dophp("load", $file)
函式進行檔案讀取。
顯然這裡的 SERVICES
變數的值可控,為了達到讀取檔案的目的, 這裡還需要繞過前面的一個 if 判斷
if(is_power_user() == 1) ...... function is_power_user(){ if($_GLOBALS["AUTHORIZED_GROUP"] == "") { return 0; } if($_GLOBALS["AUTHORIZED_GROUP"] < 0) { return 0; } return 1; }
這個函式在前面就定義了,通過全域性變數 AUTHORIZED_GROUP
來判斷返回值。這裡需要滿足 AUTHORIZED_GROUP
的值大於 0 才表示認證成功。那麼這裡全域性變數 AUTHORIZED_GROUP
值是從哪裡獲取的呢?這裡我們就需要在 cgibin
這個二進位制檔案中找到答案。
cgibin 的靜態分析
首先要明確 cgibin
這個檔案是起到解析處理指令碼語言的作用,當前 webserver 執行的是 php 指令碼,那麼 這個二進位制檔案中重點就是處理 php 語言的部分 ,也就是 phpcgi
。
CGI的英文是(COMMON GATEWAY INTERFACE)公共閘道器介面,它的作用就是幫助伺服器與語言通訊。
所以在 IDA 中我們需要找到處理 phpcgi
的相關函式。在 0x00406528 處找到這個函式
下面開始詳細分析這個函式其中的一些細節。
首先,第一步函式呼叫了 sobj_new、sobj_add_string、sobj_add_char
函式來初始化字串,為後面對解析 POST 變數的處理做一個準備。
- sobj_new 函式開闢一個堆記憶體,並維護一個數組
int *malloc_addr = malloc(0x18) heap + 0 = malloc_addr heap + 4 = malloc_addr heap + 8 = 0 heap + C = 0 heap + 10 = 0 heap + 14 = 0
接下來通過 getenv
函式來獲取請求的方法和請求的 uri。
這裡是 POST 請求,所以會走到 0x40668C
這個分支
- 在 cgi 的處理中,
getenv
是個很重要的函式,這個函式用來獲取客戶端通過 http 請求的一些請求引數,例如 uri、content-length、請求方法等。char *getenv(const char *name)
當前函式返回值為指向查詢的
name
的值的指標。
getenv 函式的返回值儲存在一個全域性二維數組裡,當你再次使用 getenv 函式時不用擔心會覆蓋上次的呼叫結果。
接著呼叫 cgibin_parse_request
這個函式,函式主要功能是進一步處理 http 請求。
cgibin_parse_request(sub_406220,malloc_addr,8)
這個函式的第一個引數 sub_406220 函式意思是拼接 POST 請求的引數名和引數值。
接著 cgibin_parse_request
函式裡呼叫 parse_uri
完後,將返回值儲存到 &(heap + 8) 處
往下就跳轉到了 loc_406760
這個分支, sobj_get_string
函式從堆空間中獲取前一步拼接的引數名和引數值,主要是判斷 POST 請求中是否存在 AUTHORIZED_GROUP
這個引數
繼續往下,這裡的開頭先判斷訪問的絕對路徑中是否包含 htdocs/mydlink
路徑,再經過 strcasecmp
函式的比較之後,跳轉到 sess_validate
函式來執行
在這裡, sess_validate
函式作用是驗證解析 AUTHORIZED_GROUP
這個引數的值,並通過 sprintf
函式後 作為全域性變數的形式格式化到棧上 。即:
sprintf($sp+0xB0-0x90,"AUTHORIZED_GROUP=%d",sess_validate())
之後再呼叫 sobj_add_string
和 sobj_add_char
函式來進行字串的拼接。
sobj_add_string
函式將 sprintf
格式化到棧上的資料作為指標,儲存到 sobj_new
開闢的堆空間裡。
sobj_add_string(&heap,$sp+0xB0-0x90)
這裡需要在 POST 資料包中加上 %0a
的原因是在呼叫 sobj_add_char
函式時,會用 n 來分隔引數
sobj_add_char(heap,0x10)
因此這裡的 payload
為:
SERVICES=DEVICE.ACCOUNT&attack=%0aAUTHORIZED_GROUP=1
所以只要將 %0aAUTHORIZED_GROUP=1
偽造作為任意一個引數名的引數值就行了。
因此在 cgibin
處理完 AUTHORIZED_GROUP
引數後,就將他作為全域性變數儲存在 php 當中。這樣就繞過了 is_power_user
函式的判斷,執行下面的任意檔案讀取操作。
動態分析
這裡直接使用 POST 方法提交上面的 payload 來進行除錯,觀察一下引數的變化。
另一處任意檔案讀取
這裡其實還有一處的任意檔案讀取,漏洞位於 htdocs/webinc/fatlady.php
中,程式碼如下:
$service
引數未經過濾就直接拼接到 $target
變數中,導致這裡可以通過 目錄穿越 讀取到其他目錄下的 .php 字尾的檔案,例如存放 admin 使用者資訊的 DEVICE.ACCOUNT.xml.php 檔案。
<?xml version "1.0" encoding "utf-8"><postxml><module><service>../../../htdocs/webinc/getcfg/DEVICE.ACCOUNT.xml</service></module></postxml>
這裡只要滿足 POST 資料包中 xml 形式的 postxml
即可, $service
變數即可控,同樣可以達到任意檔案讀取的效果。
- 注意這裡請求頭的
Content-Type
的值要為text/xml
任意檔案讀取漏洞的復現
這裡復現一下第一種任意檔案讀取的效果,我們的目的是讀取在 /htdocs/webinc/getcfg
目錄下的 DEVICE.ACCOUNT.xml.php
檔案。
SERVICES
的值可控,那麼這裡就可以傳入 SERVICES
的值為 /htdocs/webinc/getcfg
資料夾下字尾為 .xml.php
的檔名,達到未授權讀取配置檔案的目的。
使用 Burp 構造資料包傳送即可看到漏洞效果。
- 這裡的
Content-Type
需要為application/x-www-form-urlencoded
,傳送的 POST 資料才能正常被伺服器解析。
總結
DIR-850L 任意檔案讀取復漏洞現重點還是要仔細分析 phpcgi_main
這個函式的在處理 http 請求時的相關邏輯。
這裡在讀取到 DEVICE.ACCOUNT
配置檔案,得到 admin 的密碼之後,可以進一步利用一處命令執行來 getshell,具體的後面的文章中會有具體的分析。