lfi2rce-via-eternal-waiting.md (7939B)
1 --- 2 title: "LFI2RCE via Eternal waiting" 3 section: "Web Pentesting" 4 sectionSlug: "pentesting-web" 5 sourcePath: "src/pentesting-web/file-inclusion/lfi2rce-via-eternal-waiting.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/file-inclusion/lfi2rce-via-eternal-waiting.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # LFI2RCE via Eternal waiting 14 15 ## Basic Information 16 17 PHP stores accepted uploads in a temporary directory until the request ends, unless the application moves or renames them. If `upload_tmp_dir` is unset, PHP falls back to the system temporary directory, which is commonly `/tmp` on Unix-like targets; confirm the effective configuration instead of assuming that path. The exact directory and generated filename format depend on the platform and PHP configuration; names such as `php[a-zA-Z0-9]{6}` are common observations, not a guaranteed contract.<sup>[[2]](#references)</sup><sup>[[3]](#references)</sup> Some container images have also been observed generating names without digits, so derive the actual alphabet from the target environment before estimating the brute-force space. 18 19 In a local file inclusion, **if you manage to include that uploaded file, you will get RCE**. 20 21 By default, PHP accepts at most 20 uploaded files in one request through the configurable `max_file_uploads` directive.<sup>[[3]](#references)</sup> 22 23 ```text 24 ; Maximum number of files that can be uploaded via a single request 25 max_file_uploads = 20 26 ``` 27 28 Also, the **number of potential filenames are 62\*62\*62\*62\*62\*62 = 56800235584** 29 30 ### Other techniques 31 32 Other techniques relies in attacking PHP protocols (you won't be able if you only control the last part of the path), disclosing the path of the file, abusing expected files, or **making PHP suffer a segmentation fault so uploaded temporary files aren't deleted**.\ 33 This technique is **very similar to the last one but without needed to find a zero day**. 34 35 ### Eternal wait technique 36 37 In this technique **we only need to control a relative path**. If we manage to upload files and make the **LFI never end**, we will have "enough time" to **brute-force uploaded files** and **find** any of the ones uploaded. 38 39 **Pros of this technique**: 40 41 - You just need to control a relative path inside an include 42 - Doesn't require nginx or unexpected level of access to log files 43 - Doesn't require a 0 day to cause a segmentation fault 44 - Doesn't require a path disclosure 45 46 The **main problems** of this technique are: 47 48 - Need a specific file(s) to be present (there might be more) 49 - The **insane** amount of potential file names: **56800235584** 50 - If the server **isn't using digits** the total potential amount is: **19770609664** 51 - By default **only 20 files** can be uploaded in a **single request**. 52 - The **max number of parallel workers** of the used server. 53 - This limit with the previous ones can make this attack last too much 54 - **Timeout for a PHP request**. Ideally this should be eternal or should kill the PHP process without deleting the temp uploaded files, if not, this will also be a pain 55 56 So, how can you **make a PHP include never end**? Just by including the file **`/sys/kernel/security/apparmor/revision`** (**not available in Docker containers** unfortunately...). 57 58 Try it just calling: 59 60 ```bash 61 php -a # open php cli 62 include("/sys/kernel/security/apparmor/revision"); 63 ``` 64 65 ## Apache2 66 67 Apache's simultaneous-request limit is not universally 150; it depends on the active MPM and `MaxRequestWorkers`. Measure the target or use its actual configuration before applying the calculations below.<sup>[[4]](#references)</sup> The previously quoted values of 150 concurrent workers and a hypothetical tuned 8,000-worker configuration are useful only as historical calculation examples, not defaults that can be assumed on a target. The linked deployment guide shows one Apache event-MPM and PHP-FPM configuration.<sup>[[1]](#references)</sup> 68 69 By default, (as I can see in my tests), a **PHP process can last eternally**. 70 71 Let's do some maths: 72 73 - We can use **149 connections** to generate **149 \* 20 = 2980 temp files** with our webshell. 74 - Then, use the **last connection** to **brute-force** potential files. 75 - At a speed of **10 requests/s** the times are: 76 - 56800235584 / 2980 / 10 / 3600 \~= **530 hours** (50% chance in 265h) 77 - (without digits) 19770609664 / 2980 / 10 / 3600 \~= 185h (50% chance in 93h) 78 79 > [!WARNING] 80 > Note that in the previous example we are **completely DoSing other clients**! 81 82 If the Apache server is improved and we could abuse **4000 connections** (half way to the max number). We could create `3999*20 = 79980` **files** and the **number** would be **reduced** to around **19.7h** or **6.9h** (10h, 3.5h 50% chance). 83 84 ## PHP-FPM 85 86 When the site uses **PHP-FPM** instead of an in-process Apache PHP module, the pool's request timeout may affect the technique. 87 88 PHP-FPM configures **`request_terminate_timeout`** in the pool configuration, commonly under **`/etc/php/<php-version>/fpm/pool.d/www.conf`**. A value of `0` disables the timeout by default; configured values use seconds unless another unit is supplied.<sup>[[5]](#references)</sup> When a worker is forcibly killed, temporary-file cleanup behavior should be confirmed against the target PHP/FPM version rather than assumed. 89 90 On deployments where forced worker termination bypasses normal end-of-request cleanup, the uploaded temporary files remain on disk. Once that behavior is confirmed in the lab or target version, repeatedly timing out upload requests can create thousands of candidate files, substantially increasing the probability of finding one through the LFI while using fewer long-lived connections. This orphaned-file condition is the acceleration mechanism; without it, ordinary request cleanup removes the temporary uploads. 91 92 To **reduce the DoS impact**, suppose the attacker uses only **100 concurrent connections** and PHP-FPM's **`request_terminate_timeout`** is **30 seconds**. With the 20-file-per-request example above, the estimated temporary-file generation rate is `100*20/30 = 66.67` files per second. 93 94 Then, to generate **10000 files** an attacker would need: **`10000/66.67 = 150s`** (to generate **100000 files** the time would be **25min**). 95 96 Then, the attacker could use those **100 connections** to perform a **search brute-force**. Supposing a speed of 300 req/s the time needed to exploit this is the following: 97 98 - 56800235584 / 10000 / 300 / 3600 \~= **5.25 hours** (50% chance in 2.63h) 99 - (with 100000 files) 56800235584 / 100000 / 300 / 3600 \~= **0.525 hours** (50% chance in 0.263h) 100 101 Yes, it's possible to generate 100000 temporary files in an EC2 medium size instance: 102 103 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28240%29.png" alt=""><figcaption></figcaption></figure> 104 105 > [!WARNING] 106 > Note that in order to trigger the timeout it would be **enough to include the vulnerable LFI page**, so it enters in an eternal include loop. 107 108 ## Nginx 109 110 It looks like by default Nginx supports **512 parallel connections** at the same time (and this number can be improved). 111 112 ## References 113 114 - [1] [How To Configure Apache HTTP with mpm_event and PHP-FPM on Ubuntu 18.04](https://www.digitalocean.com/community/tutorials/how-to-configure-apache-http-with-mpm-event-and-php-fpm-on-ubuntu-18-04) 115 - [2] [PHP manual – POST method uploads](https://www.php.net/manual/en/features.file-upload.post-method.php) 116 - [3] [PHP manual – Core `php.ini` directives](https://www.php.net/manual/en/ini.core.php) 117 - [4] [Apache HTTP Server – MPM common directives](https://httpd.apache.org/docs/current/en/mod/mpm_common.html) 118 - [5] [PHP manual – FPM configuration](https://www.php.net/manual/en/install.fpm.configuration.php)