frida 找不到so_當Frida來“敲”門
技術標籤:frida 找不到so
0x1 滲透測試瓶頸
目前,碰到越來越多的大客戶都會將核心資產業務集中在統一的APP上,或者對自己比較重要的APP,如自己的主業務,辦公APP進行加殼,流量加密,投入了很多精力在移動端的防護上。
而現在挖漏洞除了拿到shell以外,客戶又要求可以儘可能的挖到核心業務系統的漏洞,並將漏洞範圍訂在主域名,核心業務系統現在又基本集中在移動端,移動端現在都會進行APP加殼,流量加密。這就導致無法進行平常滲透測試過程,像老生常談的中間人攻擊,進行攔截,篡改資料包就很難進行。
接下來就嘗試解決中間人攻擊的問題,目標是
1.看到明文的request和response的資料包;
2.做到可以攔截,篡改資料包。
0x2 frida
frida是平臺原生app的Greasemonkey,說的專業一點,就是一種動態插樁工具,可以插入一些程式碼到原生app的記憶體空間去,(動態地監視和修改其行為),這些原生平臺可以是Win、Mac、Linux、Android或者iOS。而且frida還是開源的。
環境需要越獄的IOS或者ROOT的Android。安裝的版本需要一致
MAC:
越獄Iphone:
通過USB連結越獄手機,可以執行frida-ps -aU 就代表環境安裝成功
0x3 越獄檢測繞過
啟動目標APP時,APP自身會進行環境檢測,如果處於越獄環境會提示如下:
點選“我知道了”就直接退出APP。
所以先嚐試先繞過第一步越獄環境檢測。可以先嚐試搜尋包含“jail,jeil,jb,break"關鍵字的函式
關於函式追蹤可以使用frida-trace,如:
# Trace recv* and send* APIs in Safari$ frida-trace -i "recv*" -i "send*" Safari# Trace ObjC method calls in Safari$ frida-trace -m "-[NSView drawRect:]" Safari# Launch SnapChat on your iPhone and trace crypto API calls$ frida-trace -U -f com.toyopagroup.picaboo -I "libcommonCrypto*"
burp的外掛brida也支援對函式名進行檢索hook,和"Jail"相關的越獄檢測函式如下:
**** Result of the search of JailOBJC: +[BLYDevice isJailBreak]OBJC: +[IFlySystemInfo isJailbroken]OBJC: +[UIScreen _shouldDisableJail]OBJC: +[UIStatusBarWindow isIncludedInClassicJail]OBJC: -[_UIHostedWindow _isConstrainedByScreenJail]OBJC: -[_UIRootWindow _isConstrainedByScreenJail]OBJC: -[_UISnapshotWindow _isConstrainedByScreenJail]OBJC: -[BLYDevice isJailbroken]OBJC: -[BLYDevice setJailbrokenStatus:]OBJC: -[RCCountly isJailbroken]OBJC: -[UIClassicWindow _isConstrainedByScreenJail]OBJC: -[UIDevice isJailbroken]OBJC: -[UIStatusBarWindow _isConstrainedByScreenJail]OBJC: -[UITextEffectsWindowHosted _isConstrainedByScreenJail]OBJC: -[UIWindow _clampPointToScreenJail:]OBJC: -[UIWindow _isConstrainedByScreenJail]
想將目標定在“OBJC: +[BLYDevice isJailBreak]”
frida啟動APP,並載入指令碼的命令如下:
frida -U -f com.x.x -l js-scripts
js指令碼編寫可以看官方文件:https://frida.re/docs/javascript-api/
//hook傳入值,ObjC: args[0] = self, args[1] = selector, args[2-n] = arguments Interceptor.attach(myFunction.implementation, { onEnter: function(args) { var myString = new ObjC.Object(args[2]); console.log("String argument: " + myString.toString()); }});//hook返回值,Interceptor.attach(Module.getExportByName('libc.so', 'read'), { onEnter: function (args) { this.fileDescriptor = args[0].toInt32(); }, onLeave: function (retval) { if (retval.toInt32() > 0) { /* do something with this.fileDescriptor */ }}});
定義js指令碼後,嘗試hook出“OBJC: +[BLYDevice isJailBreak]”的傳入值和返回值,
function hook_specific_method_of_class(className, funcName){ var hook = ObjC.classes[className][funcName]; Interceptor.attach(hook.implementation, { onEnter: function(args) { // args[0] is self // args[1] is selector (SEL "sendMessageWithText:") // args[2] holds the first function argument, an NSString console.log("[*] Class Name: " + className); console.log("[*] Method Name: " + funcName); //For viewing and manipulating arguments //console.log("[-] Value1: "+ObjC.Object(args[2])); //console.log("[-] Value2: "+(ObjC.Object(args[2])).toString()); console.log("[-]arg value "+args[2]); Interceptor.attach(hook.implementation, { onLeave: function(retval) { console.log("[*] Class Name: " + className); console.log("[*] Method Name: " + funcName); console.log("[-] Return Value: " + retval); }} ); } });}//Your class name and function name herehook_specific_method_of_class("BLYDevice", "- isJailbroken")
篡改後,發現未能繞過,可能不是這個函式做最終的邏輯判斷,想到竟然都彈窗提示了,和UI有關係。
那麼可能是“OBJC: -[UIDevice isJailbroken]這個類,最終構造繞過越獄檢測程式碼如下:
if (ObjC.available){ try { var className = "UIDevice"; var funcName = "- isJailbroken"; var hook = eval('ObjC.classes.' + className + '["' + funcName + '"]');//目標類+方法 Interceptor.attach(hook.implementation, { onLeave: function(retval) { console.log("[*] Class Name: " + className); console.log("[*] Method Name: " + funcName); console.log("[-] Return Value: " + retval);//輸出原本的返回值 var newretval = ptr("0x0") retval.replace(newretval)//替換新的返回值 console.log("[-] New Return Value: " + newretval) }} ); } catch(err) { console.log("[!] Exception2: " + err.message); } }else{ console.log("Objective-C Runtime is not available!");}
執行結果如下:
成功繞過。
0x4 HOOK加解密函式
越獄檢測繞過後,進一步開始嘗試定位加解密的函式。
關於定位加解密函式這塊在Android可以嘗試使用traceview去分析追蹤函式。
https://developer.android.google.cn/studio/profile/traceview
IOS可以嘗試使用runtime去追蹤函式,uidump從介面按鈕入手,Nslog日誌等位置入手,或者直接找相關關鍵字的函式去入手。
例如crypt(decryot,encrypt),HTTP,Network,目標廠商的名字簡寫找不到,可以嘗試搜尋NSString系統庫等。
這邊推薦一個大佬的GitHub專案。使用可以參考這個GitHub專案,非常好用,先用之前寫好的繞過越獄檢測的指令碼啟動APP,這邊通過查詢函式名找到對方關鍵的加解密函式“*encryptor”。
github專案:https://github.com/lyxhh/lxhToolHTTPDecrypt
hook此函式的所有方法,在點選登入按鈕後,觀察到有請求的資料包被當做引數傳入到-[XXEncryptor RSAEncrypt:]方法內,並返回了加密後的字串。-[XXEncryptor setRSAPublicKey:]根據定義的方法名判斷應該是RSA公鑰資訊。
其他方法則去處理了返回包。如-[XXEncrytor AESDecrypt:]方法,將服務端返回的加密欄位,使用AES對稱解密解密為明文。
之前我們在Hook請求包函式的時候發現明文的資料包裡面帶有aeskey,說明此處的邏輯應該是:
本地生成aeskey代入到request包->使用定義的RSA公鑰加密request->傳送到服務端並解密request後->處理請求包內容,並使用AESkey加密Response返回到客戶端->客戶端在使用Aeskey解密服務端的Response包。
大概是這麼一個流程,事實也證明返回包確實可以使用hook到的aeskey進行解密。
後面的思路是hook[XXEncrytor AESDecrypt:]解密方法去解密請求包和返回包,返回包是可以解,但是突然想到請求包是RSA非對稱的,需要私鑰。想嘗試在客戶端找到RSA的私鑰或者RSA解密方法,結果也確實有RSADecrypt方法。
但是事實是,從頭到尾這個方法都沒有被使用過,沒有引數被傳入,也沒有返回值。所以想,可能本地不做請求包的解密。那麼呼叫他的函式解密返回包可行,但解密請求包不行。但是咱們之前是有Hook到明文的request,可以再request被傳入到-[XXEncryptor RSAEncrypt]方法前,先去修改arg。
具體操作方法可以參考lyxhh,將加密前的請求包轉入Burp後就可以實現篡改資料了。
lyxhh:https://github.com/lyxhh/lxhToolHTTPDecrypt
新手的話可以先用la0s的JS,先看看對方是不是使用了IOS統一封裝的Crypto庫,js指令碼如下:
JS:https://la0s.github.io/2018/12/07/iOS_Crypto/
/ Intercept the CCCrypt call.Interceptor.attach(Module.findExportByName('libcommonCrypto.dylib', 'CCCrypt'), { onEnter: function (args) { // Save the arguments this.operation = args[0] this.CCAlgorithm = args[1] this.CCOptions = args[2] this.keyBytes = args[3] this.keyLength = args[4] this.ivBuffer = args[5] this.inBuffer = args[6] this.inLength = args[7] this.outBuffer = args[8] this.outLength = args[9] this.outCountPtr = args[10] console.log('CCCrypt(' + 'operation: ' + this.operation +', ' + 'CCAlgorithm: ' + this.CCAlgorithm +', ' + 'CCOptions: ' + this.CCOptions +', ' + 'keyBytes: ' + this.keyBytes +', ' + 'keyLength: ' + this.keyLength +', ' + 'ivBuffer: ' + this.ivBuffer +', ' + 'inBuffer: ' + this.inBuffer +', ' + 'inLength: ' + this.inLength +', ' + 'outBuffer: ' + this.outBuffer +', ' + 'outLength: ' + this.outLength +', ' + 'outCountPtr: ' + this.outCountPtr +')') if (this.operation == 0) { // Show the buffers here if this an encryption operation console.log("In buffer:") console.log(hexdump(ptr(this.inBuffer), { length: this.inLength.toInt32(), header: true, ansi: true })) console.log("Key: ") console.log(hexdump(ptr(this.keyBytes), { length: this.keyLength.toInt32(), header: true, ansi: true })) console.log("IV: ") console.log(hexdump(ptr(this.ivBuffer), { length: this.keyLength.toInt32(), header: true, ansi: true })) } }, onLeave: function (retVal) { if (this.operation == 1) { // Show the buffers here if this a decryption operation console.log("Out buffer:") console.log(hexdump(ptr(this.outBuffer), { length: Memory.readUInt(this.outCountPtr), header: true, ansi: true })) console.log("Key: ") console.log(hexdump(ptr(this.keyBytes), { length: this.keyLength.toInt32(), header: true, ansi: true })) console.log("IV: ") console.log(hexdump(ptr(this.ivBuffer), { length: this.keyLength.toInt32(), header: true, ansi: true })) } }})
如果只能hook到部分明文流量,再考慮去對方定義的函式裡去找關鍵的加密函式,如這個APP的關鍵的XXEncryptor類。