大家好,我是来自长亭科技的张一峰,今天我分享的题目是“iOS APP接口安全分析”。
议题主要内容有这么五方面:第一,业务开发模式变迁,第二,混合开发模式,第三,漏洞成因和漏洞基础,第四,实例展示,因为接口设计的缺陷导致的安全问题,第五,总结以及开发建议。
一、移动开发模式变迁
在开发模式变迁的时间轴,上面是一些时间节点,下面是相对应时间段在移动开发中Android和iOS使用的主流开发语言。
2007年Android和iOS问世,最初主要以java和oc作为编程语言,随后谷歌增加了c和c++的支持。后面由于html5技术的发展,逐渐出现了纯WEB的应用,不只是移动端,其它也会有,苹果在这期间推出了它的新开发语言swift,同时,谷歌也官方支持了kotlin语言进行开发。但是由于WEB APP存在缺陷,后面人们更多使用混合开发模式,也就是当前比较多的开发模式。截止到现在,从最初的java、oc,到现在官方主流推荐使用kotlin和swift开发,所以经过10多年变迁,有很大变化。同时,因为有混合开发模式,还有着js和HTML。
二、混合开发模式
从时间线来说,从原生到WEB再到混合开发模式,但是从逻辑角度来说,混合开发模式放在中间是更合适的。原生开发模式很好理解,我们用官方提供的语言,调用Framework的接口实现我们APP的功能。在webAPP里完全依赖于webview这个组件使用纯前端技术,我们APP的功能和逻辑主要由js实现。
而混合开发模式是原生和web的混合,它相当于二者间的一个结合点。主流APP的调用还是依靠原生代码进行调用,对于一些业务应用,比如更新比较频繁的,或者可能每天都会变化的业务,我们会使用纯web进行展示和渲染。
为了引出混合开发模式,就不得不提webAPP里面的优缺点,优点是跨平台、开发成本比较低,但缺点也很多,其中比如主要的就是它对复杂算法、多编程等等支持不是很好,同时,因为js能做的最多是压缩和混淆,它对以前的代码而言,与编译之后的二进制相比,对源代码保护还是有很多不足的。同时,有个最重要的缺点是与原生API交互是非常不方便的。
基于这个原因,人们开始逐渐使用混合开发模式,混合开发模式继承了webAPP里的很多优点,通过JSBridge手段避免了很多API调用的问题。JSBridge是什么?它是js代码到原生代码的接口,大家把它形容为像一个桥一样。通过这个桥,我们的API还是由原生的代码进行调用。其实JSBridge的功能并不是在混合开发模式中从零做出来的,它类似的功能在系统API本来就已经提供了,只不过在混合开发模式中程序员对API进行了一定的封装和扩展。
下面举个能实现这样功能的API,上面是UIWebView,下面是WKWebView,可以实现JSBridge的功能,后面还有相应API的例子,这里面就不再详细说了。基于这个API有一个使用很多的框架,就是这个WebViewJavascriptBridage框架,它就是对API进行封装,跟着框架所做的主要贡献是增加了回调函数,也就是说它能获取JSBridge的返回值,正常来说,我们通过这个API只能进行传参、调用,但是如果获取的JSBridge返回值,所以在复杂业务里是有缺陷的,这个框架主要是增加callback,能通过callback获取它的返回值。
另外一个是UIWebView的JavaScriptCore和WKScriptMessageHandler,它也都分别有对应的API可以实现这样的功能。后面有例子,这里就再不继续展开。说到这样的功能的API就不得不提安卓平台下这个接口,这个接口也是为了实现同样的接口,但是在2012年被人们发现它可以实现一个远程任意代码执行,这个漏洞影响特别深远,很多做开发的哪怕不懂安全,但在写代码时都会用一个API判断是否大于17来避开这个漏洞,其实这个地方是容易发生安全问题的。
三、漏洞成因和漏洞接触
首先介绍一下iOS deeplinks里的一种,就是URLScheme,它是提供了应用间IPC的方式,也就是说通过URLScheme可以调取另外一个应用再进行返回。怎么使用URLScheme这个技术?其实非常简单,只需要在xcode中进行注册,然后实现回调就可以了。
这个URLScheme大概长这个样子,它是遵从这三个规范,通过API可以传回URLScheme里哪个字段的值,一个应用如果自定义了URLScheme,其他第三方应用就可以对它进行调用,同时网页也可以进行调用。这个也是我们后面例子中远程进行攻击的一个非常依赖的点。
其实URLScheme也不是大家随便可以调的,它是有一个权限限制的,就是需要用户交互的,类似于让用户授权一样,如果通过网页进行调取的话,需要用户点击打开,如果是应用的话,在第一次是需要用户进行授权的。但那个授权其实相对来说是一个很宽泛的,它并不像强制访问控制,它只是一个需要用户点击和确认就可以了,用户很难去分辨。
说到URLScheme,我们下相对应的代码怎么去用它,前两行代码是URLScheme里面,已知URLScheme,去调取的话很简单的两行代码就可以用这个open函数进行调用了,如果是js的话也非常简单,一个链接就可以了,我们可以通过js里面这种自动点击的方式进行自动调用也可以。被动应用会进行回调函数,也就是最下面的函数,这个函数里它会获取传输URLScheme的参数,然后根据这个参数再执行相应的逻辑。
刚刚说有几个API可以实现JSBridge的功能,这里选了两个API,选了两个例子,让大家对JSBridge实现有个了解。上面是我们的前端链接,这里面Scheme是自定义的一种,并不是常见的。当这个链接被请求的时候,系统的这个函数就会被毁掉,相当于对我们请求的链接进行拦截。如果我们这里面自定义了一个Scheme,这里就可以判断这是不是我们自定义的Scheme,也就是说前后端对应好就可以实现通信,这里可以把传输参数取到,实现类似于伪协议的这样一个通信。通过这个可以实现从前端到后端的调用。另外一个是JScafffun里的函数,它和上一个区别是你可以自定义这个函数的名字,前端你可以认为它是JS函数进行调用,但是它其实也会进入这个代码,在这个代码可以进行获取参数。
有了上面那些基础知识,就引出了接口漏洞的成因:首先,因为自定义的第三方URLScheme,实际应用中你可以去市场中看,基本都有,基本是可以满足的。定义URLScheme以后,其他的应用或者网页可以对它进行调用,调用完程序可以执行URLScheme和对应回调函数,这个回调本身可能就存在一些缺陷。如果URLScheme回调函数本身没有什么特别的,我们需要关注它是不是有可能加载任意的URL,如果能加载任意的URL,也就是说我们可以执行我们任意的JS代码,执行任意的JS代码,如果恰好实现很多JSBridge,我们就可以对JSBridge接口进行调用,这种接口可能存在缺陷。两种情况都不符合的话,有其他方式允许应用加载这一段。
为了更好理解下一部分实例里的代码,这里简单讲一下OC运行时。OC是面向对象语言,它是一个大家接触比较多的消息转发机制,它在汇编层面发生函数对话时,它并不是直接把PC指针跳转到目标函数的地址,它是有个OBJC_MSGSEND的函数,把这个函数调用封装,但这个函数调用其实还是通过这个跳转过去,但是它相当于把这个消息进行转发,其实是oc里多态的一种实现。所以oc里面的多态相当于是在这运行池进行确定的。除此以外,运行池还提供performSelector的方式,Selector可以理解为函数名字,但其实不是。如果把这个函数名传里面去也可以实现函数调用,你不需要在程序里用形式把它包起来。
四、案例分析
案例1:远程窃取cookie的漏洞。我们首先看它的info.pilist的文件,因为最根本是在这个文件里。这里面我们发现它有一个自定义的URLScheme,有了这个URLScheme以后我们就要去看它的回调函数,它的回调函数逻辑很长,这里只是把相关的函数摘出来了,我们会发现它会进入APPSchemeManager这里,这里是做什么呢?它首先把我们传输的URLScheme以字符段的形式取出,然后拿到问号之后的字符,然后以dictionary的形式进行解析。因为URLScheme是我们传入的,所以这是一个可控变量。这段所要需要查询的dictionary,就是在标准URLScheme里query这个字段,它是dictionary格式的。进而判断dictionary是否有Key为type的字段,这跟我们没关系,就跳过了。
我们主要看它的eLse值,Else值里会判断是不是有Key的URL,如果有的话它继续判断是不是这个开头,如果两个check都符合的话,它会把这个value传给baseWebViewCONTRoller。下面是前端的代码,相当于是把这个View压到栈的最上面,把它渲染和展示。有了这个代码我们就知道最终需要传输的URLScheme就是这个样子,我们的K是URL,然后对应的value,前面是字符串URL,后面是链接。这个程序把我们传输的链接会进行渲染出来,然后同时也没有进行任何的校验。
我们让它打开我们的链接并不是真正的目的,所以我们还需要继续往下分析,如果它打开我们的链接,因为这个服务器是我们自己的,我们可以在这里写任意的js,任如果它定义了JSBridge接口的话,我们就可以调用它的JSBridge接口。
我们看一下这个URLSchemeJSBridge,为了实现这个远程攻击,你可以把这个URLScheme写到一个网页里。分析时发现它在初始阶段进行初始化了,初始化最重要的是这个registerLoginWithHanler这个地方,它是前面说到那个框架里面的用法,在这里面相当于前面也注册了名字就是函数的名字,然后第二个是函数名字所对应的操作。所以最终发现在我们加载页面里实现的效果,我们可以调用Module这个类下面的任意函数。
既然可以实现函数调用,我们就看看这个类下面有什么函数值得我们调,发现了有这个Bridge这个函数,根据函数名字感觉像获取用户信息,我们就需要去验证一下是不是我们想要的样子。所以尝试一下,然后断下来,发现它传输的有电话号码、UID、comparyID和token。
最后payload就呼之欲出了,我们调用getuserinfo接口,达到最终的数组,这个数组不是最终目的,我们把它发给远程服务器。这个页面里通过这个方式,把拿到的token和number发送给我们的服务器。因为这个页面本身就是www,所以它是符合策略的。这是我们实际在服务端收到一个请求,发现token和手机号都可以发送给我们。拿到token以后相当于对这个帐号实现了控制,而且在移动端里有个特点,就是token的有效期比较长。
案例2:任意文件上传漏洞。我们还是看info.plist,它有自定义的URLscheme,这个案例本身可以完全不依赖于加载自己页面本身,这可以完全挑过,因为这相当于实现问题比较多,从很多方面都可以去打。同时为了实现远程攻击,你也可以把URLscheme嵌到网页里。说到URLscheme嵌到网页里刚刚忽略了一个点,它需要有授权,这里最方便的方式是可以以领红包等等之类的方式,因为使用手机的都知道,基本所有都在用URLscheme的应用,比如第三方支付、朋友圈分享等等底层都会用到这个机制,所以对普通用户来说,很难分辨一个跳转是恶意的还是真正的业务功能。
这个里面它使用的shouldStartLoadWithRequest
这个处理,传输的最终会进入hander的URL里面,在这个里面它会首先判断这个URLscheme是不是等于空,如果等于的话它进一步查询URL参数进入handlemessage,进入下面的这个函数。在这个函数里它获取里面的action和args,这时我们还不知道它是干什么用的,但是我们知道URL是我们传入的,这个是个可控变量,也就是说它所查询到的参数也是我们可控的。这里面它有一个权限验证,就是它判断你是不是有权限调用这个API,但是这个验证是存在缺陷的。
然后就进入下一步,也就是说这两个参数我们知道是可控的。这里面首先会调用权限调用这个函数,这个函数里面会把action前面加一个jsapi_completion,把构造好的action传给performSelector,这个我们在刚刚讲oc运行池时提过这个函数,这个函数传入的相当于是对这个的调用。分析到这,我们就分析它其实可以实现的效果是对component下面的以“jsapi-”为开头,以completion为结尾的任意函数调用,我们就可以利用这个任意函数调用去做些有意思的事情。所以我们要分析这类里面到底有什么函数值得我们调用。我们发现它有一个UPloadImages的函数,因为它符合这个命名,我们分析这个函数,发现它首先会拿到这个函数的URL字段,还有一个path字段,其实这个URL是它上传图片目标的服务器地址,path是你要上传文件的路径。有了上面一些已知信息,我们最终的就出来了。
开头是jsapi,也就是我们过它第一个check,action是你所要调用的函数名,我们这里面传入刚刚看到的uploadimages,在这里面要传送两个关键字,一个是URL,一个是path,URL是我们自己的服务器,也就是说把沙箱里的文件上传到我们自己的服务轻易松手,path是沙箱里的一个文件。所以在POC里上传沙箱里的SQLline数据库。这个例子里面为什么刚刚我说其实不需要加载任意URL那个地方?因为你完全可以把上面的load写在最初的网页里,只要跳转这个目标应用,攻击在一瞬间就完成了。
分析了很多ios平台发现,发现回调函数里利用OC运行时进行函数动态调用的非常多,占比最大。这个例子可以看出你传输的字符是不是以某个数字开始,如果以某个数字开始就会调用某个函数,所以相当于传输一串数字就可以调用里面很多函数。实际分析下来,确实有很多这样的案例存在,是一个非常危险的东西。
案例3:业务逻辑漏洞。因为之前我们已经对怎么去确定URLscheme是什么样子的,所以在这里我们就把这两个都跳过了,因为可是于这几个实例是内容依次变少了,因为有很多东西思路是重复的,但代码不一样。
这个应用里,它为了实现增强网页里面的功能,它不单是加入了很多内存这样的函数,而且加入很多插件。可以通过插件ID调用这里面很多插件,我们从它可以调用的插件里发现了很引起我们兴趣的东西就是这个,就是它本身自己带了一个支付功能的应用。所以我们把这个ID传入,作为walletPaySDK传进去,它的参数里有一个很重要的OrderID,也就是说我们可以生成订单,但是我们先不支付,然后我们作为payload发过去,可能导致支付风险,就是可能支付别人那去了。当然,这个最终使用还需要依赖其他条件。但是这个例子充分说明了,对于支付类的SDK详细显示购买商品和金额是一个特别必要的事。之前交大他们做第三方支付研究时,也介绍过这一点。这个例子也是本地和远程都可以打的。
这里我们做个阶段性小结:之前我们演示的例子里主要是以文件读作为我们利用的一个点,我们自然而然想到文件写是不是也可以。同时,除了我们利用通过API这样的方式、这样的JSBridge,还有没有其他JS接口可以供我们调用?在攻击方式上,我们刚才讲了通过URLScheme和DeepLink的形式进行远程攻击,那还有没有其他方式?
下面看下JS代码执行情况,就是在ios9还是ios8,引入了一个更为安全、性能更高的WKWeView。WKWeView有一个新特性是支持JS注入。在下面的代码里,可以把沙箱的这个JS插入到页面中,以这个加载到前端页面都会自动引入我们一个JS进去。被插入的JS长什么样子?大概这样子,它也会定义很多函数。对JS程序而言,它可以当作自己实现的函数一样进行调用。JS会驻留在所有网页中,它会定义很多函数,效果类似于JSBridge,如果存在刚才案例中的情况,我们依然可以通过调用这样的函数进行渗透。
如果刚刚我们备注的JS存在沙箱或者远程的话,结合文件写等等,我们把本身给重写了,就可以实现XSS。如果热更新,当然,热更新现在已经被苹果禁止了,如果有热更新的话也可以实现效果。
下面看一个代码执行的例子,这个代码执行的例子里,它首先是判断这个scheme是不是等于这个字符串,如果等于的话它会post这个,这个到底是什么?它是ios里一个应用内部通信的机制。在之前都会调用这个server的API,类似于把这个先注册,注册时会传输这个action东西叫什么名字,它对应的是什么,也就是说它要执行的函数是什么。
所以在这个例子里,我们只要传输URLScheme,它这个字符串进去,就会导致这个函数被执行。但这里其实并没有实现任意代码执行,但是如果被执行是一个非常敏感的函数或者结合其他漏洞的话,还是可以进行利用的。
刚刚我们说了远程触发,除此之外还有一个经常被别人忽略的二维码扫描,这是普遍大家都在使用的一个方式,APP里面定义一个名以后,它可能在很多地都用同一个名进行加载,只不过这个URL传输的方式不一样。所以利用二维码扫描也是可以做到的,在之前我们团队有同事做过类似的活动,扫描二维码就可以把你的照片发送给远端服务器。
再一个是后台触发,但是这个利用难度就比较大了,因为后台权限是很难拿到的。但是这个比较有意思的一点是什么?如果把攻击向量放在后台,它是可以打所有前端,也就是说你的服务器可以打这个业务里所有的客户端,这是触发方式里比较有意思的一点。
五、总结以及开发建议
说一下怎样发现漏洞以及发现漏洞的思路是什么。首先,看看URLScheme定义情况,如果有的话我们就看它的回调函数以及重点关注它有没有加载URL这样的操作。另外,看看它有没有通过二维码或者其他方式加载任意URL。最后,对它我们可以调用的接口进行分析,看有没有可利用的点。
在开发定义里面说几点:iOS生态比安卓做得好多,也一定程度上保证了安全性,但是应用安全并不能完全依赖于系统安全。第二,对于URLScheme使用建议保证最少原则,因为增加一个URLScheme就相当于增加一个接口,增加一个接口就增加一个风险。在混合开发模式中,这种核心敏感的操作建议使用原生代码进行开发。外部可调用接口一定要严格过滤调用者,确保可信。比如刚刚的例子,如果我们在那个位置的check过不了的话,后面也就无从谈起了。
最后,对WebView本身是一个非常容易出现漏洞的组件之一,实际开发中又很难避开它去不用,所以在开发中要注意已知和未知的安全漏洞。
最后于 9小时前 被starbuck编辑 ,原因: