Canario
TAG -> Dockerlabs | CTF
Last updated
TAG -> Dockerlabs | CTF
Last updated
Después de desplegar el contenedor, podemos usar el comando ping
para verificar conectividad con el servidor:
Resultado
Como podemos ver en el resultado, tenemos conexión con la victima, ademas mediante el TTL podemos intuir contra que tipo de SO nos estamos enfrentando, en este caso Linux.
Lo siguiente que aremos en crear varias carpetas que nos ayudaran a mantener un orden en lo que vallamos encontrando, esto lo aremos con el comando mkt
:
Resultado
Nos moveremos a la carpeta recognition/nmap
y empezaremos en los escaneos:
Resultado
Solo esta el puerto 80 abierto, podemos ir a ver la pagina web para saber que contiene:
Esta es la pagina web que encontramos de primera, tiene una barra donde: Inicio nos manda al index.html
principal, Contactar no tiene nada y Subir archivo es probablemente y lo único que por ahora podemos explotar, intentemos subir un RCE.
Codigo
El nombre del archivo es rshell.php
, intentemos subir el archivo:
Al presionar Subir Archivo, nos encontramos con lo siguiente:
Deberemos ir probando diferentes extensiones php
, hasta encontrar una que nos permita subir ejemplo -> phtml
, quedaría: rshell.phtml
.
Ahora nos pondremos en escucha en el puerto 1234 en mi caso usando pwncat-cs
:
Ahora podemos probar la ruta uploads junto con el nombre de nuestro RCE:
Se queda cargando la pagina, pero si nos dirigimos a pwncat
podremos ver una conexión entrante:
Ya estamos dentro, ahora intentemos escalar privilegios, pwncat
tiene muchos módulos de enumeración, usare algunos de ellos antes de entrar a la reverse shell.
Enumero los distintos usuarios del servidor y entro a una Bash del servidor para ejecutar el comando whoami
el cual me dirá que usuario soy:
Podemos ver que hay un usuario llamado jerry y que nosotros somos www-data.
No veo binarios SUID útiles por lo cual entrare al servidor para enumerar formas de convertirnos en el usuario jerry.
Enumerando un poco, veo varias cosas interesantes:
Podemos ejecutar vim
como el usuario jerry sin proporcionar contraseña.
jerry puede ejecutar /opt/suma
y /usr/bin/python3 /opt/command_exec/command_exec.py
como root sin contraseña.
Usando searchbins
podemos ver como podemos aprovecharnos de el binario vim
:
El comando que usaremos es:
Resultado
Ahora debemos buscar una manera de aprovecharnos de los siguientes binarios:
Podemos descargarnos estos binarios para usar ingeniería inversa, esto lo podemos hacer mediante el modulo download:
Primero veamos que podemos hacer con el binario suma
, paso el binario a un contenedor Kali Linux el cual tiene todo lo necesario para hacer ingeniería inversa:
Podemos verificar información sobre el binario con el comando file
:
Resultado
Como podemos apreciar en la salida del comando, es un binario para Linux de 64 bits.
Usando checksec
podemos ver que tiene 2 protecciones:
STACK CANARY: Usa valores aleatorios para detectar sobrescrituras de buffer antes de afectar la ejecución.
NX (No eXecute) -> NX enabled
: Previene la ejecución de código en segmentos de memoria marcados como datos (como la pila).
Por otro lado podemos ver que no tiene PIE por lo cual el binario tiene direcciones de memoria fijas, entonces nos encontramos frente a un Canary Bypass.
En cuanto al otro script command_exec.py
podemos ver que su contenido es el siguiente:
El script abre el archivo /opt/command_exec/flag.txt
, y si su contenido es "ACTIVE", nos deja ejecutar un comando. El archivo flag.txt
vale "INACTIVE", por lo que no nos deja ejecutar comandos. No tenemos permisos de escritura en el archivo flag.txt
, ni en /opt
, por lo que en esta parte no podemos hacer nada por el momento, por lo tanto procedemos a seguir indagando en el binario suma
:
Si lo ejecutamos podremos ver lo siguiente:
Nos pide un nombre, nos saluda y luego nos pide una clave para acceder posiblemente a una calculadora, ingreso key
, pero no es correcto, podemos usar el comando strings
para ver si encontramos la contraseña en los caracteres imprimibles del binario:
Resultado relevante
Encima de la linea que dice Acceso garantizado podemos ver una posible contraseña en texto plano:
También podemos ver posibles funcionalidades del binario, donde nos pide 2 números, por ultimo también podemos notar 2 lineas interesantes y posiblemente lo que debamos lograr:
Procedemos a ver el binario con GDB
y buscar las funciones que contiene el binario:
Dentro de gdb
:
Resultado
Hay una función llamada set_flag
que llama la atención, intentemos ver que hace, primero podríamos usar disas
de gdbb
:
Con esto podemos ver en ensamblador, que cosas esta haciendo por detrás, para este caso en particular, tendremos que ir más aya, por lo tanto usaremos ghidra
para hacer ingeniera inversa:
Después de crear un proyecto y añadir el binario
suma
a ese proyecto deghidra
entraremos alCodeBrowser
, le diremos que si lo analice de tal forma que quedaría así:
En la parte izquierda buscaremos la ventada que dice Symbol Tree
, dentro de esa ventana vamos a expandir la carpeta Functions
y dentro buscaremos la función set_flag
, cuando lo presionemos, veremos como a la derecha se nos muestra el posible código c antes de ser compilado:
Esta función abre el archivo /opt/command_exec/flag.txt
y escribe ACTIVE
. Si pasara esto, podríamos ejecutar el script de python3
que vimos anteriormente y ejecutar cualquier comando como root, pero si ahora vemos la función main
:
Notaremos que nunca se llama a la función set_flag
, de tal modo que debemos buscar la manera de llamar a esta función mediante una vulnerabilidad presente en el binario, viendo la función main
, que nos muestra ghidra
, podremos notar lo siguiente:
La variable donde nos pide la clave se guarda con gets
el cual es vulnerable a Buffer Overflow.
Problema: El binario tiene canarios, esto impide que explotemos el Buffer Overflow de forma convencional.
¿Por qué?: Si hacemos lo anterior, se va a llamar a una función especial que va a terminar rápidamente la ejecución del programa.
Esto ocurre porque en el stack
, se almacena un canario al inicio de main()
y se le da un valor aleatorio, al final de main()
se comprueba si el canario sigue valiendo el mismo valor, de tal modo que si a cambiado, se llama a la función especial ya que esto significa que se a acontecido un Buffer Overflow y se han sobrescrito los datos del stack
.
¿Qué podemos hacer?: Si de alguna manera lográramos leer datos del stack
antes de que se llame a gets()
podríamos bypassear el canario.
¿Qué haremos?: El primer input
se almacena de forma segura, pero luego se llama a:
La primera línea está bien, el problema es la segunda línea, usa la entrada del usuario directamente sin especificar un formato seguro.
Se le pasa como primer argumento el input
, esto origina una Format String Vulnerability, ya que printf()
toma el input
como formato. Esto nos permite incluir especificadores de formato, como:
Vamos a probar lo siguiente, como nombre pasaremos %p
:
Lo que nos devuelve:
¿Qué paso?: Se llamo a printf("%p")
, por lo que se espera un segundo parámetro que sea un puntero, pero como no hay, ese valor se saca del stack
, de esta manera podemos ir leyendo uno a uno los datos del stack
, Ejemplo -> %1$p
, %2$p
...
Sabiendo esto, podemos crear un script que haga Fuzzing y lea x
valores del stack
, con el objetivo de buscar cual podría ser el canario y así quedarnos con la posición de este, luego al acontecer el Buffer Overflow poder sobrescribir el canario del stack
con su valor inicial.
fuzzing.sh
Con este script, leemos 100 valores del stack
, el siguiente paso es identificar el canario lo cual no es tan difícil, ya que el canario suelen acabar en 00
, ademas de que son números aleatorios, a diferencia de las direcciones de libc
que suelen ser 0x7ff
, sabiendo esto podemos agregar una ultima línea a nuestro script y filtrar lo que queremos con grep
.
Código final
Resultado
Nos arroja 2 resultados, podemos elegir cualquiera aunque tenga valores distintos, ya que en tiempo de ejecución acaban valiendo lo mismo.
Lo siguiente que necesitamos para que se acontezca el Buffer Overflow, es el offset
para sobrescribir el canario y el offset
para sobrescribir la dirección de retorno, estos valores ya los vimos en la función main()
dentro de ghidra
:
Lineas importantes de main()
Podemos ver que el buffer vulnerable es de 264
bytes y es local_118
, En la imagen podemos ver que este buffer esta en el offset -0x118
, que son 280 bytes, podemos ver que justo encima del buffer esta local_10
, que es el canario, lo sabemos porque en el código vemos que esta variable se le da un valor al principio y se comprueba su valor al final y si no es igual se llamada a stack_chk_fall()
.
Entonces como el canario está encima del buffer, si escribimos 264 bytes, los siguientes 8 bytes sobrescriben el canario.
El canario vale 8 bytes porque el binario es de 64 bits, de ser 32 bits el canario valdría 4 bytes.
Es decir que nos quedan otros 8 bytes para llegar a la dirección de retorno, Después de hacer todo esto sabemos que el offset
para sobrescribir el canario es de 264 y el de sobrescribir el RIP es 8.
De tal forma que el exploit final quedaría así:
En el servidor vulnerado ejecutamos:
Esto descargara pwntools
ya que esta librería no esta presente en el servidor, ya por ultimo pasamos el exploit a /tmp/
y lo ejecutamos, luego ejecutamos el script command_exec.py
como root.
Resultado