这是 酒仙桥六号部队 的第 6 篇文章。
全文共计3156个字,预计阅读时长9分钟。
1
渗透测试瓶颈
目前,碰到越来越多的大客户都会将核心资产业务集中在统一的APP上,或者对自己比较重要的APP,如自己的主业务,办公APP进行加壳,流量加密,投入了很多精力在移动端的防护上。
而现在挖漏洞除了拿到shell以外,客户又要求可以尽可能的挖到核心业务系统的漏洞,并将漏洞范围订在主域名,核心业务系统现在又基本集中在移动端,移动端现在都会进行APP加壳,流量加密。这就导致无法进行平常渗透测试过程,像老生常谈的中间人攻击,进行拦截,篡改数据包就很难进行。
接下来就尝试解决中间人攻击的问题,目标是
1.看到明文的request和response的数据包;
2.做到可以拦截,篡改数据包。
frida是平台原生app的Greasemonkey,说的专业一点,就是一种动态插桩工具,可以插入一些代码到原生app的内存空间去,(动态地监视和修改其行为),这些原生平台可以是Win、Mac、Linux、Android或者iOS。而且frida还是开源的。
环境需要越狱的IOS或者ROOT的Android。安装的版本需要一致。
MAC:
越狱Iphone:
通过USB链接越狱手机,可以执行frida-ps -aU 就代表环境安装成功。
启动目标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 Jail
OBJC: +[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]”的传入值和返回值。
篡改后,发现未能绕过,可能不是这个函数做最终的逻辑判断,想到竟然都弹窗提示了,和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("\t[-] Return Value: " + retval);//输出原本的返回值 var newretval = ptr("0x0") retval.replace(newretval)//替换新的返回值 console.log("\t[-] New Return Value: " + newretval) }} ); } catch(err) { console.log("[!] Exception2: " + err.message); } } else { console.log("Objective-C Runtime is not available!"); }执行结果如下:
成功绕过。
4 HOOK加解密函数 越狱检测绕过后,进一步开始尝试定位加解密的函数。
关于定位加解密函数这块在Android可以尝试使用traceview去分析追踪函数。
(https://developer.android.google.cn/studio/profile/traceview)
IOS可以尝试使用runtime去追踪函数,uidump从界面按钮入手,Nslog日志等位置入手,或者直接找相关关键字的函数去入手。
例如crypt(decryot,encrypt),HTTP,Network,目标厂商的名字简写找不到,可以尝试搜索NSString系统库等。
这边推荐一个大佬的github项目。使用可以参考这个githu项目,非常好用,先用之前写好的绕过越狱检测的脚本启动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类。
本文作者:酒仙桥六号部队
本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/135848.html