IOS逆向(四)-某app证书SSL pinning绕过
渗透测试中经常会遇到app存在SSL证书校验,此时就没办法抓包。需要对ios的SSL pinning的函数进行hook来绕过检测。本文对某app使用frida和objection进行绕过。
难度
★☆☆☆☆
工具
- 越狱IOS 14.4
- AppsDump
- frida
- objection
- IDA 7.7
- HTTP Catcher
分析思路
我们在ios上使用HTTP Catcher进行抓包,当开启SSL Pinning Bypass(代理为localhost生效)的时候,是可以正常抓到包的。但是我们做测试需要把流量抓到mac上的burp,这时SSL Pinning Bypass是不生效的。
虽然知道是证书校验的问题,我们可以直接用SSL Unpinning的脚本进行hook,但还是用IDA简单分析一下,了解其原理。
Appsdump砸壳
最近发现一个IOS上砸壳非常方便的app,Appsdump。支持IOS15的系统,原理就是基于trollstore来进行砸壳的,关键是非越狱的状态下也可以砸壳。
支持单脱主程序,然后使用airdrop隔空投送到mac,是真的太方便了!
IDA分析过程
既然是证书问题,把maco扔进IDA中分析完成后,直接搜索证书的关键字来定位关键函数。
双击来的字符串的位置。
查看引用该字符串的方法。
来到-[UASessionOperation URLSession:didReceiveChallenge:completionHandler:]方法,F5。
SecTrustEvaluateWithError
是iOS平台上的系统函数。这个函数来自于Security.framework
,它是用于评估一个SecTrust对象是否可以被系统信任。
该函数返回一个布尔值,如果为true,证书可以被信任;如果为false,证书不能被信任。如果评估失败,会通过第二个参数返回一个包含错误详情的CFErrorRef对象。这个函数在iOS 13.0及更高版本上可用,因此可以看到在代码中有一个版本检查部分,根据设备的系统版本来选择使用SecTrustEvaluateWithError
或者旧版本的SecTrustEvaluate
函数。
objection ios sslpinning disable
objection是基于frida封装的命令行hook工具,可以不写代码,使用非常方便。
objection -g <bundleID> explore ios sslpinning disable
frida 绕过脚本
SSL Pinning的绕过脚本网上有很多现成的,就没必要自己写了。和分析的基本一致,都是hook ssl证书校验的关键系统函数。
// Disables SSL pinning by replacing functions with no-ops. function unpin() { var SecTrustEvaluate_handle = Module.findExportByName('Security', 'SecTrustEvaluate'); var SecTrustEvaluateWithError_handle = Module.findExportByName('Security', 'SecTrustEvaluateWithError'); var SSL_CTX_set_custom_verify_handle = Module.findExportByName('libboringssl.dylib', 'SSL_CTX_set_custom_verify'); var SSL_get_psk_identity_handle = Module.findExportByName('libboringssl.dylib', 'SSL_get_psk_identity'); var boringssl_context_set_verify_mode_handle = Module.findExportByName('libboringssl.dylib', 'boringssl_context_set_verify_mode'); if (SecTrustEvaluateWithError_handle) { var SecTrustEvaluateWithError = new NativeFunction(SecTrustEvaluateWithError_handle, 'int', ['pointer', 'pointer']); Interceptor.replace( SecTrustEvaluateWithError_handle, new NativeCallback(function (trust, error) { console.log('[*] Called SecTrustEvaluateWithError()'); SecTrustEvaluateWithError(trust, NULL); Memory.writeU8(error, 0); return 1; }, 'int', ['pointer', 'pointer']) ); console.log('[+] SecTrustEvaluateWithError() hook installed.'); } if (SecTrustEvaluate_handle) { var SecTrustEvaluate = new NativeFunction(SecTrustEvaluate_handle, 'int', ['pointer', 'pointer']); Interceptor.replace( SecTrustEvaluate_handle, new NativeCallback(function (trust, result) { console.log('[*] Called SecTrustEvaluate()'); SecTrustEvaluate(trust, result); Memory.writeU8(result, 1); return 0; }, 'int', ['pointer', 'pointer']) ); console.log('[+] SecTrustEvaluate() hook installed.'); } if (SSL_CTX_set_custom_verify_handle) { var SSL_CTX_set_custom_verify = new NativeFunction(SSL_CTX_set_custom_verify_handle, 'void', ['pointer', 'int', 'pointer']); var replaced_callback = new NativeCallback(function (ssl, out) { console.log('[*] Called custom SSL verifier') return 0; }, 'int', ['pointer', 'pointer']); Interceptor.replace( SSL_CTX_set_custom_verify_handle, new NativeCallback(function (ctx, mode, callback) { console.log('[*] Called SSL_CTX_set_custom_verify()'); SSL_CTX_set_custom_verify(ctx, 0, replaced_callback); }, 'int', ['pointer', 'int', 'pointer']) ); console.log('[+] SSL_CTX_set_custom_verify() hook installed.') } if (SSL_get_psk_identity_handle) { Interceptor.replace( SSL_get_psk_identity_handle, new NativeCallback(function (ssl) { console.log('[*] Called SSL_get_psk_identity_handle()'); return 'notarealPSKidentity'; }, 'pointer', ['pointer']) ); console.log('[+] SSL_get_psk_identity() hook installed.') } if (boringssl_context_set_verify_mode_handle) { var boringssl_context_set_verify_mode = new NativeFunction(boringssl_context_set_verify_mode_handle, 'int', ['pointer', 'pointer']); Interceptor.replace( boringssl_context_set_verify_mode_handle, new NativeCallback(function (a, b) { console.log('[*] Called boringssl_context_set_verify_mode()'); return 0; }, 'int', ['pointer', 'pointer']) ); console.log('[+] boringssl_context_set_verify_mode() hook installed.') } } unpin()
将上面的脚本保存为 ssl-pinning-bypass.js 文件。
frida -UF -l ssl-pinning-bypass.js
执行结果如下:
然后就可以正常的抓包了。
赞赏微信赞赏支付宝赞赏
目前为止有一条评论