PHP中对象注入的深入讲解
序
虽然这篇文章被称为PHP对象注入,但它本质上与PHP序列化的不当使用有关。如果您阅读了PHP中的SESSION反序列化机制,您将对序列化有一个大致的了解。实际上,PHP对象注入本质上是由序列化引起的。
基础知识
php类中可能有一些叫做magic functions的神奇函数,当类中发生某些事件时会自动触发。例如,创建对象时调用__construct(),销毁对象时调用_ _ destroy(),将对象视为字符串时调用__toString。常见的魔法函数有__construct()、_ _ destroy()、__toString()、__sleep()、_ _唤醒()。
例子如下:
?phpclass测试{ public $ varr1=' abcpublic $ varr2=' 123公共函数echoP(){ echo $this-varr1。br ';} public function _ _ construct(){ echo ' _ _ construct br ';} public function _ _ destrust(){ echo ' _ _ destrust br ';} public function _ _ toString(){ return ' _ _ toString br ';} public function _ _ sleep(){ echo ' _ _ sleep br ';返回数组(' varr1 ',' var R2 ');} public function _ _ wake up(){ echo ' _ _ wake up br ';} } $ obj=new test();//实例化对象,调用__construct()方法,输出_ _ construct $ obj-echoP();//调用echoP()方法并输出‘ABC’EcHo $ obj;//obj对象输出为字符串,调用__toString()方法,输出_ _ toString $ s=serialize($ obj);//obj对象被序列化,调用__sleep()方法并输出_ _ sleep echo unserialize($ s);//$s将首先被反序列化,并将调用__wake()方法。如果反序列化的对象被视为字符串,将调用_toString()方法。//脚本结束后会调用_ _ destroy()方法,输出_ _ destroy?原则
为什么要用顺序词?主要是为了方便数据传输,数据恢复后,数据的属性不会改变。例如,在反序列化一个对象之后,该对象的所有信息仍然被保存。同时,序列化的值可以保存在文件中,这样就可以直接从文件中读取数据,然后在需要时反序列化。序列化()和非序列化()在PHP中用于序列化和反序列化。
序列化的危害在于,如果序列化的内容是用户可控的,那么用户可以注入精心构造的有效载荷。序列化时,有可能在对象中启动一些神奇的方法,造成意想不到的伤害。
对象注入
本质上serialize()和serialize()在PHP内部实现上没有漏洞,漏洞主要是应用程序对对象、魔法函数的处理以及序列化相关问题造成的。
如果一个类用于将日志临时存储到程序中的一个文件中,当调用_ _ _ destruct()方法时,日志文件将被删除。
代码大致如下:
logfile.php
?phpclass LogClass { public $ Logfilename=' ';公共函数log data($ text){ echo ' log data }。$文本。br/';FILE _ put _ contents($ this-log filename,$text,FILE _ apped);} public function _ _ destrust(){ echo ' deletes }。$ this-log filename;取消链接(dirname(__FILE__)。'/'.$ this-log filename);}}?在其他类中使用日志类
logLogin.php
?phpinclude ' index.php$ obj=new LogClass();$ obj-log filename=' log in . log ';$obj-logdata('日志');上面的代码是使用LogClass类进行日志记录的正常功能。
下面是一个带有对象注入漏洞的使用示例。
news.php
?phpinclude ' logfile.php//某些代码使用LogClassClass User { public $ age=0;public $ name=公共函数print_data() { echo 'User '。$这个名字。是。$这个年龄。年old.br/'; } }//从用户接受输入序列化为用户对象$usr=未序列化($ _ GET[' User ']);上面显示的代码使用了日志类对象,并且还接受来自用户的输入,用于序列化和转换为用户对象。
当我们提交以下数据时,
news.php?用户=O:4: '用户' :2:{s:3: '年龄';i:20s:4:“名称”;s:4:约翰’;}这种语句可以正常使用,也是程序员想用的方法。
但是,如果提交的数据是:
news.php?user=o :8: ' LogClass ' :1: { s :11: ' log filename ';s 33609:’。htaccess ';}然后删除。最终将输出htaccess。
可以看到,由构造的数据导致LogClass中的_ _ _ destruct()方法被执行,然后网站中的重要配置文件被删除。
从上面的例子可以看出,如果不严格控制用户的输入,同时反序列化用户的输入,可能会实现代码执行的漏洞。
倾析点
PHP对象注入一般在程序逻辑之上。例如,一个User类定义了__toString()用于打印格式,但是还有一个file类定义了__toString()方法来读取File内容然后显示,因此攻击者有可能通过反序列化User类来构造一个File类来读取网站的配置文件。
user.php
?phpclass FileClass { public $ filename=' error . log ';公共函数_ _ tostring(){ echo ' filename changed=='。$ this-文件名;返回@ file _ get _ contents($ this-filename);} } class UserClass { public $ age=0;public $ name=公共函数__toString() {返回“User”。$这个名字。是。$这个年龄。岁。br/';}}$obj=未序列化($ _ GET[' usr ']);回声$ obj//调用obj的__toString()方法?在正常情况下,我们应该传入一个由UserClass序列化的字符串,比如user.php?usr=O:9: ' UserClass ' :2: { s :3: ' age ';i:18s:4:“名称”;s:3:《汤姆》;},页面会输出用户Tom已经18岁了。
这也是一种理想的使用方法。
但是如果我们传入的数据是user.php呢?usr=O:9: ' File Class ' :1: { s :8: ' filename ';s 336010:“config . PHP”;},页面的最终输出是文件名已经更改==config.php,并且已经执行了FileClass中的__toString()方法。
这样,你就可以在config.php阅读源代码。
漏洞挖掘
这种洞一般很难挖。虽然显示看起来简单,但要求的条件实际上相当苛刻。而且,查找对象注入的漏洞一般是通过审计源代码来发现,看unserialize()的参数是否可控,是否有反序列化其他参数对象的可能。
保护
测试程序中的各种边界条件
避免用户对unserialize()参数可控,考虑使用json_decode方法传递参数。
摘要
以上就是本文的全部内容。希望本文的内容能给你的学习或工作带来一些帮助。有问题可以留言交流。谢谢你的支持。
版权声明:PHP中对象注入的深入讲解是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。