freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

当Frida来“敲”门
2020-10-23 22:14:16

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 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]”的传入值和返回值,

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("\n\t[*] Class Name: " + className);
        console.log("[*] Method Name: " + funcName);
        //For viewing and manipulating arguments
        //console.log("\t[-] Value1: "+ObjC.Object(args[2]));
        //console.log("\t[-] Value2: "+(ObjC.Object(args[2])).toString());
        console.log("\t[-]arg value   "+args[2]);
        Interceptor.attach(hook.implementation, 
            {
              onLeave: function(retval) {
                console.log("[*] Class Name: " + className);
                console.log("[*] Method Name: " + funcName);
                console.log("\t[-] Return Value: " + retval);
             }}
            );
      }
    });
}
//Your class name  and function name here
hook_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("\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!");
}

执行结果如下:

成功绕过。

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类。

# Frida # Frida Hook
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录