HackerWhale - Offensive Docker Image
Last modified on September 6, 2026 • 12 min read • 2,353 words
HackerWhale
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:latestKubernetes
kubectl apply -f hackerwhale-k8s.yamlPara casos de uso e detalhes, basta seguir neste humilde artigo. :)

Motivação
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:
-
Container Docker do Kali Linux
: O proxy de rede bloqueou o repositório do Kali (A atualização por
apt update && apt -y install kali-linux-headlessnão era possível); - Hacker Container : É uma opção interessante, mas tudo está centralizado no Dockerfile, algo que, na minha humilde opinião, torna a manutenção deste projeto mais confuso e difícil de manter.
Então, aqui trago duas opções após essa jornada de testes:
- Uma versão completa e expandida disponível em Docker Hub .
- Possibilidade de criar sua própria imagem personalizada.
Características
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:
- Imagem disponível no registry Dockerhub para facilitar o seu uso.
- Imagem em multiplataforma e script de expansão preparados para construção em amd64 e arm64.
- Customização de armazenamento de histórico para uso contínuo em
/workdir/.zsh_history_docker, podendo ser mapeado e mantido no host, podemos ser reutilizado em outras imagens criadas. - Shell com boa experiência para o usuário com ZSH (Ohmyzsh) e TMUX (Ohmytmux).
- Dockerfile pronto para expansão através de argumentos de uso de scripts em bash local ou a partir de uma URL.
- Dezenas de ferramentas disponíveis.
Lista de Ferramentas
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 |
Instalação e execução
Contrução do container no modo “Baleião” pronto para uso
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:latestExecute o container.
docker run -d -v ${PWD}:/workdir --name hackerwhale hackerwhaleAcesse o terminal do container.
docker exec -it hackerwhale /bin/zsh Construção do container utilizando a função de expansão
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 .Execução e acesso ao container
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/workdirdo 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 hackerwhaleExecução do container na mesma rede do host.
docker run -d --net=host -v ${PWD}:/workdir --name hackerwhale hackerwhaleExecução do container expondo portas de serviço (Ex.: netcat).
docker run -d -v ${PWD}:/workdir -p 4444:4444 --name hackerwhale hackerwhaleUma 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 Casos de uso
Proxy externo
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 ZAProxy
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.crtDepois é 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.crtApó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_proxyBurpSuite
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.crtRealize 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_proxyConexões Reversas
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 hackerwhaleAcesse 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.

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:
- 0xtiago/hackerwhale
- postgres:latest
- nginx:latest
- rabbitmq:latest
Todos os arquivos necessários para os laboratórios abaixo estão dentro da pasta k8s no repositório deste projeto.
Minikube
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.2Edite 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.keyAgora, 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.crtAgora 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/zshE a partir do pod, podemos listar os demais. \o/
kubectl get pods
Kubernetes
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.yamlUma 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-a1e7754f6131Confirme 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.yamlVerifique 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.yamlAgora é só subir o pod.
kubectl apply -f hackerwhale-k8s.yamlPara 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/zshAgora é 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.
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 37mAgora é 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.txtPassando o arquivo para o nmap.
nmap -iL pod_ips.txtOu fazendo um rápido teste com nuclei para encontrar misconfigurations:
cat pod_ips.txt | nuclei --silent
👾