PHP反序列化漏洞学习
1、类与对象
<?php
class hero{          //定义一个类
  var $name;         //定义一个成员属性
  var $sex;
  function jineng($var1) {            //定义一个成员方法
    echo $this->name;                 //输出name
    echo $var1;
    }
}
$cyj= new hero();                    //创建一个对象
$cyj->name='chengyaojin';            //修改对象的属性为name
$cyj->sex='man';                     
$cyj->jineng('zuofan');              //调用方法并赋值
print_r($cyj);                       //打印整个对象的结构和内容活 var_dump();
?>
2、权限修饰符
public、protected、private
PHP 对属性或方法的访问控制,是通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现的。
public(公有):公有的类成员可以在任何地方被访问。
protected(受保护):受保护的类成员则可以被其自身以及其子类和父类访问。
private(私有):私有的类成员则只能被其定义所在的类访问。
<?php
class hero{
  public  $name='chengyaojin';
  private  $sex='man';
  protected  $shengao='165';
  function jineng($var1) {
    echo $this->name;
    echo $var1;
    }
}
$cyj= new hero();
echo $cyj->name."<br />";
echo $cyj->sex."<br />";
echo $cyj->shengao."<br />";
?>

只有public的成员属性能被访问。
3、什么是反序列化操作? - 类型转换
- PHP & JavaEE & .NET & Python
 序列化:对象转换为数组或字符串等格式
 反序列化:将数组或字符串等格式转换成对象
 serialize() //将对象转换成一个字符串
 unserialize() //将字符串还原成一个对象
有两种情况必须把对象序列化:
把一个对象在网络中传输
把对象写入文件或数据库
序列化的转换格式如下:
<?php
highlight_file(__FILE__);
class TEST {
    public $data;
    public $data2 = "dazzhuang";
    private $pass;
    public function __construct($data, $pass)
    {
        $this->data = $data;
        $this->pass = $pass;
    }
}
$number = 34;
$str = 'user';
$bool = true;
$null = NULL;
$arr = array('a' => 10, 'b' => 200);
$test = new TEST('uu', true);
$test2 = new TEST('uu', true);
$test2->data = &$test2->data2;
echo serialize($number)."<br />";
echo serialize($str)."<br />";
echo serialize($bool)."<br />";
echo serialize($null)."<br />";
echo serialize($arr)."<br />";
echo serialize($test)."<br />";
echo serialize($test2)."<br />";
?>

详细看下对象的系列化格式:
<?php
highlight_file(__FILE__);
class test{
    public $pub='benben';
    function jineng(){
        echo $this->pub;
    }
}
$a = new test();
echo serialize($a);
?>
O:4:"test":1:{s:3:"pub";s:6:"benben";}


PHP序列化需注意以下几点:
1、序列化只序列属性,不序列方法
2、因为序列化不序列方法,所以反序列化之后如果想正常使用这个对象的话我们必须要依托这个类要在当前作用域存在的条件
3、我们能控制的只有类的属性,攻击就是寻找合适能被控制的属性,利用作用域本身存在的方法,基于属性发动攻击
3.2、基于不同权限修饰符的序列化区别
public:属性被序列化的时候属性值会变成 属性名
protected:属性被序列化的时候属性值会变成 %00*%00属性名
private:属性被序列化的时候属性值会变成 %00类名%00属性名
如下例:
private私有:
<?php
highlight_file(__FILE__);
class test{
    private $pub='benben';
    function jineng(){
        echo $this->pub;
    }
}
$a = new test();
echo serialize($a);
?>
O:4:"test":1:{s:9:"testpub";s:6:"benben";}

protected受保护的:
<?php
highlight_file(__FILE__);
class test{
    protected $pub='benben';
    function jineng(){
        echo $this->pub;
    }
}
$a = new test();
echo serialize($a);
?>

4、魔术方法
__construct()            //类的构造函数,创建对象时触发
__destruct()             //类的析构函数,对象被销毁时触发
__call()                 //在对象上下文中调用不可访问的方法时触发
__callStatic()           //在静态上下文中调用不可访问的方法时触发
__get()                  //读取不可访问属性的值时,这里的不可访问包含私有属性或未定义
__set()                  //在给不可访问属性赋值时触发
__isset()                //当对不可访问属性调用 isset() 或 empty() 时触发
__unset()                //在不可访问的属性上使用unset()时触发
__invoke()               //当尝试以调用函数的方式调用一个对象时触发
__sleep()                //执行serialize()时,先会调用这个方法
__wakeup()               //执行unserialize()时,先会调用这个方法
__toString()             //当反序列化后的对象被输出在模板中的时候(转换成字符串的时候)自动调用
__construct()构造函数:
<?php
class User {
    public $username;
    public function __construct($username) {
        $this->username = $username;
        echo "触发了构造函数1次" ;
    }
}
$test = new User("benben");           //触发构造函数1次
$ser = serialize($test);
unserialize($ser);
?>
__destruct()析构函数:
<?php
class User {
    public function __destruct()
    {
        echo "触发了析构函数1次"."<br />" ;
    }
}
$test = new User("benben");          //触发了析构函数1次
$ser = serialize($test);
unserialize($ser);                   //触发了析构函数1次
?>
__sleep()
<?php
class User {
    const SITE = 'uusama';
    public $username;
    public $nickname;
    private $password;
    public function __construct($username, $nickname, $password)    {
        $this->username = $username;
        $this->nickname = $nickname;
        $this->password = $password;
    }
    public function __sleep() {
        return array('username', 'nickname');
    }
}
$user = new User('a', 'b', 'c');
echo serialize($user);                   //serialize()触发sleep函数一次
?>
__wakeup()与sleep相反
<?php
error_reporting(0);
class User {
    const SITE = 'uusama';
    public $username;
    public $nickname;
    private $password;
    private $order;
    public function __wakeup() {
        $this->password = $this->username;
    }
}
$user_ser = 'O:4:"User":2:{s:8:"username";s:1:"a";s:8:"nickname";s:1:"b";}';
var_dump(unserialize($user_ser));         //unserialize()触发wakeup函数一次
?>
__toString()把对象当作字符串输出触发,对象只能使用print_r和var_dump输出
<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
    var $benben = "this is test!!";
         public function __toString()
         {
             return '格式不对,输出不了!';
          }
}
$test = new User() ;
print_r($test);
echo "<br />";
echo $test;             //把对象当作字符串输出触发toString函数一次。
?>
__invoke() 将对象当作函数test()处理
<?php
class User {
    var $benben = "this is test!!";
         public function __invoke()
         {
             echo  '它不是个函数!';
          }
}
$test = new User() ;
echo $test ->benben;
echo "<br />";
echo $test() ->benben;             //将对象当作函数test() 处理触发invoke函数一次
?>
__call() 调用不存在的成员方法名称和参数
<?php
class User {
    public function __call($arg1,$arg2)
    {
        echo "$arg1,$arg2[0]";
          }
}
$test = new User() ;
$test -> callxxx('a');         //触发了call函数一次,callxxx方法不存在
?>
__callstatic() 调用不存在的静态成员方法名称和参数
<?php
class User {
    public function __callStatic($arg1,$arg2)
    {
        echo "$arg1,$arg2[0]";
          }
}
$test = new User() ;
$test::callxxx('a');          //触发了callstatic函数一次,静态方法不存在
?>
__get() 调用不存在的成员属性
<?php
class User {
    public $var1;
    public function __get($arg1)
    {
        echo  $arg1;
    }
}
$test = new User() ;
$test ->var2;             //var2不存在,函数get触发一次
?>
__set() 给不存在的属性赋值
<?php
class User {
    public $var1;
    public function __set($arg1 ,$arg2)
    {
        echo  $arg1.','.$arg2;
    }
}
$test = new User() ;
$test ->var2=1;           //var2不存在,触发set函数一次
?>
__isset() 检查不可访问的成员属性或者不存在的属性
<?php
class User {
    private $var;
    public function __isset($arg1 )
    {
        echo  $arg1;
    }
}
$test = new User() ;
isset($test->var);
?>
__unset() 删除不可访问的成员属性或者不存在的属性
<?php
class User {
    private $var;
    public function __isset($arg1 )
    {
        echo  $arg1;
    }
}
$test = new User() ;
unset($test->var);
?>
__clone() clone拷贝后会触发
<?php
class User {
    private $var;
    public function __clone( )
    {
        echo  "__clone test";
          }
}
$test = new User() ;
$newclass = clone($test)       //拷贝$test至$newclass,触发class()一次
?>
 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号