T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/get_payment.py:44
- Finding
- WooshPay API Credential Disclosure Through an Attacker-Controlled Lookup URL## Vulnerability Details **File Location**: `scripts/get_payment.py`, lines 44–45 and 68–73 **Vulnerability Type**: Arbitrary authenticated request, credential disclosure, and server-side request forgery **Risk Level**: High ### Vulnerable Code ```python # 补全完整URL if not order_id.startswith("http"): if not order_id.startswith("pi_"): print("❌ 订单ID格式错误,应以 pi_ 开头") sys.exit(1) return order_id ``` ```python # 构建URL if order_id.startswith("http"): url = order_id else: url = f"{BASE_URL}/{order_id}" print(f"\n⏳ 正在查询订单 {order_id}...") try: response = requests.get(url, headers=headers, timeout=30) ``` The `headers` object attached to this request contains the merchant credential: ```python headers = { "Authorization": f"Basic {api_key}" } ``` ### Technical Analysis The order lookup accepts any input beginning with `http` as a complete request URL. The script then sends a request to that URL while attaching the `WOOSHPAY_API_KEY` in an HTTP Basic Authorization header. There is no restriction requiring the destination to use HTTPS, no allowlist requiring the hostname to be `api.wooshpay.com`, and no strict validation that the input is a payment intent ID. Consequently, a user can be induced to enter an attacker-controlled URL instead of an order ID. Because the Authorization header is attached before the destination is validated, the merchant API key is disclosed directly to the selected server. An `http://` destination would additionally transmit it without transport encryption. The same behavior can access internal network addresses, creating an SSRF primitive. This behavior exceeds the minimum privileges required for payment status lookup. The declared functionality only requires requests to the fixed WooshPay endpoint. ### Attack Path 1. An attacker supplies a purported payment identifier such as `https://attacker.example/collect`. 2. A merchant ...[truncated 1305 chars]
- Remediation
- ## Remediation Suggestions - Accept only payment intent identifiers, not complete URLs. - Validate input with a strict, length-bounded allowlist expression such as `^pi_[A-Za-z0-9]+$`. - Always construct the request URL from the trusted constant: ```python import re if not re.fullmatch(r"pi_[A-Za-z0-9]+", order_id): raise ValueError("Invalid payment intent ID") url = f"{BASE_URL}/{order_id}" response = requests.get(url, headers=headers, timeout=30) ``` - If configurable endpoints are required for testing, place them behind explicit configuration and verify that the parsed scheme is `https` and the hostname exactly matches an approved allowlist. - Never attach the API credential before validating the final destination. - Use a restricted API key with only read permission for status lookup if WooshPay supports scoped credentials. - Rotate the existing API key if the vulnerable lookup feature has been used with any untrusted URL.
