php反序列化漏洞
O:6:"Animal":1:{s:4:"name";N;}
O
有一个对象:
下一句6
名字是6个字符:
下一句- "Animal"内容是Animal
:
下一句1
对象有一个属性:
下一句{
对象属性描述开始s
属性是一个字符串 string:
下一句4
属性名字是4个字符:
下一句"name"
属性名字是name,
属性名字描述完成N
没有值,值为NULL;
属性值描述完了}
对象属性描述完了
结论
反序列化和类的方法无关,不能把类方法序列化。
反序列化流程
- 找到反序列化字符串规定的类名字
- 实例化这个类,但是不是调用构造方法
- 有了实例化的类对象,对它的属性进行赋值
- 执行魔术方法
- 返回构造好的对象
问题
接口
是否可以序列化?serialize
方法序列化传一个类的对象,类的实力,并返回反序列化的字符串,如果要序列化一个接口,那么就要有这个接口的实例化对象,而因为接口不能new
,所以就没有实例化对象,所以没法给serialize
方法作为参数,就没有返回的序列化字符串。
接口不能直接序列化匿名类
是否可以序列化?缺少名字,无法序列化
trait
是否可以序列化?因为不能
new
,所以就没有实例化对象,所以没法给serialize
方法作为参数,就没有返回的序列化字符串。
魔术方法
总结
- 魔术方法是一类类的方法特殊
- 会在序列化和反序列化及其他情况下,自动执行
分类
1. __construct
- 在实例化一个对象(new)时,会被自动调用
- 不允许重复声明
- 可以作为非public权限属性的初始化
2. __sleep
和__wakeup
方法
- 序列化时自动调用
__sleep
方法 - 反序列化之后
__wakeup
被调用
3. __destruct
析构方法
类对象将要销毁,也就是脚本执行完毕后执行清理工作时自动执行
添加杀死进程能够system('taskkill /fi "imagename eq php.exe" /f');
绕过system('taskkill /fi "imagename eq php-cgi.exe" /f');
4. __call
和__callstatic
对象执行执行类不存在的方法的时候,会自动调用__call
方法
后者执行执行类的不存在的静态方法时,会自动调用__callstatic
方法
5. __get
__set
和__isset
__unset
魔术方法
__get
对不可访问属性或不存在属性 进行访问引用时自动调用
__set
对不可访问属性或不存在属性 进行写入时自动调用
6. __tostring
方法
类的实例 和字符串进行拼接或者作为字符串引用时,会自动调用__tostring方法
7. __invoke
方法
当类的实例被作为函数名字执行的时候,会自动调用__invoke
方法
8. __set_state
方法
文档中说 执行 var_export
时自动调用
9. __debugInfo
方法的属性修饰符
执行var_dump时自动调用
10. __clone
方法
当使用clone关键字 ,clone一个对象时,会自动调用
php的反序列化漏洞
条件
- 有反序列化提交的入口
- 被反序列化的类的魔术方法,有可能被利用
绕过方法
1. 绕过__wakeup
方法
- php5至php5.6.25 之间的版本可以绕过
- php7到php7.0.10 直接的版本可以绕过
绕过方法:
- 反序列化字符串中表示属性数量的值 大于 大括号内实际属性的数量时 ,
wakeup
方法会被绕过
2. 绕过 +号
正则匹配
参数有过滤,不让输入 O:数字
的形式,试图防止反序列化某个对象
O:数字
改为 O:+数字
就可以绕过上面的O:数字
过滤
3. 引用绕过相等
使用&符号表示两个变量指向相同的内存引用地址,就是指针
4. 16进制绕过
反序列化后的字符串 不能出现某个关键单词时,可以使用大S绕过
,变成支持ASCII的反序列化
O:8:"backdoor":1:{s:4:"name";s:10:"phpinfo();";}
O:8:"backdoor":1:{S:4:"n\97me";s:10:"phpinfo();";}
$data='O:8:"backdoor":1:{S:4:"n\97me";s:15:"system(\'calc\');";}';
5. exception 绕过
不影响析构方法执行
例题web59
6. php反序列化字符逃逸
如果代码对提交的反序列化字符串进行了过滤,将里面的内容进行了替换
比如fuck
替换为loveu
未修正字符串长度时,再进行反序列化,就会出现逃逸情况
例题web60
- 可以控制某个类中的属性值
- 间接控制了某个类的反序列化字符串
- 由于存在无脑过滤,字符增减,造成 描述中字符串的长度 和实际的不一致
- 从而能够逃逸出若干个字符,实现字符可控,从而闭合前面的双引号
- 实现反序列化字符串的完全可控
phar
反序列化
Phar
认为是java
的jar
包 (被封装成calc.exe
类似的文件)
phar
能做什么
多个php
合并为独立压缩包
不解压就能执行里面的php文件
支持web服务器
和命令行
phar
协议
phar://xxx.phar/aaa.php
metaData
可以存放一个类实例,生成phar
后,会将这个类示例以序列化字符串形式存放形式存放刀Phar
文件内,当使用phar
协议加载phar
文件时,会自动反序列化这个类的序列化字符串
总结
- 生成
phar包
时,可以往metaData
里面放对象 - 生成后,对象会自动序列化保存到
phar包
中 - 使用
phar协议
读取phar包
时,如果当前脚本识别了这个类,会自动调用这个类的魔术方法
可以包含的方法
include "phar://ctfshow.phar";
file_exists("phar://ctfshow.phar");
file_get_contents("phar://ctfshow.phar");
file_put_contents("phar://ctfshow.phar",'111');
require "phar://ctfshow.phar";
fileinclude("phar://ctfshow.phar");
filemtime("phar://ctfshow.phar");
filesize("phar://ctfshow.phar");
ls_dir("phar://ctfshow.phar");
scandir("phar://ctfshow.phar");
highlight_file("phar://ctfshow.phar");
哪里使用的头
如果有上传头,上传文件的前半部分可控,后缀黑名单,不能对危险的后缀,php
phps
phtml
ini
没有禁止上传phar文件
能够上传phar文件
,找到大量使用的file_exists等文件读取函数,通过控制phar://
头,来使用phar协议来解析phar包
就能自动进行反序列化
条件:
- 能够生成
phar包
并上传写入 - 有可利用的文件操作函数,并控制了协议头,使用
phar协议
解析 - 有可利用的恶意类
session
反序列化
PHP_SESSION_UPLOAD_PROGRESS
php的session是存放在文件中 其默认位置是/tmp/sess_PHPSESSID
session
是可以放字符串、数字,也可以放对象
总结:
- session里面存放对象时,会自动进行序列化,存放序列化后的字符串
- session里面拿取对象时,会自动进行反序列化,执行对象的魔术方法