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

PHP wrappers β€” LFI to RCE

After using LFI mainly for local file disclosure, the next step is often remote code execution on the back-end. Viability depends on language, sink (include vs read-only APIs), and server configuration.

Paths that do not need stream wrappers

Before reaching for wrappers, enumeration still matters: read config.php for DB passwords (possible reuse as OS user passwords), home .ssh/id_rsa when permissions allow, then pivot via SSH or another remote session.

allow_url_include and php.ini

Several PHP stream attacks require allow_url_include = On (off by default). It also gates php://input abuse and RFI. Some apps (e.g. certain WordPress plugins/themes) enable it anyway.

Confirm by reading php.ini through LFI, using php://filter/read=convert.base64-encode/resource=... so the response stays usable (similar to reading other .ini / PHP files):

  • Apache: /etc/php/X.Y/apache2/php.ini
  • Nginx / PHP-FPM: /etc/php/X.Y/fpm/php.ini

Try current X.Y first, then older versions. Prefer cURL or Burp over a browser for long Base64 output. Decode and search:

echo '<BASE64>' | base64 -d | grep allow_url_include

data:// β€” inline PHP

When allow_url_include is on, data:// can supply PHP source to an execution-capable include. Use text/plain;base64, and URL-encode the Base64 payload.

Example shell one-liner to encode:

echo '<?php system($_GET["cmd"]); ?>' | base64

Request shape (conceptually): vulnerable parameter set to data://text/plain;base64,<PAYLOAD> plus &cmd= for the command (if the app merges GET into the included context as expected).

php://input β€” POST body as PHP

Same allow_url_include dependency. Point the include at php://input and send raw PHP in the POST body. If $_GET["cmd"] is not merged into the executed code path, bake the command into the POST body (e.g. <?php system('id'); ?>) instead of a tiny dynamic shell.

expect:// β€” command execution stream

expect is an optional extension (extension=expect in php.ini). Presence in config does not prove it loads at runtimeβ€”test with:

expect://id

(or the equivalent in your vulnerable parameter). No separate web shell payload is required; the wrapper is built for command-style use. Same idea appears elsewhere (e.g. XXE with expect).

The php://filter chain for source disclosure (without RCE) is covered in php-filters-lfi.md. Phar and zip stream tricks paired with uploads and LFI are covered in lfi-file-uploads.md.