GaRY's Blog

Beginning is always beautiful

PHP substr_compare() Vulnerability 浅析


新blog开张,贴个文章测试一下

PHP substr_compare() Vulnerability 浅析

Author: wofeiwo
Date: Aug 14th 2006

先看看公告:



PHP多个远程安全漏洞

发布日期:2006-08-03
更新日期:2006-08-04

受影响系统:
PHP PHP 4.4.x
不受影响系统:
PHP PHP 4.4.3
描述:
--------------------------------------------------------------------------------
CVE(CAN) ID: CVE-2006-3016

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

PHP的substr_compare()函数没有正确的验证偏移/长度参数。此外,PHP还没有正确的处理会话名称中的某些字符。攻击者可以利用这些漏洞远程执行任意代码。

<*来源:Secunia
  
  链接:http://secunia.com/advisories/21328/print/
        http://www.php.net/release_4_4_3.php
*>



公告里只说明影响为 PHP <= 4.4.3, 其实 PHP 5.1.3 以下也受到这个漏洞影响.

再来看看 PHP 手册里对这个函数的描述.



Description

int   substr_compare   (   string   main_str  ,     string   str  ,   int offset [  ,   int length [  ,   bool case_insensitivity]] ) 

substr_compare() compares main_str from position offset with str up to length characters. 

Returns < 0 if main_str from position offset is less than str, > 0 if it is greater than str, and 0 if they are equal. If length is equal or greater than length of main_str and length is set, substr_compare() prints warning and returns FALSE. 

If case_insensitivity is TRUE, comparison is case insensitive



看来是一个比较字符串与子字符串的函数.下面来分析漏洞原因,看代码:

//   ripped from PHP-5.1.2  
PHP_FUNCTION(  substr_compare  ) 

    char  
*  s1  ,     *  s2; 
    int s1_len 
,   s2_len; 
    long offset 
,   len  =   0  ; 
    zend_bool cs 
=   0  ; 
    uint cmp_len; 

     
if   (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC  ,     "  ssl|lb  "   ,     &  s1  ,     &  s1_len  ,     &  s2  ,     &  s2_len  ,     &  offset  ,     &  len  ,     &  cs)   ==   FAILURE) { 
        RETURN_FALSE; 
    } 

     
if   (len   &&   offset   >=   s1_len) {    //   简单的检查了 len 是否 != 0 并且 offset 大于 strlen(main_str). 但是 len 和 offset 都可以为负数  
        php_error_docref(  NULL   TSRMLS_CC  ,     E_WARNING   ,     "  The start position cannot exceed initial string length.  "  ); 
        RETURN_FALSE; 
    } 

     
if   (offset   <     0  ) {   //   offset 为负数,则从 main_str 的尾向前偏移  
        offset   =   s1_len   +   offset;   //   这里出现问题了.如果 offset 是负数,且绝对值大于 s1_len 呢?得到的 offset 仍然为负数  
    } 

    cmp_len  
=   (uint) (len   ?   len   :     MAX  (s2_len  ,   (s1_len   -   offset)));   //   这里确定比较的长度,很容易被控制.  

     
if   (  !  cs) {   //   传递参数给 zend_binary_strncmp 或 zend_binary_strncasecmp  
        RETURN_LONG(zend_binary_strncmp(s1   +   offset  ,   (s1_len   -   offset)  ,   s2  ,   s2_len  ,   cmp_len)); 
    }  
else   { 
        RETURN_LONG(zend_binary_strncasecmp(s1  
+   offset  ,   (s1_len   -   offset)  ,   s2  ,   s2_len  ,   cmp_len)); 
    } 


再来看 zend_binary_strncmp 函数:

ZEND_API int zend_binary_strncmp(char   *  s1  ,   uint len1  ,   char   *  s2  ,   uint len2  ,   uint length) 

    int retval; 
     
    retval  
=   memcmp(s1  ,   s2  ,     MIN  (length  ,     MIN  (len1  ,   len2))); 
     
if   (  !  retval) { 
         
return   (  MIN  (length  ,   len1)   -     MIN  (length  ,   len2)); 
    }  
else   { 
         
return   retval; 
    } 


很明显,如果提交我们设计的参数,很容易造成crash.

下面是 PHP-5.1.4 修补后的代码

//   ripped from PHP-5.1.4  
PHP_FUNCTION(  substr_compare  ) 

    char  
*  s1  ,     *  s2; 
    int s1_len 
,   s2_len; 
    long offset 
,   len  =   0  ; 
    zend_bool cs 
=   0  ; 
    uint cmp_len; 

     
if   (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC  ,     "  ssl|lb  "   ,     &  s1  ,     &  s1_len  ,     &  s2  ,     &  s2_len  ,     &  offset  ,     &  len  ,     &  cs)   ==   FAILURE) { 
        RETURN_FALSE; 
    } 

     
if   (ZEND_NUM_ARGS()   >=     4     &&   len   <=     0  ) {   //   len不能为负数了  
        php_error_docref(  NULL   TSRMLS_CC  ,     E_WARNING   ,     "  The length must be greater than zero  "  ); 
        RETURN_FALSE; 
    } 

     
if   (offset   <     0  ) { 
        offset  
=   s1_len   +   offset; 
        offset  
=   (offset   <     0  )   ?     0     :   offset;    //   检查是否 offset 仍然为负数,是,则设为0  
    } 

     
if   ((offset   +   len)   >=   s1_len) {   //   offset+len 也不能大于 s1_len  
        php_error_docref(  NULL   TSRMLS_CC  ,     E_WARNING   ,     "  The start position cannot exceed initial string length  "  ); 
        RETURN_FALSE; 
    } 

    cmp_len  
=   (uint) (len   ?   len   :     MAX  (s2_len  ,   (s1_len   -   offset))); 

     
if   (  !  cs) { 
        RETURN_LONG(zend_binary_strncmp(s1  
+   offset  ,   (s1_len   -   offset)  ,   s2  ,   s2_len  ,   cmp_len)); 
    }  
else   { 
        RETURN_LONG(zend_binary_strncasecmp(s1  
+   offset  ,   (s1_len   -   offset)  ,   s2  ,   s2_len  ,   cmp_len)); 
    } 

posted on 2006-08-15 14:42 wofeiwo 阅读(529) 评论(0)  编辑 收藏 引用 网摘 所属分类: PHP security


只有注册用户登录后才能发表评论。
网站导航:

导航

<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

统计

留言簿(10)

随笔分类(90)

随笔档案(61)

搜索

最新随笔

最新评论