// HackTricks · Network Services

PHP SSRF

PHP SSRF

URL-aware PHP functions

When URL-aware wrappers are enabled, functions such as file_get_contents(), fopen(), file(), and md5_file() can accept remote URLs. Passing an attacker-controlled URL to them without validating its resolved destination can create an SSRF vulnerability.[1]

file_get_contents("http://127.0.0.1:8081");
fopen("http://127.0.0.1:8081", "r");
file("http://127.0.0.1:8081");
md5_file("http://127.0.0.1:8081");

WordPress SSRF via DNS rebinding

Patchstack demonstrated that WordPress’s wp_safe_remote_get() checks could be bypassed with DNS rebinding in the versions they tested. The validation path called wp_http_validate_url() before the connection, allowing a hostname to resolve to an allowed address during validation and a different address during the request.[2] Treat this as version-dependent research: verify the exact WordPress and HTTP-library versions before reproducing it.

The same research identified the following callers or wrappers as potentially affected:[2]

  • wp_safe_remote_request()
  • wp_safe_remote_post()
  • wp_safe_remote_head()
  • WP_REST_URL_Details_Controller::get_remote_url()
  • download_url()
  • wp_remote_fopen()
  • WP_oEmbed::discover()

Historical CRLF header injection

PHP bug #81680 documented CRLF injection through the from INI setting used by the HTTP stream wrapper. The behavior is version-dependent and should not be assumed on a patched runtime.[3]

// The following creates a From header and injects an additional header.
ini_set("from", "Hi\r\nInjected: I HAVE IT");
file_get_contents("http://127.0.0.1:8081");
GET / HTTP/1.1
From: Hi
Injected: I HAVE IT
Host: 127.0.0.1:8081
Connection: close

[!WARNING] Test header-injection behavior only against an isolated target: the example sends attacker-controlled HTTP headers to the destination.

Separately, stream contexts intentionally support setting request headers. This is not itself a vulnerability, but it becomes dangerous when an application copies untrusted input into the header string.[4]

$url = "https://example.com/";
$options = [
    'http' => [
        'method' => 'GET',
        'header' => "Accept-Language: en\r\n" .
                    "Cookie: foo=bar\r\n" .
                    "User-Agent: SecurityTestClient/1.0\r\n",
    ],
];

$context = stream_context_create($options);
$file = file_get_contents($url, false, $context);

References

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.