HackerWhale - Offensive Docker Image
Posted on August 11, 2024 (Last modified on August 14, 2024) • 11 min read • 2,343 wordsUma imagem Docker para propostas ofensivas com funções de expansão. Compatível com ambientes Kubernetes.
Este projeto é o resultado da necessidade encontrada durante um trabalho onde haviam poucos recursos disponíveis, e uma imagem de container personalizada foi a solução criada.
Assim foi criado o “HackerWhale”, que em poucos minutos te disponibilizará tudo o que é necessário para realizar testes em sua rede, seja em redes corporativas ou ambiente Kubernetes.
Docker
docker pull 0xtiago/hackerwhale:latest
Kubernetes
kubectl apply -f hackerwhale-k8s.yaml
Para casos de uso e detalhes, basta seguir neste humilde artigo. :)
Recentemente, me deparei com uma situação onde precisei realizar uma avaliação rápida em um ambiente, mas naquele exato momento eu ainda não possuía direitos administrativos, e muito menos ferramentas de virtualização (VBox/VMWare) no laptop fornecido. No entanto, estava disponível o Docker Desktop. Então, decidi gastar um tempo construindo meu próprio container com minhas ferramentas favoritas.
Haviam outras opções disponíveis, mas elas não eram viáveis naquele momento devido aos seguintes motivos:
apt update && apt -y install kali-linux-headless
não era possível);Então, aqui trago duas opções após essa jornada de testes:
A maior preocupação durante a construção da imagem base era manter a mesma experiência de trabalho em um host Ubuntu Linux que geralmente trabalho, incluido Zsh com Oh-My-Zsh, Oh-My-Tmux, e manutenção do histórico de comandos em $(PWD)
.
Abaixo um resumo das características deste projeto:
/workdir/.zsh_history_docker
, podendo ser mapeado e mantido no host, podemos ser reutilizado em outras imagens criadas.Abaixo a lista de ferramentas disponível até o momento da última atualização deste post. O código sempre será mantido e atualizado em https://github.com/0xtiago/HackerWhale.
# | Tool |
---|---|
1. | Altdns |
2. | Alterx |
3. | Amass |
4. | Anew |
5. | Antiburl |
6. | Arjun |
7. | arp_scan |
8. | Assetfinder |
9. | Burl |
10. | Brutespray |
11. | ChaosClient |
12. | Collector |
13. | DalFox |
14. | Dirsearch |
15. | Dnsx |
16. | DNSValidator |
17. | ffuf |
18. | Findomains |
19. | Gau |
20. | Gauplus |
21. | Gf |
22. | GithubSearch |
23. | GitDorker |
24. | GitDumper |
25. | GoogleChrome |
26. | GoSpider |
27. | Gowitness |
28. | Hakrawler |
29. | Hakrevdns |
30. | Haktrails |
31. | Httprobe |
32. | Httpx |
33. | JohnTheRipper |
34. | JSScanner |
35. | K9s |
36. | Kiterunner |
37. | Kubectl |
38. | LinkFinder |
39. | Mapcidr |
40. | Massdns |
41. | Masscan |
42. | MegaPy |
43. | Metabigor |
44. | Naabu |
45. | Netcat |
46. | Nmap |
47. | Notify |
48. | Nrich |
49. | Nuclei |
50. | ParamSpider |
51. | Qsreplace |
52. | ShufleDNS |
53. | Sqlmap |
54. | Sub404 |
55. | Subfinder |
56. | Subjs |
57. | sslscan |
58. | Stress_ng |
59. | Telegram_Send |
60. | TurboSearch |
61. | Unfurl |
62. | Uro |
63. | Waybackurls |
64. | wafw00f |
65. | WPScan |
Este modo carinhosamente chamado de Baleião, se trata do container em seu formato completo, com todas as ferramentas. Você pode contruí-lo acessando o repositório no Github ou realizar o pull desta imagem completa diretamente do DockerHub.
Faça o pull da imagem do repositório do Github.
docker pull 0xtiago/hackerwhale:latest
Execute o container.
docker run -d -v ${PWD}:/workdir --name hackerwhale hackerwhale
Acesse o terminal do container.
docker exec -it hackerwhale /bin/zsh
Como citado no inicío, uma da maiores motivações para construir esta imagem era a possibilidade torná-la expansível, entregando uma imagem base para uma boa experiência de uso. Para realizar a construção no formato expansível basta seguir os passos seguintes.
Clone o repositório, acesse-o.
git clone https://github.com/0xtiago/HackerWhale && cd HackerWhale
Com o uso de argumentos você poderá expandir o container com as aplicações que desejar escrevendo o seu próprio shell script de expansão. Você pode usar este aqui como base, se quiser.
Esta imagem está configurada para receber dois argumentos a partir da opção --build-arg
, EXPANSION_SCRIPT_LOCAL
e EXPANSION_SCRIPT_URL
, onde você pode passar um script local ou um script disponível em uma URL, respectivamente.
Exemplo utilizando script local:
docker build --build-arg \
EXPANSION_SCRIPT_LOCAL=scripts/expansion_script.sh -t hackerwhale .
Exemplo utilizando script remoto:
docker build --build-arg \
EXPANSION_SCRIPT_URL="https://raw.githubusercontent.com/0xtiago/HackerWhale/main/scripts/expansion_script.sh" -t hackerwhale .
Nos exemplos há algumas opções de uso que serão aprofundadas mais adiante. No comando docker run
iremos utilizar algumas opções de destaque:
-v ${PWD}:/workdir
: Mapeamento do diretório local de execução do host com o diretório /workdir
do container. Essa é uma opções que particularmente gosto para fins de organização.--net=host
: Configuração do container na mesma rede do host, assim seus serviços podem se comunicar entre si. Um exemplo de necessidade desta opção é a comunicação entre o container e a porta de um proxy no host, como ZAProxy ou BurpSuite.-p {Porta do Container}:{Porta do Host}
: Opção muito utilizada para expor portas de serviços do container para acesso pelo host. Mais adiante demonstramos seu uso para o recebimento de conexões reversas. Obs.: Não deve ser usada em conjunto com --host
.No exemplo abaixo, configuramos para que sua pasta local seja mapeada no em /workdir
no container para facilitar a organização durante os trabalhos. No comando também foi adicionada a opção --net=host
para extender a possibilidade de interagir com algum proxy do host, como ZAProxy, Burp Suite, Caido, etc.
Execução do container sem configurações de redes ou portas. Opcionalmente, mantendo o seu nome como hackerwhale
.
docker run -d -v ${PWD}:/workdir --name hackerwhale hackerwhale
Execução do container na mesma rede do host.
docker run -d --net=host -v ${PWD}:/workdir --name hackerwhale hackerwhale
Execução do container expondo portas de serviço (Ex.: netcat
).
docker run -d -v ${PWD}:/workdir -p 4444:4444 --name hackerwhale hackerwhale
Uma vez executado o container em qualquer das opções, acesso o container pelo entrypoint /bin/zsh
para acessar a shell e iniciar o seu uso.
docker exec -it hackerwhale /bin/zsh
Para utilizar proxies externos e poder interceptar comunicação HTTPS, é necessário instalar seus certificados. As configurações de certificado dos proxies são todas realizadas a partir da shell do container.
docker exec -it hackerwhale /bin/zsh
Após executar o ZAProxy, baixe o certificado acessando o endereço padrão http://localhost:8080.
Ou baixe o certificado pela próprio GUI:
Após o download, pela shell do container, é necessário converter o certificado em DER ou PEM para Base64.
openssl x509 -inform PEM -in ZAPCACert.cer -out ZAPCACert.crt
Depois é só importar o certificado convertido. Para facilitar este passo, foi criado shell script. Os mesmos passos serão necessários para os outros proxies.
sh -c "$(curl -fsSL https://raw.githubusercontent.com/0xtiago/HackerWhale/main/scripts/check_and_copy_cert.sh)" - ZAPCACert.crt
Após a importação basta configurar as variavéis de ambiente http_proxy
e https_proxy
do container para acessar a portar do proxy do host. No exemplo abaixo a porta é a 8080.
# Caso queira usar o Burp, basta exportar internamente as portas de proxy
export http_proxy="http://host.docker.internal:8080" ;\
export https_proxy="http://host.docker.internal:8080"
Para desfazer a configuração do proxy, basta remover as variáveis de ambiente.
#Para desconfigurar
unset http_proxy && unset https_proxy
Após executar o BurpSuite, baixe o certificado acessando o endereço padrão http://localhost:8080.
Ou baixe pelo pela interface da ferramenta seguinte os seguintes passos:
Pela shell do container, converta o certificado do formato DER para Base64.
openssl x509 -inform DER -in burp_cert.der -out burp_cert.crt
Realize a importação do certificado:
sh -c "$(curl -fsSL https://raw.githubusercontent.com/0xtiago/HackerWhale/main/scripts/check_and_copy_cert.sh)" - burp_cert.crt
Configure as variáveis de ambiente para direcionar o tráfego para o Burp:
export http_proxy="http://host.docker.internal:8080" ;\
export https_proxy="http://host.docker.internal:8080"
E agora é só testar.
Para desfazer a configuração do proxy, basta remover as variáveis de ambiente.
#Para desconfigurar
unset http_proxy && unset https_proxy
Se quiser abrir uma porta para receber conexões reversas, no exemplo é demonstrado o mapeamento da porta 4444 no host e direcionando para mesma porta no container:
docker run -d -v ${PWD}:/workdir -p 4444:4444 --name hackerwhale hackerwhale
Acesse o container e execute o netcat
para receber as conexões:
docker exec -it hackerwhale /bin/zsh
nc -nlvp 4444
Recebendo conexão de um outro pod em ambiente Kubernetes.
Uma vez que temos um container com estas capacidades, podemos utilizá-lo dentro de um cluster de Kubernetes como um pod. Estão disponíveis dois YAMLs para este laboratório, um para Minikube e outro para Kubernetes, além de outros YAMLs prontos para outros serviços que utilizaremos no laboratório. As imagens que utilizaremos serão as seguintes:
Todos os arquivos necessários para os laboratórios abaixo estão dentro da pasta k8s no repositório deste projeto.
Para um laboratório mais simples como o nosso, como prova de conceito, podemos utilizar o Minikube para simular um cluster de Kubernetes. Neste laboratório precisaremos customizar algumas coisas para evitar conflitos com caminhos absolutos dentro do pod do HackerWhale. Mas tenha fé que tudo vai dar certo! :-)
Instale o Minikube e o inicie.
minikube start
Utilizando o nosso repositório base de scripts, aplique os yamls prontos para os nossos tres serviços alvos.
kubectl apply -f postgresql.yaml
kubectl apply -f nginx.yaml
kubectl apply -f rabbitmq.yaml
Verifique se os pods estão em execução.
kubectl get deployments
kubectl get services
kubectl get pods
Agora será necessário realizar algumas customizações no kubeconfig. Obtenha o endereço do Minikube para que possamos gerar um arquivo kubeconfig especifico para o Pod no minikube para evitar conflitos dos endereçamentos.
minikube ip
192.168.49.2
Edite o arquivo minikube-kubeconfig
com o endereço obtido em seu ambiente. No ambiente testado ficou assim:
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority: /etc/kubernetes/certs/ca.crt
server: https://192.168.49.2:8443 # IP do Minikube ($ minikube ip)
name: minikube
contexts:
- context:
cluster: minikube
user: minikube
name: minikube
current-context: minikube
users:
- name: minikube
user:
client-certificate: /etc/kubernetes/certs/client.crt
client-key: /etc/kubernetes/certs/client.key
Agora, vamos criar o Secret para o nosso kubeconfig personalizado e para os certificados :
$ kubectl create secret generic minikube-kubeconfig-secret --from-file=config=./minikube-kubeconfig
$ kubectl create secret generic minikube-certificates \
--from-file=client.crt=$HOME/.minikube/profiles/minikube/client.crt \
--from-file=client.key=$HOME/.minikube/profiles/minikube/client.key \
--from-file=ca.crt=$HOME/.minikube/ca.crt
Agora podemos aplicar nosso hackerwhale-minikube.yaml para gerar o nosso pod.
kubectl apply -f hackerwhale-minikube.yaml
Liste novamente os pods para verificiar se todos estão disponíveis:
kubectl get pods
Comos todos disponíveis em nosso Minikube, podemos acessar o pod e verificar se podemos utilizar o kubectl internamente. Isso nos permitirá mais facilmente enumerar o ambiente interno e poder realizar qualquer assessment necessário.
Agora já podemos acessar o nosso pod com os secrets importados durante sua criação.
kubectl exec -it hackerwhale -- /bin/zsh
E a partir do pod, podemos listar os demais. \o/
kubectl get pods
Para este laboratório, foi criado um cluster de Kubernetes no Vultr.
Baixe as configurações do seu cluster e adicione à variável de ambiente $KUBECONFIG
. O nome do meu arquivo exportado foi vke-58626b70-6270-4106-b1a1-a1e7754f6131.yaml
.
export KUBECONFIG=$KUBECONFIG:~/.kube/config:~/.kube/vke-58626b70-6270-4106-b1a1-a1e7754f6131.yaml
Uma vez com as configurações importadas, podemos visualizar todos os contextos disponíveis:
kubectl config get-contexts
Saia do contexto do Minikube e inicie os trabalhos em nosso novo cluster criado.
kubectl config use-context vke-58626b70-6270-4106-b1a1-a1e7754f6131
Confirme a mudança e listando o contexto e os seus nodes.
$ kubectl config current-context
$ kubectl get nodes
Agora podemos subir os mesmos pods gerados em nosso lab no Minikube.
kubectl apply -f postgresql.yaml; \
kubectl apply -f nginx.yaml; \
kubectl apply -f rabbitmq.yaml
Verifique se os pods estão disponíveis.
kubectl get pods
Com o ambiente preparado para atender o YAML disponível para ambientes em K8s, podemos gerar o secret para ser importado para o nosso pod e poder interagir com o cluster de dentro do pod.
kubectl create secret generic k8s-kubeconfig-secret --from-file=config=/Users/tiago/.kube/vke-58626b70-6270-4106-b1a1-a1e7754f6131.yaml
Agora é só subir o pod.
kubectl apply -f hackerwhale-k8s.yaml
Para acompanhar o pull da imagem e sua configuração durante ou após sua aplicação, pode executar o seguinte comando.
kubectl describe pod hackerwhale-k8s
Uma vez com status de RUNNING
, podemos entrar no terminal do nosso container.
kubectl exec -it hackerwhale-k8s -- /bin/zsh
Agora é só testar e verificar se os certificados foram importados para interagir com o cluster.
$ kubectl get pods
$ kubectl get pods -o wide -n default
O mesmo pode ser feito com o K9s, disponível na imagem.
Uma vez com estes casos de uso demonstrados, podemos seguir com os labs de Discovery & Scan.
Utlizando como base o ambiente em Kubernetes, podemos combinar o uso de kubectl e k9s com as ferramentas tradicionais de testes para um assessment interno.
O jeito mais fácil dentro do K8s é obter o endereçamento dos pods, para isso vamos verificar em qual namespace estamos. Se retornar vazio, significa que estamos no “default”.
kubectl config view --minify --output 'jsonpath={..namespace}'
Para confirmar, podemos listar todos os namespaces.
$ kubectl get namespaces
NAME STATUS AGE
default Active 37m
kube-node-lease Active 37m
kube-public Active 37m
kube-system Active 37m
Agora é só obter os IPs dos pods, assim também saberá em qual range eles estão.
$ kubectl get pods -o wide -n default
# ou
$ kubectl get pods -o wide
O K9s também está disponível na imagem, podendo obter as mesmas informações de forma mais “amigável”.
Se você é roots, pode utilizar métodos mais tradicionais para a enumeração de ips em redel local, consultando a tabela arp ou fazendo “ping sweep”.
arp-scan --interface=eth0 --localnet
Com isso, pode iniciar os testes, como exemplo, realizando um nmap
nos pods do Nginx, Postgres e RabbitMQ.
nmap -p- -Pn 10.244.0.4-6
Para facilitar um pouco mais, sabendo que estamos no namespace “default”, copie e cole o código abaixo para guardar todos os IPs dos pods com exceção de nosso container em um arquivo, e reutilizá-lo para outras tarefas.
CURRENT_POD_NAME=$(hostname); \
kubectl get pods -o wide -n default| grep -v $CURRENT_POD_NAME | awk '{print $6}' > pod_ips.txt
Passando o arquivo para o nmap
.
nmap -iL pod_ips.txt
Ou fazendo um rápido teste com nuclei
para encontrar misconfigurations:
cat pod_ips.txt | nuclei --silent
👾