T09 · Insecure Skill Coding Practices
- Location
src/multi_writing_skills/platforms/wechat.py:74- Finding
Arbitrary Local File Disclosure and SSRF Through WeChat Cover Upload
- Content
View full analysis
ImageUploadResult: """上传图片到微信素材库""" token = await self._get_access_token() # 判断是 URL 还是本地文件 if image_path.startswith(("http://", "https://")): # 下载远程图片 resp = await self._client.get(image_path) image_data = resp.content filename = image_path.split("/")[-1].split("?")[0] else: # 读取本地文件 path = Path(image_path) if not path.exists(): return ImageUploadResult( success=False, message=f"图片文件不存在: {image_path}" ) image_data = path.read_bytes() filename = path.name # 上传到微信 url = f"{self.BASE_URL}/material/add_material" params = {"access_token": token, "type": "image"} files = {"media": (filename, image_data, "image/jpeg")} resp = await self._client.post(url, params=params, files=files) ``` ### Technical Analysis The cover path is treated as either an unrestricted HTTP(S) URL or an unrestricted local filesystem path. The implementation does not: - Restrict local paths to an approved content directory. - reject symbolic links or sensitive system files. - Decode and validate that the input is an actual image. - Validate the response MIME type. - Restrict response size. - Block loopback, private, link-local, or cloud metadata addresses. - Validate redirect destinations. All retrieved bytes are labeled as `image/jpeg` and uploaded to WeChat. Therefore, a non-image local file or internal HTTP response can be transferred to an external platform. This behavior exceeds the minimum privilege needed for cover-image publication because the process can read any file accessible to its operating-system account and connect to arbitrary netwo ...[truncated 1150 chars]- Remediation
View remediation
