渗透测试中经常会遇到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
objection -g <bundleID> explore
ios sslpinning disable
objection -g <bundleID> explore
ios sslpinning disable
frida 绕过脚本
SSL Pinning的绕过脚本网上有很多现成的,就没必要自己写了。和分析的基本一致,都是hook ssl证书校验的关键系统函数。
// Disables SSL pinning by replacing functions with no-ops.
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']);
SecTrustEvaluateWithError_handle,
new NativeCallback(function (trust, error) {
console.log('[*] Called SecTrustEvaluateWithError()');
SecTrustEvaluateWithError(trust, NULL);
Memory.writeU8(error, 0);
}, 'int', ['pointer', 'pointer'])
console.log('[+] SecTrustEvaluateWithError() hook installed.');
if (SecTrustEvaluate_handle) {
var SecTrustEvaluate = new NativeFunction(SecTrustEvaluate_handle, 'int', ['pointer', 'pointer']);
SecTrustEvaluate_handle, new NativeCallback(function (trust, result) {
console.log('[*] Called SecTrustEvaluate()');
SecTrustEvaluate(trust, result);
Memory.writeU8(result, 1);
}, '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')
}, 'int', ['pointer', 'pointer']);
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) {
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']);
boringssl_context_set_verify_mode_handle,
new NativeCallback(function (a, b) {
console.log('[*] Called boringssl_context_set_verify_mode()');
}, 'int', ['pointer', 'pointer'])
console.log('[+] boringssl_context_set_verify_mode() hook installed.')
// 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()
// 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
frida -UF -l ssl-pinning-bypass.js
frida -UF -l ssl-pinning-bypass.js
执行结果如下:
然后就可以正常的抓包了。
Post Views: 2,769
赞赏
微信赞赏
支付宝赞赏
目前为止有一条评论