T09 · Insecure Skill Coding Practices
- Location
scripts/dingbot.py:37- Finding
Unrestricted Webhook Destination Enables Disclosure of Local File Contents
- Content
View full analysis
str: url = os.environ.get("DING_WEBHOOK") or die("未设置 DING_WEBHOOK") secret = os.environ.get("DING_SECRET") if not secret: return url ts = str(round(time.time() * 1000)) string_to_sign = f"{ts}\n{secret}".encode("utf-8") sign = base64.b64encode( hmac.new(secret.encode("utf-8"), string_to_sign, digestmod=hashlib.sha256).digest()) return f"{url}×tamp={ts}&sign={urllib.parse.quote_plus(sign)}" def send(body: dict) -> None: req = urllib.request.Request( signed_url(), json.dumps(body, ensure_ascii=False).encode("utf-8"), {"Content-Type": "application/json"}) with urllib.request.urlopen(req, timeout=20) as resp: r = json.loads(resp.read()) ``` ```python def read_arg_or_file(v: str) -> str: return open(v, encoding="utf-8").read() if os.path.isfile(v) else v ``` ```python elif cmd == "markdown": len(rest) >= 2 or die("用法: markdown <标题> ") send({"msgtype": "markdown", "markdown": {"title": rest[0], "text": read_arg_or_file(rest[1])}}) ``` ### Technical Analysis The documentation states that network requests are limited to `oapi.dingtalk.com`, but the implementation obtains the complete destination from the `DING_WEBHOOK` environment variable and does not validate its scheme, hostname, port, user-information component, or resolved address. The Markdown command treats its second argument as a local path whenever `os.path.isfile()` succeeds. It reads the entire file and places its contents in the outbound JSON message. This is expected when deliberately sending a report file to DingTalk, but the absenc ...[truncated 2587 chars]- Remediation
View remediation
