欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

变量覆盖和正则表达式/e漏洞

程序员文章站 2022-05-15 09:18:40
...

变量覆盖和正则表达式/e的漏洞

变量覆盖:extract(),parse_str()和import_request_variables()
第一种:extract()函数
精简代码如下:

 <?php
include("secret.php");
?>
                    <?php if ($_SERVER["REQUEST_METHOD"] == "POST") { ?>
                        <?php
                        extract($_POST);
                        if ($pass == $thepassword_123) { ?>
                            <div class="alert alert-success">
                                <code><?php echo $theflag; ?></code>
                            </div>
                        <?php } ?>
                    <?php } ?>

如代码所知,我们的目的是需要pass=thepassword_123,但是我们并不知道thepassword_123,但却有一个extract,这个函数是从数组中将变量导入到当前的符号表,因此我们可以利用变量覆盖
传入pass和thepassword_123,
payload:pass=3&thepassword_123=3

第二种:
精简代码如下:

<?php

$a='aaa';
$aaa='xxx';
echo $$a; //$$a=$($a)=$(aaa)='xxx'

?>

输出的是xxx

第三种:全局变量覆盖
当register_global=ON时,

<?php
echo "Register_globals: ".(int)ini_get("register_globals")."<br/>";

if ($auth){
   echo "private!";
}
?>

此时如果提交url?auth=1,那么会打印出:
Register_globals:1
private!
注意:如果auth的初始值是0,那么即便url?auth=1也不会打印出private
由于$GLOBALS获取的变量,可能导致变量覆盖

<?php
echo "Register_globals:".(int)ini_get("register_globals")."<br/>";
if (ini_get('register_globals')) foreach($_REQUEST as $k=>$v) unset(${$k});
print $a;
print $_GET[b];
?>

由于变量a并未初始化,所以在register_globals=ON时,传入url?a=1&b=2,会因这段代码出错,
但是如果传入url?GLOBALS[a]=1&b=2则可以成功输出变量a的值,这是因为unset默认只会销毁局部变量,

要销毁全局变量必须使用GLOBAL

第四种:遍历初始化变量
常见的一些以遍历的方式释放变量的代码可能会导致变量覆盖

<?  
    $chs = '';  
    if($_POST && $charset != 'utf-8'){  
        $chs = new Chinese('UTF-8', $charset);  
        foreach($_POST as $key => $value){  
            $$key = $chs->Convert($value);  
        }  
        unset($chs);  
    }  
    ?>

若提交参数的值是chs,则可覆盖变量chs的值

第五种:import_request_variables变量覆盖

<?php  
    $auth = '0';  
    import_request_variables('G');  
      
    if($auth == 1){  
      echo "private!";  
    }else{  
      echo "public!";  
    }  
    ?>

当传入url?auth=1时,网页上会输出private
而import_request_variables(‘G’); 函数是指从导入GET请求的变量,如果禁用了register_globals,但是又想用一些全局变量可以考虑这个函数

第六种:
parse_str()变量覆盖

//var.php?var=new
var=init;parsestr(var='init'; parse_str(_SERVER[‘QUERY_STRING’]);
print $var;

跟之前的extract很像,不过这个函数是将字符串解析为多个变量,类似的函数还有mb_parse_str()

经典问题:正则表达式,/e漏洞,原文章:https://xz.aliyun.com/t/2557
首先,先了解一下:

1、/g 表示该表达式将用来在输入字符串中查找所有可能的匹配,返回的结果可以是多个。如果不加/g最多只会匹配一个
2、/i 表示匹配的时候不区分大小写,这个跟其它语言的正则用法相同
3、/m 表示多行匹配。什么是多行匹配呢?就是匹配换行符两端的潜在匹配。影响正则中的^$符号
4、/s 与/m相对,单行模式匹配。
5、/e 可执行模式,此为PHP专有参数,例如preg_replace函数。
6、/x 忽略空白模式。

此时,/e为可执行模式,就可导致代码执行,案例如下:

变量覆盖和正则表达式/e漏洞

第一个和第三个参数都可控,第二个参数被固定为:strtolower("\1"),正则表达式相当于eval(‘strtolower("\1");’),当中的\1实际上就是\1,\为转义,而\1在正则表达式中有自己的含义,

反向引用:
对一个正则表达式模式或部分模式两边添加圆括号,将导致相关匹配存储到一个临时缓冲区,每个子匹配都按照正则表达式从左到右出现的顺序存储。

而这里的\1实际上指定的是第一个子匹配项,官方payload为:/?.*={KaTeX parse error: Expected 'EOF', got '}' at position 12: {phpinfo()}}̲ 即get方式传入的参数名为/…{phpinfo()}}

那么原先的语句:preg_replace(’/(’ . $regex . ‘)/ei’, ‘strtolower("\1")’, KaTeX parse error: Expected '}', got 'EOF' at end of input: …ower("\\1")', {{phpinfo()}});

然而,如果直接将以上payload写入代码里面,是可以执行phpinfo(),但是

.*是通过get方式传入,通过url之后,.*会变成_*,
非法字符不为首字母的情况下,php会将 (空格),+,.,[,_替换成下划线

因此payload: \S*=${phpinfo()} (注意前面有个空格)

对于为什么要匹配{${phpinfo()}} 或者 ${phpinfo()} 才能执行phpinfo函数,实际上是因为php可变变量(关于可变变量的事情我好像咕咕咕了很久)

在PHP中双引号包裹的字符串中可以解析变量,而单引号则不行。

${phpinfo()} 中的 phpinfo() 会被当做变量先执行,执行后,即变成 ${1} (phpinfo()成功执行返回true)。

var_dump(phpinfo()); // 结果:布尔 true
var_dump(strtolower(phpinfo()));// 结果:字符串 '1'
var_dump(preg_replace('/(.*)/ie','1','{${phpinfo()}}'));// 结果:字符串'11'

var_dump(preg_replace('/(.*)/ie','strtolower("\\1")','{${phpinfo()}}'));// 结果:空字符串''
var_dump(preg_replace('/(.*)/ie','strtolower("{${phpinfo()}}")','{${phpinfo()}}'));// 结果:空字符串''
这里的'strtolower("{${phpinfo()}}")'执行后相当于 strtolower("{${1}}") 又相当于 strtolower("{null}") 又相当于 '' 空字符串

总之:

反正遇见preg_replace(’/(’ . re.)/ei,strtolower("1"),re . ')/ei','strtolower("\\1")',str);这样的代码可以考虑payload: \S*=${phpinfo()}’