Log poisoning for LFI → RCE
When an execution-capable include processes a path as PHP, any included file that contains PHP runs. Log poisoning (also called log contamination) means writing attacker PHP into a log or session file the app already writes, then including that file through the LFI parameter. It works wherever the web process can read the poisoned file—paths and permissions differ by stack and hardening.
The same read / execute / remote URL matrix applies as in intro to file inclusions: sinks that execute (e.g. PHP include / include_once, Java import / .NET include in risky patterns) enable RCE; require / Node res.render rows in course material stress no remote URL in some cases—here the focus is execute + readable poisoned file.
PHP session poisoning
PHP stores session data in files keyed by PHPSESSID:
| Platform | Typical directory |
|---|---|
| Linux | /var/lib/php/sessions/ |
| Windows | C:\Windows\Temp\ |
Filename: sess_ + cookie value, e.g. cookie abc123 → /var/lib/php/sessions/sess_abc123.
Workflow
- Confirm you have a
PHPSESSID; map it tosess_<id>on disk. - Include the session file via LFI and inspect serialized contents for fields you control (e.g. a
pagekey tied to?language=). - Poison: request with
?language=set to URL-encoded PHP (e.g.<?php system($_GET['cmd']); ?>). - Include the session file again and pass
&cmd=id(or another command) on the include request.
Operational notes
- Cookie values differ per session; always use your
sess_*path. - After you include the session file with
language=/var/lib/php/sessions/sess_..., the app may overwrite session data with that path string—re-poison before another command. Prefer dropping a persistent webshell under the web root or reverse shell once you have execution. - If nothing in the session file is user-influenced, this chain does not apply.
Web server access logs (Apache / Nginx)
access.log records each request, including the User-Agent header—fully client-controlled. Poison by sending any request (not only the LFI URL) with User-Agent: <?php system($_GET['cmd']); ?>, then include the log via LFI.
Default log locations (verify; fuzz with an LFI wordlist if needed)
| Server | Linux | Windows (common) |
|---|---|---|
| Apache | /var/log/apache2/ | C:\xampp\apache\logs\ |
| Nginx | /var/log/nginx/ | C:\nginx\log\ |
Permissions
- Nginx logs are often readable by low-privileged users (e.g.
www-data) by default. - Apache logs are often restricted to root / adm; older or misconfigured Apache may allow www-data read access.
cURL example (write header to a file, then send):
echo -n "User-Agent: <?php system(\$_GET['cmd']); ?>" > Poison
curl -s "http://<SERVER_IP>:<PORT>/index.php" -H @Poison
Then trigger LFI against access.log and add &cmd=id.
Safety
- Logs can be very large; including them may be slow or harmful in production. Minimize noise and avoid repeated full-log pulls.
Alternatives
User-Agentmay also appear under/proc/(e.g./proc/self/environ,/proc/self/fd/Nfor small N). If logs are unreadable, these are worth trying—they may still require elevated read.
Other service logs
If LFI can read a log and you can inject PHP into a logged field, the same idea applies:
/var/log/sshd.log— e.g. username set to PHP when SSH is exposed./var/log/vsftpd.log— FTP username as payload./var/log/mail(or similar) — mail content that gets logged.
Generalize: any log that records attacker-controlled bytes and is readable through LFI can become a staged PHP file for an execute-capable include.
Defensive takeaway
Treat session files and logs as sensitive: strict path allowlists for includes, no user input in include resolution, least privilege on log and session directories, and avoid execute semantics on attacker-influenced paths. From an assurance perspective, readable logs + LFI + execute include is a full RCE chain.