UEditor 1.4.3.3的SSRF漏洞

  • Post author:
  • Post category:其他



产生漏洞的代码片段:


ueditor\php\Uploader.class.php

private function saveRemote()
{
    $imgUrl = htmlspecialchars($this->fileField);
    $imgUrl = str_replace("&", "&", $imgUrl);

    //http开头验证 如果不是的话 就return
    if (strpos($imgUrl, "http") !== 0) {
        $this->stateInfo = $this->getStateInfo("ERROR_HTTP_LINK");
        return;
    }

    preg_match('/(^https*:\/\/[^:\/]+)/', $imgUrl, $matches);
    $host_with_protocol = count($matches) > 1 ? $matches[1] : '';

    // 判断是否是合法 url
    if (!filter_var($host_with_protocol, FILTER_VALIDATE_URL)) {
        $this->stateInfo = $this->getStateInfo("INVALID_URL");
        return;
    }

    preg_match('/^https*:\/\/(.+)/', $host_with_protocol, $matches);
    $host_without_protocol = count($matches) > 1 ? $matches[1] : '';

    // 此时提取出来的可能是 ip 也有可能是域名,先获取 ip
    $ip = gethostbyname($host_without_protocol);
    // 判断是否是私有 ip
    if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) {
        $this->stateInfo = $this->getStateInfo("INVALID_IP");
echo "<br>判断是否是私有 ip<br>";
        return;
    }
    //获取请求头并检测死链
    $heads = get_headers($imgUrl, 1);
    if (!(stristr($heads[0], "200") && stristr($heads[0], "OK"))) {
        $this->stateInfo = $this->getStateInfo("ERROR_DEAD_LINK");
        return;
    }
    //格式验证(扩展名验证和Content-Type验证)
    $fileType = strtolower(strrchr($imgUrl, '.'));
    if (!in_array($fileType, $this->config['allowFiles']) || !isset($heads['Content-Type']) || !stristr($heads['Content-Type'], "image")) {
        $this->stateInfo = $this->getStateInfo("ERROR_HTTP_CONTENTTYPE");
        return;
    }

    //打开输出缓冲区并获取远程图片
	ob_start();
	$context = stream_context_create(
	    array('http' => array(
	        'follow_location' => false // don't follow redirects
	    ))
	);
	readfile($imgUrl, false, $context);
	$img = ob_get_contents();
	ob_end_clean();
    ...省略...


检测流程:


1、

http

开头验证

2、判断是否是合法

url


3、判断是否是私有

ip


4、获取请求头并检测死链

5、格式验证(扩展名验证和

Content-Type

验证)


PHP FILTER_VALIDATE_IP 过滤器:

FILTER_FLAG_IPV4 - 要求值是合法的 IPv4 IP(比如 255.255.255.255FILTER_FLAG_IPV6 - 要求值是合法的 IPv6 IP(比如	2001:0db8:85a3:08d3:1319:8a2e:0370:7334FILTER_FLAG_NO_PRIV_RANGE - 要求值是 RFC 指定的私域 IP (比如 192.168.0.1FILTER_FLAG_NO_RES_RANGE - 要求值不在保留的 IP 范围内。该标志接受 IPV4IPV6 值。


两种方法可以绕过:



1、正则绕过

在第五步检测之前 唯一起到限制的就是

preg_match('/(^https*:\/\/[^:\/]+)/', $imgUrl, $matches);

,那么就说明我们只要绕过这条正则表达就可以了,那么其实就是一个正常的URL。


2、利用DNS重绑定

参考文章:

http://admintony.com/UEditor-1-4-3-3-SSRF-AND-DNS-rebinding-attack.html

http://www.bendawang.site/2017/05/31/%E5%85%B3%E4%BA%8EDNS-rebinding%E7%9A%84%E6%80%BB%E7%BB%93/