Natas Nivel 24 a 25

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

Este nivel explora una de las trampas mas conocidas del sistema de tipos de PHP: comparar una cadena con un array usando strcmp() produce NULL, y la comparacion laxa NULL == 0 es true. Un formulario de contrasena que parece solido se puede bypassear sin conocer el secreto.

Este laboratorio es legal y autorizado. Solo practica estas tecnicas en entornos de wargame como OverTheWire o sistemas sobre los que tengas permiso explicito.

Objetivo

Enviar el parametro passwd como array en lugar de string para que strcmp() devuelva NULL, la condicion de acceso sea verdadera y el servidor revele la contrasena de natas25.

Que aprendes

Concepto / ComandoPara que sirve
strcmp($a, $b) en PHPCompara dos strings; devuelve 0 si son iguales, NULL si algun argumento no es string
NULL == 0 (comparacion laxa)En PHP, NULL es igual a 0 con == pero no con ===
passwd[]= en la URLHace que PHP interprete passwd como array en $_REQUEST
curl -G --data-urlencodeEnvia parametros GET de forma segura desde la terminal
Diferencia == vs ===== permite coercion de tipos; === exige tipo y valor identicos

Concepto clave: Type Juggling con strcmp y arrays

La categoria de vulnerabilidad es PHP Type Juggling (OWASP A03 - Injection / A04 - Insecure Design segun el contexto).

El patron es el siguiente: el codigo del servidor hace una comparacion como:

if (strcmp($_REQUEST['passwd'], $secret) == 0) {
    // acceso concedido
}

strcmp() esta disenada para comparar strings. Cuando PHP recibe passwd[]= en la query string, $_REQUEST['passwd'] deja de ser un string y se convierte en un array. Al pasar un array a strcmp(), la funcion no puede operar y devuelve NULL en lugar de un entero. La comparacion laxa NULL == 0 evalua a true porque PHP convierte NULL a 0 antes de comparar. Resultado: el bloque de acceso se ejecuta sin que el atacante conozca el secreto.

La vulnerabilidad desaparece si se usa === 0 (identico estricto: NULL === 0 es false) o si se valida el tipo de $_REQUEST['passwd'] antes de la comparacion.

Paso a paso

1. Acceder al nivel con las credenciales de natas24

# Usuario: natas24
# Contrasena: <pass_natas24>
# URL: http://natas24.natas.labs.overthewire.org

2. Inspeccionar el formulario

El nivel muestra un campo de contrasena. Al introducir cualquier valor incorrecto, la pagina responde con un mensaje de error generico. El codigo fuente relevante (visible en el propio nivel o deducible) es:

<?php
if(array_key_exists("passwd", $_REQUEST)){
    if(!strcmp($_REQUEST['passwd'], $secret)){
        echo "<br>The secret is: " . $secret . "<br>";
        echo "Password for natas25 is: <contrasena_natas25>";
    } else {
        echo "<br>Wrong secret";
    }
}
?>

3. Construir el payload con passwd como array

En HTTP, enviar passwd[]= (con corchetes) hace que PHP lo procese como el primer elemento de un array. No es necesario darle ningun valor al elemento.

curl -u natas24:<pass_natas24> \
  "http://natas24.natas.labs.overthewire.org/?passwd[]="

Alternativamente, desde el navegador, edita la URL directamente en la barra de direcciones:

http://natas24.natas.labs.overthewire.org/?passwd[]=

Si el navegador codifica los corchetes como %5B%5D, comprueba que el servidor los interpreta igual (PHP los acepta en ambas formas).

4. Verificar la respuesta

La pagina debe mostrar el secreto interno y la contrasena de natas25. Si ves el mensaje de error, revisa que los corchetes lleguen sin codificar o prueba con curl que tiene mas control sobre la peticion en crudo.

# Variante con --data-urlencode para control explicito del parametro
curl -u natas24:<pass_natas24> \
  -G \
  --data-urlencode "passwd[]=" \
  "http://natas24.natas.labs.overthewire.org/"

Nota: --data-urlencode "passwd[]=" puede codificar los corchetes segun la version de curl. Si es necesario, usa la URL directa con corchetes literales o Burp Suite para interceptar y editar la peticion manualmente antes de enviarla.

Por que funciona

PHP tiene dos niveles de comparacion:

  • == (loose): realiza coercion de tipos. NULL == 0 es true, "0" == false es true, etc.
  • === (strict): exige mismo tipo y mismo valor. NULL === 0 es false.

strcmp() solo sabe trabajar con strings. Cuando recibe un tipo inesperado (array, objeto), PHP emite un warning interno y devuelve NULL. El codigo usa == 0 (no === 0), por lo que NULL == 0 supera la condicion sin que el valor del array importe en absoluto.

Este comportamiento esta documentado en las versiones de PHP anteriores a 8.0 (donde strcmp con array genera un TypeError fatal en lugar de devolver NULL silenciosamente). En PHP 8+ el bypass falla porque la excepcion interrumpe la ejecucion antes de llegar a la comparacion.

Errores comunes

  1. Usar passwd=[] en lugar de passwd[]=: el primero envia la cadena literal [] como valor del parametro string; el segundo convierte passwd en array. La sintaxis de array en PHP query strings es nombre[]=valor.

  2. El navegador codifica los corchetes: algunos navegadores convierten [ y ] en %5B y %5D. PHP los acepta igual, pero si el servidor tiene algun filtro intermedio puede rechazarlos. Usa curl si tienes dudas.

  3. Asumir que el bypass funciona en PHP 8+: en PHP 8.0+ strcmp() lanza un TypeError al recibir un array, lo que interrumpe el script con un error 500 en lugar de devolver NULL. Este nivel de natas corre en una version de PHP donde el comportamiento es el classico (NULL silencioso).

  4. Confundir !strcmp(...) con strcmp(...) == 0: son equivalentes cuando el resultado es un entero (!0 es true), pero el comportamiento con NULL difiere: !NULL es true y NULL == 0 tambien es true, por lo que el bypass funciona en ambas variantes del codigo.

Pasar al siguiente nivel

La respuesta del servidor incluye la contrasena para acceder a natas25:

  • URL: http://natas25.natas.labs.overthewire.org
  • Usuario: natas25
  • Contrasena: <pass_natas25>

El nivel 25 introduce una vulnerabilidad diferente relacionada con manipulacion de rutas o parametros de inclusion de archivos (LFI / path traversal). Revisar como el servidor construye rutas a partir de entrada del usuario es el punto de partida.

Conexiones