T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/md2docx_with_images.py:207
- Finding
- Arbitrary Local File Inclusion Through Markdown Image References<![CDATA[ ## Vulnerability Details **File Location**: `scripts/md2docx_with_images.py`, lines 207-241 **Vulnerability Type**: Unrestricted local file access and path traversal **Risk Level**: High ### Vulnerable Code ```python src = element.get('src', '') alt = element.get('alt', '图片') if not src: return # 查找图片 image_path = None if self.image_processor: image_path = self.image_processor.find_image(src) # 如果没找到,尝试其他方法 if not image_path and image_dir: # 相对于图片目录 potential_path = os.path.join(image_dir, src) if os.path.exists(potential_path): image_path = potential_path if not image_path: # 直接使用路径 image_path = src # 检查图片文件是否存在 if not os.path.exists(image_path): if self.debug: print(f"⚠️ 图片文件不存在: {image_path}") # 添加替代文本 p = self.doc.add_paragraph(f"[图片: {alt}]") p.alignment = WD_ALIGN_PARAGRAPH.CENTER return try: # 处理图片(调整大小等) processed_path = image_path image_data = {'original_path': image_path} if self.image_processor: processed_path, image_data = self.image_processor.process_image( image_path, max_width=1200, max_height=800, quality=85 ) self.image_info.append(image_data) # 添加图片到文档 from docx.shared import Inches self.doc.add_picture(processed_path, width=Inches(5.0)) ``` ### Technical Analysis The image source is derived directly from attacker-controlled Markdown. The implementation accepts absolute paths and paths containing parent-directory components such as `../`. It checks only whether the path exists; it does not canonicalize the path, reject absolute paths, or verify that the resolved file remains inside an approved image directory. If no image is found through the configured search logic, the code assigns the untrusted source directly to `image_path`. Any readable file that the DOCX library recognizes as an image can consequently be embedded in the generated document. ### Attack ...[truncated 1218 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Reject absolute image paths supplied by Markdown. 2. Resolve the configured image directory and candidate path with `os.path.realpath()`. 3. Verify containment with `os.path.commonpath()` before opening the file. 4. Reject paths containing traversal components or resolving through symlinks outside the approved directory. 5. Permit only explicitly supported image extensions and validate actual file content. 6. Run conversion under a dedicated, least-privileged account with access only to required input and output directories. Example containment check: ```python base_dir = os.path.realpath(image_dir) candidate = os.path.realpath(os.path.join(base_dir, src)) if os.path.isabs(src): raise ValueError("Absolute image paths are not permitted") if os.path.commonpath([base_dir, candidate]) != base_dir: raise ValueError("Image path escapes the approved image directory") if not os.path.isfile(candidate): raise FileNotFoundError(candidate) ``` ]]>
