Linux netfilter OOB root提权漏洞分析

2016-08-31 +30 393198人围观 ,发现 14 个不明物体 漏洞

*本文中涉及到的相关漏洞已报送厂商并得到修复,本文仅限技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担。

* 本文原创作者:肥肥草,本文属FreeBuf原创奖励计划,未经许可禁止转载 

著名的ExploitDatabase网站(www.exploit-db.com)最近贴出了一个netfilter模块的提权POC,作者是Vitaly Nikolenko。OOB!Netfilter!顿感好奇,决定分析一下,现将分析过程和成果分享如下。

0×0 依葫芦画瓢提权成功

参照文章的提示,下载Ubuntu16.04并成功安装,uname -a确认版本号为4.4.0-21-generic。

下载POC,编译,按照提示关闭SMEP,加载ip_tables.ko,运行decr,等到出现“Done!Nowrun.pwn”后运行pwn,成功获得root shell。

0×01 漏洞初识

查看pwn.c的代码,可以看出POC采用了常见的通过ptmx提权方法:

        

猜测decr中修改了ptmx_fops的release指针,并根据代码可推断出修改后该指针的值为0xff814e30b0。然后pwn中调用close(fd)调用ptmx_fops->release函数,从而提权。

事实是否如此呢?

通过cat/proc/kallsyms查看ptmx_fops的地址为0xffffffff821de3e0,则release指针所在地址为0xffffffff821de3e0 + 13*8 = 0xffffffff821de448。

通过kgdb进行调试,查看在decr运行之前,0xffffffff821de448的值0xffffffff814e30b0,如下图所示:

运行decr之后,0xffffffff821de448地址的值,为0x ff814e30b0,如下图所示:

也就是说,decr运行之后,0xffffffff821de448地址的值由0xffffffff814e30b0被修改为0xff814e30b0,而0xff814e30b0地址是应用层地址,是可控的,在pwn代码中向0xff814e30b0地址写入了提权ShellCode。

下面的问题就是decr是如何做到的,内核漏洞在哪?

0×02 定位修改ptmx_fops关键指令

仔细分析decr.c,无非是设置了ipt_replace、ipt_entry等数据结构,然后调用setsockopt函数设置内核,看完之后仍然毫无头绪,还得从内核着手,看看应用层在调用完setsockopt函数之后内核到底做了那些事。

一种思路是重新编译内核,这样可以实现源码级的内核调试,但是重新编译之后一些内核参数(如ptmx_fops)地址发生改变,POC可能会运行失败,这样不利于定位修改ptmx_fops的漏洞代码。

所以还是针对Ubuntu16.04原生的内核进行调试,采用源码分析+动态调试的方法定位修改ptmx_fops的关键指令。本次采用的动态调试方法为vmware双虚拟机kgdb调试方法,具体方法可以通过百度获得,文中不再复述。        

查看ip_tables.c源码,首先找到模块初始化函数ip_tables_init:

        

在ip_tables_init中,调用nf_register_sockopt注册处理函数,注册参数如下所示。

set函数有两个,do_ipt_set_ctl和compat_do_ipt_set_ctl,内核会调用哪一个函数呢?

通过kgdb双虚拟机调试,分别设置两个函数为断点,发现当应用层调用setsockopt时,内核调用的是compat_do_ipt_set_ctl,如下图所示。

        

继续分析,获得如下函数执行路径:

compat_do_ipt_set_ctl--> compat_do_replace --> translate_compat_table --> check_compat_entry_size_and_hooks

POC代码中提示check_compat_entry_size_and_hooks/check_entry有问题,那么重点分析check_compat_entry_size_and_hooks函数,看是在那一步去修改了ptmx_fops,最好的办法是动态调试。

经过痛苦的、纯体力活的kgdb汇编代码调试,最终定位到是在compat_release_entry函数中修改了ptmx_fops,并且修改了两次,进一步跟踪定位到是该函数中的两个module_put函数修改了ptmx_fops。

        

同时通过打印$rdi(module_put的第一个参数)的值,发现该值为0xffffffff821de10d,如下所示:

        

而该值正是decr.c中设置的magicnumber,如下图所示:

        

再看module_put函数:

其中最关键的就是对modue->refcnt减一。

refcnt在structmodule结构中的偏移为0×340,因此&module->refcnt的值为 0xffffffff821de10d + 0×340 = 0xffffffff821de44d, 而ptmx_fops->tty_release的地址为0xffffffff821de448,因此当执行减一操作后,会将0xffffffff821de44d开始的0xff 0xff 0xff逐步减为0,最终ptmx_fops->tty_release的值变为0xff814e30b0。

到此,总算搞明白了,应用层通过构造特殊的ipt_replace、ipt_entry、xt_entry_match、xt_standard_target结构,将一个值为magic number的地址传到内核,并让内核中对该地址的内容执行多次-1操作,从而达到修改ptmx_fops的目的。

0×03 漏洞成因分析

通过上节分析知道,该漏洞可以在内核中修改应用层指定的地址,执行递减1的修改操作。那么内核代码中的漏洞到底在哪呢?

继续分析内核源代码ip_tables.c,既然问题出在ematch->u.kernel.match->me和t->u.kernel.target->me上,那么转到与之相关的代码处,即compat_copy_entry_from_user函数内部的代码,重点分析以下代码:

将xt_ematch_foreach函数展开,如下:

 entry->elems指向的是紧跟在ipt_entry后的xt_entry_match(见如下示意图),即entry->elems= entry + sizeof(ipt_entry),而sizeof(ipt_entry) = 112字节。

由于应用层设置的target_offset为74,导致判断语句

(pos)< (struct xt_entry_match *)((char *)(entry) + \

(entry)->target_offset); \

返回的结果为false,因此整个循环一次未能执行,也即compat_find_calc_match函数未能执行,本应在compat_find_calc_match函数中设置ematch->u.kernel.match->me的指令也未能执行。

与ematch错误处理情况类似,这段代码中,由于应用层设置的target_offset为74,导致t = compat_ipt_get_target()函数获取的target是错误的,后面尽管通过t->u.kernel.target= target;设置了target,但设置到了错误的地址,未改变真正的t->u.kernel.target值。

0×4漏洞总结

综上所属,该漏洞是内核netfilter处理setsockopt相关代码(check_compat_entry_size_and_hooks和check_entry函数)在处理应用层传下来的数据时审查不严格,处理逻辑也存在缺陷,使内核在调用module_put函数时操作了应用层传下来的地址,导致内核直接对应用层输入的地址执行减一操作,相当于是内核任意地址写漏洞。

该漏洞要利用内核模块的module_put函数,而Android系统一般不打开内核模块动态加载功能,因此本文介绍的漏洞利用方法无法在Android系统上成功执行。

最近爆出和netfilters/getsockopt相关的漏洞不少,如CVE-2016-3134,后期将继续分析其他漏洞。

欢迎技术交流。本人QQ:16588753,root技术交流群:437812386。

* 本文原创作者:肥肥草,本文属FreeBuf原创奖励计划,未经许可禁止转载

这些评论亮了

  • 话说qq群只有18人,对于我这种小白都不敢加进去了
    )9( 亮了
  • 小菜B 回复
    @ 周鸿尔 哥 你们服务器用ubuntu吗
    )6( 亮了
发表评论

已有 14 条评论

取消
Loading...

这家伙太懒,还未填写个人描述!

1 文章数 0 评论数

特别推荐

推荐关注

活动预告

填写个人信息

姓名
电话
邮箱
公司
行业
职位
css.php