You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
37
37
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
+
38
51
## Attack workflow (step by step)
39
52
40
53
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__':
129
142
130
143
## Troubleshooting
131
144
- 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.
132
149
- 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.
133
150
- LFI path blocked by open_basedir or chroot: You must point the LFI to an allowed path or switch to a different LFI2RCE vector.
134
151
- Temp directory not /tmp: phpinfo() prints the full absolute tmp_name path; use that exact path in the LFI.
135
152
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
+
136
159
## Defensive notes
137
160
- Never expose phpinfo() in production. If needed, restrict by IP/auth and remove after use.
138
161
- 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.
0 commit comments