原文地址:《"BLIND" Reversing - A Look At The Blind iOS App》
譯者:hello1900@知道創宇404實驗室

前言

“Blind是一款用于工作場合的匿名社區應用。” 換句話說,作為一名員工,如果有“暢所欲言”或以匿名方式抨擊雇主或同事的打算(當然這種情況極為常見),那么Blind可能是你的不二之選。這款有趣的小應用能夠幫助我們透過事物表象了解真實情況。

范圍與環境

我重點關注應用本身,因此使用Linkedin賬戶而非工作郵件注冊。這樣一來就產生了一些訪問限制。此外,我也并未花時間了解所有功能,僅考慮一些自己看來具有核心功能的組件,所以難免存在局限性。

環境搭建如下:

-運行iOS 9.3.3的越獄iPhone 5S
-jtool
-IDA Pro
-Hopper
-BurpSuite Pro
-Frida

越獄檢測

首先,這款應用不具備任何越獄檢測例行程序。對于你的不屑一顧我暫時保持緘默。我開始也不認為缺少此類檢查本身是安全問題,但從更寬泛、深入的防御觀點來看卻不盡然。

證書固定

第二個觀察結果是該應用沒有通過SSL證書驗證(證書固定)檢查遠程端點的真實性,因此可以通過中間人(MiTM)攻擊監聽并篡改數據。事實上,情況并不像聽起來那么糟。原因如下:1. 攻擊者必須欺騙用戶在設備上安裝惡意證書;2. 該應用對發送至后端的數據進行加密。

另一方面,攻擊者大多數情況下需要先欺騙用戶安裝惡意證書。對此,我表示贊同,因為確實存在旨在簡化流程的工具。Sensepost上的博客文章(https://sensepost.com/blog/2016/too-easy-adding-root-cas-to-ios-devices/)就是一個很好的例子。

最后,該應用程序提供兩種登錄選項:工作電子郵件與LinkedIn驗證(見下圖)。如上所述,我傾向使用后者。副作用是攻擊者可在證書未固定的情況下捕獲LinkedIn登陸憑據,當然是在安裝惡意證書的前提下。

登錄選項

獲取二進制文件

解決這些問題后就可以獲得二進制文件、開始逆向了。我使用 dumpdecrypted dylib,僅需 ssh登錄設備并運行以下代碼:

root@Jekyl (/var/root)# su mobile  
 mobile@Jekyl (/var/mobile/Documents)# DYLD_INSERT_LIBRARIES=/var/root/dumpdecrypted.dylib /private/var/containers/Bundle/Application/3C411AB3-6018-4604-97D2-DC2A546EAB85/teamblind.app/teamblind  
 mach-o decryption dumper  
 DISCLAIMER: This tool is only meant for security research purposes, not for application crackers.  
 [+] detected 64bit ARM binary in memory.  
 [+] offset to cryptid found: @0x1000d4f28(from 0x1000d4000) = f28  
 [+] Found encrypted data at address 00004000 of length 7995392 bytes - type 1.  
 [+] Opening /private/var/containers/Bundle/Application/3C411AB3-6018-4604-97D2-DC2A546EAB85/teamblind.app/teamblind for reading.  
 [+] Reading header  
 [+] Detecting header type  
 [+] Executable is a plain MACH-O image  
 [+] Opening teamblind.decrypted for writing.  
 [+] Copying the not encrypted start of the file  
 [+] Dumping the decrypted data into the file  
 [+] Copying the not encrypted remainder of the file  
 [+] Setting the LC_ENCRYPTION_INFO->cryptid to 0 at offset f28  
 [+] Closing original file  
 [+] Closing dump file  

應注意使用root權限在iOS 9.3.3版本運行DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib可殺掉被注入進程:

 root@Jekyl (/var/root)# DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib /private/var/containers/Bundle/Application/3C411AB3-6018-4604-97D2-DC2A546EAB85/teamblind.app/teamblind  
 zsh: killed   DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib   
 root@Jekyl (/var/root)# 

解決辦法是先切換至手機再cd至上圖/var/mobile/Document。還應注意我們之所以能夠注入自己的 dylib 是因為Blind應用沒有 __RESTRICT Segment。

 LC_SEGMENT_64     Mem: 0x100008000-0x100008000     __RESTRICT  
      Mem: 0x100008000-0x100008000          __RESTRICT.__restrict 

這是個null segment (size 0),用于通知 DLYD 不要相信任何 DLYD* 環境變量。

識別端點

我在查看二進制文件時通常會轉儲字符串并搜索URL端點,然后用該列表確認Burpsuite流量。

 macho-reverser:BLIND macho-reverser$ jtool -d __TEXT.__cstring teamblind.decrypted | grep "http"  
 Address : 0x1006dcfd0 = Offset 0x6dcfd0  
 0x1006df366: https://api.linkedin.com/v1/people/~:(id,email-address,first-name,last-name,headline,num-connections,industry,summary,specialties,positions:(id,title,summary,start-date,end-date,is-current,company:(id,name,universal-name,type,size,industry,ticker,email-domains)),educations:(id,school-name,field-of-study,start-date,end-date,degree,activities,notes),associations,interests,num-recommenders,date-of-birth,publications:(id,title,publisher:(name),authors:(id,name),date,url,summary),patents:(id,title,summary,number,status:(id,name),office:(name),inventors:(id,name),date,url),languages:(id,language:(name),proficiency:(level,name)),skills:(id,skill:(name)),certifications:(id,name,authority:(name),number,start-date,end-date),courses:(id,name,number),recommendations-received:(id,recommendation-type,recommendation-text,recommender),honors-awards,three-current-positions,three-past-positions,volunteer)?format=json  
 0x1006df80e: http://us.teamblind.com  
 0x1006e19ad: https://api.linkedin.com/v1/people/~:(id,email-address)?format=json  
 0x1006e75df: https://m.facebook.com/settings/email  
 0x1006e760c: https://www.linkedin.com/m/settings/email  
 0x1006ea5ec: https://docs.google.com/forms/d/e/1FAIpQLSc_J26TtkDL7HXcLeFXC2jy6lb1PmJSPnh51_ng7fr1638p_Q/viewform  
 0x1006ee9c3: https://www.linkedin.com/uas/oauth2/authorization?response_type=code&client_id=%@&scope=%@&state=%@&redirect_uri=%@  
 0x1006f4865: https://krapi.teamblind.com  
 0x1006f4881: https://usapi.teamblind.com  
 0x1006f489d: http://kr.stage.teamblind.com:8080  
 0x1006f48c0: http://us.stage.teamblind.com:8080  
 0x1006f48e3: http://dev.teamblind.com:8080  
 0x1006f4901: http://us.dev.teamblind.com:8080  
 0x1006f4922: https://kr.teamblind.com  
 0x1006f493b: https://us.teamblind.com  
 0x1006f4954: https://krnotifier.teamblind.com  
 0x1006f4975: https://usnotifier.teamblind.com  
 ----

也可以用它獲取其他潛在目標列表。于是,啟動Burpsuite并檢查流量。如前文所述,Blind應用能否實現證書固定并沒有那么重要,因為該應用對發送至后端的數據進行加密。下圖為常用請求示例。

請求樣本

唯一能夠確定的是早前識別的鏈接。所以說我們實際上處于“盲”狀態。但如果數據經過加密,那么加密秘鑰的存儲或生成的方式與位置是什么?能否看到應用發送至服務器的明文數據?

提取類

為了回答這些問題,首先轉儲類,看看是否存在有價值的內容。列出代碼段后發現關于Objective-C的引用:

macho-reverser:BLIND macho-reverser$ jtool -l teamblind.decrypted   
 LC 00: LC_SEGMENT_64     Mem: 0x000000000-0x100000000     __PAGEZERO  
 LC 01: LC_SEGMENT_64     Mem: 0x100000000-0x1007a4000     __TEXT  
      Mem: 0x100007a90-0x100663f18          __TEXT.__text     (Normal)  
      Mem: 0x100663f18-0x10066723c          __TEXT.__stubs     (Symbol Stubs)  
      Mem: 0x10066723c-0x10066a560          __TEXT.__stub_helper     (Normal)  
      Mem: 0x10066a560-0x100671ec0          __TEXT.__const       
      Mem: 0x100671ec0-0x1006dcfc9          __TEXT.__objc_methname     (C-String Literals)  
      Mem: 0x1006dcfd0-0x10074ca58          __TEXT.__cstring     (C-String Literals)  
      Mem: 0x10074ca58-0x100754bb2          __TEXT.__objc_classname     (C-String Literals)  
      Mem: 0x100754bb2-0x100767daa          __TEXT.__objc_methtype     (C-String Literals)  
      Mem: 0x100767daa-0x100768e18          __TEXT.__ustring       
      Mem: 0x100768e18-0x100788c4c          __TEXT.__gcc_except_tab       
      Mem: 0x100788c50-0x10078b967          __TEXT.__swift3_typeref       
      Mem: 0x10078b968-0x10078c6a0          __TEXT.__swift3_capture       
      Mem: 0x10078c6a0-0x10078d720          __TEXT.__swift3_fieldmd       
      Mem: 0x10078d720-0x10078e67d          __TEXT.__swift3_reflstr       
      Mem: 0x10078e680-0x10078edc8          __TEXT.__swift3_assocty       
      Mem: 0x10078edc8-0x10078f3c8          __TEXT.__swift2_proto       
      Mem: 0x10078f3c8-0x10078f478          __TEXT.__swift2_types       
      Mem: 0x10078f478-0x10078f4dc          __TEXT.__swift3_builtin       
      Mem: 0x10078f4dc-0x1007a3d20          __TEXT.__unwind_info       
      Mem: 0x1007a3d20-0x1007a4000          __TEXT.__eh_frame       
 LC 02: LC_SEGMENT_64     Mem: 0x1007a4000-0x100980000     __DATA  
      Mem: 0x1007a4000-0x1007a4ba8          __DATA.__got     (Non-Lazy Symbol Ptrs)  
      Mem: 0x1007a4ba8-0x1007a6dc0          __DATA.__la_symbol_ptr     (Lazy Symbol Ptrs)  
      Mem: 0x1007a6dc0-0x1007a6e00          __DATA.__mod_init_func     (Module Init Function Ptrs)  
      Mem: 0x1007a6e00-0x1007cfd20          __DATA.__const       
      Mem: 0x1007cfd20-0x10080f300          __DATA.__cfstring       
      Mem: 0x10080f300-0x100811498          __DATA.__objc_classlist     (Normal)  
      Mem: 0x100811498-0x1008114d0          __DATA.__objc_nlclslist     (Normal)  
      Mem: 0x1008114d0-0x100811890          __DATA.__objc_catlist     (Normal)  
      Mem: 0x100811890-0x1008118e8          __DATA.__objc_nlcatlist     (Normal)  
      Mem: 0x1008118e8-0x1008123b0          __DATA.__objc_protolist       
      Mem: 0x1008123b0-0x1008123b8          __DATA.__objc_imageinfo       
      Mem: 0x1008123b8-0x10092bf38          __DATA.__objc_const       
      Mem: 0x10092bf38-0x100944b20          __DATA.__objc_selrefs     (Literal Pointers)  
      Mem: 0x100944b20-0x100944c88          __DATA.__objc_protorefs       
      Mem: 0x100944c88-0x100946ee0          __DATA.__objc_classrefs     (Normal)  
      Mem: 0x100946ee0-0x100948918          __DATA.__objc_superrefs     (Normal)  
      Mem: 0x100948918-0x10094ee80          __DATA.__objc_ivar       
      Mem: 0x10094ee80-0x100965188          __DATA.__objc_data  
 ------

我們可以通過jtool - JCOLOR=1 jtool -v -d objc teamblind.decrypted的 objc 選項轉儲類與方法。

用jtool提取類信息

還需要說明一點,雖然本文使用IDA,讀者完全可以根據自身反匯編需要使用jtool,例如研究某個特殊類時輸入jtool -d UserControl:getSecretUserDefaultString: teamblind.decrypted

拆分類信息

破解加密值

現在,我們已經成功攔截流量,但如何破解加密流量成為難題。不妨先找出加密實現方法。如上文所述,Blind允許通過工作郵件或LinkedIn賬戶登錄。登錄后將看到創建賬戶選項:

登錄界面

應用設置可在com.teamblind.blind.plist中查找,具體位置在 /private/var/mobile/Containers/Data/Application/<app_id>/Library/Preferences/com.tea mblind.blind.plist。此時,檢查文件就會發現其中包含明文電子郵件以及登錄時填寫的公司信息。可以使用plutil實用程序讀取文件。

plist代碼段

一旦輸入密碼、用戶名,點擊“開始”后就是另一番景象了。

現在,你的電子郵件不再以明文存儲而是經過加密,新增了密碼與其他幾個值。牢記千萬不要在plist文件中存儲密碼等敏感信息。敏銳的讀者可能會注意到我并沒有隱藏password_enc值。秘鑰名稱以_enc結尾表示該值可能被加密,但事實是否果真如此?另外,應注意該值是加密過程必不可少的一部分,具體原因將在后文介紹。接下來,我們將繼續探索關于這個值的更多細節。

“加密”密碼僅是一個md5哈希值,可以在AuthCompleteViewController的requestPassword中看到。

創建密碼哈希值

在 0x000000010004EB50 位置得到用戶提供值后計算 0x000000010004EB8C 位置的md5 哈希值。為了證實這一點,我們在與上文plist取值相同處使用Python。現在,我的超級密碼一目了然。

 >>> import hashlib  
 >>> m = hashlib.md5()  
 >>> m.update("password#1")  
 >>> print m.hexdigest()  
 5486b4af453c7830dcea12f347137b07  
 >>> 

識別ViewControllers

為了確定需要檢查的類,我首先來到賬戶創建頁面,用cycript 確定ViewController外觀:

 root@Jekyl (/var/root)# ps aux | grep blind  
 mobile  4136  0.1 5.8  815696 59532  ?? Ss  4:10PM  0:06.85 /var/containers/Bundle/Application/3C411AB3-6018-4604-97D2-DC2A546EAB85/teamblind.app/teamblind  
 root   4139  0.0 0.0  657104  212 s000 R+  4:11PM  0:00.01 grep blind  
 root@Jekyl (/var/root)# cycript -p 4136  
 cy# [[[UIWindow keyWindow] rootViewController] _printHierarchy].toString()  
 "<UINavigationController 0x15615d000>, state: appeared, view: <UILayoutContainerView 0x157415a30>\n  | <RootViewController 0x1570db260>, state: disappeared, view: <UIView 0x157337ab0> not in the window\n  | <AuthCompleteViewController 0x155f587c0>, state: appeared, view: <UIView 0x155da07a0>"  
 cy#  

別忘了你的電子郵件經過加密處理。加密慣例也在requestPassword 方法中呈現。你的電子郵件首先通過 plist(NSUserDefaults) 找回,然后傳輸至NSString ([NSString encryptHES256:]) encryptHES256 方法。

讀取用戶電子郵件

encryptHES256方法在過渡至AES256EncryptWithKey方法(涵蓋加密過程)前生成帶有密碼簡單異或的加密秘鑰和一些“隨機”值。從技術角度看,此方法調用另一個功能,但整體情況已趨向明朗,不難發現“隨機性”。

Frida

稍微借助Frida就可以看到實際效果。對于還不了解 Frida的讀者,我強烈建議您將這款工具添加至兵器庫并在使用go腳本前檢查Frida CodeShare。當然,代碼運行前的例行檢查無論何時都是必要步驟。

通過Frida與CodeShare ObjC method observer腳本,我們能夠觀察到AES256EncryptWithKey方法起到的作用:

 macho-reverser:BLIND macho-reverser$ frida -U --codeshare mrmacete/objc-method-observer -f com.teamblind.blind  
    ____  
   / _ |  Frida 10.6.15 - A world-class dynamic instrumentation framework  
   | (_| |  
   > _ |  Commands:  
   /_/ |_|    help   -> Displays the help system  
   . . . .    object?  -> Display information about 'object'  
   . . . .    exit/quit -> Exit  
   . . . .  
   . . . .  More info at http://www.frida.re/docs/home/  
 Spawned `com.teamblind.blind`. Use %resume to let the main thread start executing!  
 [iPhone::com.teamblind.blind]-> %resume  
 [iPhone::com.teamblind.blind]-> observeSomething('*[* *AES256EncryptWithKey:*]');  
 (0x125fcdca0) -[NSData AES256EncryptWithKey:]  
 AES256EncryptWithKey: password#1^0123456789abcdefghijk  
 0x1001b25cc teamblind!0x11e5cc  
 0x1000e2c7c teamblind!0x4ec7c  
 ---- 

現在,密碼與電子郵件加密方式已知。基本說來,重復以上步驟就能找到其他加密值。接下來,了解實際流量。

如上文所述,設備出口流量監控結果顯示,所有請求均包括一個載荷。在IDA中搜索字符串后發現多數請求參數在[NetworkControl encRequestWithParams:showAlert:completionBlock:failBlock:] 方法中設置。

encRequestWithParams

此方法首先嘗試找回之前生成的加密秘鑰與初始化向量(IV),如果失敗則調用 EncriptControl 類 makeKeyAndIvForEnc (-[EncriptControl makeKeyAndIvForEnc]) 方法。沒錯,是帶有IV的Encript。 或許可稱之為隱匿式安全……:)

makeKeyAndIvForEnc

這種做法的有趣之處在于加密秘鑰通過用戶密碼與硬編碼值組合生成。還記得之前提到的加密密碼(password_enc)嗎?該方法首先嘗試將其找回:

根據硬編碼值生成另一個md5哈希值:

生成靜態值

如果涉及用戶密碼找回問題,則再生成一個哈希值:

最后,秘鑰設置完畢,以 hash1+hash2 或 hash1+password_enc 組合結尾。

生成實際秘鑰

在此例中,加密秘鑰應為 md5("QkdEhdk") + md5(“password#1"),并由此得到“c07bcdc2 3522ed81 fb76db0c 0c4387cf 5486b4af 453c7830 dcea12f3 47137b07”。

該方法的其余部分用于設置初始向量(IV):

生成 IV

沖破黑暗

NetworkControl 類 encRequestWithParams 方法通過調用EncriptControl 類makeKeyAndIvForEnc設置加密。設置完畢后使用encRequestWithParams 方法調用 EncriptControl 類makePayloadDataWithJsonString。該方法使用之前提及的加密秘鑰與IV調用CocoaSecurity aesEncryp,結果返回base64 編碼密文,也就是Burp呈現的內容。

加密載荷

暫時返回 jtool -d objc dump,注意 EncriptControl 類實例變量:

Encript實例變量

收集到所有線索后編寫Frida腳本,獲得實例變量,即加密秘鑰、明文數據與相應密文:

if(ObjC.available){  
   var makeKandIv = ObjC.classes.EncriptControl["- makePayloadDataWithJsonString:"];  
     Interceptor.attach(makeKandIv.implementation, {  
      onEnter: function(args) {  
        /* Get Class/Params */  
        var obj = ObjC.Object(args[0]);  
        var params = ObjC.Object(args[2]);  
        /* Get ivars */  
        var ivar = obj.$ivars;  
        // Print ivars values   
        console.log("-----------------------------------------------------------\n");  
        console.log("_encKey: " + ivar["_encKey"] + "\n");  
        console.log("_encIv: " + ivar["_encIv"] + "\n");  
        console.log("_encIvStr: " + ivar["_encIvStr"] + "\n");   
        console.log("_encKeyForDM: " + ivar["_encKeyForDM"] + "\n");   
        console.log("_encKeyForDM: " + ivar["_encIvForDM"] + "\n");   
        console.log("-----------------------------------------------------------\n");   
        console.log("PARAMS: " + params);  
       },  
      onLeave: function onLeave(retval) {  
         console.log("Encrypted Payload: " + new ObjC.Object(retval).toString() + "\n");  
      }  
   });  
 }  

一次加密的burp流量

現在變成:

具有加密秘鑰的明文數據

此外,連接EncriptControl 類convertDictionaryEncWithResultStr: 方法后打印出服務器響應明文。這時需要考慮將Brida Burpsuite 插件用于其他選項。

結語

Ok,今天就到此為止。如前文所述,我沒有注冊Blind 賬戶,因此無法使用會員功能。但這些都無妨,我只對Burp上的一些數據感興趣。考慮到應用程序性質與要求,我打算探索更多內容。因此,并無惡意流量發送至Blind服務器。

Happy hacking!!!

本文可用于圍繞Blind應用開展進一步調研。對于提供信息的采用,本博客不承擔任何責任。


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/440/