标题 简介 类型 公开时间
关联规则 关联知识 关联工具 关联文档 关联抓包
参考1(官网)
参考2
参考3
详情
[SAFE-ID: JIWO-2024-2631]   作者: 浩丶轩 发表于: [2020-04-10]

本文共 [367] 位读者顶过

关于CVE-2017-16995 ebpf 符号扩展漏洞学习笔记

  漏洞分析  [出自:jiwo.org]

关于这个漏洞网上已经有很多的文章分析了,这里不做太多描述,只记录一些比较重要的点。

首先是ebpf,上一张图:



ebpf首先需要ring3传入一段指令(传到JIT),它会在BPF_PROG_RUN里做包过滤,  内核会申请一块共享内存(MAP),内核的数据经过过滤之后放到MAP里面,然后ring3就可以读写MAP来获取内核数据。

这个漏洞简单来说就是符号扩展没有检查好,像前面说的,ebpf分成verifier和BPF_PROG_RUN 两个部分。

传入的指令其实就是原本x64上指令的一个映射,它会检查指令的CFG,是不是有非法内存访问之类的(如果可以的话就直接是内核代码注入了,可以任意执行代码),效率上的考虑,会忽略掉一些分支的检查,像下面这样,r9的值固定是0xffffffff,那么就不会跳转到[4]的部分,所以就不用检查它了,节省时间。



首先看上面第一条指令ALU_MOV_K(9,0xffffffff),它等效于r9 = 0xffffffff,对应的代码在:

https://elixir.bootlin.com/linux/v4.4.110/source/kernel/bpf/verifier.c#L1782


调用check_alu_op函数,最后调用regs[insn->dst_reg].imm = insn->imm;,这里的立即数是用signed int保存的。

然后第二条指令JMP_JNE_K(9,0xffffffff,2),其检查在check_cond_jmp_op函数里,这时候用的imm依然是signed int类型,然后后续检查的时候发现前面r9和JMP_JNE_K的imm一样,于是就不去检查[4]开始的指令了。

然后到了运行的之后,对应__bpf_prog_run 函数:

https://elixir.bootlin.com/linux/v4.4.110/source/kernel/bpf/core.c#L195

ALU_MOV_K:DST=(u32)IMM这个时候DST=0xffffffff

JMP_JNE_K:比较DST和IMM,此时IMM是signed int类型,DST 是 uint64_t 类型, IMM会做位扩展,原来的0xffffffff也就是-1变成0xffffffff ffffffff,0xffffffff != 0xffffffff ffffffff,于是就会跳到前面指令的LD_IMM_DW(9,1,3), // [4] r9=mapfd开始执行,verifrier的时候并没有这一段指令做检查,这时候就可以在内核做任意代码执行了。

我们可以写一段代码验证一下:

输出的结果是vuln,接下来是如何利用。

漏洞利用的话,前面的分析我们知道可以在内核任意代码执行,手写ebpf的指令(其实就和我们手写汇编一样),基本利用思路如下:

  • 泄露出task_struct的地址

  • 借助task_struct地址泄露出cred地址

  • 直接内存写改uid,gid,然后/bin/sh getshell

复现的环境我用的内核是4.4.110版本, 附件中有我的config文件,主要是加上CONFIG_BPF=y 和CONFIG_BPF_SYSCALL=y

这里使用的bpf指令如下,参照panda师傅的分析:

  
	

首先是r6=map[0],r7=map[1],r8=map[2] (map 是前面提到的共享内存)

然后是三个判断:

  • map[0]==0根据 map[1] 的值来读内存;

  • map[0]==1时,获取rbp的值==>addr & ~(0x4000 - 1); 可以读取到 task_struct 的地址;

  • map[0] ==2时,*map[1]= map[2]([r7]=r8)。


评论

暂无
发表评论
 返回顶部 
热度(367)
 关注微信