作者:哈霓@螞蟻安全實驗室
原文鏈接:https://mp.weixin.qq.com/s/WzzCFQBgg7BcVUFWT8npuQ
在今年的Black Hat Asia上,螞蟻安全實驗室共入選了5個議題和3個工具。本期分享的是螞蟻天宸實驗室的議題《自動化挖掘gRPC網絡接口漏洞》。
01 簡介
隨著移動互聯網和工業互聯網的快速發展,大量開發者、廠商不得不關注網絡通訊的實效性和效能問題,而gRPC框架正是基于這樣的浪潮下應運而生的網絡通訊框架。它是一個高可用、開源的網絡庫,提供了跨平臺的接入能力,在網絡開銷、性能、安全性上相比HTTP通訊有著極大的優勢。然而對于這樣的基礎設施,業界很少有以端到云的協議通訊視角,進行漏洞挖掘技術的文章。

本次螞蟻安全實驗室的曹世杰,在Black Hat Asia 2021的議題《基于gRPC協議封裝的移動/ IoT應用程序的安全漏洞狩獵》中, 對這個方向進行了相關技術分享。
在本議題中,我們提出了一種方法,該方法突破了端上在RPC協議漏洞挖掘的限制,可以為使用gRPC框架的應用程序自動挖掘服務器接口漏洞。
02 技術挑戰
業界針對gRPC框架在RPC協議下的漏洞挖掘,一直缺少相應方案進行介紹,原因是:
-
協議包構造問題: gRPC框架具有特定的組包邏輯,并且傳輸過程中使用了Protobuf進行數據序列化,在沒有理解gRPC框架組包過程的情況下,很難構造一個具有攻擊效果的網絡數據包。
-
端上安全機制繞過: 在客戶端應用程序中通常會內置數據簽名等安全保護機制。該機制導致在不注入應用程序的情況下,構造的網絡數據包極有可能會存在安全校驗失敗的問題。
由于這些特性,使得在RPC協議下的應用程序,漏洞挖掘變得十分困難。而本次我們在會議中提到的方案,正是解決了客戶端上RPC通訊的漏洞挖掘問題。

這應該是首個基于gRPC框架的自動化漏洞挖掘的議題,基于這個框架,我們發現了若干gRPC網絡服務的RCE漏洞。這套方案的理念是:從端安全技術的視角,實現對不同應用RPC請求的數據獲取與重放能力,以及快速拔插的HUB能力,將不同應用接入到該方案,可將傳統WEB漏洞挖掘的思路應用到移動終端。另外一方面,將RPC接口的脆弱性分析部署在云端,極大地提高了漏洞挖掘的準確率和效率。
03 什么是gRPC
RPC協議稱為遠程過程調用,此協議已應用于大量的互聯網移動應用程序和IoT設備。該協議的優點是,在C/S模式通信過程中,應用程序僅需知道遠程服務的名稱和訪問遠程服務的輸入參數,即可獲取遠程服務的資源。
gRPC是Google提供的,高度可用的RPC框架,已經大規模應用于移動App以及工業互聯網領域,在Http2.0以及Protobuf的加持下,具有傳輸更快、并且能夠在傳輸期間壓縮網絡數據的優點。

04 gRPC框架的組包流程
應用程序通過gRPC框架構造RPC請求的示意圖如下。一個RPC請求,由不同的SDK參與構造,其中紅色部分,代表網絡通訊中不可或缺的數據,包含了Cookie與OperationType。OperationType可以理解為是HTTP請求的路徑,告訴服務端網關當前請求對應的RPC應用服務。
而橙色部分,是應用在性能與安全層面進行的優化:1. 通過操作系統提供的API如CCCrypt或者SecurityEnclave實現數據的簽名,部分注重安全的廠商會使用自研的安全SDK通過黑盒算法進行簽名。2. 將部分設備的環境安全檢測信息,放入RPC Header中用于后期的風險防控。3. 通過Protobuf生成Proto對象,壓縮網絡字節,提高網絡效率。

05 構建測試系統
針對不同的應用程序,我們開發了大量工具鏈用于快速開發注入的動態庫,實現了端上流量獲取以及重放的能力,并且可快捷接入到RPC重放網絡,提供對外調用接口。這樣的好處是,在漏洞挖掘的過程中,我們將不必關注端上的實現邏輯,僅僅通過幾行代碼就能實現模擬登錄、自動重放的能力。

在整個工程中,我們在工具鏈中實現的技術包括:
-
IDAPython調用鏈分析工具:主要基于IDAPython對gRPC網絡接口推導應用程序網絡庫的函數調用關系,尋找封裝gRPC的網絡庫API。并通過葉子節點尋找構造gRPC數據包所依賴的其他接口(如簽名接口、安全檢測接口),從而尋找Hook切入點,以及方便接入人員構造注入代碼。
-
安全模塊:通用的安全檢測對抗模塊,集成通用的安全對抗代碼,用于繞過證書校驗、模塊檢查機制。另外針對不同應用程序,通過反射調用的方法實現擴展能力:用于構造協議中的數據簽名、解析Protobuf數據塊。
-
網關注冊模塊:任務網關的接入模塊,負責與任務網關進行設備注冊、心跳報活、任務執行。
-
任務執行模塊:執行網關注冊模塊下發的任務,基于gRPC框架構造通用的RPC報文,借助安全模塊實現簽名、數據篡改和安全環境繞過,并進行重放。并負責將重放的返回結果,通過網關注冊模塊返回給任務中心。
-
守護進程模塊:保障任務執行設備在遇到Crash的情況下能夠主動重啟任務進程。
-
自動登錄模塊:用于對不同的應用程序實現模擬登錄的能力,通過RPC請求的返回值截獲Response中所獲取的Cookie,從而在任務執行的過程中自動帶上獲取的登陸態信息。
下面我們會對其中的一些工具鏈技術進行介紹:
5.1 通過IDAPython進行網絡庫推導
如上圖所示,通過對gRPC網絡庫的分析,需要梳理出一批接口,作為底層函數向上分析的關系鏈。以iOS為例,這里的難點在于,iOS應用程序使用了objc_msgsend,以消息通知的方式進行函數調用,所以IDA是無法直接獲取關系鏈的。于是,為了自動推到葉子結點(也就是網絡庫的根函數),我們使用了下面的方法:

技術點:
1.基于消息的調用方式,需要對sel進行2次關系鏈查詢。
2.引入了Symbol Execution, 需要對引用的Position調用idc.GetDisasm獲取上下文并執行推導,來校驗objc_msgsend的Class是不是Target Function引用的Class,否則會出現大量的結果不準確問題。

5.2 重放能力的構建
為了能夠運行大規模的漏洞測試任務,我們使用了真實的設備構建了任務執行平臺。這個平臺的優勢是:
(1)基于gRPC框架,實現了與真實設備的網絡通訊,使得任務下發到執行可以在1-2秒完成。
(2)每臺設備的應用程序,都具備守護進程和心跳包,保障任務調度的穩定性,保障平臺本身的可用性。

當我們的RPC模塊集成到應用程序中,就會向網關對當前設備進行注冊。我們定義了一種設備注冊的網絡格式,用于后期對不同設備不同應用的鑒別,使不同的漏洞測試任務能夠快速下發到指定的應用。注冊設備的報文如下:
// 客戶端
Client Heartbeat package :
client = GRPCAgent.instance
client.worker = nil;
client.platfrom = @"iOS/Android";
client.isHeartBeat = True;
client.isRun = FALSE;
client.taskLock = Object;
// 服務端
Server Side register:
executor_info = {
'tenant': device_tenant,
'did': device_id,
'type': device_type,
'name': device_name,
'cluster': device_cluster,
'others': device_others,
'last_update': now,
'metrics': mobilegw_pb2.DeviceMetrics(),
'last_sync_timestamp': now,
'task_runner': 0,
'app_version': device_app_version,
'hook_version': device_hook_version
}
相比Android設備,iOS的守護進程保護,顯得更加困難。在iOS 8的設備中,我們應用了Mirmir(這是Cydia提供的一個多進程運行的越獄程序,在iOS 8以后就不能使用)對程序本身進行了保護。對于iOS 9以后的越獄設備,我們通過注冊守護進程,來實現對應用程序的保護監控:
-
deamon進程通過注冊消息通知的方式,監聽全局消息。
-
應用程序在遇到不可抗力的Crash崩潰時,通知我們的deamon進程,延時2秒后重新喚起我們的應用程序。
相關代碼如下:
void run_cmd(char *cmd)
{
pid_t pid;
const char *argv[] = {"sh", "-c", cmd, NULL};
int status;
status = posix_spawn(&pid, "/bin/sh", NULL, NULL, (char* const*)argv, nil);
if (status == 0) {
if (waitpid(pid, &status, 0) == -1) {
perror("waitpid");
}
}
}
static void Monitor(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
sleep(2)
run_cmd("killall -9 target process");
run_cmd("open bundleid");
}
int main(int argc, char *argv[], char *envp[]) {
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, Monitor, CFSTR("com.sec.iosecdamond"), NULL,CFNotificationSuspensionBehaviorCoalesce);
CFRunLoopRun(); // keep it running in backgroundcom.sec.iosecdamond
return 0;
}
5.3 端上安全機制繞過
在程序中我們會通過注入動態庫的方式實現終端上任務執行的能力,然而部分應用程序內部有著安全保護機制,會導致任務執行模塊出錯甚至異常。所以,我們實現了獨立的對抗邏輯,幫助我們繞過程序中的安全檢測機制。下面會介紹我們碰到的部分對抗手法和一些繞過思路:
(1)堆棧環境檢測:在反射調用簽名函數的過程中,部分應用程序會通過調用的堆棧節點來判斷調用來源是否可信,通常會使用backtrace來判斷調用上游的目標函數是否滿足預期。

而我們的對抗思路,并不是對trace函數進行Hook,而是通過5.1推導的網絡庫的葉子結點,尋找應用層的加簽函數,從而構造正確的簽名數據。

(2)部分應用對符號替換的Hook方式有針對性檢測,比如通過dladdr對進程中模塊的符號表進行遍歷檢測,來識別符號替換的Hook風險。

這里的繞過方式,我們應用inlinehook對目標函數進行控制,并且,在目前Hook代碼中,我們通過修改函數目標地址的一行指令來對應用程序API進行Hook。這樣的優勢是,對函數本身的修改降為最低,篡改痕跡更小,也更難以發現。
上述的對抗場景屬于我們對個別應用發現存在的校驗機制,除此也會有部分通用的端安全對抗邏輯保持默認開啟,規避端上安全檢測對任務執行帶來的影響:
(1)通過對libboringssl.dylib的SSLCreateContext、SSLHandshake、SSLSetSessionOption、tls_helper_create_peer_trust函數進行Hook,強制不進行證書校驗。
(2)對_dyld_get_image_name()函數Hook并將我們注入的任務執行模塊偽裝為系統模塊。
5.4 Protobuf數據還原
我們發現大量的應用,使用了Protobuf的格式對數據進行了編碼,如果沒有還原Protobuf數據的能力,我們就無法對應用Protobuf的網絡接口進行漏洞測試。
由于我們沒有Proto文件,所以我們需要有方法對數據進行還原。經過分析,在沒有對應數據對象的情況下,是可基于字節碼數據對Protobuf數據格式進行原始結構的還原。
Protobuf有自己的格式結構,需要對數據塊進行解析。Protobuf數據包含了自己的數據頭和數據內容。通常數據頭中包含了:1. 字段數,2. 數據起始位置,3. 數據類型。
需要注意的是,部分數據類型可能會告訴你仍然是一個對象,所以需要使用遞歸的方式進行層層解析,直到還原出每一個字段的具體數據。

06 流量抽樣 & 漏洞測試
應用的流量是非常大的,我們對流量進行了采集和抽樣,提高了漏洞挖掘的效率和檢出率。
6.1 數據抽樣
我們對不同的應用程序,在不同的場景進行數據采集,經過實驗,如果我們對每一條數據都進行測試,會導致我們在漏洞挖掘的效率上大打折扣。我們希望盡可能覆蓋全面,能夠對每個接口進行有效的測試:
(1)隨機散列算法:對于初始的大量數據,我們希望盡可能地保證每種RPC請求都能夠覆蓋,但又能夠把大數化小,這樣能夠保障在有足夠覆蓋率的情況下,不會消耗太多數據庫性能。

(2)分群抽樣算法:經過(1)的處理,仍然會出現部分請求,存在大量相似的數據。比如上圖中的接口1,仍然會存在同一個接口有2GB數據,也就是數據分析中遇到的數據傾斜問題。這時需要使用分群抽樣算法,這種算法能夠保證對已有數據,均勻抽取固定的數據量,最后會把大量的數據壓縮到一個很小的數據集提供給后續的漏洞挖掘流程。

如下面的代碼,使用cluster_sample對operationType進行分群,并且把flag=True的結果進行select檢出,我們可以均勻地對每個operationType的數據體抽樣10條數據進行分析。
cluster_sample(bigint <x>[, bigint <y>])
over (partition by <col1>[, <col2>...])
select tmp.operationType, tmp.request_data, tmp.response_data
from (
select operationType, request_data, response_data, cluster_sample(operationType, 10) as flag
from grpc_table
) tmp
where tmp.flag = true;
6.2 脆弱性分析
通過上面的方法,我們可以知道,怎么把數據從大化小,提高漏洞掃描的效率。除此之外,我們將一些接口脆弱性分析的邏輯,也內置到了云端進行處理。

- 1.接口私有性判斷:基于數據的漏洞挖掘,我們提出接口“私有性”的度量方法,可以理解為:如果一個接口具備漏洞利用的價值,數據必定與用戶個人相關,這類接口定義為私有性接口。我們通過對同一個接口的不同數據進行聚合,如果數據結果的聚合數量與數據總量的比越趨近于1,則越趨向于私有性接口。
F(p) = Group ( OperationType (Count (Distinct Request/Response) / Count (Distinct User))
Group代表一個分群,如果針對一個OperationType的分群結果,如果請求/返回的結果與測試賬號個數的比趨近于1,則接口私有性越強;
- 2.相似度分析:比較不同測試賬號的返回結果的相似性。對于不同賬號,我們會構造同樣的參數進行驗證,如果出現越接近相似的結果,則越可能出現越權漏洞相關風險。相似度的判斷,涉及了對請求參數,返回結果向量化的運算,由于篇幅問題,就不深入展開,感興趣的同學可以在網上搜索相關文章。
此外,我們也加入了其他的規則輔助分析:比如通過對接口的抽樣數據,分析返回結果中是否包含了敏感參數,這些規則可以通過正則表達式進行分析。以及分析數據的有效性,如:請求的結果,如果僅僅是一些無效的內容,比如日期、一些公共的id,則不會有太大的價值。我們還會維護一個全局的白名單,產生誤報后,會加入到白名單里進行過濾,防止二次誤報。
07 使用這個方法,發現的漏洞
7.1 越權類漏洞

思路:
- 用不同的測試賬號,對于相同屬性的業務參數(如policyList),去探測其他用戶的敏感數據(修改汽車車牌號以及電話號碼獲取他人車輛違章信息)。
案例:
- 圖中的例子,在這個請求中,我們可以看到gRPC服務端,允許輸入任意的車牌號和電話號碼,以至于我們填入正確的數據時,可以看到其他人的車輛違章信息,并可以進行修改。導致這樣漏洞的原因,是因為服務端并沒有校驗當前的用戶登陸態,是否有對應業務數據的訪問以及修改權限,從而導致了邏輯越權的安全風險。
7.2 SSRF漏洞

思路:
- 猜測服務端對請求中的URL參數解析的可能,嘗試替換為自己的測試域名,在測試域名掛起代理服務,請求后,判斷是否會收到內部應用的網絡請求。
案例:
- 從圖中的調用鏈路來看,我們發現,從真實的攻擊鏈路來看,其實resourceUrl經過了多個服務端傳遞,這也是企業面臨的困難,當一個攻擊payload請求到服務端后,任何一個服務器節點都可能成為被攻擊的對象。
7.3 Fastjson漏洞

思路:
- 根據請求特征,判斷請求中的參數,是否可能被后端進行反序列化,用于執行惡意代碼(示例中的@”type”特征),并嵌入Fastjson payload。
案例:
- 這是一個因為日志服務導致的遠程RCE漏洞。當時我們發現了一個請求中帶有 “@type”的字符串,我們猜測有可能是本地的數據,被服務端進行反序列化。所以,我們在請求中植入了一個Fastjson的遠程命令執行的payload。而我們從白天等到了晚上,當我們都以為可能沒有成功時,我們收到了相應結果,確認執行成功。因為最終觸發的安全漏洞,并不是我們攻擊的服務器,而是因為這臺服務器將數據以日志的方式存儲之后,這些日志被另外一臺服務器解析,解析的過程中使用了Fastjson反序列化,從而執行了參數中具有反序列化漏洞的字符串。
08 該方案的局限性
盡管我們對整個方案的設計,可以以極低的介入方式集成到整個漏洞挖掘體系中,但是該方案仍然有一定的局限性,主要包含幾個方面:
-
復雜的邏輯漏洞挖掘,仍然是需要攻克的技術難題:我們在漏洞挖掘的過程中,發現部分請求存在上下游關聯的應用場景,這導致貿然對單一的RPC請求進行測試,無法滿足執行條件,從而不能觸發攻擊Payload。
-
數據覆蓋度的強依賴:本方案的核心是在于給予大量的數據結合算法來進行大規模的漏洞挖掘,這對于大型互聯網公司來說,可以借助日志服務通過云計算能力來提高漏洞挖掘的效率。但是從黑盒視角,通過模擬執行以及人工介入,能夠獲取的RPC數據在覆蓋率上要低于擁有日志服務能力的應用程序。
09 總結
在過去的時間中,我們基于這樣的方法挖掘了上百個安全漏洞。本文主要介紹了我們如何針對gRPC框架,構建漏洞挖掘平臺。對于如何挖掘漏洞,在文末只是簡單的涉及幾種頗有成效的經驗。我們認為,在有了體系化的框架之后,可以引入其他的漏洞利用方式,給予框架新的漏洞挖掘能力,用于新的應用場景。
正所謂,授人以魚不如授人以漁,希望各位看官學習這篇文章之后,可以基于這樣的思路發現更多有意思的漏洞利用方法。

同時我們也意識到,移動互聯網的安全問題,不能僅僅依靠移動應用程序上的安全保護機制,來保障應用的安全,需要從端到云,在基礎架構層面來構建自己的護城河。這種風險對抗不是單一的防御措施,而是在服務器端構建縱深防御體系的構建。
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1616/