专栏名称: 狗厂
目录
相关文章推荐
51好读  ›  专栏  ›  狗厂

request导致的安全性问题分析

狗厂  · 掘金  ·  · 2018-05-09 10:06

正文

说明

最近在看P神之前分析的漏洞的文章,本篇文章就是记录看了P神的 贷齐乐系统最新版SQL注入 之后的一点体会和收获。

本篇文章主要是要说明的是在PHP中由于对于 $_REQUEST 的认识不足或者是使用不当而导致的漏洞。目前漏洞类型主要是有两种,对 $_REQUEST 认识不足从而导致过滤失效,使用不当则指的存在HPP的漏洞从而导致全局WAF失效,这个漏洞也就是在P神文章中所说明的漏洞。

$_REQUEST使用不当绕过WAF

php手册上面对于 $_REQUEST 的说法是:

由于 $_REQUEST 中的变量通过 GET,POST 和 COOKIE 输入机制传递给脚本文件,因此可以被远程用户篡改而并不可信

这话是什么意思呢?表示的是 $_REQUEST 是直接从GET,POST 和 COOKIE中取值,不是他们的引用。即使后续 GET,POST 和 COOKIE 发生了变化,也不会影响 $_REQUEST 的结果。如下:

foreach ($_REQUEST as $key=>$value) {
    $_REQUEST[$key] = md5($value);
}
var_dump($_REQUEST);
var_dump($_GET);

我们访问 http://localhost/test/index.php?id=1&uname=spoock&[email protected] 的结果如下:

可以看到 $_REQUEST 的结果发生了改变,但是 $_GET 的结果并没有改变。

漏洞代码

假设如果存在如下的代码:

foreach ($_REQUEST as $key=>$value) {
    $_REQUEST[$key] = waf($value);
}
if(isset($_POST['submit'])) {
    $id = $_POST['id'];
    $sql = "select * from user where id=$id";
    mysql_query($sql);
    //....
}

虽然使用了 waf 进行过滤,但是waf过滤的是 $_REQUEST ,在业务代码中使用的是 $_POST 。这样就导致前面的WAF过滤没有任何的作用,防护完全失效。

这个特性在ripstech中的2017年的题目中就出现过。这种问题我相信一定会存在。

REQUEST导致的HPP漏洞

PHP中的特性

在说明这个漏洞之前,我们先需要了解一下在PHP中的几个小的特性。

  1. php自身在解析请求的时候,如果参数名字中包含空格、 . [ 这几个字符,会将他们转换成 _ 。测试如下:

  2. php在遇到相同参数时接受的是第二个参数。

  3. 通过 $_SERVER['REQUEST_URI'] 方式获得的参数并不会进行转换。如:

基于以上三种PHP的特性,就可能会导致存在HPP漏洞,从而绕过系统防护。

漏洞代码

<?php
function dhtmlspecialchars($string) {
    if (is_array($string)) {
        foreach ($string as $key => $val) {
            $string[$key] = dhtmlspecialchars($val);
        }
    }
    else {
        $string = str_replace(array('&', '"', '<', '>', '(', ')'), array('&', '"', '<', '>', '(', ')'), $string);
        if (strpos($string, '&#') !== false) {
            $string = preg_replace('/&((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string);
        }
    }
    return $string;
}
function dowith_sql($str) {
    $check = preg_match('/select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/is', $str);
    if ($check) {
        echo "非法字符!";
        exit();
    }
    return $str;
}
// 经过第一个waf处理
foreach ($_REQUEST as $key => $value) {
    $_REQUEST[$key] = dowith_sql($value);
}
// 经过第二个WAF处理
$request_uri = explode("?", $_SERVER['REQUEST_URI']);
var_dump($request_uri);
if (isset($request_uri[1])) {
    $rewrite_url = explode("&", $request_uri[1]);
    foreach ($rewrite_url as $key => $value) {
        $_value = explode("=", $value);
        if (isset($_value[1])) {
            $_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));
        }
    }
}
// 业务处理
if (isset($_REQUEST['submit'])) {
    $user_id = $_REQUEST['user_id'];
    $sql = "select * from users where id=$user_id";
    var_dump($sql);
}
  1. 第一个WAF,采用了 dowith_sql() 函数,如果 $_REQUEST 存在 select|insert|update|delete 等敏感关键字或者是字符,则直接exit()。如果不存在,则原字符串返回。
  2. 第二个WAF,通过 $_SERVER['REQUEST_URI'] 得到请求参数,之后利用 explode("&", $request_uri[1]) 得到每个参数,包括参数名和参数值。对每个参数值采用 dhtmlspecialchars() 过滤,对字符 & " < > ( ) 都进行了替换。替换完毕之后重新得到 _REQUEST
  3. 在最后的业务处理中,通过 $_REQUEST 获取参数进行处理。

如果能够利用HPP漏洞的原理,在WAF对参数进行过滤时处理的是一个参数,但是在进入到业务中处理的是第二个参数,那么我们就能够绕过WAF了。用一个简单的例子进行说明:







请到「今天看啥」查看全文