Author: Sniperhg@Vulpecker Team 、MissCoconut@Vulpecker Team
前言
在去年Mobile Pwn2Own上MWR Labs成功攻破了三星S8,据官方报道整个攻击链总共由11个exp构成,是目前Pwn2Own历史上最长利用链。上周MWR Labs公开了他们的Slide,在本次攻击中,其高深之处并不在于使用了多少个0day,而是使用的都是系统自带App层面的逻辑漏洞甚至是App特性的一连串的精巧组合而形成的攻击链。本文根据这个slide来对其攻击思路和步骤进行简要分析。
初探
纵观整个利用链,攻击者的代码得以成功执行以及敏感信息得以成功获取,其中最关键的漏洞出现在三星手机自带的应用商店Galaxy Apps中。
由于开发者疏忽,release版本的Galaxy Apps中存在如下遗留的调试代码:
这段代码的作用是为了调试Galaxy Apps与升级服务器之间的连通性而去加载SD卡上的配置文件(/sdcard/saconfig.ini),为方便本地调试,配置文件中可以手动指定升级服务器地址,配置文件格式如下所示:
攻击者通过指定升级服务器地址,再加上中间人转发请求,可以让Galaxy Apps安装攻击者指定的任意App。一般我们在平时的审计中,可以写一个App去释放这个配置文件或是直接adb push配置文件到/sdcard中,但在Pwn2Own比赛环境下,是无法连接USB进行adb install的,有限的物理接触也仅能进行点击浏览器,打开url等操作。那么这里是如何生成或是释放这个关键的saconfig.ini文件的,以及如何通过浏览器发动这个远程攻击的,笔者就来根据slide大胆猜测一下攻击思路,一步步进行逆向探究。
在探究之前我们先要明确一点:起点入口是三星自带的浏览器SBrowser,最终目标是在Galaxy S8上执行攻击者的代码并窃取隐私文件。我们从整个利用链的最后一环入手来倒序分析,假定此时Galaxy Apps已经等着配置文件来进行下一步攻击了。
Step 1 如何释放saconfig.ini配置文件?
由于代码中硬编码了配置文件路径(/sdcard/saconfig.ini),我们必须在指定目录下生成配置文件,那么该如何生成到指定目录?通过浏览器强制下载?显然不可,下载的文件位于Download目录内。这里利用了Samsung Notes中的一处Zip解压缩目录穿越漏洞。Samsung Notes中存在一处导出组件,可加载memo类型的文件,memo类型文件本质上是个zip压缩包,在Samsung Notes加载memo文件时会调用一个方法去解压这个memo文件,通过解压恶意构造的memo文件来准确释放文件到/sdcard下:
存在漏洞的组件:com.samsung.android.app.notes.composer.ConvertToSdocActivity
这里我们可以猜想memo文件的构成,其中有个文件的路径应该是这样的:”../../../../../../sdcard/saconfig.ini”,组合上面代码会产生路径“/data/data/com.samsung.android.app.notes/cache/unzip_1529565489/../../../../../../sdcard/saconfig.ini”,这样最终的路径就变成“/sdcard/saconfig.ini”了。
Step 2 如何让Samsung Notes打开memo文件?
如何打开特定App的特定activity?我们知道平时开发过程中直接使用Context.startActivity()即可,但在比赛环境下,并不能这样方便的执行代码,而且Samsumg Nots中存在问题的组件,并没有响应“android.intent.category.BROWSABLE”action,也无法通过浏览器打开。
所以安全人员这里找了一个跳板——Android Vending,也就是Google Play应用商店。在这个App中存在一个导出的Activity——LaunchUrlHandlerActivity,可接收外部传入的参数,并打开指定的应用,具体代码如下所示:
并且这个Activity是响应“android.intent.category.BROWSABLE”的,意味着我们能通过浏览器利用其自定义协议进行调用。安全人员随后测试了http协议、file协议以及content协议,发现这里只能使用content协议,随后尝试了Chrome的Content Provider和系统Media Provider,最终确定了可行的URI:market://details?url=content://media/external/file/350&id=com.samsung.android.app.notes
这样Android Vending会唤起com.samsung.android.app.notes并把content://media/external/file/{file_id}作为data传递给Samsung Notes。
Step 3 如何确定file_id?
Step 2中,我们看到URI中有一个content://media/external/file/{file_id},这个file_id是不确定的数字,代表的是memo文件在系统 Media Provider里对应的ID,至于如何确定,下面是MWR给出的一段JS代码:
这段JS的意思是从id=300开始向下枚举Meida Provider中的文件,如果文件不存在的话就一直递减,直到出现一个存在的文件正确显示在页面上。
如下图示是上一个下载的文件payload.html:
先取得memo文件将占位的ID(这个时候memo文件还没下载呢),然后保证这个时候手机的外部储存里不会多出任何文件,因为任何文件的出现都会让ID变化。如果发现i=100的时候出现了payload.html页面,那么101就是memo文件将会存在的ID值。
Step 4 这段JS由谁执行?
MWR Labs安全人员指出:
由于三星自带的浏览器不支持content协议,所以这里需要用Chrome来加载payload.html,那么如何唤起Chrome?这里利用Chrome的特性即可:
googlechrome://navigate?url=content://com.android.chrome.FileProvider/downloads/payload.html
Step 5 payload.html从何而来?
三星浏览器SBrowser支持Content-Type: application/force-download; ,通过配置攻击者的Web服务器使其强制下载payload.html:
经过强制下载后,payload.html是保存在/sdcard/Download下的。巧合的是Chrome中的一个Content Provider指向路径即为/sdcard/Download,所以这里通过传入:googlechrome://navigate?url=content://com.android.chrome.FileProvider/downloads/payload.html即可引导Chrome打开payload.html。
小结
到这里暂时休息整理一下,我们从后往前看,就是从浏览器访问一个页面到释放配置文件的过程了。
结合官方给出的图示:
顺序就是:
- SBrowser访问index.html,index.html页面里也许使用<iframe>嵌入了一个payload.html,这个时候利用force-download特性将payload.html下载到手机里再利用googlechrome://navigate?url=content://com.android.chrome.FileProvider/downloads/payload.html唤起Chrome并加载payload.html;
- 在payload.html中我们枚举出memo文件占位ID,接着请求下载了memo文件,再跳转market://details?url=content://media/external/file/350&id=com.samsung.android.app.notes唤起了Android Vending;
- Android Vending被唤起后发送Intent继而唤醒Samsung Notes访问memo文件进行解压缩释放了配置文件saconfig.ini。
进一步利用
Step 6 如何突破比赛时间限制?
进行到这里我们已经有了这个配置文件,虽然配置文件能控制Galaxy Apps的行为去劫持下载,但是由于其使用的是Android Job Scheduler,Scheduler的执行间隔是15分钟,而比赛的规则是每次尝试只有5分钟时间,所以这里碰到了一个限制需要突破:如何让Scheduler快速执行?
由于Galaxy Apps在启动时会去加载这个配置文件,这里想到了让Galaxy Apps重启。由于Galaxy Apps是系统应用,会在系统启动时自启动,那么这里面临着两个选择,是选择crash掉一个App还是crash system引起手机重启?MWR Labs选择了后者。
Step 7 如何重启系统?
在这一步的攻击中,利用了系统进程“com.android.server.telecom”中的组件“com.android.server.telecom.components.UserCallActivity”中存在一处隐藏的空指针异常未捕获的问题:
如果外部直接启动这个Activity不带任何data进来,URI将会为null,null传给getScheme()方法时就会产生空指针异常,系统进程的崩溃意味着系统的崩溃,这个时候手机就会重启,由于Galaxy Apps会在系统启动时自启动,那么重启手机之后,Galaxy App也会重新启动并加载我们的配置文件。
Step 8 如何发送特定Intent?
如何能发送这样一个Intent呢,一个只带包名和组件名,不会填充data数据的Intent?这里利用了另外一个跳板——Samsung Members:
Samsung Members中的导出组件LauncherActivity可在浏览器中使用其自定义协议voc://唤起,并且可传入特定的包名和组件名。
Step 9 如何唤醒Samsung Members?
在这一步中,依旧利用Chrome来进行跳转,“voc://activity/general?packageName=com.android.server.telecom&className=com.android.server.telecom.components.UserCallActivity”,这个URI会被Samsung Members所接收到。所以Chrome在整个攻击链中要执行2次打开其他应用的任务,一次是为了唤起Android Vending跳板,另外一次是为了唤醒Samsung Members跳板。这里我们能注意到被当作跳板的组件的一个共同点:都响应了“android.intent.category.browsable”Action,导致攻击者可以通过浏览器打开进行远程利用,这里对我们来说算是一个小Tips吧,今后的漏洞挖掘工作中,对可通过浏览器打开的组件需要格外关注。
后续利用
Step 10 中间人劫持App更新
Galaxy Apps启动后加载配置文件,这个时候就会去请求这个攻击者的中间地址,攻击者再将所有的请求转发给真正的升级服务器https://uk-odc.samsungapps.com/,同时也会将服务器的响应返回给手机。
Galaxy Apps进行更新请求时会与服务器产生三次交互:
- 客户端发送当前所有的App列表给服务器/服务端做出响应告知客户端所有App的最新版本
- 客户端如果发现版本比自己高的App则请求这个App的最新信息/服务端返回App的包名版本等信息
- 客户端请求最新App的下载地址等/服务器返回下载地址以及App大小签名等信息
中间人在这里修改了1次请求和3次返回的包,让Galaxy Apps误以为自己手机上的某个App是旧版本的,然后下载了攻击者执行的App进行了安装。
Step 11 如何绕过权限申请弹窗?
在Android 6.0及后续版本中,App申请危险权限时,系统会弹出对话框询问用户是否授予其特定权限,这里绕过的方式也很简单,编译apk时将targetSdkVersion设置为18及以下即可。
Step 12 如何启动恶意App?
根据slide中的描述,MWR的研究人员也以为到这里就够了,恶意App已经成功安装,还有什么不能做的呢?但是ZDI却要求现场就能运行App并进行敏感数据的窃取等操作。所以这里就涉及到一个问题:Android 3.1以后,新安装的App默认都是停止状态的。
接着研究人员发现,三星对Android Contacts Provider进行了重度修改,在一个App的状态发生改变时,会有一个方法去做一次query查询:
所以在恶意App里创建一个provider,meta-data中写上android.content.ContactDirectory属性为true,则可以在安装上之后,执行provider中的query方法。
到此为止,恶意App成功安装并启动。
总结
经过对整个攻击链的分析,将这么多“不起眼”的逻辑漏洞(或bug)利用起来,居然能从浏览器到代码执行做远程攻击。在这个攻击链中,总共涉及到了6个App、3个系统组件以及系统的总共11个逻辑漏洞(或bug甚至只是特性),我们再来回顾一下:
组件 | 作用 |
三星SBrowser | 支持强制下载,保存了payload.html |
Chrome | 支持googlechrome://协议,加载payload.html文件 |
Android Vending
(Play Store) |
跳板1,可被浏览器用scheme唤醒并发送Intent |
Android Media Provider | 系统特性,通过id能访问到相应的文件 |
Samsung Notes | 解压缩目录穿越漏洞,为释放配置文件提供了便利 |
Samsung Members | 跳板2,同样可被浏览器用scheme唤醒并发送Intent |
Android Telecom | 空指针异常,可被利用来重启系统 |
Galaxy Apps | 遗留调试代码,可指定任意服务器调试App的升级更新过程;且存在中间人攻击风险 |
三星定制Android Contacts Provider | App更新时会去执行query方法导致恶意App自启动 |
系统特性 | 通过设置targetSdkVersion绕过权限弹框 |
参考链接
- https://labs.mwrinfosecurity.com/publications/chainspotting-building-exploit-chains-with-logic-bugs/
- https://www.zerodayinitiative.com/advisories/ZDI-18-562/
- https://www.zerodayinitiative.com/advisories/ZDI-18-561/
- https://www.zerodayinitiative.com/advisories/ZDI-18-560/
关于Vulpecker Team
360威派克团队(Vulpecker Team)隶属于360信息安全部,负责集团内部移动APP和OS类产品安全攻防, 制定了公司内部安卓产品安全开发规范,自主开发并维护了在线安卓应用安全审计系统-360显危镜。同时团队高度活跃在谷歌、三星、华为等各大手机厂商的致谢名单中,截止2018年,累计获得近百个CVE编号和官方致谢,多次在国内外各大安全会议上分享研究成果。