php-ssrf.md (3503B)
1 --- 2 title: "PHP SSRF" 3 section: "Network Services" 4 sectionSlug: "network-services-pentesting" 5 sourcePath: "src/network-services-pentesting/pentesting-web/php-tricks-esp/php-ssrf.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/php-tricks-esp/php-ssrf.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # PHP SSRF 14 15 ## URL-aware PHP functions 16 17 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.<sup>[[1]](#references)</sup> 18 19 ```php 20 file_get_contents("http://127.0.0.1:8081"); 21 fopen("http://127.0.0.1:8081", "r"); 22 file("http://127.0.0.1:8081"); 23 md5_file("http://127.0.0.1:8081"); 24 ``` 25 26 ## WordPress SSRF via DNS rebinding 27 28 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.<sup>[[2]](#references)</sup> Treat this as version-dependent research: verify the exact WordPress and HTTP-library versions before reproducing it. 29 30 The same research identified the following callers or wrappers as potentially affected:<sup>[[2]](#references)</sup> 31 32 - `wp_safe_remote_request()` 33 - `wp_safe_remote_post()` 34 - `wp_safe_remote_head()` 35 - `WP_REST_URL_Details_Controller::get_remote_url()` 36 - `download_url()` 37 - `wp_remote_fopen()` 38 - `WP_oEmbed::discover()` 39 40 ## Historical CRLF header injection 41 42 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.<sup>[[3]](#references)</sup> 43 44 ```php 45 // The following creates a From header and injects an additional header. 46 ini_set("from", "Hi\r\nInjected: I HAVE IT"); 47 file_get_contents("http://127.0.0.1:8081"); 48 ``` 49 50 ```http 51 GET / HTTP/1.1 52 From: Hi 53 Injected: I HAVE IT 54 Host: 127.0.0.1:8081 55 Connection: close 56 ``` 57 58 > [!WARNING] 59 > Test header-injection behavior only against an isolated target: the example sends attacker-controlled HTTP headers to the destination. 60 61 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.<sup>[[4]](#references)</sup> 62 63 ```php 64 $url = "https://example.com/"; 65 $options = [ 66 'http' => [ 67 'method' => 'GET', 68 'header' => "Accept-Language: en\r\n" . 69 "Cookie: foo=bar\r\n" . 70 "User-Agent: SecurityTestClient/1.0\r\n", 71 ], 72 ]; 73 74 $context = stream_context_create($options); 75 $file = file_get_contents($url, false, $context); 76 ``` 77 78 ## References 79 80 - [1] [PHP manual - Using remote files](https://www.php.net/manual/en/features.remote-files.php) 81 - [2] [Patchstack - Exploring unpatched WordPress SSRF](https://patchstack.com/articles/exploring-the-unpatched-wordpress-ssrf) 82 - [3] [PHP bug #81680 - HTTP wrapper CRLF injection via the `from` directive](https://bugs.php.net/bug.php?id=81680) 83 - [4] [PHP manual - HTTP context options](https://www.php.net/manual/en/context.http.php)