CVE-2022-23222:Linux 内核 eBPF 本地权限提升
2022-10-18 08:10:24 Author: 系统安全运维(查看原文) 阅读量:23 收藏

https://github.com/tr3ee/CVE-2022-23222

    近期在对Linux eBPF进行代码审计的过程中,发现了一枚权限提升漏洞CVE-2022-23222。    此漏洞影响Linux Kernel 5.8 - 5.16,并在5.10.92 / 5.15.15 / 5.16.1中修复。完整利用代码详见:https://github.com/tr3ee/CVE-2022-23222----[ 1.1 - eBPF verifer

我们在写eBPF程序时,会发现调用bpf_map_lookup_elem()返回的结果,一定要判断是否为NULL,否则就会被拒绝加载。因为,bpf_map_lookup_elem()运行结果的结果,可能是有效的指针,但也可能返回NULL来表示没有查找到与key相关的值。在eBPF中有很多类似的用法,那么eBPF verifier是如何跟踪这些值的类型呢?* bpf.h *

/* types of values stored in eBPF registers *//* Pointer types represent: * pointer * pointer + imm * pointer + (u16) var * pointer + (u16) var + imm * if (range > 0) then [ptr, ptr + range - off) is safe to access * if (id > 0) means that some 'var' was added * if (off > 0) means that 'imm' was added */enum bpf_reg_type { NOT_INIT = 0, /* nothing was written into register */ SCALAR_VALUE, /* reg doesn't contain a valid pointer */ PTR_TO_CTX, /* reg points to bpf_context */ CONST_PTR_TO_MAP, /* reg points to struct bpf_map */ PTR_TO_MAP_VALUE, /* reg points to map element value */ PTR_TO_MAP_VALUE_OR_NULL, /* points to map elem value or NULL */ PTR_TO_STACK, /* reg == frame_pointer + offset */ PTR_TO_PACKET_META, /* skb->data - meta_len */ PTR_TO_PACKET, /* reg points to skb->data */ PTR_TO_PACKET_END, /* skb->data + headlen */ PTR_TO_FLOW_KEYS, /* reg points to bpf_flow_keys */ PTR_TO_SOCKET, /* reg points to struct bpf_sock */ PTR_TO_SOCKET_OR_NULL, /* reg points to struct bpf_sock or NULL */ PTR_TO_SOCK_COMMON, /* reg points to sock_common */ PTR_TO_SOCK_COMMON_OR_NULL, /* reg points to sock_common or NULL */ PTR_TO_TCP_SOCK, /* reg points to struct tcp_sock */ PTR_TO_TCP_SOCK_OR_NULL, /* reg points to struct tcp_sock or NULL */ PTR_TO_TP_BUFFER, /* reg points to a writable raw tp's buffer */ PTR_TO_XDP_SOCK, /* reg points to struct xdp_sock */ /* PTR_TO_BTF_ID points to a kernel struct that does not need * to be null checked by the BPF program. This does not imply the * pointer is _not_ null and in practice this can easily be a null * pointer when reading pointer chains. The assumption is program * context will handle null pointer dereference typically via fault * handling. The verifier must keep this in mind and can make no * assumptions about null or non-null when doing branch analysis. * Further, when passed into helpers the helpers can not, without * additional context, assume the value is non-null. */ PTR_TO_BTF_ID, /* PTR_TO_BTF_ID_OR_NULL points to a kernel struct that has not * been checked for null. Used primarily to inform the verifier * an explicit null check is required for this struct. */ PTR_TO_BTF_ID_OR_NULL, PTR_TO_MEM, /* reg points to valid memory region */ PTR_TO_MEM_OR_NULL, /* reg points to valid memory region or NULL */ PTR_TO_RDONLY_BUF, /* reg points to a readonly buffer */ PTR_TO_RDONLY_BUF_OR_NULL, /* reg points to a readonly buffer or NULL */ PTR_TO_RDWR_BUF, /* reg points to a read/write buffer */ PTR_TO_RDWR_BUF_OR_NULL, /* reg points to a read/write buffer or NULL */ PTR_TO_PERCPU_BTF_ID, /* reg points to a percpu kernel variable */};

    上面是eBPF中寄存器的完整类型列表,可以知道它通过`*_OR_NULL`类型来表示一个未知的指针类型。当寄存器的类型是`*_OR_NULL`时,它能做的操作是很有限的,一般只能进行NULL比较或者作为参数调用辅助函数。    一个类型为`*_OR_NULL`的寄存器做完!=NULL的比较后,才可能变为`PTR_TO_*`类型,或者`SCALAR_VALUE`也就是数值0。----[ 1.2 - 漏洞分析* C *

/* Handles arithmetic on a pointer and a scalar: computes new min/max and var_off. * Caller should also handle BPF_MOV case separately. * If we return -EACCES, caller may want to try again treating pointer as a * scalar. So we only emit a diagnostic if !env->allow_ptr_leaks. */static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, struct bpf_insn *insn, const struct bpf_reg_state *ptr_reg, const struct bpf_reg_state *off_reg){... switch (ptr_reg->type) { case PTR_TO_MAP_VALUE_OR_NULL: verbose(env, "R%d pointer arithmetic on %s prohibited, null-check it first\n", dst, reg_type_str[ptr_reg->type]); return -EACCES; case CONST_PTR_TO_MAP: /* smin_val represents the known value */ if (known && smin_val == 0 && opcode == BPF_ADD) break; fallthrough; case PTR_TO_PACKET_END: case PTR_TO_SOCKET: case PTR_TO_SOCKET_OR_NULL: case PTR_TO_SOCK_COMMON: case PTR_TO_SOCK_COMMON_OR_NULL: case PTR_TO_TCP_SOCK: case PTR_TO_TCP_SOCK_OR_NULL: case PTR_TO_XDP_SOCK: verbose(env, "R%d pointer arithmetic on %s prohibited\n", dst, reg_type_str[ptr_reg->type]); return -EACCES; default: break; }... return 0;}

    上面的代码中adjust_ptr_min_max_vals()是eBPF verifier用于检验指针加减运算的函数。其中的switch分支用于过滤不支持加减运算的指针类型,比如各种OR_NULL类型。但是这个switch分支却少了很多类型的判断,比如`PTR_TO_MEM_OR_NULL`, `PTR_TO_RDONLY_BUF_OR_NULL`,`PTR_TO_RDWR_BUF_OR_NULL`。这意味着,我们可以对一些OR_NULL类型做加减运算!

参考资料

[1] Advisory: https://www.openwall.com/lists/oss-security/2022/01/13/1[2] Exploit Overview: https://www.openwall.com/lists/oss-security/2022/01/18/2[3] Patch: []()[4] Source: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/kernel/bpf/verifier.c?h=v5.10.83

好文推荐

红队打点评估工具推荐
干货|红队项目日常渗透笔记
实战|后台getshell+提权一把梭
一款漏洞查找器(挖漏洞的有力工具)
神兵利器 | 附下载 · 红队信息搜集扫描打点利器
神兵利器 | 分享 直接上手就用的内存马(附下载)
推荐一款自动向hackerone发送漏洞报告的扫描器
欢迎关注 系统安全运维

文章来源: http://mp.weixin.qq.com/s?__biz=Mzk0NjE0NDc5OQ==&mid=2247511269&idx=2&sn=fae1951851f9f63e81e3b9caa8355c88&chksm=c3087f95f47ff683e960c9c82241f6ea082b2d2a9aa063834b054dc37838abbf360ced45fe54#rd
如有侵权请联系:admin#unsafe.sh