移动操作系统代码漏洞挖掘及研究

编者按:2022年5月,由网络安全研究国际学术论坛(InForSec)汇编的《网络安全国际学术研究进展》一书正式出版。全书立足网络空间安全理论与实践前沿,主要介绍网络和系统安全领域华人学者在国际学术领域优秀的研究成果,内容覆盖创新研究方法及伦理问题、软件与系统安全、基于模糊测试的漏洞挖掘、网络安全和物联网安全等方向。全书汇总并邀请了近40篇近两年在网络安全国际顶级学术议上发表的论文(一作为华人),联系近百位作者对研究的内容及成果进行综述性的介绍。从即日起,我们将陆续分享《网络安全国际学术研究进展》的精彩内容。

本文从敏感系统接口的输入空间出发,通过程序分析、机器学习等技术对操作系统代码异常行为进行表征,在主流移动操作系统中发现数百个代码漏洞,并获得华为、谷歌等厂商的高危漏洞证明与致谢。

移动操作系统通常采用权限模型对敏感系统接口进行保护,以限制恶意软件访问系统敏感资源的能力。但是,权限本身并不能很好表述系统接口的代码逻辑。因此,在满足权限要求的情况下,恶意软件仍然可以让系统接口执行进入异常的代码逻辑,从而实现提权、资源滥用等攻击。我们从敏感系统接口的输入空间出发,通过程序分析、机器学习等技术对操作系统代码异常行为进行表征,在主流移动操作系统中发现数百个代码漏洞,并获得华为、谷歌等厂商的高危漏洞证明与致谢。部分研究成果已发表在网络安全国际顶级学术会议ACM CCS 2018上,论文名为“Invetter: Locating Insecure Input Validations in Android Services”。

移动应用程序通过系统服务提供的公开接口访问系统能力。作为向上层应用提供服务的基础,Android向系统服务中添加大量功能,以在保护系统敏感资源安全的前提下,为用户提供更加丰富和流畅的用户体验。同时,为了防止不可信的应用程序滥用这些系统服务,Android在系统组件层(又称Android框架层)实现了一套对敏感资源的访问控制机制。虽然已有很多相关研究聚焦在Android框架层的访问控制问题,但这些研究主要集中在基于权限的显示访问控制,对隐式的输入验证问题缺乏充分研究。

相比权限验证,输入验证分散在Android框架层的安全检查代码中,被使用的次数更多,是Android系统安全的重要保障。不幸的是,这些输入验证缺乏统一的格式规范和定义标准,并且分散在Android系统服务中,使得对输入验证问题的分析成为一个十分具有挑战性的问题。为了解决该问题,我们结合机器学习和程序分析等技术,对系统代码进行特征提取并尝试理解其语义信息,在Android系统服务中定位可能存在安全问题的敏感输入验证。通过对来自谷歌、华为、小米等知名厂商的8个Android系统镜像进行测试,我们发现:

■ 大量输入验证存在安全问题,并核实了其中至少20处可以被用于提权或隐私泄露等攻击的问题;

■ 目前的输入验证主要聚焦于对输入数据的数值或格式等进行检查,忽略了对应用程序输入数据大小的限制,导致超过35%的Android系统服务接口(包括高权限等级的系统接口)存在资源滥用的风险,可以被恶意攻击者利用发起拒绝服务等攻击,甚至发起针对移动设备的永久性拒绝服务攻击。

我们将这些发现报告给了相关厂商,得到了积极的反馈与致谢以及高危漏洞的证明。

一、研究背景

1. Android系统服务

Android框架层包含上百个系统服务,它们提供了访问各种系统资源的功能,如获取用户地理位置信息、发送短信、检查网络连接等。这些系统服务及其相关进程的执行环境享有更多的特权并与应用层软件相互隔离。例如,多媒体系统服务执行在名为media_server的系统进程中,该进程享有系统级权限,可以执行很多威胁命令。为了方便应用层获取可用的系统服务,系统服务会先在ServiceManager中进行注册。

实际运行中,应用层进程通过系统服务开放的一系列公开接口访问系统资源,这些接口由Android接口描述语言(AIDL)来定义。在Android框架层代码的编译过程中,被AIDL语言定义的接口会被编译成两个Java类,即Stub和Proxy。它们分别扮演着服务器Server和客户端Client(可以是应用层进程或其他系统服务的进程)的角色进行跨进程通信。具体而言,Stub继承了系统服务类并实现了服务的具体功能和开发接口,而Proxy封装了跨进程间通信的逻辑及协议(RPC),以便clients访问。

在向系统服务发起一个访问请求时,客户端必须先向Android ServiceManager发送查询以获得该系统服务对应的Android Binder对象(ServiceManager维护着系统服务与Binder对象的映射关系),如图1所示。接着客户端通过ServiceManager返回的Binder对象去调用服务器开放的接口(即在Proxy中定义的接口),以获得系统服务提供的功能接口。但是,在这样一个接口访问的流程中,因为ServiceManager无法制止应用层软件伪造其输入的数据,所以原则上它不应该信任任何应用层提供的数据。 

图1

此外,为了方便开发者使用,在Proxy包装并抽象了系统接口之后,Android SDK也提供了一系列系统服务对应的管理器作为封装,并提供了与之对应的另外一套API以简化开发者的使用。不同于系统服务本身具有的代码,这些管理器与应用层软件代码运行在同一进程空间,所以恶意软件开发者可以重写这部分代码。由此可见,系统服务也不能信任应用层软件可控代码内的任何安全验证,即这些管理器中的安全验证也都应当不可信。

2. Android系统服务中的敏感输入验证

敏感输入验证对于Android服务的安全性起着至关重要的作用。通常,输入验证一般是将输入数据与一系列预先定义的期望值进行对比或与可信数据源进行交叉验证,然后根据对比结果再执行一系列的对应代码行为。因此,我们更关心的是与安全相关的敏感输入验证。

为此,我们总结了两种与安全相关的输入验证模式:

(1) 对输入者的身份/属性的验证;

(2) 约束敏感资源的使用。

对于模式(1),在Android系统中,输入者的身份/属性标识主要包括广泛使用的几个输入字段,如uid、pid、package name,也包括一些比较模糊的字段,如token、cert等。对于(2),系统中很多共有资源的使用也依赖于特定的检查,例如,可以通过检查应用层进程所提供的唯一资源定向符号(URI)的范围,来实现对系统服务所持有的内容提供器(content provider)中资源的访问限制。

二、输入验证相关的安全漏洞模型分析

基于实验和观察,我们总结了4类可能导致安全问题的输入验证缺陷。

1.不正确地信任来自应用层的数据

一些系统服务将应用程序提供的输入数据作为对其身份验证的依据。显然,由于该输入来源于不可信的应用程序,其应该也为不可信数据,不应作为敏感输入验证的判断依据。因此,如果一个敏感的输入验证使用了应用软件提供的输入数据,那么该输入验证可能是存在漏洞的。

2.不正确地信任来自应用层的代码

由于输入验证的非结构化特点,权限检查的验证逻辑代码经常会被错误放置在应用进程中。具体而言,Android SDK中存在与各系统服务对应的管理器,它运行在应用层进程空间充当服务代理的角色,通常它会先对应用程序的输入数据进行包装,再通过进程间通信的方式将数据转发到系统服务进程。在数据包装过程中,这些管理器也会执行部分输入验证,而这些输入验证由于运行在应用层进程空间内,可以被开发者利用反射等技术进行绕过。如果这部分在Android SDK中执行的输入验证在Android系统服务中不再进行同样的输入检查,则可以认为是存在漏洞的。

3.系统定制化引入的输入验证弱化

定制化系统为实现更加丰富的业务需求,会对Android原生系统服务的功能进行修改,这部分修改可能会导致系统原有的输入验证被弱化。通过识别定制化系统和原生Android系统对应的系统服务接口,比较二者安全验证的差异,如果存在不一致,则认为可能存在漏洞。

4.输入数据大小检查缺陷

系统中大部分输入检查聚焦于客户端输入数据的语义信息,忽略了对客户端输入数据大小的限制。但是,由于应用程序的输入数据需要通过进程间通信的方式发送到系统服务进程,涉及跨Java和C的数据传输,服务器很难甚至无法对应用软件的原始输入数据大小进行检查,从而导致应用程序可以对系统服务进程中的资源进行无限制消耗。

作者简介

张磊,复旦大学助理研究员,主要在移动安全、系统安全和区块链安全领域进行安全漏洞相关研究,包括程序代码分析技术、软件自动化测试技术以及漏洞挖掘技术等。他目前已在IEEE S&P、ACM CCS等网络安全顶级学术会议上发表多篇一作文章,并获得2020年ACMSIGSAC中国优博奖和ACM中国优博提名奖。

(本文选取了文章部分章节,更多精彩内容请阅读《网络安全国际学术研究进展》一书。)

版权声明:本书由网络安全研究国际学术论坛(InForSec)汇编,人民邮电出版社出版,版权属于双方共有,并受法律保护。转载、摘编或利用其它方式使用本研究报告文字或者观点的,应注明来源。

Bookmark the permalink.

Comments are closed.