Skip to content

Commit 4fdadfd

Browse files
authored
Merge pull request #2084 from HackTricks-wiki/research_update_src_pentesting-web_file-inclusion_lfi2rce-via-phpinfo_20260402_024824
Research Update Enhanced src/pentesting-web/file-inclusion/l...
2 parents b7477b6 + f142d1d commit 4fdadfd

1 file changed

Lines changed: 25 additions & 0 deletions

File tree

src/pentesting-web/file-inclusion/lfi2rce-via-phpinfo.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,19 @@ LFI-With-PHPInfo-Assistance.pdf
3535

3636
In Windows the temp files are commonly under something like C:\\Windows\\Temp\\php*.tmp. In Linux/Unix they are usually in /tmp or the directory configured in upload_tmp_dir.
3737

38+
## What to verify in `phpinfo()` before racing
39+
40+
Before sending thousands of requests, extract the values that decide whether the race is realistic:
41+
42+
- `file_uploads`: must be `On`.
43+
- `upload_tmp_dir`: if set, this is the directory your LFI must be able to include. If empty, expect the system default temp directory.
44+
- `open_basedir`: if enabled, your vulnerable include path still needs to be able to reach the temp directory shown in `tmp_name`.
45+
- `output_buffering`: `4096` is a common/default size and is why many PoCs read in 4KB chunks, but this value can differ.
46+
- `zlib.output_compression`, `output_handler`, and any framework-level buffering: these reduce the chance of seeing `tmp_name` early enough.
47+
- `Server API`: useful to decide how much buffering may exist between PHP and you (`apache2handler` is usually easier to reason about than `fpm-fcgi` behind a reverse proxy).
48+
49+
If the page does not show `$_FILES`, make sure you are really sending a `multipart/form-data` request with an actual file part. PHP only populates `tmp_name` for upload fields that were parsed.
50+
3851
## Attack workflow (step by step)
3952

4053
1) Prepare a tiny PHP payload that persists a shell quickly to avoid losing the race (writing a file is generally faster than waiting for a reverse shell):
@@ -129,10 +142,20 @@ if __name__ == '__main__':
129142

130143
## Troubleshooting
131144
- You never see tmp_name: Ensure you really POST multipart/form-data to phpinfo(). phpinfo() prints $_FILES only when an upload field was present.
145+
- `tmp_name` appears only at the very end of the response: This is usually a buffering problem, not a PHP-version problem. Large `output_buffering` values, `zlib.output_compression`, userland output handlers, or reverse-proxy/FastCGI buffering can delay the phpinfo() body until the upload request is almost done.
146+
- You only get reliable streaming in a lab, not through the real site: A CDN, WAF, or reverse proxy may be buffering the upstream response. If you have multiple routes to the same app, prefer the most direct origin path.
147+
- The classic 4096-byte offset logic misses the leak: Treat 4096 as a starting point derived from common `output_buffering` defaults, not as a universal constant. Parse incrementally and stop as soon as `tmp_name` is complete.
148+
- The temp file is included but your shell dies immediately: Use a tiny stager that writes a second file, because the uploaded temp file will still be deleted when the original request ends.
132149
- Output doesn’t flush early: Increase padding, add more large headers, or send multiple concurrent requests. Some SAPIs/buffers won’t flush until larger thresholds; adjust accordingly.
133150
- LFI path blocked by open_basedir or chroot: You must point the LFI to an allowed path or switch to a different LFI2RCE vector.
134151
- Temp directory not /tmp: phpinfo() prints the full absolute tmp_name path; use that exact path in the LFI.
135152

153+
## Practical notes for modern stacks
154+
155+
- This technique is still reproducible in modern lab environments; for example, Vulhub keeps a demonstrator on PHP 7.2. In practice, success tends to depend more on output buffering and proxying than on a phpinfo-specific patch level.
156+
- `flush()` and `implicit_flush` only influence PHP's own output layer. They do not guarantee that a FastCGI gateway, reverse proxy, browser, or intermediary will release partial chunks immediately.
157+
- If the target is `fpm-fcgi` behind Nginx/Apache proxying, think in layers: PHP buffer, PHP output handlers/compression, FastCGI buffering, then proxy buffering. The race only works if enough of the phpinfo() response escapes that chain before request shutdown deletes the temp file.
158+
136159
## Defensive notes
137160
- Never expose phpinfo() in production. If needed, restrict by IP/auth and remove after use.
138161
- Keep file_uploads disabled if not required. Otherwise, restrict upload_tmp_dir to a path not reachable by include() in the application and enforce strict validation on any include/require paths.
@@ -161,4 +184,6 @@ lfi2rce-via-eternal-waiting.md
161184
## References
162185
- LFI With PHPInfo() Assistance whitepaper (2011) – Packet Storm mirror: https://packetstormsecurity.com/files/download/104825/LFI_With_PHPInfo_Assitance.pdf
163186
- PHP Manual – POST method uploads: https://www.php.net/manual/en/features.file-upload.post-method.php
187+
- PHP Manual – Flushing System Buffers: https://www.php.net/manual/en/outcontrol.flushing-system-buffers.php
188+
- Vulhub – PHP Local File Inclusion RCE with PHPINFO: https://github.com/vulhub/vulhub/blob/master/php/inclusion/README.md
164189
{{#include ../../banners/hacktricks-training.md}}

0 commit comments

Comments
 (0)