寫在最前:一定要先將task3.sct
文件鏈接到項目中,具體操作后文有寫,而且我在附加內(nèi)容里解釋了sct文件的含義。
終于可以告別這個實踐了。大家在心得里可以加一句“任務(wù)量較大,建議減少任務(wù)量”嗎?
實驗三 FreeRTOS-MPU 保護繞過
實驗要求
MPU預(yù)設(shè)置:
a) 編寫 C 代碼實現(xiàn)基于 FreeRTOS-MPU v10.4 的提權(quán)代碼和指定函數(shù)查找
b) 利用溢出漏洞實現(xiàn)在 FreeRTOS MPU V10.4 版本的系統(tǒng)提權(quán)和 Flag 函數(shù)打印
子任務(wù)1
首先,和上一個實驗相似地,也是看一下.c
文件的結(jié)構(gòu)。
main
函數(shù):
① 定義無符號整型變量id
,賦值為學號末4位;
② 調(diào)用prvSetupHardware()
,硬件初始化;
③ 調(diào)用StartFreeRTOS(id, vTask3)
;
④ 使用for(;;)
讓程序不退出。
vTask3
的內(nèi)容,只有for(;;)
。
頭文件和lib文件的作用與上一個實驗差不多。
因此,所有任務(wù)要求都集中在StartFreeRTOS
里了,我們需要進行進一步的逆向分析。
逆向分析StartFreeRTOS
IDA Pro打開并反編譯StartFreeRTOS
如下:
可以看到,該函數(shù)大致有如下幾個操作:
① 將byte_7714
的地址指向的內(nèi)容拷貝給任務(wù)參數(shù)xTask3Parameters
;
② 設(shè)置任務(wù)參數(shù)xTask3Parameters
的函數(shù)指針為vTask3
;
③ val *= id;
④ 調(diào)用xTaskCreateRetricted
,結(jié)合任務(wù)參數(shù)xTask3Parameters
創(chuàng)建被約束的任務(wù);
⑤ 調(diào)用vTaskStartScheduler()
啟動任務(wù)序列。
我們要做的是在vTask3
中調(diào)用提權(quán)函數(shù),然后再調(diào)用打印flag的函數(shù)。
打印flag的函數(shù)好找,直接在IDA Pro的字符串窗口找就行,找到后雙擊點開,再查看引用:
總之能找到一個叫vTaskRemove
的函數(shù),它是無參數(shù)函數(shù),能夠打印flag。
找提權(quán)函數(shù),我一開始完全不知道怎么找。
直到看了下實驗講解的PPT,看到了下面這張圖:
可見,如果普通任務(wù)要使用內(nèi)核API,不能直接使用,而是要添加MPU_
,添加了這個的函數(shù),其中包含提權(quán)操作。而且,提權(quán)操作應(yīng)該是利用SVC
中斷。
因此,我們不妨在IDA Pro中隨便打開一個MPU_
的函數(shù)逆向分析一下,找到其中的提權(quán)操作。我以MPU_vTaskDelete
為例。IDA Pro中分析如下:
上圖中我框出來的就是提權(quán)操作??梢钥吹剑褪窍茸隽诵┻M入函數(shù)的壓棧操作,隨后把要提權(quán)的Task
傳入R4
,如果R4
不為0就跳轉(zhuǎn)提權(quán)函數(shù)(如果為0那么本身就是特權(quán)級)。然后再常規(guī)地執(zhí)行xTaskDelete
這個內(nèi)核API。
很明顯,提權(quán)函數(shù)就是xPortRaisePrivilege
,而且利用寄存器R4
傳參,所以它也是個無參數(shù)的函數(shù)。
先不急著把地址填入,因為這個地址在修改了vTask3
內(nèi)容之后會發(fā)生改變。先假裝已經(jīng)找到了地址,并在vTask3
中使用地址調(diào)用這些函數(shù),這樣,找到之后就只需要修改成對應(yīng)的地址。添加代碼如下:
注意,隨便填的地址不得為全零、不得相同,最好是填得像一點,否則二度修改后,地址又會變化。
void (*pPrivilege)();
void (*pFunc)();
pPrivilege=(void(*)())0x00001051;
pFunc=(void(*)())0x000029AD;
pPrivilege();
pFunc();
Rebuild后,去IDA Pro中查找函數(shù)地址。在使用地址調(diào)用函數(shù)的時候,需要加一。
需要注意的是,當函數(shù)地址已經(jīng)添加正確,但
log.txt
中卻連vTask3
都沒有的時候,很可能是項目的配置出了問題,如內(nèi)存地址分布,出問題的log.txt
如下:
此時必須要導(dǎo)入老師發(fā)的
task3.srt
,導(dǎo)入方式如下:
導(dǎo)入完成后,重新構(gòu)建項目,并重新逆向分析地址。(我導(dǎo)入后重新截了一遍圖)
sct具體的作用見下文的附加內(nèi)容1。
打印 Flag 函數(shù)名稱和地址
名稱:vTaskRemove
;
地址:0x000005F4
。
內(nèi)容如下圖所示:
用于提權(quán)的函數(shù)名稱和地址
名稱:xPortRaisePrivilege
;
地址:0x00008EDC
。
內(nèi)容如下圖所示:
填寫的代碼
注意,需要將上述找到的地址加1后再調(diào)用。填寫的代碼如下圖所示:
模擬運行截圖
運行并打印flag的結(jié)果如下:
log.txt如下:
之所以要把log.txt
也截圖出來,是因為之前分析的時候,可以看到xPortRaisePrivilege
函數(shù)提權(quán)的任務(wù)是R4
寄存器,而我們只是簡單地調(diào)用了該函數(shù),并未對R4
寄存器做處理??墒且渤晒α?。
查看log.txt
會發(fā)現(xiàn),并沒有明顯的對R4寄存器處理的內(nèi)容,最近一次賦值是prvSVCHandler的pop。我猜測有可能只需要執(zhí)行svc 2
中斷即可提權(quán),具體情況尚未明確,不過對本實驗無任何影響。
后來寫flag5報告的時候,意識到當R4為0時,提權(quán)的應(yīng)該就是本任務(wù);或者,不論R4的取值,都會對本任務(wù)提權(quán)。
附加內(nèi)容1:sct文件的作用
下圖左是老師發(fā)的sct文件,下圖右是軟件自動生成的sct文件。
sct文件也就是內(nèi)存映射布局文件。
一般情況,編譯的時候,只是讓所有的函數(shù)(如上圖中的.ANY (+R0)
)按照函數(shù)名稱排序放進內(nèi)存里,并沒有所謂特權(quán)訪問還是用戶訪問的訪問控制。
在我們設(shè)置了MPU之后,編譯并不會跟著受影響。因此,非特權(quán)函數(shù)可能就放入了我們自己規(guī)定的MPU中特權(quán)函數(shù)的那個區(qū)域,非特權(quán)函數(shù)保存在了特權(quán)函數(shù)的地址范圍,MPU就不會允許執(zhí)行了。
而老師修改后的sct文件,對API的位置重新布局,把特權(quán)函數(shù)privileged_functions
放到了第一段(就是MPU中只允許特權(quán)執(zhí)行的那個位置ER_IROM1
),把其他的放到了ER_IROM2
。并把特權(quán)數(shù)據(jù)privileged_data
放到了MPU中只允許特權(quán)可讀寫的段RW_IRAM1
,把其他的放到了RW_IRAM2
。
這樣,就會讓內(nèi)存映射與MPU設(shè)置一致了。
子任務(wù)2
逆向分析StartFreeRTOS
該任務(wù)逆向分析函數(shù)地址與上一個任務(wù)相似,在此不做贅述。
打印 Flag 函數(shù)名稱和地址
名稱:vTaskDelayBackup
;
地址:0x00001C7C
。
內(nèi)容如下:
用于提權(quán)的函數(shù)名稱和地址
名稱:xPortRaisePrivilege
;
地址:0x000086E2
。
內(nèi)容如下:
分析過程
該文件的main
函數(shù)超長,不過很簡單,如下圖所示:
暫時只能看到,最后輸入的字符串InputBuffer
長度最大為0x63。
再點開StartFreeRTOS:
只是運行了受約束的vTask3
任務(wù),其他啥也沒干。
由于這是傳參傳進來的,不能直接點開,所以先返回上一級,然后再點開vTask3
任務(wù):
vTask3
調(diào)用了Function()
,根據(jù)經(jīng)驗,這就是出問題的代碼了。
找到存在在溢出的緩沖區(qū)
點開Function
:
發(fā)現(xiàn)居然Function
傳進來了參數(shù),更重要的是出現(xiàn)了length
和InputBuffer
,而且賦值給HelperBuffer
,并且HelperBuffer
的大小只有12,說明HelperBuffer
可能就是溢出的緩沖區(qū)。
以匯編形式顯示Function
函數(shù)如下:
對PUSH和POP的解釋:ARM架構(gòu)的棧是遞減棧,PUSH的時候從右至左,POP的時候從左至右。
查看匯編之后,會發(fā)現(xiàn)其實嚴格來說,壓根就沒有什么溢出緩沖區(qū)。Function
函數(shù),它先push
了4個寄存器,然后在函數(shù)的最開始使用mov buffer, sp
,直接改變棧頂指針sp,接下來立馬用InputBuffer
的內(nèi)容逐一填充buffer
(在我的IDA中該變量名被解析成HelpBuffer
)。最后pop
的時候,pop
的內(nèi)容不就是HelpBuffer[0]
、HelpBuffer[1]
、HelpBuffer[2]
等嗎?
覆蓋寄存器的值,就是Function
的目的,也是設(shè)置buffer
的作用,它就是想往棧上寫、往寄存器上寫。
既然,該緩沖區(qū)的每一個比特,目的都是向不應(yīng)該寫的寄存器或棧上寫數(shù)據(jù),它就壓根沒有與正常功能有關(guān)的部分。就像拿一張紙畫畫,正常操作畫在紙上,但是涂多了就溢出到桌上了,這張紙叫做溢出緩沖區(qū);而這個代碼就是連紙都沒有。
綜上,我認為這并不是一個典型的緩沖區(qū)溢出代碼。如果非要說有個溢出緩沖區(qū),那就是故意構(gòu)造的這個HelpBuffer
,而且該溢出緩沖區(qū)的長度為0,沒有正常功能。
棧示意圖
點開Function的bp,可以看到IDA Pro中的棧幀。
不過,這并不能很好地解釋棧中的內(nèi)容。我重新繪制了棧示意圖。
當執(zhí)行mov buffer, sp
前后的棧示意圖如下:
LR(Function)需要覆蓋成提權(quán)函數(shù),而調(diào)用提權(quán)函數(shù)之后,還需要調(diào)用打印flag的函數(shù),所以還要構(gòu)造溢出提權(quán)調(diào)用的返回地址的棧結(jié)構(gòu)。完全構(gòu)造完畢的棧示意圖在后續(xù)“溢出提權(quán)”中會畫出。
溢出提權(quán)
溢出提權(quán)調(diào)用xPortRaisePrivilege
,地址是0x000086E2
。該函數(shù)第一行是PUSH {xRunningPrivileged,LR}
。如果執(zhí)行這一句,就會改變我們已經(jīng)構(gòu)造好的棧幀結(jié)構(gòu),也會導(dǎo)致返回地址無法被覆蓋,因此需要跳過這一句,從地址0x000086E4
開始。再加上基地址是1,因此LR(Function)
需要被覆蓋成0x000086E5
。
順利進入并執(zhí)行提權(quán)函數(shù)后,還要繼續(xù)執(zhí)行打印flag的函數(shù)。返回時執(zhí)行了POP {xRunningPrivileged,LR}
因此需要構(gòu)造8個HelpBuffer
字節(jié),完整的棧示意圖如下:
覆蓋返回地址的解析過程
在Function
中返回值被覆蓋成xPortRaisePrivilege
第二行地址加1,即0x000086E5
,因此它返回時會從xPortRaisePrivilege
的第二行代碼開始執(zhí)行,并完成提權(quán);
在xPortRaisePrivilege
中返回值被覆蓋成vTaskDelayBackup
地址加1,即0x00001C7C
,因此它返回時會從vTaskDelayBackup
的第一行代碼執(zhí)行,并完成flag打印。
模擬運行截圖
文章來源:http://www.zghlxwxcb.cn/news/detail-489669.html
完結(jié)撒花!?。?span toymoban-style="hidden">文章來源地址http://www.zghlxwxcb.cn/news/detail-489669.html
到了這里,關(guān)于【HUST】網(wǎng)絡(luò)攻防實踐|6_物聯(lián)網(wǎng)設(shè)備固件安全實驗|實驗三 FreeRTOS-MPU 保護繞過的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!