0x00 背景
我们知道,为了防止中间人攻击(MITM ),一般iOS应用会使用SSL Pinning。大多数情况下,这些App会使用AFNetworking或者NSURLSession这两个库来做SSL Pinning。但是这两个库使用了iOS提供的AFSecurityPolicy类,该类又调用了SSLHandshake,SSLCreateContext等iOS的API,只要hook了这几个API(ssl-kill-switch2),即可bypass SSL Pinning。但是最近发现某App并未使用这两个库进行SSL Pinnig,而是使用了libcurl和openssl来做SSL Pinning,这两个库被编译成native代码,静态链接到App binary中,没有symbol了,让我们的分析与bypass难度大大增加。
0x01 寻找libcurl的位置
上网下载build-libcurl-ios,按照README编译出iOS对应的libcurl.a文件(注意需要自己下载curl源码CURL Realeases)。然后用IDA打开libcurl.a文件,选择libcurl_la-easy.o,就能看到诸如_curl_easy_perform
,_curl_easy_setopt
等函数了。
__text:00000000000001F4 ; CURLcode __cdecl curl_easy_perform(CURL *curl)
__text:00000000000001F4 _curl_easy_perform
__text:00000000000001F4
__text:00000000000001F4 var_70 = -0x70
__text:00000000000001F4 var_68 = -0x68
__text:00000000000001F4 var_64 = -0x64
__text:00000000000001F4 var_60 = -0x60
__text:00000000000001F4 var_50 = -0x50
__text:00000000000001F4 var_40 = -0x40
__text:00000000000001F4 var_30 = -0x30
__text:00000000000001F4 var_20 = -0x20
__text:00000000000001F4 var_10 = -0x10
__text:00000000000001F4
__text:00000000000001F4 STP X28, X27, [SP,#var_60]!
__text:00000000000001F8 STP X26, X25, [SP,#0x60+var_50]
__text:00000000000001FC STP X24, X23, [SP,#0x60+var_40]
__text:0000000000000200 STP X22, X21, [SP,#0x60+var_30]
__text:0000000000000204 STP X20, X19, [SP,#0x60+var_20]
__text:0000000000000208 STP X29, X30, [SP,#0x60+var_10]
__text:000000000000020C ADD X29, SP, #0x60+var_10
__text:0000000000000210 SUB SP, SP, #0x10
__text:0000000000000214 MOV X19, X0
__text:0000000000000218 CBZ X19, loc_23C
__text:000000000000021C LDR X8, [X19,#0x60]
__text:0000000000000220 CBZ X8, loc_244
__text:0000000000000224 ADRP X1, #ltmp2@PAGE ; "easy handle already used in multi handl"...
__text:0000000000000228 ADD X1, X1, #ltmp2@PAGEOFF ; "easy handle already used in multi handl"...
__text:000000000000022C MOV X0, X19
__text:0000000000000230 BL _Curl_failf
__text:0000000000000234 MOV W21, #2
__text:0000000000000238 B loc_3A8
注意倒数第五第六行的字符串"easy handle already used in multi handl"...这个字符串,我们可以用IDA在我们的App binary里查找该字符串,即可定位到curl_easy_perform
的位置。如下(该函数本来是没有symbol的,函数名是我修改过后的名字):
__text:0000000100A94AF0 curl_easy_perform ; CODE XREF: sub_1003CD74C+28p
__text:0000000100A94AF0 ; sub_10042732C+20p
__text:0000000100A94AF0
__text:0000000100A94AF0 var_80 = -0x80
__text:0000000100A94AF0 var_78 = -0x78
__text:0000000100A94AF0 var_70 = -0x70
__text:0000000100A94AF0 var_68 = -0x68
__text:0000000100A94AF0 var_5C = -0x5C
__text:0000000100A94AF0 var_58 = -0x58
__text:0000000100A94AF0 var_54 = -0x54
__text:0000000100A94AF0 var_50 = -0x50
__text:0000000100A94AF0 var_40 = -0x40
__text:0000000100A94AF0 var_30 = -0x30
__text:0000000100A94AF0 var_20 = -0x20
__text:0000000100A94AF0 var_10 = -0x10
__text:0000000100A94AF0
__text:0000000100A94AF0 FA 67 BB A9 STP X26, X25, [SP,#var_50]!
__text:0000000100A94AF4 F8 5F 01 A9 STP X24, X23, [SP,#0x50+var_40]
__text:0000000100A94AF8 F6 57 02 A9 STP X22, X21, [SP,#0x50+var_30]
__text:0000000100A94AFC F4 4F 03 A9 STP X20, X19, [SP,#0x50+var_20]
__text:0000000100A94B00 FD 7B 04 A9 STP X29, X30, [SP,#0x50+var_10]
__text:0000000100A94B04 FD 03 01 91 ADD X29, SP, #0x50+var_10
__text:0000000100A94B08 FF C3 00 D1 SUB SP, SP, #0x30
__text:0000000100A94B0C F3 03 00 AA MOV X19, X0
__text:0000000100A94B10 33 01 00 B4 CBZ X19, loc_100A94B34
__text:0000000100A94B14 68 32 40 F9 LDR X8, [X19,#0x60]
__text:0000000100A94B18 28 01 00 B4 CBZ X8, loc_100A94B3C
__text:0000000100A94B1C 01 0B 00 D0 ADRP X1, #aEasyHandleAlre@PAGE ; "easy handle already used in multi handl"...
__text:0000000100A94B20 21 BC 0B 91 ADD X1, X1, #aEasyHandleAlre@PAGEOFF ; "easy handle already used in multi handl"...
__text:0000000100A94B24 E0 03 13 AA MOV X0, X19
__text:0000000100A94B28 21 D0 FF 97 BL sub_100A88BAC
__text:0000000100A94B2C F5 03 1F 32 MOV W21, #2
而紧邻该函数的上方就是curl_easy_setopt
函数,该函数是一个不定长参数的函数(见该函数原型curl_easy_setopt
),不容易hook,我们可以找到它调用的下一层函数Curl_setopt
,hook该函数会容易一些。在我们的App binary中跳转到该函数地址,使用frida hook它。
0x02 通过hook bypass SSL Pinning
原本我以为SSL Pinning仅跟CURLOPT_PINNEDPUBLICKEY
有关,按照该页面的教程将burp的证书sha256哈希值写入到原App binary,覆盖掉App的原哈希值,结果发现并没有效果。经反复实验,我发现传给Curl_setopt
的opt中与SSL Pinning相关的参数一共有三个(参见curl.inc.in):
CURLOPT_SSL_VERIFYHOST
---- 00081CURLOPT_SSL_VERIFYPEER
---- 00064CURLOPT_PINNEDPUBLICKEY
---- 10230
通过patch binary的方法太麻烦了,我们可以使用hook方法。hook Curl_setopt
函数,当传入的opt为以上三个数字时,全部忽略掉或者将第三个参数指向的内存设为0即可。
在调试自己的hook时还可以hook curl_easy_perform
函数,打印出返回值,返回值表示的意义在curl.h里可以查找到,便于我们调试。
0x03 hook代码
涉及到一些机密,暂时不能公开。