PHP-Audit-Labs-day14

从变量覆盖到getshell

题目源码

//content.php
<?php
include './global.php';
extract($_REQUEST);

$sql = "select * from test.content where id=$message_id";
$arr = select($sql);


?>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Page Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="./css/body.css" rel='stylesheet' type='text/css' />
    <script src="main.js"></script>
</head>
<body>
    <center>
        <h1><?php echo $arr['0']['title']; ?></h1>
        <?php echo $arr['0']['content']; ?>
    </center>
    
</body>
</html>
//waf.php
<?php
    
foreach ($_GET as $key => $value) {
    if ($key != "username"&&strstr($key, "password") == false) {
        $_GET[$key] = filtering($value);
    }
}
foreach ($_POST as $key => $value) {
    if ($key != "username"&&strstr($key, "password") == false) {
        $_POST[$key] = filtering($value);
    }
}
foreach ($_REQUEST as $key => $value) {
    if ($key != "username"&&strstr($key, "password") == false) {
        $_REQUEST[$key] =   ($value);
    }
}

$request_uri = explode("?", $_SERVER['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]));
        }
    }
}

foreach ($_POST as $key => $value) {
    $_POST[$key] = safe_str($value);
    $_GET[$key] = dhtmlspecialchars($value);
}
foreach ($_COOKIE as $key => $value) {
    $_COOKIE[$key] = safe_str($value);
    $_GET[$key] = dhtmlspecialchars($value);
}
    
?>
//function.php
<?php
include './config.php'; 
function select($sql){
    $re = mysql_query($sql);
    $arr = array();
    while ($row = mysql_fetch_array($re)) {
        $arr[] = $row;
    }
    return $arr;
}
function filtering($str) {
    $check= eregi('select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile', $str);
    if($check)
        {
        echo "非法字符!";
        exit();
    }

    $newstr="";
    while($newstr!=$str){
    $newstr=$str;
    $str = str_replace("script", "", $str);
    $str = str_replace("execute", "", $str);
    $str = str_replace("update", "", $str);
    $str = str_replace("master", "", $str);
    $str = str_replace("truncate", "", $str);
    $str = str_replace("declare", "", $str);
    $str = str_replace("select", "", $str);
    $str = str_replace("create", "", $str);
    $str = str_replace("delete", "", $str);
    $str = str_replace("insert", "", $str);
    $str = str_replace("\'", "", $str);

    }
    return $str;
}
function safe_str($str){
    if(!get_magic_quotes_gpc()) {
        if( is_array($str) ) {
            foreach($str as $key => $value) {
                $str[$key] = safe_str($value);
            }
        }else{
            $str = addslashes($str);
        }
    }
    return $str;
}

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;
}

?>

源码分析:

在content.php里,$message_id被直接拼接进sql语句,不过之前会过waf.php,在waf.php里,接受GET,POST,COOKIE三种传值方式,并且对各类变量的过滤方式不完全相同,COOKIE方式传值,只用了safe_str和dhtmlspecialchars两种方式过滤,而我们的sql注入语句并不会受到影响,所以这里可以直接注入

利用方法:

1.ereg函数的截断漏洞:

ereg()函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。ereg函数如果遇到%00则默认为字符串的结束,该函数在php5.3以上版本已经被弃用

2._REQUEST特性:

超全局数组 $_REQUEST 中的数据,是 $_GET 、 $_POST 、 $_COOKIE 的合集,而且数据是复制过去的,并不是引用。所以对 $_GET 、 $_POST 处理并不会影响 $_REQUEST 中的数据

payload:

message_id=0 union select 1,2,flag,4 from flag使用cookie传递参数

image-20201216221843326

还有一种解法,用的就是上面两个利用方法

可以通过 GETPOSTcontent.php 文件传递如下 payload 获取flag:

message_id=-1/*%00*/union/**/select/**/1,flag,3,4/**/from/**/flag

如果是 GET 方式传递数据的话,数据会经过 filtering 函数过滤,而在 filtering 函数中,开头的 eregi 检测,我们又可以使用 %00 截断绕过,但是下方还有循环替换恶意字符的代码,这里无法绕过。 filtering 函数代码如下:

img

也就是说我们的 payload 经过 filtering 函数处理后变成了下面这样( select 被过滤掉了):

-1/*%00*/union/**//**/1,flag,3,4/**/from/**/flag

当我们继续看代码时,会发现下面的代码又把 message_id 变量的值还原了。因为 content.php 文件中有代码: extract($_REQUEST) ,所以这里也就造成了注入。

img

那如果是 POST 方式传送数据,会先经过 filtering 函数处理,然后经过 safe_str 函数。 safe_str 函数主要用了 addslashes 函数过滤数据,可以发现对我们的 payload 并没有影响。 safe_str 函数代码如下:

img

这里能进行注入的原因,主要是因为超全局数组 $_REQUEST 中的数据,是 $_GET$_POST$_COOKIE 的合集,而且数据是复制过去的,并不是引用。所以对 $_GET$_POST 处理并不会影响 $_REQUEST 中的数据。

img

——来自七月火师傅

拓展内容:

[红日安全]代码审计Day14 - 从变量覆盖到getshell:https://xz.aliyun.com/t/2911

posted @ 2020-12-19 14:16  N0ri  阅读(83)  评论(0编辑  收藏  举报