T09 · Insecure Skill Coding Practices
- Location
scripts/fix_font_transparency.py:119- Finding
Unbounded Decompression and Parsing of Attacker-Controlled PPTX Archives
- Content
View full analysis
0: stats["slides_modified"] += 1 stats["runs_modified"] += count data = new_data zout.writestr(item, data) ``` ### Technical Analysis A PPTX file is a ZIP archive whose members and compression properties can be controlled by the user. The script enumerates every archive member and calls `zin.read(item.filename)`, decompressing the entire member into memory before determining whether it is relevant to the transformation. The implementation does not enforce limits on: - The number of archive members - The uncompressed size of an individual member - The aggregate uncompressed archive size - The ratio between compressed and uncompressed sizes - The size of slide XML passed to `ElementTree` - The processing time or output size Consequently, a highly compressed archive member can consume substantial memory when read. A large number of members can also cause excessive CPU and I/O consumption. For matching slide entries, the decompressed XML is parsed into an in-memory element tree, further increasing memory usage. The vulnerable operation applies to every member, including media and embedded objects that the script does not need to inspect. ### Attack Path 1. An attacker creates a syntactically valid PPTX/ZIP archive containing one or more highly compressed members with very large uncompressed sizes. 2. The attacker submits the crafted PPTX for processing through the documented Skill workflow. 3. Th ...[truncated 1046 chars]- Remediation
View remediation
MAX_MEMBERS: raise ValueError("Archive contains too many members") total_size = 0 for item in items: if item.file_size > MAX_MEMBER_SIZE: raise ValueError(f"Archive member is too large: {item.filename}") total_size += item.file_size if total_size > MAX_TOTAL_SIZE: raise ValueError("Archive expands beyond the permitted size") compressed_size = max(item.compress_size, 1) if item.file_size / compressed_size > MAX_COMPRESSION_RATIO: raise ValueError(f"Suspicious compression ratio: {item.filename}") is_slide = ( item.filename.startswith("ppt/slides/slide") and item.filename.endswith(".xml") ) if is_slide and item.file_size > MAX_XML_SIZE: raise ValueError(f"Slide XML is too large: {item.filename}") ``` Metadata checks should be combined with runtime resource controls because ZIP metadata alone should not be treated as a complet ...[truncated 24 chars]
