Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

🏠 Back to Blog

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:

PlatformTypical directory
Linux/var/lib/php/sessions/
WindowsC:\Windows\Temp\

Filename: sess_ + cookie value, e.g. cookie abc123/var/lib/php/sessions/sess_abc123.

Workflow

  1. Confirm you have a PHPSESSID; map it to sess_<id> on disk.
  2. Include the session file via LFI and inspect serialized contents for fields you control (e.g. a page key tied to ?language=).
  3. Poison: request with ?language= set to URL-encoded PHP (e.g. <?php system($_GET['cmd']); ?>).
  4. 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)

ServerLinuxWindows (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-Agent may also appear under /proc/ (e.g. /proc/self/environ, /proc/self/fd/N for 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.logFTP 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.