DF-0965 / trigger.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | /* * DF-0965 - twofish_set_key missing key-length validation. * FUNCTION-LEVEL HARNESS (not a kernel exploit). * * In-kernel callers (twofish_cbc_setkey, twofish_xts_setkey in * sys/crypto/cryptoapi/cryptoapi.c) validate the key length against * {128,192,256}/{256,512} white-lists BEFORE calling twofish_set_key, * and cryptoapi_cipher_find() requires cipher->probe() to accept the size. * So the bug is NOT reachable through the kernel syscall surface. * * This harness compiles the real twofish.c source and demonstrates the * OOB stack write the function performs when key_len_bits >= 320 (k_len>=5): * - loop at twofish.c:435-440 writes me_key[i]/mo_key[i] (size-4 stack * arrays) past their bounds, corrupting other stack locals including * the `l_key`/`s_key` pointers, then the function SIGBUS/SIGSEGVs * when dereferencing them (l_key[i] = a+b;), * - writes s_key[k_len-i-1] past s_key[4] (into mk_tab) in the ctx, * - reads ((const u_int32_t *)in_key)[i+i(+1)] past the caller's key, * - gen_mk_tab's switch (twofish.c:335) silently falls through for * k_len not in {2,3,4}, leaving mk_tab[] uninitialized garbage. * * Build: cc -I. -I/usr/src/sys -Wall -O2 -o trigger trigger.c * Run: ./trigger */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> #include "twofish_src.c" /* copy of sys/crypto/twofish/twofish.c */ static int try_call(int key_len_bits) { pid_t pid = fork(); if (pid == 0) { /* child: silence stdout, just trigger the bug */ size_t ctx_sz = sizeof(twofish_ctx); unsigned char *blob = calloc(1, ctx_sz + 256); if (!blob) _exit(50); twofish_ctx *ctx = (twofish_ctx *)blob; size_t keybytes = key_len_bits / 8; uint8_t *key = calloc(1, keybytes + 256); if (!key) _exit(51); memset(key, 0x55, keybytes); /* The next call corrupts the function's stack via the size-4 * me_key[4]/mo_key[4] arrays when k_len >= 5. */ twofish_set_key(ctx, key, key_len_bits); /* If we got here without crashing (small k_len), report k_len. */ FILE *f = fopen("/tmp/df965_klen", "w"); if (f) { fprintf(f, "%u\n", ctx->k_len); fclose(f); } _exit(0); } int status = 0; waitpid(pid, &status, 0); return status; } int main(void) { printf("sizeof(twofish_ctx) = %zu (l_key[40]=160B, s_key[4]=16B, " "mk_tab[4*256]=4096B, k_len)\n", sizeof(twofish_ctx)); /* 1. Sanity: supported key_len_bits=256 (k_len=4) - should NOT crash */ int st = try_call(256); printf("[control] key_len_bits=256 (k_len=4): status=0x%x ", st); if (WIFEXITED(st) && WEXITSTATUS(st) == 0) { unsigned k = 0; FILE *f = fopen("/tmp/df965_klen", "r"); if (f) { fscanf(f, "%u", &k); fclose(f); } printf("exited 0, k_len=%u (function OK)\n", k); } else if (WIFSIGNALED(st)) { printf("CRASHED sig=%d (UNEXPECTED for valid keylen)\n", WTERMSIG(st)); } else { printf("unknown status\n"); } unlink("/tmp/df965_klen"); /* 2. Trigger: key_len_bits=640 (k_len=10) - SHOULD crash/OOB */ st = try_call(640); printf("[trigger] key_len_bits=640 (k_len=10): status=0x%x ", st); if (WIFSIGNALED(st)) { printf("CRASHED sig=%d (%s)\n", WTERMSIG(st), WTERMSIG(st) == SIGBUS ? "SIGBUS" : WTERMSIG(st) == SIGSEGV ? "SIGSEGV" : "other"); printf("BUG CONFIRMED at function level: twofish_set_key overflowed\n" " the size-4 stack arrays me_key[4]/mo_key[4] (twofish.c:427)\n" " during the loop at twofish.c:435 with i=0..9, corrupting\n" " adjacent stack locals (including l_key/s_key pointers at\n" " twofish.c:428). The subsequent l_key[i]=a+b write at line\n" " 447 dereferenced a corrupted pointer and faulted.\n"); return 0; } else if (WIFEXITED(st)) { unsigned k = 0; FILE *f = fopen("/tmp/df965_klen", "r"); if (f) { fscanf(f, "%u", &k); fclose(f); } if (k > 4) { printf("exited 0, k_len=%u (function accepted oversized key)\n", k); printf("BUG CONFIRMED: ctx->k_len=%u > supported max(4). The\n" " function silently took the bad size, ran the OOB loop at\n" " twofish.c:435 (i=0..%u), wrote past me_key[4]/mo_key[4]\n" " (stack) and s_key[4] (struct, into mk_tab), then gen_mk_tab\n" " at twofish.c:335 fell through (k_len not in {2,3,4}) leaving\n" " mk_tab as the OOB-written garbage.\n", k, k-1); return 0; } printf("exited %d, k_len=%u (no bug observed)\n", WEXITSTATUS(st), k); } else { printf("unknown status\n"); } return 1; } |