{"id":"CVE-2026-33731","aliases":["EUVD-2026-45041","GHSA-95jh-7r58-xmxw"],"status":"published","title":"AVideo has an Authorize.Net Webhook Signature Bypass that Enables Wallet Balance Inflation via Forged Payment Data","description":"WWBN AVideo is an open source video platform. In versions prior to 29.0, 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. Three flaws combine into an exploit chain: signature bypass via OR logic (webhook.php:33), payload values override API-fetched values (AuthorizeNet.php:169-171, webhook.php:44-48) and a missing approval check (webhook.php:61-75). By forging payment metadata, an attacker can credit arbitrary amounts to any user's wallet without a corresponding payment and include a  plans_id  to activate premium subscriptions (webhook.php:86-134), enabling free access to all paid and premium content and causing direct revenue loss to the platform owner. This issue has been fixed in version 29.0.","severity":{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-10","score":0.00163,"percentile":0.05584,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-345"],"published":"2026-06-22T19:58:50Z","modified":"2026-10-28T02:30:33Z","fixes":[{"ecosystem":"Packagist","package":"wwbn/avideo","fixed_in":["29.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"Packagist","package":"wwbn/avideo","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"28.0"},{"fixed":"29.0"}]}]},{"source":"osv","ecosystem":"Packagist","package":"wwbn/avideo","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"29.0"}]}],"versions":["10.4","10.8","11","11.1","11.1.1","11.5","11.6","12.4","14.3","14.3.1","14.4","18.0","21.0","22.0","24.0","25.0","26.0"]}],"products":[{"vendor":"wwbn","product":"avideo"}],"references":[{"url":"https://github.com/WWBN/AVideo/security/advisories/GHSA-95jh-7r58-xmxw","tags":["x_refsource_CONFIRM","exploit","advisory","web"]},{"url":"https://github.com/WWBN/AVideo/commit/033e83ae904cacb99495dbea7cbcfb3738cf42e4","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/advisories/GHSA-95jh-7r58-xmxw"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/33xxx/CVE-2026-33731.json","tags":["advisory"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-33731","tags":["advisory"]},{"url":"https://github.com/WWBN/AVideo","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-33731","title":"AVideo has an Authorize.Net Webhook Signature Bypass that Enables Wallet Balance Inflation via Forged Payment Data","description":"WWBN AVideo is an open source video platform. In versions prior to 29.0, 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. Three flaws combine into an exploit chain: signature bypass via OR logic (webhook.php:33), payload values override API-fetched values (AuthorizeNet.php:169-171, webhook.php:44-48) and a missing approval check (webhook.php:61-75). By forging payment metadata, an attacker can credit arbitrary amounts to any user's wallet without a corresponding payment and include a  plans_id  to activate premium subscriptions (webhook.php:86-134), enabling free access to all paid and premium content and causing direct revenue loss to the platform owner. This issue has been fixed in version 29.0.","status":"published","published":"2026-07-16T20:46:53.407Z","modified":"2026-07-17T11:12:04.938Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-345"],"updated_at":"2026-11-02T14:20:08.009733Z"}],"euvd":[{"id":"EUVD-2026-45041","description":"WWBN AVideo is an open source video platform. In versions prior to 29.0, 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. Three flaws combine into an exploit chain: signature bypass via OR logic (webhook.php:33), payload values override API-fetched values (AuthorizeNet.php:169-171, webhook.php:44-48) and a missing approval check (webhook.php:61-75). By forging payment metadata, an attacker can credit arbitrary amounts to any user's wallet without a corresponding payment and include a  plans_id  to activate premium subscriptions (webhook.php:86-134), enabling free access to all paid and premium content and causing direct revenue loss to the platform owner. This issue has been fixed in version 29.0.","status":"published","published":"2026-07-16T20:46:53Z","modified":"2026-07-17T11:12:04Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N","level":"medium"}],"updated_at":"2026-11-02T18:19:50.846094Z"}],"ghsa":[{"id":"GHSA-95jh-7r58-xmxw","title":"AVideo has an Authorize.Net Webhook Signature Bypass that Enables Wallet Balance Inflation via Forged Payment Data","description":"## Summary\n\nThe 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.\n\n## Details\n\nThree flaws combine into an exploit chain:\n\n### 1. Signature Bypass via OR Logic (webhook.php:33)\n\n```php\nif (!$parsed['signatureValid'] \u0026\u0026 (empty($txnInfo) || !empty($txnInfo['error']))) {\n    http_response_code(401);\n    echo 'invalid signature';\n    exit;\n}\n```\n\nThe 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.\n\n### 2. Payload Values Override API-Fetched Values (AuthorizeNet.php:169-171, webhook.php:44-48)\n\nIn `analyzeTransactionFromWebhook()`, `users_id` and `amount` are extracted from the attacker-controlled webhook **payload** first:\n\n```php\n$users_id = isset($metadata['users_id']) ? (int)$metadata['users_id'] : null;\n$amount   = isset($payload['amount']) ? (float)$payload['amount'] : ...;\n```\n\nThe fallback logic in webhook.php only applies when the analysis values are empty/falsy:\n\n```php\nif (!$analysis['users_id'] \u0026\u0026 !empty($txnInfo['users_id'])) {\n    $analysis['users_id'] = (int)$txnInfo['users_id'];\n}\nif (!$analysis['amount'] \u0026\u0026 isset($txnInfo['amount'])) {\n    $analysis['amount'] = (float)$txnInfo['amount'];\n}\n```\n\nSince the forged payload already provides both values, the authoritative API-fetched values are never used.\n\n### 3. Missing Approval Check (webhook.php:61-75)\n\nThe 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.\n\n## PoC\n\n**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`).\n\n1. Immediately after the purchase completes (to race the legitimate webhook), send a forged webhook:\n\n```bash\ncurl -X POST https://target.com/plugin/AuthorizeNet/webhook.php \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"eventType\": \"net.authorize.payment.authcapture.created\",\n    \"payload\": {\n      \"id\": \"60123456789\",\n      \"amount\": 99999.99,\n      \"responseCode\": 1,\n      \"metadata\": {\n        \"users_id\": 2\n      }\n    }\n  }'\n```\n\n2. The signature check fails (no `X-ANET-Signature` header), but `getTransactionDetails('60123456789')` succeeds because it is a real transaction. The OR condition on line 33 is not fully satisfied, so execution continues.\n\n3. `analyzeTransactionFromWebhook()` uses the forged payload's `amount: 99999.99` and `metadata.users_id: 2`.\n\n4. `processSinglePayment()` credits $99,999.99 to user ID 2's wallet via `addBalance()`.\n\n5. The dedup key is `sha1('net.authorize.payment.authcapture.created' . '60123456789')`, so the legitimate webhook arriving later is silently discarded as a duplicate.\n\n6. The attacker can repeat with new transaction IDs from additional small purchases for cumulative balance inflation.\n\n## Impact\n\n- **Wallet balance inflation:** Attacker credits arbitrary amounts to any user's wallet without corresponding payment, bypassing the payment gateway's actual charge amount.\n- **Premium content access:** Inflated wallet balance allows purchasing all paid/premium video content without real payment.\n- **Subscription fraud:** By including `plans_id` in forged metadata, the attacker can activate premium subscriptions (webhook.php:86-134) without corresponding payment.\n- **Financial loss:** Platform owner loses revenue from fraudulently accessed premium content and services.\n\n## Recommended Fix\n\n**1. Reject webhooks with invalid signatures unconditionally** — the transaction lookup should only be used for data enrichment *after* signature validation passes:\n\n```php\n// webhook.php line 33 — FIX: reject on invalid signature alone\nif (!$parsed['signatureValid']) {\n    _error_log('[Authorize.Net webhook] Bad signature');\n    http_response_code(401);\n    echo 'invalid signature';\n    exit;\n}\n```\n\n**2. Use API-fetched values as authoritative** — in webhook.php lines 44-55, invert the precedence so `$txnInfo` values always override payload values:\n\n```php\n// Always prefer API-fetched values over payload values\nif (!empty($txnInfo['users_id'])) {\n    $analysis['users_id'] = (int)$txnInfo['users_id'];\n}\nif (isset($txnInfo['amount'])) {\n    $analysis['amount'] = (float)$txnInfo['amount'];\n}\n```\n\n**3. Check `isApproved` before processing** — add a gate before `processSinglePayment()`:\n\n```php\nif (!$analysis['isApproved']) {\n    _error_log('[Authorize.Net webhook] Transaction not approved');\n    http_response_code(400);\n    echo 'transaction not approved';\n    exit;\n}\n```","status":"published","published":"2026-06-22T19:58:50Z","modified":"2026-06-22T19:58:51Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N","level":"medium"}],"cwes":["CWE-345"],"updated_at":"2026-11-02T14:40:31.656952Z"}],"nvd":[{"id":"CVE-2026-33731","description":"WWBN AVideo is an open source video platform. In versions prior to 29.0, 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. Three flaws combine into an exploit chain: signature bypass via OR logic (webhook.php:33), payload values override API-fetched values (AuthorizeNet.php:169-171, webhook.php:44-48) and a missing approval check (webhook.php:61-75). By forging payment metadata, an attacker can credit arbitrary amounts to any user's wallet without a corresponding payment and include a  plans_id  to activate premium subscriptions (webhook.php:86-134), enabling free access to all paid and premium content and causing direct revenue loss to the platform owner. This issue has been fixed in version 29.0.","status":"published","published":"2026-07-16T21:17:20.317Z","modified":"2026-10-24T20:10:00.137Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-345"],"updated_at":"2026-11-02T14:12:30.250437Z"}],"osv":[{"id":"CVE-2026-33731","title":"AVideo has an Authorize.Net Webhook Signature Bypass that Enables Wallet Balance Inflation via Forged Payment Data","description":"WWBN AVideo is an open source video platform. In versions prior to 29.0, 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. Three flaws combine into an exploit chain: signature bypass via OR logic (webhook.php:33), payload values override API-fetched values (AuthorizeNet.php:169-171, webhook.php:44-48) and a missing approval check (webhook.php:61-75). By forging payment metadata, an attacker can credit arbitrary amounts to any user's wallet without a corresponding payment and include a  plans_id  to activate premium subscriptions (webhook.php:86-134), enabling free access to all paid and premium content and causing direct revenue loss to the platform owner. This issue has been fixed in version 29.0.","status":"published","published":"2026-07-16T20:46:53.407Z","modified":"2026-10-28T02:30:33.925635423Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N","level":"medium"}],"cwes":["CWE-345"],"updated_at":"2026-11-02T15:45:01.896044Z"},{"id":"GHSA-95jh-7r58-xmxw","title":"AVideo has an Authorize.Net Webhook Signature Bypass that Enables Wallet Balance Inflation via Forged Payment Data","description":"## Summary\n\nThe 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.\n\n## Details\n\nThree flaws combine into an exploit chain:\n\n### 1. Signature Bypass via OR Logic (webhook.php:33)\n\n```php\nif (!$parsed['signatureValid'] \u0026\u0026 (empty($txnInfo) || !empty($txnInfo['error']))) {\n    http_response_code(401);\n    echo 'invalid signature';\n    exit;\n}\n```\n\nThe 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.\n\n### 2. Payload Values Override API-Fetched Values (AuthorizeNet.php:169-171, webhook.php:44-48)\n\nIn `analyzeTransactionFromWebhook()`, `users_id` and `amount` are extracted from the attacker-controlled webhook **payload** first:\n\n```php\n$users_id = isset($metadata['users_id']) ? (int)$metadata['users_id'] : null;\n$amount   = isset($payload['amount']) ? (float)$payload['amount'] : ...;\n```\n\nThe fallback logic in webhook.php only applies when the analysis values are empty/falsy:\n\n```php\nif (!$analysis['users_id'] \u0026\u0026 !empty($txnInfo['users_id'])) {\n    $analysis['users_id'] = (int)$txnInfo['users_id'];\n}\nif (!$analysis['amount'] \u0026\u0026 isset($txnInfo['amount'])) {\n    $analysis['amount'] = (float)$txnInfo['amount'];\n}\n```\n\nSince the forged payload already provides both values, the authoritative API-fetched values are never used.\n\n### 3. Missing Approval Check (webhook.php:61-75)\n\nThe 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.\n\n## PoC\n\n**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`).\n\n1. Immediately after the purchase completes (to race the legitimate webhook), send a forged webhook:\n\n```bash\ncurl -X POST https://target.com/plugin/AuthorizeNet/webhook.php \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"eventType\": \"net.authorize.payment.authcapture.created\",\n    \"payload\": {\n      \"id\": \"60123456789\",\n      \"amount\": 99999.99,\n      \"responseCode\": 1,\n      \"metadata\": {\n        \"users_id\": 2\n      }\n    }\n  }'\n```\n\n2. The signature check fails (no `X-ANET-Signature` header), but `getTransactionDetails('60123456789')` succeeds because it is a real transaction. The OR condition on line 33 is not fully satisfied, so execution continues.\n\n3. `analyzeTransactionFromWebhook()` uses the forged payload's `amount: 99999.99` and `metadata.users_id: 2`.\n\n4. `processSinglePayment()` credits $99,999.99 to user ID 2's wallet via `addBalance()`.\n\n5. The dedup key is `sha1('net.authorize.payment.authcapture.created' . '60123456789')`, so the legitimate webhook arriving later is silently discarded as a duplicate.\n\n6. The attacker can repeat with new transaction IDs from additional small purchases for cumulative balance inflation.\n\n## Impact\n\n- **Wallet balance inflation:** Attacker credits arbitrary amounts to any user's wallet without corresponding payment, bypassing the payment gateway's actual charge amount.\n- **Premium content access:** Inflated wallet balance allows purchasing all paid/premium video content without real payment.\n- **Subscription fraud:** By including `plans_id` in forged metadata, the attacker can activate premium subscriptions (webhook.php:86-134) without corresponding payment.\n- **Financial loss:** Platform owner loses revenue from fraudulently accessed premium content and services.\n\n## Recommended Fix\n\n**1. Reject webhooks with invalid signatures unconditionally** — the transaction lookup should only be used for data enrichment *after* signature validation passes:\n\n```php\n// webhook.php line 33 — FIX: reject on invalid signature alone\nif (!$parsed['signatureValid']) {\n    _error_log('[Authorize.Net webhook] Bad signature');\n    http_response_code(401);\n    echo 'invalid signature';\n    exit;\n}\n```\n\n**2. Use API-fetched values as authoritative** — in webhook.php lines 44-55, invert the precedence so `$txnInfo` values always override payload values:\n\n```php\n// Always prefer API-fetched values over payload values\nif (!empty($txnInfo['users_id'])) {\n    $analysis['users_id'] = (int)$txnInfo['users_id'];\n}\nif (isset($txnInfo['amount'])) {\n    $analysis['amount'] = (float)$txnInfo['amount'];\n}\n```\n\n**3. Check `isApproved` before processing** — add a gate before `processSinglePayment()`:\n\n```php\nif (!$analysis['isApproved']) {\n    _error_log('[Authorize.Net webhook] Transaction not approved');\n    http_response_code(400);\n    echo 'transaction not approved';\n    exit;\n}\n```","status":"published","published":"2026-06-22T19:58:50Z","modified":"2026-09-10T03:50:49.093166317Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N","level":"medium"}],"cwes":["CWE-345"],"updated_at":"2026-11-02T15:46:43.123867Z"}]}}
