php反序列化 魔术方法,浅析PHP反序列化漏洞之PHP常见魔术方法(一)

作为一个学习web安全的菜鸟,前段时间被人问到PHP反序列化相关的问题,以前的博客中是有这样一篇反序列化漏洞的利用文章的。但是好久过去了,好多的东西已经记得不是很清楚。所以这里尽可能写一篇详细点的文章来做一下记录。

我们来参考这里:

https://secure.php.net/manual/zh/language.oop5.magic.php

我们根据官方文档中的解释,一个一个来进行测试。

__construct() 和 __destruct()

__construct()被称为构造方法,也就是在创造一个对象时候,首先会去执行的一个方法。

4e2e303be68949ffc1be7a829ce1e1ef.png

我写了这样的一个demo来做测试:

classtest {private $flag = '';public $filename = '';public $data = '';function __construct($filename, $data) {$this->filename = $filename;$this->data = $data;echo 'construct function in test class';echo "
";

}

}

$a = new test('test.txt', 'data');

测试结果:

0aa878e2e6f7e7e4639b53accd047b94.png

同样的,我们编写一个类的析构方法,__destruct()

析构函数的作用:

a0ef4e1736445b809318d336c33f6b19.png

代码如下:

classtest {private $flag = '';public $filename = '';public $data = '';function __construct($filename, $data) {$this->filename = $filename;$this->data = $data;echo 'construct function in test class';echo "
";

}function__destruct() {echo 'destruct function in test class';echo "
";

}

}$a = new test('test.txt', 'data');

运行结果:

d55c88aa8476137229105831f57eca44.png

__set()    __get()    __isset()    __unset()    作用如下:

80156a74ae65de6b2e1c79b0cc4668ce.png

我们一样是来写一个代码进行验证:

classtest {private $flag = '';#用于保存重载的数据

private $data = array();public $filename = '';public $content = '';function __construct($filename, $content) {$this->filename = $filename;$this->content = $content;echo 'construct function in test class';echo "
";

}function__destruct() {echo 'destruct function in test class';echo "
";

}function __set($key, $value) {echo 'set function in test class';echo "
";$this->data[$key] = $value;

}function __get($key) {echo 'get function in test class';echo "
";if (array_key_exists($key, $this->data)) {return $this->data[$key];

}else{return null;

}

}function __isset($key) {echo 'isset function in test class';echo "
";return isset($this->data[$key]);

}function __unset($key) {echo 'unset function in test class';echo "
";unset($this->data[$key]);

}public function set_flag($flag) {$this->flag = $flag;

}public functionget_flag() {return $this->flag;

}

}$a = new test('test.txt', 'data');#__set() 被调用

$a->var = 1;#__get() 被调用

echo $a->var;#__isset() 被调用

var_dump(isset($a->var));#__unset() 被调用

unset($a->var);var_dump(isset($a->var));echo "

";

运行结果:

d95b81e42b1d52e9a585754ab7807bbf.png

我们可以看到调用的顺序为: 构造方法 => set方法(我们此时为类中并没有定义过的一个类属性进行赋值触发了set方法) => get方法 => isset方法 => unset方法 => isset方法 => 析构方法

同时也可以发现,析构方法在所有的代码被执行结束之后进行的。

__call()    __callStatic()

官方文档中的解释:

39cc0866c8a82815a1892f40f209ec80.png

类似以上介绍过的__set()和__get(),刚刚是访问不存在或者不可访问属性时候进行的调用。现在是访问不存在或者不可访问的方法时候:

代码如下:

classtest {private $flag = '';#用于保存重载的数据

private $data = array();public $filename = '';public $content = '';function __call($funcname, $args) {echo 'function name is: ' . $funcname. ' args is: ' . implode(', ', $args);echo "
";

}public static function __callStatic($funcname, $args) {echo 'static function name is: ' . $funcname. ' args is: ' . implode(', ', $args);echo "
";

}public function set_flag($flag) {$this->flag = $flag;

}public functionget_flag() {return $this->flag;

}

}$obj = newtest;#调用一个不存在或者无法访问到的方法时候将会调用__call()

$obj->run('run args, test');#调用一个不存在的静态方法,将会去调用__callStatic()

$obj::run('static test');

运行结果:

e757930397dd248dc93b77033e9bb7d5.png

看文档或者注释应该很明白了。

接下来是对于反序列化漏洞利用最重要的一些方法了。

__sleep()    __wakeup()    __toString()

3eb38fcf781c436bd42831f8d542380c.png

写个代码来进行验证:

classtest {private $flag = '';#用于保存重载的数据

private $data = array();public $filename = '';public $content = '';function __construct($filename, $content) {$this->filename = $filename;$this->content = $content;echo 'construct function in test class';echo "
";

}function__destruct() {echo 'destruct function in test class';echo "
";

}#反序列化时候触发

function__wakeup() {//file_put_contents($this->filename, $this->data);

echo 'wakeup function in test class';echo "
";

}#一般情况用在序列化操作时候,用于保留数据

function__sleep() {echo 'sleep function in test class';echo "
";return array('flag', 'filename', 'data');

}#当需要输出得到对象名称时候会调用

function__toString() {return $this->data;

}public function set_flag($flag) {$this->flag = $flag;

}public functionget_flag() {return $this->flag;

}

}$key = serialize(new test('test.txt', 'test'));var_dump($key);$b = unserialize($key);

运行结果:

4e0b72230942c726f19b76d37a0e07f8.png

在进行序列化的时候,执行了__sleep()方法,在反序列化的时候执行了__wakeup()方法。

然后是__toString()方法:

classtest {private $flag = '';#用于保存重载的数据

private $data = array();public $filename = '';public $content = '';function __construct($filename, $content) {$this->filename = $filename;$this->content = $content;echo 'construct function in test class';echo "
";

}function__destruct() {echo 'destruct function in test class';echo "
";

}#当需要输出得到对象名称时候会调用

function__toString() {return $this->content;

}

}$a = new test('test.txt', 'data');echo $a."
";

结果:

1e627f6e4e176ec771ad4e475b9c7dd7.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值