简介
Android的USB有host和Accessory两种模式,处于Host模式下的Android设备可以对外围设备进行交互,如读取U盘数据、USB键盘鼠标等。当有外设插入到Android设备上时,系统读取外设的一些硬件信息(如LABEL、UUID等)并自动为其挂载。
常见的USB设备有以下几种:
- USB Sticks
即U盘,用于手机和U盘之间拷贝数据,即使处于锁屏状态,系统也会自动mount。(Android 9之后,系统阻止了锁屏情况下的mount) - USB keyboards
USB键盘或鼠标,可以用来操控手机,在锁屏下也会自动mount。 - USB ethernet adapters
USB网卡,插入手机后,通过DHCP可以获取ip地址联通网络,锁屏下也可以使用
Android系统中由Vold进程(Volume Daemon)负责管理和控制外设,Vold进程如果在挂载外设时没有正确的对硬件信息进行解析,则有可能出现安全问题。CVE-2018-9445就是Vold在挂载外设时,由于未正确获取UUID导致的目录穿越问题。
漏洞细节
当USB设备插到Android手机上时,Vold进程(Volume Daemon)会自动为USB设备进行mount,即使手机处于锁屏状态。Vold进程在mount之前需要知道所连接设备的一些硬件信息,如UUID、type、Label等,其具体实现在system/vold/Utils.cpp的readMetadata()函数中:
1 | /* |
该函数通过调用了blkid命令,从结果中依次按行读取字符串分别对TYPE、UUID和LABEL进行解析。
在Android中运行blkid命令(需要root权限)结果如下图:
默认情况下,Android系统将USB设备mount在/mnt/media_rw/目录下,且以UUID为文件名,例如:
通常情况下UUID字段不会包含特殊字符,因为它是blkid经过格式化输出的二进制字符串,TYPE字段是固定的几种文件系统格式类型,如fat、ext、ntfs等,而LABEL字段可以自定义,该字段即外设的名称,允许用户自定义。
需要注意的是Android上运行blkid后会首先输出LABEL,通过构造特殊的LABEL,我们可以实现注入攻击:
根据readMetadata()函数的代码,UUID由运行”blkid -c /dev/null -s TYPE -s UUID -s LABEL”后的结果解析得出,因此,如果设备的LABEL的值为“aaa UUID=”../../sdcard””,运行上述命令后,结果为:1
LABEL="aaa UUID="../../sdcard"" UUID="4823-0029" TYPE="vfat"
此时被sscanf解析后的UUID值变成了“../../sdcard”,被系统mount后就会创建/mnt/media_rw/../../sdcard目录,导致了目录穿越。
漏洞利用
直接在操作系统中更改USB外设的LABEL值相当于修改文件名,在操作系统中修改文件名为包含“../”的字符串是不可行的,我们需要通过文件系统进行修改。
由于romfs文件格式比较简单(如下图),我们直接使用echo就可以对其LABEL值进行修改,
修改LABEL(上图中的volume name字段)值为TYPE=”vfat” UUID=”../../data”1
echo '-rom1fs-########TYPE="vfat" UUID="../../data"' > /dev/block/sda1
更改后,运行blkid命令:
插入修改后的设备后,发现并没有mount成功。查看logcat,发现UUID确实会被识别为“../../data”,但fsck_msdos出现了错误
这是因为虽然我们提供的type为vfat,但内核在读取文件头部时,获取的文件格式为romfs。而fsck_msdos是用来处理vfat格式,因此出现了错误。
为了不产生fsck_msdos错误,我们创建一个文件格式为vfat,LABEL为自定义的真实USB设备:
修改USB设备文件格式为vfat,设置label为AAAAAAAAAAA(经测试vfat的label最多为11个字符,超过11个将会自动截断)
1
mkfs.vfat -n 'AAAAAAAAAAA' /dev/block/sda1
通过dd命令查找AAAAAAAAAAA字符串并替换为’UUID=”../aa’(替换后的字符长度必须和之前相同,UUID=”../aa为11个字符)
1
dd if=/dev/block/sda1 bs=1M count=200 | sed 's|AAAAAAAAAAA|UUID="../aa|g' | dd of=/dev/block/sda1 bs=1M
插入修改后的usb设备,我们可以看到/mnt/目录下多了aa文件
运行mount|grep aa进一步验证:
上述过程完成了漏洞的验证,但局限在于vfat的LABEL最多只有11个字符,由于UUID=”本身占用了6个字符,只剩下5个字符可以控制。这意味着我们只能向上穿越一层,且mount后的目录只有2个字符。考虑到romfs格式的LABEL允许更大的长度(romfs格式中的LABEL字段遇到\0才会结束),因此,为了实现更大的攻击,需要在USB协议上进行攻击:
即实现一个USB硬件,使系统读取blkfs时外设获取的为romfs格式,mount操作时外设告知系统自己为vfat格式。(vfat为了保证外设可以被mount为外部存储设备,romfs为了保证LABEL有足够长进而可以穿越到其他路径)
漏洞原作者使用树莓派开启USB模式作为一个USB硬件,实现了从Pixel 2设备中窃取照片的PoC(https://github.com/offensive-security/exploit-database-bin-sploits/raw/master/bin-sploits/45192.zip),这个PoC假设了一种场景,即存在一个app在收到外设mount事件后会自动把本机照片写入外设。为实现该场景,需要修改AOSP源码编译到Pixel设备,新增了部分代码实现一个MountReceiver,在收到mount事件后自动将DCIM目录下的照片复制到data/exfiltrate-photo目录。利用漏洞构造一个USB外设使外设被mount到/data目录,进而实现照片文件的窃取。
漏洞修复
CVE-2018-9445这个漏洞出现的根本原因有2个:
- Vold进程在获取TYPE、UUID和LABEL时,使用不严格的字符串匹配方式来读取内容;
- Android系统中的blkid并未按照期待的顺序(TYPE、UUID和LABEL)输出,而是最先输出可自定义的LABEL值;
结合了上述两个问题,最终导致了mount时的目录穿越。
漏洞修复时也是针对这2点分别进行了修复:
在blkid的输出内容里过滤掉了引号
使用strstr是增加了对引号的匹配
总结
CVE-2018-9445是一个典型的字符串注入攻击,一般的注入漏洞常常发生在exec的参数中,即命令注入,这个漏洞虽然也和exec有关,但是由exec的结果导致下游解析出现了目录穿越。关注解析字符串的代码和exec相关的代码,可能还会有其他的问题。
参考: