【建议收藏】这个工具专门用于寻找路由器中的安全漏洞
關(guān)于工具
現(xiàn)有工具
現(xiàn)在,現(xiàn)成的污點(diǎn)分析工具已經(jīng)有很多了。其中,我最感興趣的是Triton和bincat,因為兩者已經(jīng)相當(dāng)成熟。然而,我們卻無法使用這兩種工具,因為它們不支持目標(biāo)設(shè)備所使用的MIPS架構(gòu)。
使用angr進(jìn)行符號執(zhí)行測試
因此,我們把工作重點(diǎn)放在利用angr打造自己的工具上;angr是一個基于Python的二進(jìn)制分析框架。我們之所以選擇angr,是因為它支持大多數(shù)架構(gòu),包括我們所針對的MIPS和ARM架構(gòu)。早些時候,@puzzor曾經(jīng)對angr進(jìn)行了一些自定義修改,用于靜態(tài)污點(diǎn)分析:在angr的幫助下,通過符號執(zhí)行模擬程序,然后根據(jù)生成的VEX IR程序跟蹤信息進(jìn)行靜態(tài)分析。通過這種方法,我們成功地在測試固件中發(fā)現(xiàn)了命令注入漏洞。
然而,我們很快就遇到了一個問題:為了生成程序跟蹤信息,我們需要angr通過模擬每條指令來模擬每個函數(shù),并使用符號執(zhí)行來決定是否跟隨一條分支指令。
具體來說,angr會維護(hù)一個狀態(tài)堆棧。一個狀態(tài)包含諸如寄存器值和內(nèi)存內(nèi)容方面的信息。因此,當(dāng)模擬一個函數(shù)時,它只會從一個狀態(tài)開始。當(dāng)遇到一個分支指令時,如果angr不確定是否跟隨該分支,angr將重復(fù)該狀態(tài),其中一個將跟隨該分支,而另一個則不跟隨。
大多時候,函數(shù)中都會存在循環(huán)。如果循環(huán)條件是基于一些用戶的輸入,那么,狀態(tài)的堆棧就會“爆炸”。由于angr將始終不確定是繼續(xù)還是從循環(huán)中跳出,所以,它會不斷復(fù)制狀態(tài)。此外,還需要注意的一件事是,這些狀態(tài)并不是同時模擬的。相反,每次只有一個狀態(tài)被模擬。在這種情況下,需要很長的時間才會有一個狀態(tài)達(dá)到易受攻擊的代碼;或者,如果該函數(shù)根本沒有易受攻擊的代碼,那么,模擬過程可能永遠(yuǎn)不會終止。
作為一個符號執(zhí)行框架,angr提供了多種可定制設(shè)置(稱為模擬技術(shù))來決定先模擬哪個狀態(tài)。并且,在嘗試了許多不同的技術(shù)后,我們?nèi)匀粺o法改善執(zhí)行時間。
例如,在為待分析二進(jìn)制代碼中的每個函數(shù)設(shè)置了2分鐘的超時的情況下,有時候即使經(jīng)過了2小時,仍然無法完成對二進(jìn)制文件的分析(因為如果一個函數(shù)沒有漏洞,它將一直模擬執(zhí)行下去,直到超時位置)。更糟糕的是,angr中存在一個未知的內(nèi)存泄漏問題,所以,2小時后,電腦的內(nèi)存早就耗盡了……
別忘了,我們之前的目標(biāo)是希望這個工具比手工方式更快一些。所以,通過這種方式是不可能的,所以,我們繼續(xù)尋找改進(jìn)方法或替代方案。
應(yīng)用angr的Reaching Definition分析技術(shù)
最終,我們偶然發(fā)現(xiàn)了這個漏洞后,燃起了我們對于angr的Reaching Definitions分析技術(shù)的熱情,于是仔細(xì)閱讀了下面的資料:
?A reaching definition engine for binary analysis built-in in angr
?Handle function calls during static analysis in angr
?CSE545 Guest Lecture: Binary Analysis
Use-def關(guān)系
總之,這種分析方法將生成函數(shù)中原子之間的use-def關(guān)系。在這里,所謂原子類似于變量,并且,原子也具有各種類型,如寄存器、棧變量、堆變量。實(shí)際上,只要把原子看作變量,事情就很簡單了,下面,我們舉例說明:
在上面的函數(shù)中,存在一個明顯的命令注入漏洞:含有漏洞的代碼為system(command),其中注入的命令來自querystring的參數(shù)name。在這個函數(shù)中,querystring和其他原子之間的use-def關(guān)系如下所示:
首先,我們看到querystring被定義為函數(shù)vuln的參數(shù),并被get_querystring_value函數(shù)作為參數(shù)querystring使用。除此之外,函數(shù)get_querystring_value還定義了一個參數(shù)name。最后,get_querystring_value函數(shù)定義了一個返回值,并用到了上面定義的兩個參數(shù)。
然后,通過下圖我們可以看到,在調(diào)用sprintf函數(shù)時,還用到了name變量(函數(shù)get_querystring_value的返回值)和一個字符串echo %s >> /tmp/log。這一次,情況略有不同。因為我們知道sprintf函數(shù)的第一個參數(shù)是目標(biāo),所以,我們必須對command進(jìn)行適當(dāng)?shù)亩x,從而讓它全面使用提供給spritnf函數(shù)的2個參數(shù),而不是僅使用返回值。生成的use-def關(guān)系如下所示:
使用相同的概念,通過這種分析方法可以為函數(shù)中的所有原子生成相應(yīng)的use-def關(guān)系。正如我們在上面看到的,這種關(guān)系可以被建模為一個圖:“使用”用邊表示,“定義”用節(jié)點(diǎn)表示。 因此,我們可以將其轉(zhuǎn)換為圖分析問題。
在污點(diǎn)分析術(shù)語中,source點(diǎn)是程序中產(chǎn)生污點(diǎn)數(shù)據(jù)的地方,而sink點(diǎn)是污點(diǎn)數(shù)據(jù)可能到達(dá)也可能不到達(dá)的地方。污點(diǎn)分析是確定來自source點(diǎn)的數(shù)據(jù)是否到達(dá)sink點(diǎn)。在上面的例子中,get_querystring_value函數(shù)是source點(diǎn),因為它從用戶輸入中提取一些值,而system函數(shù)是sink點(diǎn)。就本例來說,來自source點(diǎn)的數(shù)據(jù)確實(shí)到達(dá)了sink點(diǎn)。
在我們的use-def圖中,我們可以確定source點(diǎn)和sink點(diǎn)的定義(即節(jié)點(diǎn)),然后用一些啟發(fā)式方法遍歷該圖,以確定source點(diǎn)的數(shù)據(jù)是否被sink點(diǎn)所使用。如果是,那么我們就把source點(diǎn)標(biāo)記為易受攻擊的,并繼續(xù)對其進(jìn)行篩查。
工具總結(jié)
總而言之,我們的工具首先利用angr的Reaching Definitions分析方法,生成一個由路由器固件中的函數(shù)的組成的use-def關(guān)系圖。然后,對該圖進(jìn)行相應(yīng)的分析,以檢測可能的安全漏洞,比如,如果(來自source點(diǎn))用戶輸入到達(dá)一個危險的函數(shù)(即sink點(diǎn)),如system函數(shù),我們就認(rèn)為發(fā)現(xiàn)了一個潛在的安全漏洞。
實(shí)際上,我們的工具與CodeQL或Joern等引擎的功能非常類似,只是我們的工具缺乏強(qiáng)大的查詢界面而已。
測試結(jié)果
前面我們曾提到,使用符號執(zhí)行的方法時,有時分析一個程序需要花費(fèi)2個多小時。但是,使用上面介紹的方法,分析同樣的程序只需2分鐘左右就能搞定了。所以,這看起來的確是一個不錯的工具。 在對該工具進(jìn)行改進(jìn)以消除假陽性并覆蓋更多的假陰性后,我們在DLink和PROLiNK路由器上進(jìn)行了測試。
PROLiNK PRC2402M
使用該工具,我們立即發(fā)現(xiàn)了近20個命令注入漏洞,其中10個不需要身份驗證,可以直接通過WAN接口進(jìn)行訪問。我們馬上向PROLiNK報告了這些漏洞,他們也很快做出了回應(yīng)。在這些漏洞被修復(fù)后,我們申請了相應(yīng)的CVE編號,它們分別為CVE-2021-35400到CVE-2021-35409。 下面是一些易受攻擊的代碼片段,其中source點(diǎn)和sink點(diǎn)分別是:
?Source點(diǎn):web_get
?Sink點(diǎn):system, do_system, popen
硬編碼的密碼,還是后門?
在這個過程中,我還發(fā)現(xiàn)了一些其他的安全漏洞。似乎有一個硬編碼的密碼或后門密碼,可以用來登錄到路由器的管理面板:管理頁面將用戶提供的密碼的md5哈希值發(fā)送到login.cgi進(jìn)行驗證,相應(yīng)的偽代碼如下所示:
然而,在它后面還有一段可疑的代碼:
通過將user作為密碼,我們竟然成功登錄到管理頁面。通過這種方式登錄后,顯示的儀表板會略有不同,它似乎比通過實(shí)際密碼登陸的用戶提供的功能要少。然而,我們?nèi)匀豢梢栽L問http://prc2402m.setup/setting.shtml,這就足以控制路由器的設(shè)置了。
我們向供應(yīng)商報告了這一情況,他們很快就更新了固件。為了確定該后門已經(jīng)消失,我再次打開了同一個函數(shù)。這次,我并沒有再看到strcat(salted_password, “user”),卻看到了以下內(nèi)容:
實(shí)際上,通過nvram查找Password_backup的值也不是什么難事。
我們很快又向供應(yīng)商報告了這個問題。幸運(yùn)的是,經(jīng)過再次修復(fù)后,已經(jīng)找不到Debugdoor或后門密碼了。
基于堆棧的緩沖區(qū)溢出
由于缺乏邊界檢查,我們還發(fā)現(xiàn)了許多基于堆棧的緩沖區(qū)溢出漏洞。通過利用這些漏洞,攻擊者可以覆蓋堆棧上的返回地址,從而獲得對程序執(zhí)行的控制。在上面的一些命令注入的例子中可以看到,用戶的輸入是通過sprintf函數(shù)而不是snprintf函數(shù)復(fù)制到一個字符串中的。
拒絕服務(wù)漏洞
在測試緩沖區(qū)溢出漏洞的PoC時,我還發(fā)現(xiàn)了一個漏洞,它導(dǎo)致路由器停止響應(yīng)請求,直到用電源按鈕手動重啟為止。在下面的偽代碼中,cli_num被作為參數(shù)傳給/sbin/sta_qos.sh腳本。
通過檢查腳本內(nèi)容,我發(fā)現(xiàn)以下for循環(huán),其中$sta_num用于保存cli_num的值。
如果cli_num是一個很大的值,例如999999999,那么這個腳本幾乎會永遠(yuǎn)困在循環(huán)中,實(shí)際上,這就是陷入了一個無限的循環(huán)。通過向路由器發(fā)送這樣的請求,會有很多這種腳本被執(zhí)行并卡在循環(huán)中。 一段時間后,路由器會停止響應(yīng)任何請求,這時,只能通過手動重啟,它們才能再次正常工作。
時間線
●6月9日:向供應(yīng)商報告了10個命令注入漏洞。
●6月11日:供應(yīng)商修復(fù)了相應(yīng)的漏洞。
●6月11日:建議供應(yīng)商使用一些額外的過濾器來防止此類漏洞。
●6月28日:供應(yīng)商根據(jù)我們的建議進(jìn)行了修復(fù)。
●7月9日:向供應(yīng)商報告了另外3個漏洞(后門、緩沖區(qū)溢出、DoS)。
●7月23日:供應(yīng)商進(jìn)行了修復(fù)。
DLink DIR-1960
除了PROLiNK路由器外,我們還在DIR-1960固件上運(yùn)行了該工具。這一次,該工具返回了近200個檢測結(jié)果。然而,在對結(jié)果進(jìn)行篩查后,發(fā)現(xiàn)只有4個是通過HNAP API的命令注入漏洞(我們已經(jīng)在前面報告過了),并且所有這些都需要認(rèn)證。(由此看來,在消除假陽性方面,還有很大的改進(jìn)空間!)
對于HNAP,這里簡單介紹一下:它是Home Network Administration Protocol的首字母縮寫,它實(shí)際上就是一個基于SOAP的協(xié)議,用于與路由器管理面板進(jìn)行通信。
DLink DIR-X1560
接著,我決定也在DIR-X1560固件上試一下這個工具。上面的兩個路由器都是基于MIPS架構(gòu)的,但DIR-X1560是在ARM處理器上運(yùn)行的。通過稍加調(diào)整,該工具就可以正確地分析基于ARM的固件。由此證明,這個工具具有很好的架構(gòu)兼容性,這讓我很開心。
然而,由于抽象層很多,識別固件上的漏洞并不那么簡單。有鑒于此,該工具在固件逆向工程中提供了巨大的幫助。我不確定固件所基于的框架的確切名稱,但我設(shè)法在GitHub上找到了一些源代碼,這非常有幫助,因為其中含有許多注釋。我在源代碼中找到的最接近的術(shù)語是CMS(CPE管理系統(tǒng))、CPE(客戶駐地設(shè)備)和TR-069。但是,請注意,此repo不包含任何特定于dlink的代碼,因此需要執(zhí)行一些逆向分析。
在我看來,它類似于MVC(模型-視圖-控制器)架構(gòu),盡管它也可能不是這樣的。
關(guān)于術(shù)語和縮略語的進(jìn)一步解釋,請參考這里。
DAL(數(shù)據(jù)聚合層)API,顧名思義,是用來與數(shù)據(jù)進(jìn)行交互的,主要是傳遞路由器的配置。但是數(shù)據(jù)的實(shí)際存儲是由MDM(內(nèi)存數(shù)據(jù)模式)和ODL(對象調(diào)度層)API完成的。DAL使用cmsObj_get和cmsObj_set函數(shù)(或其變體)作為與MDM/ODL的接口,以獲取或設(shè)置某些對象的值。例如,獲取IP_PING_DIAG MDM對象并將其存儲在ipPingObj中,然后在修改后將其保存回來的代碼如下所示:
下面解釋用到的參數(shù):
?MDMOID_DEV2_IP_PING_DIAG:一個枚舉變量,指定訪問IP_PING_DIAG對象。
?iidStack:一些我們不需要關(guān)心的內(nèi)部數(shù)據(jù)。
?ipPingObj:IP_PING_DIAG對象的內(nèi)容。
除此之外,還有RCL(運(yùn)行時配置層)和RUT(運(yùn)行時使用工具)API。每個MDM對象(例如MDMOID_DEV2_IP_PING_DIAG)都有一個相應(yīng)的RCL處理程序(rcl_dev2IpPingDiagObject)。每次調(diào)用cmsObj_set時,ODL都會調(diào)用該對象的RCL處理程序,它又會進(jìn)一步調(diào)用RUT的實(shí)用工具函數(shù)。
通過逆向分析,我們發(fā)現(xiàn)其工作流程如下所示:
1、用戶提出一個POST請求,與HNAP API進(jìn)行交互(例如SetTimeSettings)。
2、HNAP API處理程序調(diào)用DAL API(例如:cmsDal_setNtpCfgDLink_dev2)。
3、DAL API調(diào)用MDM/ODL API(cmsObj_set)來設(shè)置MDM對象(例如Dev2TimeDlinkObject)。 例如 cmsObj_set(MDMOID_DEV2_TIME_DLINK, &iidstack, 0, &timeDlinkObj)
4、ODL API調(diào)用RCL處理程序(例如rcl_dev2TimeDlinkObject)。
5、RCL處理程序調(diào)用RUT API(例如rut_TZ_Nvram_update)。
如果我們查看上面提到的HNAP和RUT函數(shù),我們會看到:
經(jīng)過漫長的旅程,NTPServer參數(shù)最終出現(xiàn)在一個傳遞給system的命令中。
正如我們在上面看到的,用戶輸入字符串(來自 HNAP)途徑許多函數(shù)后,最終到達(dá)system調(diào)用(在RUT中),從而導(dǎo)致命令注入漏洞。如果我手動查看固件,除非我很幸運(yùn),否則我?guī)缀跻ê荛L時間才能找到它。然而,在這個工具的幫助下,雖然無法直接實(shí)現(xiàn)HNAP到RUT的連接,但至少我能把相關(guān)的DAL函數(shù)列出來看看,從而節(jié)省了我很多時間。
DAL和RCL/RUT之間的關(guān)系
在這里,我們進(jìn)一步考場一下DAL API與RCL/RUT API的關(guān)系。其中,cmsDal_setNtpCfgDLink_dev2(如前所述由HNAP API調(diào)用的DAL API)的偽代碼如下所示:
上面的代碼片段展示了DAL函數(shù)設(shè)置/更新MDM對象的典型過程。請注意,cmsObj_get是以值為0x416的MDMOID(MDM對象ID)進(jìn)行調(diào)用的。由于沒有源代碼,我只看到值(0x416),而沒有看到枚舉名稱(MDMOID_DEV2_TIME_DLINK),這些都是從固件中的函數(shù)名稱和一些字符串推斷出來的。
如前所述,當(dāng)cmsObj_set被調(diào)用時,ODL API將調(diào)用相應(yīng)的RCL處理程序,在這種情況下是rcl_dev2TimeDlinkObject。我沒有研究cmsObj_set的實(shí)現(xiàn)細(xì)節(jié),因為它相當(dāng)復(fù)雜——需要執(zhí)行許多檢查和函數(shù)調(diào)用。如果你有興趣,可以考察這一行,它將調(diào)用RCL處理函數(shù)。
獲得MDMOID和RCL處理程序之間的映射關(guān)系并不困難,因為它存儲在固件的OID表中,具體如下所示:
在這個表中,我們很容易看出0x416是TimeDlink MDM對象的MDMOID,而rcl_dev2TimeDlinkObject是RCL處理程序。在這里,我們也看到了一個叫做STL處理程序的東西,但是它并沒有做太多事情。
現(xiàn)在,RCL處理程序rcl_dev2TimeDlinkObject看起來像下面這樣:
我們看到,newMdmObj被傳遞給易受攻擊的函數(shù)rut_TZ_Nvram_update(前面講過)。而這個newMdmObj正是剛才DAL函數(shù)傳遞給cmsObj_set的那個timeDlinkObj。所以,DAL和RCL的關(guān)系如下圖所示:
DAL會為cmsObj_set提供一個MDMOID和一個對象,然后
?MDMOID決定調(diào)用哪個RCL處理程序
?該對象被交給RCL處理程序進(jìn)行處理
我們看到,從一個DAL函數(shù)中,找出哪個RCL函數(shù)被調(diào)用并非難事,因為我們不僅有MDMOID,而且還可以參考上面的OID表。但在尋找命令注入漏洞時,步驟就反過來了。
首先,通過這個工具,我找到了可能有漏洞的RCL/RUT函數(shù),source點(diǎn)是函數(shù)的參數(shù),sink點(diǎn)是system函數(shù)(或其變體)。這里沒有什么新東西。但是現(xiàn)在,我需要找到訪問相關(guān)MDM對象的DAL函數(shù)。換句話說,就像上面一樣,雖然我知道MDMOID,但這次,我不是找RCL處理程序,而是回答下面的問題:哪些DAL函數(shù)用這個MDMOID調(diào)用cmsObj_set?
起初,我使用的是一種笨方法:逐一查看cmsObj_set的交叉引用,直到找到一個被調(diào)用的MDMOID正確為止。因為這里的交叉引用太多,足足有200多個,幾分鐘后,我就放棄了。所以,我決定用這個工具來幫助我過濾出使用某個MDMOID的函數(shù)。特別是,我只關(guān)心作為字符串/緩沖區(qū)的MDM對象字段。如果一個字段保存一個整數(shù)值,那么它對于命令注入或緩沖區(qū)溢出其實(shí)是沒有用的。
回顧一下,一個MDM對象的字符串字段是這樣設(shè)置的:
所以,我不需要對這個工具做太多的修改,只需把source點(diǎn)設(shè)置為cmsObj_get,把sink點(diǎn)設(shè)置為cmsMem_free即可。結(jié)果,我成功了。對于每個MDMOID,我過濾出了修改相關(guān)MDM對象的幾個DAL函數(shù)。然后,我檢查這些DAL函數(shù)的交叉引用,看它們是如何被HNAP API調(diào)用的,以找出用戶輸入是如何被傳入MDM對象的。 借助于這個工具,工作效率獲得了巨大提升,最后,我成功地在這個固件中找到了4個命令注入漏洞。
小結(jié)
目前,該工具仍處于開發(fā)的初級階段:只能挖掘命令注入漏洞。此外,在分析像DIR-X1560這樣復(fù)雜的固件時,仍然需要做一些手工工作,因為它不能自動判斷出哪些HNAP函數(shù)是易受攻擊的。我會繼續(xù)對這個工具進(jìn)行改進(jìn),希望有一天它能協(xié)助發(fā)現(xiàn)其他類型的漏洞,如固件中的緩沖區(qū)溢出、UAF等漏洞。
最后
我整理網(wǎng)絡(luò)安全相關(guān)的學(xué)習(xí)資料,有需要的可以關(guān)注私我或👇👇
【網(wǎng)安資料】
總結(jié)
以上是生活随笔為你收集整理的【建议收藏】这个工具专门用于寻找路由器中的安全漏洞的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 巅峰极客2021 what_pickle
- 下一篇: 【安全漏洞】Cisco命令注入漏洞CVE