# [SFuzz: Slice-based Fuzzing for Real-Time Operating Systems](https://huhong789.github.io/papers/chen:sfuzz.pdf)
## Abstract
* RTOS 是現在嵌入式系統的主要類別之一
* 缺乏安全機制,設備容易受攻擊
* RTOS 常把任務與服務整合成一個 binary file,造成程式分析的困難
* 提出 SFuzz,基於切片的模糊器
* 將複雜的 Binary file 切成許多任務
* SFuzz 先辨識 user input 的 function,建構出 call graphs,再利用前向切片修剪路徑
* 接著會做 coverage-guided fuzzing
* 最後利用前向和後向切片驗證路徑,確定漏洞是否誤報
* 在 35 個 RTOS 樣本上發現 77 個 zero-day 漏洞,67 個被分配 CVE 或 CNVD (中國的 CVE)
## Introduction
* 為了應付 Real-time tasks,RTOS 捨棄 kernel space 和 user space 的隔離,帶來更多潛在威脅
* 在 local network 沒差,但很多設備直接連到 Internet
* RTOS 常以 blob-firmware 格式呈現,無法在一般 CPU 上執行,使大部分錯誤檢測無法正常運作
* 靜態分析部分,因為 binary 檔很大,常遇到路徑爆炸等問題
* 動態分析部分,需要實際設備或是依賴穩定的模擬
* 皆缺乏可擴展性
* RTOS 可以劃分成許多獨立任務,且任務如果是同一類型,他們的 data flow 可能也是相似的
* 搜尋從外部進入點到 sink function(如 memcpy 等) 的 data flow,將任務切分
* 只要切的夠小,就可以降低 Fuzzing 的模擬難度和分析複雜度
---
* SFuzz 利用前向切片建構一個 code space,接著跑 Fuzzing
* 結合反向切片來執行 concolic,驗證崩潰輸入
* 四個主要元件
* Forward Slicer
* 識別 user input function
* 修改與輸入無關的條件分支,確保控制流可以到這些地方
* 透過 DMA 覆蓋 data-sharing paradigm,前向切片器可以擴展到不同的任務
* 不太懂
* Control Flow Node Handler
* 如果直接 Fuzz 片段的 code,會因為缺少上下文和執行狀態而失敗
* 處理與 user input 無關的部分提高 Fuzz 穩定性
* Micro Fuzzing
* 透過指令級模擬執行上下文 (user mode)
* Concolic Analyzer
* 符號化被忽略 function 的上下文,利用 concolice 提供具體值
* 基於 Ghidra, UnicornAFL 寫了 6200 行 Python、4300 行 C 和 5100 行 Java 實現
* 為了驗證有效性,應用於 11 個供應商的 35 個韌體
* 發現 77 個未知漏洞
* 優於其他工具
---
* 提出基於切片的方式測試 RTOS
* 透過模擬執行 Fuzz,有效檢測 RTOS 韌體漏洞
* 從 35 個真實世界的韌體發現 77 個未知漏洞
* 已分配 86 個 CVE/CNVD ID
## Problem and Approach Overview
### RTOS and Embedded Devices
* 因為空間限制和要求快速回應,將所有功能編譯成單個 binary
* strip 減少大小
* 造成分析困難
### Motivation Example
```c=
void devDiscoverHandle (int sockfd) {
int len , ret;
struct sockaddr_in src_addr;
int addrlen = sizeof(struct sockaddr_in );
memset ((uint8 *)&src_addr , 0, 0x10);
memset(Global_addr , 0, 0x5C0);
len = recvfrom(sockfd , Global_addr +0x1c , 0x5a4 , 0, (struct sockaddr *)&src_addr , ( socklen_t *)&addrlen);
if ( len != ERROR )
ret = protocol_handler (( packet *)( Global_addr +0 x1c));
if (ret == ERROR)
logOutput(" devDiscoverHandle Error!");
}
int protocol_handler (packet *data) {
bytes [4] = {0xe1 , 0x2b , 0x83 , 0xc7 };
if ( header_check (data))
if ( magic_check (data ->magic_bytes , bytes , 4))
if (checksum(data))
return msg_handler (data);
return ERROR;
}
int msg_handler (packet *data) {
int ret = ERROR;
if (data ->version == 0x01)
ret= parse_advertisement (data ->payload , data ->payloadLen );
return ret;
}
int parse_advertisement (uint8 *payload , int payloadLen) {
char* dst;
char* var_addr;
char buffer [64];
int index;
var_addr = DAT_404d33a8 ;
msg_element *element;
msg_element_header * element_header ;
element = parse_msg_element (payload , payloadLen );
element_header = element ->header;
if ( element_header ) {
index = (int)*( var_addr +4));
dst = buffer+index;
if ( copy_msg_element ((char *) element ->data , dst ,
ment_header ->len)) == 0) // Stack Overflow
return SUCCESS;
}
return ERROR;
}
```
* 第 40 行有 BOF
* 利用 SFuzz 發現 TP-Link WDR7660 的這個錯誤
* CVE-2020-28877
* 第 7 行接收外部數據,9~44 行處理
* 18 行根據版本去 call 對應的處理程式
* 35~36 分析標頭,並在 40 行複製到內存空間
* 建構 package 的 len 值,即可造成 BOF
* 現有工作難以分析這種 case
### Necessity and Reasonability of SFuzz
* 因為 code 邏輯太複雜,動態分析會比靜態適合
* 在沒有人工分析和穩健的全系統方法下,使用的方法是基於功能獨立的做 Fuzz
* slice-based fuzzing
* 如果收集了一個程式切片,包含接收和處理的完整 function,配合 Fuzzing 和指令級模擬就能有效找出漏洞
* 能忽略模擬時的困難
* 因為執行時缺少完整的上下文,需要處理與外部無關的控制流節點 (引導程式走到想要的位址)
* 發現漏洞後就根據 input 進行 concolic,幫助判斷是否為誤報
* 靜態分析的部分只有切片找出風險 function,減少路徑爆炸的問題
---
* 分析了四個設備,搜尋所有數據 input 點 (如 recvfrom()),並將他們的調用 function 當作 root,搜尋下面所有子 function
* 
* 發現 function 的重複率很低,表示 function 之間很獨立
* 全域變數也是
### Challenges of Slice-based Fuzzing
* 三個主要挑戰
1. 如何確定切片範圍
2. 如何處理切片中控制流的點
3. 如何有效進行模糊測試和驗證 PoC
## Design
### Approach Overview
* 
* 輸入是 RTOS 的 Firmware,輸出是 code 中的漏洞
### Forward Slicer
* Sensitive Call Graph Constructor 檢測輸入獲取的 function 和全域數據讀取點,將這些 function 的 caller 作為 root 建構圖
* Call Graph Pruning 修剪路徑
* 不易受攻擊的分支會被修剪
* Call Graph Stitching 拼接不同圖之間的邊
#### Call Graph Pruning
* 利用汙點分析過濾路徑
#### Call Graph Stitching
* set_env 和 get_env 等等的外部輸入可能會被 data sharing paradigms 中斷
* 是否跳轉到這種讀取點根據隨機機率
* 不太懂
### Control Flow Nodes Handler
* 經過 Forward Slicer 可以得到 call graph tree
* 但為了 Fuzzing 順利,需要處理不必要的控制流
* 缺少 RTOS 上下文,需要指導 fuzzer 走到正確的分支
#### Call Instruction
* 如果發現路徑上有 function 的參數和回傳值都不會影響 user input,fuzzing 時就跳過他
#### Conditional Branch
* 如果期間發生分支,且只有一個分支能到達 recv
* 若條件受到 input 影響,盡量避免 fuzzer 到這裡
* 否則讓分支無條件跳轉至目標分支
* 如果期間發生分支,且兩個分支都能到達 recv
* 隨機跳轉
* 如果期間發生分支,且兩個分支都無法到達 recv
* 直接退出當前路徑探索
### Micro Fuzzing
* 本篇基於切片的 Fuzzing 技術取名為 Micro Fuzzing
#### Image Loader
* 讀取完韌體後,經由前面的元件得到切片過的程式碼區段,還有對應要跳過或是另外處理的 Function or Address
* call function 改為 NOP
* Address 根據目標設為 Avoid Explore 或是退出、跳轉等操作
#### Fuzzing Engine
* 基於 UnicornAFL
* 卡住時會呼叫 concolic,將輸入符號化,求解強制走之前沒走過的地方
* 時間內沒找到新路徑就退出
* 不太確定為何是 concolic 不是 symbolic
* 跳過硬體模擬相關的指令,這些通常與中斷相關,不影響數據流
#### Memory Safety Policies
* memory 區塊可分為兩種
* 可利用靜態分析得到大小
* 執行到 sink function 時判斷是否溢出
* 不可利用...
* concolic 階段確認大小
### Concolic Analyzer
* Fuzzer 退出後,Concolic Analyzer 會檢查所有崩潰輸入
* 給定具體輸入與目標路徑,求解
* 因為修剪了 function 和對分支做過處理,才需要重新檢查是否誤報

* recv 是第 8 行,sink 是 24 行
* stitched by data sharing paradigms,同一個 function 在第 17 行
* 利用 concolic 找出 9、10 的回傳值,符合 11 13 16 的分支條件
* 9 10 可能受到其他輸入影響造成假陰性
* sink 的大小會可也造成假陽性或假陰性
* 利用反向追蹤,會定位出 9 10 14 為 source
* 如果依然可以到達 recv 表示 11 行是否進入根本不重要
## Implementation
* 使用 6200 行 Python、4300 行 C 與 5100 行 Java 完成原型
* 汙點分析和語意恢復利用 Ghidra 完成
* Fuzzing 基於 UnicornAFL 與 Driller、concolic 分析器基於 Angr
* 擴展 Driller 使其適用 RTOS
### Image Extraction
* BinWalk 提取
### Base Address Recognition
* 需要知道 Base Address 才能正確做跳轉
* 從系統中的 data reference pointers 提取
* 基於 PCode 完成,這是 Ghidra 的中間語言,因此可以支援更多架構
### Function Semantic Reconstruction
* 希望恢復三種類型的 Funciton
* 接收外部輸入
* sink function (memcpy 等)
* set or get 全域資料
* Symbol File & Log Function
* 一些廠商會發布 symbol file 可以幫助恢復
* Virtual Execution
* 比較 function 的參數和 return 的數量,和標準函式庫比對
* 找出 strcpy 等等
* 也會比較對記憶體的影響
* 找出 memcpy 或 printf 等等
* Web Service Semantic
* 利用前端和後端的標記 user input 來匹配 web 相關的 function
* SaTC
* Open source firmware
* 直接用開源的
## Evaluation
* RQ
* RQ1: SFuzz 是否能發現 RTOS 真實漏洞?
* RQ2: SFuzz 每個部分都對發現漏洞是必要的?與最先進的工具比較表現如何?
* RQ3: SFuzz 每一步發現漏洞都準確嗎?
* 
* 11 個供應商 35 個韌體
* 包含 23 台路由器、7 台印表機、2 台防火牆和一台 Brain-Computer Interface(人機介面)
#### Environment Setup
* Ubuntu 18.04 host with a RAM of 256 GB and a 32-core Intel Xeon Processor of 2.4 GHz
* 每個 Fuzz 跑六小時,一個 CPU core 去跑
* 過了這個時間後就都沒找到新路徑或崩潰
#### Experiments Design
* 
* SFuzz-Handler: 不處理控制流
* SFuzz-FHandler: 只處理條件分支
* SFuzz-CHandler: 只處理 Call Function 刪減
#### Bug Confirmation
* 手動驗證每個警報是否為真的錯誤
### Real-world Vulnerabilities
* 發現 77 個錯誤
* 
* 左邊是在哪些 input
* 右邊是在哪些任務
* 
* 其實所有 Fuzzer 都有發現漏洞
* UnicornAFL 和 SFuzz-Handler 能探索的路徑很少
* SFuzz-FHandler 加上分支判斷後,探索路徑加了很多,但 bug 沒變
* SFuzz-CHandler 加上 Function 剪枝後路徑只加一點,但能發現的 bug 變為 17 個
* SFuzz 優於所有工具
#### Stability
* 
* Fuzzing 時的模擬成功率
* 分支處理會造成成功率下降
* Function 處理會讓成功率大幅上升
#### Efficiency
* 
* 越複雜需要跑越久
* 不知道什麼時候算結束
### Comparison with Symbolic Execution
* 
* 修改 angr 讓他可以跑 RTOS
* SFuzz 速度最快、路徑看到最多
* 有些比較少是因為太快找到漏洞了
* 加上 Control Flow Nodes Handler 可以讓一般的 SE 提高性能
* 不清楚這邊到底是給定什麼
* source and sink 然後看什麼時候找到路徑?
### Accuracy and Efficiency
#### Semantic Reconstruction
* 31 個樣本中可以恢復 25 個 base address
* 剩下要手動修復
#### Forward Slicer
* 每個切片程式平均占全部的 1.52%
* 有效節省分析工作
#### Micro Fuzzing
* 平均一棵樹分析 7~30 分鐘
* 一整個模型總分析從 20 分鐘 ~ 21 小時不等,取決於樹的複雜度
#### Concolic Analyzer
* 在 Fuzzer 找到的 302 個 Crash 中,115 個發出警報
* 其中有 99 個真的是 bug
* GitHub 上介紹成因和如何確認
## Related Work
#### RTOS Security
#### Symbolic Execution
#### Greybox Fuzzing & Dynamic testing
#### Code Fragment Execution
## Conclusion
* 提出了 SFuzz,基於切片的 Fuzzing 方法,用於檢測 RTOS 漏洞
* 發現 20 個設備中的 77 個 zero-day,其中 67 個被分配 CVE/CNVD ID
* 實驗證明優於最先進的工具
###### tags: `paper`