移动应用安全基础篇——解密iOS加密数据 – 作者:tales

本篇为 tales 移动安全专题第三篇。

1、移动应用安全基础篇——Android、ios 环境准备  https://www.freebuf.com/column/199666.html

2、移动应用安全基础篇——绕过 iOS 越狱检测   https://www.freebuf.com/column/201114.htm

介绍

如今,在做 APP 安全测试的时候,越来越多的 APP 数据使用加密传输,一般的做法都需要去逆向 APP 并寻找到加解密算法。今天主要介绍一下 iOS 的一些逆向基础知识,教大家碰到加密数据的 APP 后该如何去解密。

今天主要是针对两款有不同加密方式的 iOS 应用,难度由低到高。

案例一:

首先解决挂代理抓不到包的问题

使用 objection ios sslpinning disable 绕过证书绑定

在登录处抓包发现,request 包和 response 包都为加密传输:

appmon 提供的 scripts 

hack.lu 提供的 scripts

通过参考 github 上的 js 脚本,改写了个较为全面的 hook.js 脚本:

// 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
            }))
        }
    }
})

使用 frida hook CCCrypt 函数

可以直观的看到加密请求数据和解密返回数据为明文。

operation: 0x0 代表加密,0x1 代表解密,CCAlgorithm: 0x0 指加密方式是 kCCAlgorithmAES128,CCOptions: 0x1 指模式是 cbc,key=DATA_KEY20150116 和 iv=20150116

参阅 CommonCryptor.h 各参数意义

案例二:

在登录处抓包发现,request 包和 response 包都为加密传输: 使用 hook.js 脚本发现 hook 不到 老方法,首先使用 frida-ios-dump 对该 APP 进行一键 dump

frida-ios-dump,该工具基于 frida 提供的强大功能通过注入 js 实现内存 dump 然后通过 python 自动拷贝到电脑生成 ipa 文件,通过配置完成之后真的就是一条命令砸壳。 

砸壳完成后会生成 ipa 文件,我们解压缩然后使用 IDA 加载完二进制文件 然后在 String 窗口搜索 loginbypassword(这个是登录时的信息),搜索后进入对应的类,接下来我们进入这个类看它用了哪些方法  

找到这个字符串引用的代码位置 之后双击 callWebAPI:data:method:ssl:completionHandler: 找到 [WebService callWebAPI:data:method:ssl:completionHandler:] 然后 F5 一下 浏览该类发现可以看到 data 等关键加密信息,接着我们尝试搜索 data 前面的 setValue:forKey [_priv_NBSSafeMutableDictionary setValue:forKey:] 查看该类发现无结果,返回上一步重新查看加密所在的类

v87 由 v86 = -[WebService returnDictionaryWithDataPath:](v11,「returnDictionaryWithDataPath:」, v201) 返回

查看 returnDictionaryWithDataPath: 

v8 = +[RSA encryptString:privateKey:](&OBJC_CLASS___RSA,「encryptString:privateKey:」, v4, v6); v4 由 convertToJsonData:返回(明文)v6 由 AppPrivate 返回(密钥)

查看密钥返回函数 AppPrivate 和 encryptString:privateKey 函数 然后使用 frida 进行 hook 可以看到解密后的信息

使用 objection

ios hooking watch method 「+[RSA encryptString:privateKey:]」 –dump-args
ios hooking watch method 「+[RSA encryptString:privateKey:]」 –dump-return

直接使用 objection 的这两句命令可以达到同样的效果

附 JS:

if (ObjC.available){    try{        var className = "RSA";        var funcName = "+ encryptString:privateKey:";        var hook = eval('ObjC.classes.' + className + '["' + funcName + '"]');        console.log("[*] Class Name: " + className);        console.log("[*] Method Name: " + funcName);
        Interceptor.attach(hook.implementation, {          onEnter: function(args) {            var param1 = new ObjC.Object(args[2]);            console.log("args[2] -> " + param1);            var param2 = new ObjC.Object(args[3]);            console.log("args[3] -> " + param2);
          },          onLeave: function(retval) {            var retur = new ObjC.Object(retval);            console.log("retval -> " + retur);     
          }
        });
    }    catch(err){        console.log("[!] Exception2: " + err.message);
    }
}else{    console.log("Objective-C Runtime is not available!");
}

来源:freebuf.com 2019-05-22 16:05:19 by: tales

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论