使用frida辅助分析iOS App加密算法

admin 2023年6月6日09:45:53评论861 views字数 7986阅读26分37秒阅读模式


点击上方蓝字 关注联想全球安全实验室

使用frida辅助分析iOS App加密算法

使用frida辅助分析iOS App加密算法


使用frida辅助分析iOS App加密算法

1、前言

移动端App为了提升通信过程的安全性,通常会采用加密算法来加密通信数据。例如:在App客户端,使用AES加密算法加密HTTPS请求数据包中的数据后发送给服务器;服务器返回响应包时,也采用AES加密响应包中的数据后再返回给客户端。这种端到端的加密方式使App和服务器之间的通信更加安全,对于渗透测试也增加了一定难度。对于安卓App的加密算法分析,一种通用的方法是使用hook工具hook Java系统库提供的加密算法API(通常开发者会调用Java系统库的加密算法API来实现加密功能),通过hook来动态分析、迅速定位加密算法实现代码。那么对于iOS App,是否也存在类似的通用方法?下面将对此问题展开分析。

使用frida辅助分析iOS App加密算法

2、iOS是否提供了加密算法的系统API

通过查阅资料了解到,iOS上也提供了多种加密算法的系统API,接下来一起了解一下具体的API函数。


2.1 对称加密算法——CCCrypt函数

CCCrypt函数提供了对称加密算法功能,该函数可以一次完成对称加密算法的加密或解密操作,是CCCrytor系列函数:CCCrytorCreate(), CCCryptorUpdate(), CCCryptorFinal() 和CCCryptorRelease()的过程封装。使用该函数时需要引入系统库:

#import <CommonCrypto/CommonCryptor.h>

1.函数定义

CCCryptorStatus CCCrypt(    CCOperation op,     // 加密还是解密,枚举值    CCAlgorithm alg,    /* 定义加密算法类型,枚举值 */    CCOptions options,  /*  选择的填充方式,以及是否选择ECB模式,默认是CBC模式 */    const void *key,    /* 密钥 */    size_t keyLength,   /* 密钥长度 */    const void *iv,     /* iv向量,只能用于CBC模式,不传默认为16位0 */    const void *dataIn, /* 要加解密的数据 */    size_t dataInLength, /* 要加解密的数据长度 */    void *dataOut,       /* 加解密结果数据 */    size_t dataOutAvailable, /* 结果数据缓存大小 */    size_t *dataOutMoved    /* 执行成功后,结果数据实际占用的空间大小 */)

函数返回值是CCCryptorStatus类型,该类型是枚举值,定义如下:

enum {    kCCSuccess            = 0,     // 成功      kCCParamError        = -4300, // 非法参数    kCCBufferTooSmall    = -4301, // 表明dataOut的空间不足    kCCMemoryFailure    = -4302, // 内存不足    kCCAlignmentError    = -4303, // 输入数据不正确    kCCDecodeError        = -4304, // 输入数据未解码或解密出错    kCCUnimplemented    = -4305  // 不支持该算法  }

2.主要参数说明

a) op: 加密还是解密,枚举值,定义如下:

enum {    kCCEncrypt = 0,  // 加密    kCCDecrypt = 1   // 解密};

b) alg:加密算法类型,枚举值,定义如下:

enum {    kCCAlgorithmAES128 = 0,    kCCAlgorithmAES = 0,    kCCAlgorithmDES,    kCCAlgorithm3DES,           kCCAlgorithmCAST,           kCCAlgorithmRC4,    kCCAlgorithmRC2,       kCCAlgorithmBlowfish}; 

最常用的是AES、DES、3DES加密算法,说明如下:

  • kCCAlgorithmAES128 和 kCCAlgorithmAES:两个类型是一样的,AES分组加密,固定的加密块是128位,即16个字节,跟密钥长度无关,我们常说的128、192、256主要指的就是密钥长度(当然还有加密轮数也不一样),加密运算操作相同,所以这两个是一样的。

  • kCCAlgorithmDES: DES明文按照64位进行分组,分组后的明文与密钥按位替代或交换的方法形成密文组。密钥的长度是 64 位即8个字节(其实是56位,其中有8位是奇偶校验位)。

  • kCCAlgorithm3DES: 3DES是针对DES算法密钥过短、存在安全性的问题而改进的一个措施,被称为“3DES”。其实只是通过简单的执行3次DES来达到增加密钥长度和安全而已,3DES算法在对明文M进行加密时,采用了三次加密过程,其中第一次和第三次是采用DES的加密算法,第二次采用的则是解密算法,从而得到最终的密文C。这种加密过程为“加密-解密-加密”,所以又称为EDE(Encrypt-Decrypt-Encrypt)方案。

c) options:  填充方式,以及加解密模式,枚举值,定义如下:

enum {    kCCOptionPKCS7Padding   = 0x0001, // CBC模式, PKCS7Padding    kCCOptionECBMode        = 0x0002  // ECB模式};

d) const void *key: 加解密算法的密钥

e) keyLength: 密钥长度,可以使用以下枚举值:

enum {    kCCKeySizeAES128          = 16,    kCCKeySizeAES192          = 24,    kCCKeySizeAES256          = 32,    kCCKeySizeDES             = 8,    kCCKeySize3DES            = 24,    kCCKeySizeMinCAST         = 5,    kCCKeySizeMaxCAST         = 16,    kCCKeySizeMinRC4          = 1,    kCCKeySizeMaxRC4          = 512,    kCCKeySizeMinRC2          = 1,    kCCKeySizeMaxRC2          = 128,    kCCKeySizeMinBlowfish     = 8,    kCCKeySizeMaxBlowfish     = 56};

关于CCCrypt中的key和keyLength

AES数据块长度为128位,所以IV长度需要为16个字符(ECB模式不用IV),密钥根据指定密钥位数分别为16、24、32个字符,IV与密钥超过长度则截取,不足则在末尾填充''补足。


f) dataOutMoved:操作成功之后,被写入dataOut的字节长度, 所以最后我们根据dataOutMoved从dataOut截取我们最后获得的数据


2.2 对称加密算法——CCCryptorCreate等函数

除了使用CCCrypt函数一次完成加解密,也可以分步骤调用系列函数:CCCrytorCreate(), CCCryptorUpdate(), CCCryptorFinal() 和CCCryptorRelease()。使用这些函数时也需要引入系统库:

#import <CommonCrypto/CommonCryptor.h>

1.函数定义

分步骤调用对称加解密算法涉及的函数如下:

// 创建加密器CCCryptorRefCCCryptorStatus CCCryptorCreate(CCOperation op, CCAlgorithm alg, CCOptions options,     const void* key, size_t keyLength, const void* iv, CCCryptorRef* cryptorRef);
// 获取输出数据的最大长度size_t CCCryptorGetOutputLength(CCCryptorRef cryptorRef, size_t inputLength,bool final)
// 加密处理CCCryptorStatus CCCryptorUpdate(CCCryptorRef cryptorRef, const void* dataIn, size_t dataInLength, void* dataOut, /*返回数据 */ size_t dataOutAvailable, size_t *dataOutMoved /* 实际写入的数据长度 */)
// 处理最后的数据块CCCryptorStatus CCCryptorFinal( CCCryptorRef cryptorRef, void* dataOut, /*返回数据 */ size_t dataOutAvailable, size_t *dataOutMoved /* 实际写入的数据长度 */)
// 释放加密器CCCryptorStatus CCCryptorRelease(CCCryptorRef cryptorRef)

函数参数与CCCrypt函数参数基本一致。


2.3 MD5哈希算法函数——CC_MD5

CC_MD5函数提供了MD5算法功能,该函数可以一次完成MD5算法操作,是系列函数:CC_MD5_Init()、CC_MD5_Update()、CC_MD5_Final()等函数过程的封装。

使用这些函数时需要引入系统库:

#import <CommonCrypto/CommonDigest.h>

1. 函数定义

unsigned char * CC_MD5(    const void *data, // 待处理的字符串    CC_LONG len,      // 待处理的字符串长度    unsigned char *md // 接收结果的数组, 也是返回值);
// 分步骤的MD5算法函数int CC_MD5_Init(CC_MD5_CTX *c);int CC_MD5_Update(CC_MD5_CTX *c, const void *data, CC_LONG len);int CC_MD5_Final(unsigned char *md, CC_MD5_CTX *c);


2.4 其他哈希算法函数

iOS上SHA1、SHA256哈希算法都有对应不同的函数名,系统提供了多个哈希函数:CC_SHA1、CC_SHA224、CC_SHA256、CC_SHA384、CC_SHA512等。函数参数与CC_MD5类似,也有对应的过程函数:CC_XXX_Init、CC_XXX_Update、CC_XXX_Final,这里不再介绍,详细内容见:https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/CC_SHA.3cc.html


使用frida辅助分析iOS App加密算法

3、使用frida hook iOS加密算法API

既然iOS上系统提供了加解密算法的API函数,而且一般开发者使用加解密算法时也是直接调用系统库的这些API函数,所以,可以直接hook这些函数快速分析加解密算法。

这里以CCCrypt函数为例,编写frida hook代码,  实现动态监控该函数调用的效果:当App调用到该函数时输出加解密相关参数、明文、密文、调用堆栈等。主要代码如下:

// hook CCCrypt函数'use strict';
const CCoptions = [ "0", "1 CBC PKCS7Padding", "2 ECB", "3 ECB PKCS7Padding"];const CCAlgorithm = [ { name: "kCCAlgorithmAES128", blocksize: 16 }, { name: "kCCAlgorithmDES", blocksize: 8 }, { name: "kCCAlgorithm3DES", blocksize: 8 }, { name: "kCCAlgorithmCAST", blocksize: 8 }, { name: "kCCAlgorithmRC4", blocksize: 8 }, { name: "kCCAlgorithmRC2", blocksize: 8 }];
const CCMode = ["null", "kCCModeECB", "kCCModeCBC", "kCCModeCFB", "kCCModeCTR", "kCCModeF8", "kCCModeLRW", "kCCModeOFB", "kCCModeXTS", "kCCModeRC4", "kCCModeCFB8"];
const CCPadding = ["ccNoPadding", "ccPKCS7Padding"];const CCModeOptions = ["null", "KCCModeOptionCTR_LE", "kCCModeOptionCTR_BE"];
// 把char* 转换成base64var charToBase64 = function(input,length){ var NSData = ObjC.classes.NSData; var data = NSData.dataWithBytes_length_(ptr(input), length); var base64 = data.base64EncodedStringWithOptions_(32); return base64;};
var dumpBytes = function(inBytes, inLength) { if (inBytes == 0x0) { console.log("null"); } else { console.log( hexdump(ptr(inBytes), { length: inLength, header: true, ansi: true }) ); console.log("Base64:" + charToBase64(inBytes, inLength)); }};

var handlers = { CCCrypt: { onEnter: function(args) { var operation = args[0].toInt32(); var alg = CCAlgorithm[args[1].toInt32()].name; this.options = CCoptions[args[2].toInt32()]; var keyBytes = args[3]; var keyLength = args[4].toInt32(); var ivBuffer = args[5]; var inBuffer = args[6]; this.inLength = args[7].toInt32(); this.outBuffer = args[8]; var outLength = args[9].toInt32(); this.outCountPtr = args[10];
if (operation === 0) { this.operation = "encrypt" console.log("***************** encrypt begin **********************"); } else { this.operation = "decrypt" console.log("***************** decrypt begin **********************"); }
console.log("CCCrypt(" + "operation: " + this.operation + ", " + "CCAlgorithm: " + alg + ", " + "CCOptions: " + this.options + ", " + "keyBytes: " + keyBytes + ", " + "keyLength: " + keyLength + ", " + "ivBuffer: " + ivBuffer + ", " + "inBuffer: " + inBuffer + ", " + "inLength: " + this.inLength + ", " + "outBuffer: " + this.outBuffer + ", " + "outLength: " + outLength + ", " + "outCountPtr: " + this.outCountPtr + ")" );
console.log("key: "); dumpBytes(keyBytes, keyLength);
console.log("IV: "); // ECB模式不需要iv,所以iv是null dumpBytes(ivBuffer, keyLength);
console.log("In buffer:"); dumpBytes(inBuffer, this.inLength); }, onLeave: function(retVal) { console.log("Out buffer:"); dumpBytes(this.outBuffer, Memory.readUInt(this.outCountPtr)); // 输出调用堆栈,会识别类名函数名 console.log('CCCrypt called from:n' + Thread.backtrace(this.context, Backtracer.ACCURATE) .map(DebugSymbol.fromAddress).join('n') + 'n'); } },};

if (ObjC.available) { console.log("frida attach"); for (var func in handlers) { console.log("hook " + func); Interceptor.attach(Module.findExportByName("libcommonCrypto.dylib", func), handlers[func]); }} else { console.log("Objective-C Runtime is not available!");}


使用frida辅助分析iOS App加密算法

4、案例

 某App发送短信验证码的数据包中对手机号进行了加密处理,请求数据包如下图所示:

使用frida辅助分析iOS App加密算法

使用之前编写的hook脚本进行尝试,命令如下图:

使用frida辅助分析iOS App加密算法

hook输出的日志中发现了界面输入的手机号138*88,输出的加密日志如下图:

使用frida辅助分析iOS App加密算法

可以看到,加密算法使用的是AES,加密模式、key、iv、加密结果、调用堆栈都清晰的打印出来了,加密后的Base64字符串正好是请求包中msisdn的值。搞清楚了加密算法及加密参数后,就可以修改明文数据包,然后使用同样的加密方式加密后发给服务器进行渗透测试。

注意,本文中的hook脚本只实现了对CCCrypt函数的hook,实际测试时还会有其他加密函数,还需要不断完善才能更方便的定位常用加密算法,提升渗透测试效率。

使用frida辅助分析iOS App加密算法

5、参考

  • https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/CCCrypt.3cc.html

  • https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/CC_MD5.3cc.html

  • https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/CC_SHA.3cc.html

  • https://www.jianshu.com/p/93466b31f675

  • https://www.cnblogs.com/cocoajin/p/6150203.html

-END-

往期精彩合集

●联想的产品安全

●通过Tor + Proxychains/Proxifier 来尽可能隐藏入侵痕迹

Web安全开发实战(一)

一个赏金3000美元的一键账号接管漏洞分析

●CVE-2023-24055

●开源软件安全治理的思考与实践

NFC卡复制和修改

Flutter逆向初探

●密钥管理实践

RSA-CRT 故障注入攻击

CSL

使用frida辅助分析iOS App加密算法

联想GIC全球安全实验室(中国)

[email protected]

原文始发于微信公众号(联想全球安全实验室):使用frida辅助分析iOS App加密算法

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年6月6日09:45:53
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   使用frida辅助分析iOS App加密算法http://cn-sec.com/archives/1653365.html

发表评论

匿名网友 填写信息