您的位置:首页 > 编程语言 > PHP开发

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

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

我们来参考这里:

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


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

__construct() 和 __destruct()

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



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

class test {

private $flag = '';
public $filename = '';
public $data = '';

function __construct($filename, $data) {
$this->filename = $filename;
$this->data = $data;
echo 'construct function in test class';
echo "<br>";
}
}
$a = new test('test.txt', 'data');


测试结果:



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

析构函数的作用:



代码如下:

class test {

private $flag = '';
public $filename = '';
public $data = '';

function __construct($filename, $data) {
$this->filename = $filename;
$this->data = $data;
echo 'construct function in test class';
echo "<br>";
}

function __destruct() {
echo 'destruct function in test class';
echo "<br>";
}
}

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


运行结果:



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



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

class test {

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 "<br>";
}

function __destruct() {
echo 'destruct function in test class';
echo "<br>";
}

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

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

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

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

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

public function get_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 "\n";


运行结果:



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

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

__call() __callStatic()

官方文档中的解释:



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

代码如下:

class test {

private $flag = '';

# 用于保存重载的数据
private $data = array();

public $filename = '';

public $content = '';

function __call($funcname, $args) {
echo 'function name is: ' . $funcname. ' args is: ' . implode(', ', $args);
echo "<br>";
}

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

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

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

$obj = new test;

# 调用一个不存在或者无法访问到的方法时候将会调用__call()
$obj->run('run args, test');

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


运行结果:



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

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

__sleep() __wakeup() __toString()



写个代码来进行验证:

class test {

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 "<br>";
}

function __destruct() {
echo 'destruct function in test class';
echo "<br>";
}

# 反序列化时候触发
function __wakeup() {
// file_put_contents($this->filename, $this->data);
echo 'wakeup function in test class';
echo "<br>";
}

# 一般情况用在序列化操作时候,用于保留数据
function __sleep() {
echo 'sleep function in test class';
echo "<br>";
return array('flag', 'filename', 'data');
}

# 当需要输出得到对象名称时候会调用
function __toString() {
return $this->data;
}

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

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

$key = serialize(new test('test.txt', 'test'));

var_dump($key);

$b = unserialize($key);


运行结果:



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

然后是__toString()方法:

class test {

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 "<br>";
}

function __destruct() {
echo 'destruct function in test class';
echo "<br>";
}

# 当需要输出得到对象名称时候会调用
function __toString() {
return $this->content;
}
}

$a = new test('test.txt', 'data');
echo $a."<br>";


结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: