Detect phpSysInfo PSI_ALLOWED IP Allowlist Bypass via Spoofed Forwarding Headers (CVE-2026-55584) in Google Chronicle
Detects exploitation of CVE-2026-55584, an authentication/authorization bypass (CWE-290) in phpSysInfo <= 3.4.5. The PSI_ALLOWED IP allowlist trusts client-supplied X-Forwarded-For, Client-IP, and related forwarding headers when deriving the requestor IP. An attacker can inject a spoofed header value matching an allowlisted address to bypass the IP restriction and access the phpSysInfo interface and its JSON/XML system information endpoints (index.php?json, xml.php) that expose host, network, sensor, and mount details. Detection focuses on inbound HTTP requests to phpSysInfo endpoints carrying suspicious or spoofed forwarding headers, particularly private/loopback allowlist values arriving from external source IPs. Fixed in v3.4.6.
MITRE ATT&CK
- Tactic
- Initial Access Discovery
YARA-L Detection Query
rule phpsysinfo_psi_allowed_bypass_cve_2026_55584 {
meta:
author = "Argus"
description = "phpSysInfo PSI_ALLOWED bypass via spoofed forwarding headers"
cve = "CVE-2026-55584"
severity = "HIGH"
events:
$e.metadata.event_type = "NETWORK_HTTP"
(re.regex($e.network.http.referral_url, `phpsysinfo|index\.php|xml\.php`) or re.regex($e.target.url, `phpsysinfo|index\.php|xml\.php|json`))
re.regex($e.additional.fields["x_forwarded_for"], `127\.0\.0\.1|::1|localhost|10\.|192\.168\.|172\.1[6-9]\.`)
not net.ip_in_range_cidr($e.principal.ip, "10.0.0.0/8")
not net.ip_in_range_cidr($e.principal.ip, "192.168.0.0/16")
not net.ip_in_range_cidr($e.principal.ip, "172.16.0.0/12")
condition:
$e
} YARA-L rule matching phpSysInfo HTTP requests where a forwarding header asserts an allowlisted internal address but the principal IP is external.
Data Sources
Required Tables
False Positives & Tuning
- Trusted proxy tiers legitimately setting internal XFF values.
- Approved external scanners.
- CDN-forwarded traffic carrying private-range XFF.
Other platforms for CVE-2026-55584
Testing Methodology
Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.
- Test 1Spoofed X-Forwarded-For loopback bypass
Expected signal: Web access log entry with external source IP and XFF header value of 127.0.0.1 targeting index.php?json.
- Test 2Spoofed Client-IP private-range bypass
Expected signal: Access log showing Client-IP: 192.168.1.10 from an external source IP hitting xml.php.
- Test 3Windows PowerShell forwarding-header spoof
Expected signal: IIS/proxy log entry with X-Forwarded-For 10.0.0.5 from an external client to phpSysInfo.
References (5)
- https://github.com/phpsysinfo/phpsysinfo/security/advisories/GHSA-786w-p5pm-cvgh
- https://github.com/phpsysinfo/phpsysinfo/commit/019fa2d7e568ea11461adb4bd33da5dc87c4b9ab
- https://github.com/phpsysinfo/phpsysinfo/releases/tag/v3.4.6
- https://github.com/advisories/GHSA-786w-p5pm-cvgh
- https://www.exploit-db.com/exploits/52648
Response Playbook
Triage
- Confirm the target host is running phpSysInfo and identify its version; anything <= 3.4.5 is vulnerable to CVE-2026-55584.
- Extract the forwarding headers (X-Forwarded-For, Client-IP, X-Real-IP) from the flagged requests and compare the asserted value against the real TCP source IP recorded by the web server/proxy.
- Determine whether PSI_ALLOWED is configured on the instance and which addresses it trusts; a spoofed value matching one of those entries confirms an intended bypass.
- Review whether the requests reached sensitive endpoints (index.php?json, xml.php) that leak host, network, sensor, and mount data.
Containment
- Upgrade phpSysInfo to v3.4.6 or later, which stops trusting client-supplied forwarding headers for PSI_ALLOWED evaluation.
- As an interim control, place phpSysInfo behind an authenticated reverse proxy and strip/overwrite inbound X-Forwarded-For, Client-IP, and X-Real-IP headers at the edge before they reach the app.
- Restrict network access to the phpSysInfo port with a firewall/ACL so the IP allowlist is enforced at L3 rather than via HTTP headers.
Evidence Collection
- Preserve web server access logs and proxy logs containing the spoofed forwarding headers, source IPs, user agents, and requested URIs.
- Capture the phpSysInfo JSON/XML responses returned to the attacker to scope what system information was disclosed.
- Snapshot the phpSysInfo configuration (phpsysinfo.ini) showing the PSI_ALLOWED value and version at time of incident.
Escalation Criteria
- !Escalate if spoofed-header requests successfully retrieved phpSysInfo system information (HTTP 200 to json/xml endpoints), indicating confirmed disclosure.
- !Escalate if the same external source IP subsequently pivots to enumeration, credential access, or other hosts, suggesting the leaked host/network detail was used for follow-on activity.
Investigation Guide
Related Techniques
Forensic Artifacts
- >
Web server access logs showing X-Forwarded-For/Client-IP values inconsistent with the connecting socket IP. - >
phpsysinfo.ini configuration containing the PSI_ALLOWED allowlist. - >
HTTP responses from index.php?json / xml.php recording disclosed system information.
Tuning Guidance
Baseline your legitimate reverse proxy and load balancer IPs and exclude their appended XFF chains, since those are trusted rewrites rather than spoofs. If phpSysInfo sits directly behind a CDN, focus the detection on the left-most (client-asserted) XFF value rather than the full chain. Suppress known scanner and monitoring source IPs. After upgrading to 3.4.6 the header is no longer authoritative, so alerts should be treated as recon attempts rather than successful bypasses.
Hunting Queries
Hunt for external clients presenting private/loopback forwarding-header values while accessing phpSysInfo endpoints, over a wide lookback window.
W3CIISLog | where csUriStem has_any ("index.php","xml.php","phpsysinfo") | extend xff=tostring(column_ifexists("csXForwardedFor","")) | where xff has_any ("127.0.0.1","192.168.","10.","::1") and not(ipv4_is_private(tostring(cIP))) | summarize count() by tostring(cIP), xff, csUriStem index=web (uri_path="*phpsysinfo*" OR uri_path="*xml.php*" OR uri_query="*json*") | eval xff=coalesce('http_x_forwarded_for','client_ip') | regex xff="(127\.0\.0\.1|::1|10\.|192\.168\.)" | where NOT cidrmatch("10.0.0.0/8",src_ip) AND NOT cidrmatch("192.168.0.0/16",src_ip) | stats count by src_ip, xff, uri_path Atomic Red Team Tests
Sends a phpSysInfo JSON request with an X-Forwarded-For header asserting 127.0.0.1 to bypass a PSI_ALLOWED loopback allowlist.
Command
curl -s -H 'X-Forwarded-For: 127.0.0.1' 'http://target.lab/phpsysinfo/index.php?json' -o /tmp/psi_bypass.json Cleanup
rm -f /tmp/psi_bypass.json Expected Telemetry
Web access log entry with external source IP and XFF header value of 127.0.0.1 targeting index.php?json.
Expected Detection
Detection fires on the mismatch between the loopback XFF value and the public source IP.
Uses a Client-IP header asserting a private allowlisted address against the phpSysInfo XML endpoint.
Command
curl -s -H 'Client-IP: 192.168.1.10' 'http://target.lab/phpsysinfo/xml.php?plugin=complete' -o /tmp/psi_xml.xml Cleanup
rm -f /tmp/psi_xml.xml Expected Telemetry
Access log showing Client-IP: 192.168.1.10 from an external source IP hitting xml.php.
Expected Detection
Detection fires on private-range forwarding header from a public source.
Issues a phpSysInfo request from Windows with a spoofed X-Forwarded-For header to test allowlist bypass telemetry.
Command
powershell -Command "Invoke-WebRequest -Uri 'http://target.lab/phpsysinfo/index.php?json' -Headers @{'X-Forwarded-For'='10.0.0.5'} -OutFile $env:TEMP\psi.json" Cleanup
powershell -Command "Remove-Item $env:TEMP\psi.json -ErrorAction SilentlyContinue" Expected Telemetry
IIS/proxy log entry with X-Forwarded-For 10.0.0.5 from an external client to phpSysInfo.
Expected Detection
Detection fires on the spoofed private XFF value from an external source IP.