Edit report at https://bugs.php.net/bug.php?id=63455&edit=1
ID: 63455 Updated by: tony2...@php.net Reported by: jakub dot galczyk at gmail dot com Summary: SIGSEGV -Status: Open +Status: Not a bug Type: Bug -Package: *Math Functions +Package: PCRE related Operating System: Ubuntu PHP Version: 5.4.8 Block user comment: N Private report: N New Comment: This is PCRE overflowing the stack due to an (endless?) recursion in your regular expression. It's a well known PCRE problem and solutions to it are described here: http://manpages.courier-mta.org/htmlman3/pcrestack.3.html and here: http://pcre.org/pcre.txt (look for "STACK USAGE") Previous Comments: ------------------------------------------------------------------------ [2012-11-07 14:34:49] jakub dot galczyk at gmail dot com More info: (gdb) bt (...) #21790 0x080b8f3b in php_pcre_exec (argument_re=0x4657380, extra_data=0xbe9e6ed4, subject=0x43c3abc "HTTP/1.1 302 Found\r\nDate: Wed, 07 Nov 2012 13:14:24 GMT\r\nServer: Apache/2.2.22 (Ubuntu)\r\nX-Powered-By: PHP/5.3.10-1ubuntu3.4\r\nCache-Control: no-cache, must-revalidate\r\nExpires: Sat, 26 Jul 1997 05:00:"..., length=11527, start_offset=0, options=0, offsets=0x43bad0c, offsetcount=6) at /home/kuba/src/php-5.4.8/ext/pcre/pcrelib/pcre_exec.c:6098 #21791 0x080bd6cd in php_pcre_match_impl (pce=0x46574d8, subject=0x43c3abc "HTTP/1.1 302 Found\r\nDate: Wed, 07 Nov 2012 13:14:24 GMT\r\nServer: Apache/2.2.22 (Ubuntu)\r\nX-Powered-By: PHP/5.3.10-1ubuntu3.4\r\nCache-Control: ---Type <return> to continue, or q <return> to quit--- no-cache, must-revalidate\r\nExpires: Sat, 26 Jul 1997 05:00:"..., subject_len=11527, return_value=0x43c20a4, subpats=0x43bd784, global=0, use_flags=0, flags=0, start_offset=0) at /home/kuba/src/php-5.4.8/ext/pcre/php_pcre.c:634 #21792 0x080be13d in php_do_pcre_match (ht=3, return_value=0x43c20a4, global=0, return_value_ptr=<optimized out>, this_ptr=<optimized out>, return_value_used=<optimized out>) at /home/kuba/src/php-5.4.8/ext/pcre/php_pcre.c:528 #21793 0x08374d58 in zend_do_fcall_common_helper_SPEC (execute_data=<optimized out>) at /home/kuba/src/php-5.4.8/Zend/zend_vm_execute.h:642 #21794 0x08337ced in execute (op_array=<optimized out>) at /home/kuba/src/php-5.4.8/Zend/zend_vm_execute.h:410 #21795 0x082d9a93 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /home/kuba/src/php-5.4.8/Zend/zend.c:1309 #21796 0x0827a6b2 in php_execute_script (primary_file=0xbe9e93dc) at /home/kuba/src/php-5.4.8/main/main.c:2482 #21797 0x0837749e in do_cli (argc=4, argv=0xbe9ea664) at /home/kuba/src/php-5.4.8/sapi/cli/php_cli.c:988 #21798 0x08067254 in main (argc=4, argv=0xbe9ea664) at /home/kuba/src/php-5.4.8/sapi/cli/php_cli.c:1364 (gdb) (gdb) (gdb) x/10i 0x80a99bb => 0x80a99bb <match+5787>: mov %edi,0x8(%esp) 0x80a99bf <match+5791>: mov %ebp,0x14(%esp) 0x80a99c3 <match+5795>: mov 0x40(%esp),%ebp 0x80a99c7 <match+5799>: movzbl 0x83e2dc0(%eax),%eax 0x80a99ce <match+5806>: mov %ebp,0x10(%esp) 0x80a99d2 <match+5810>: mov 0x3c(%esp),%ebp 0x80a99d6 <match+5814>: lea (%esi,%eax,1),%edx 0x80a99d9 <match+5817>: mov 0x30(%esp),%eax 0x80a99dd <match+5821>: mov %ebp,0xc(%esp) 0x80a99e1 <match+5825>: mov 0x184(%esp),%ebp (gdb) p $1 History has not yet reached $1. (gdb) p $eip $1 = (void (*)()) 0x80a99bb <match+5787> (gdb) i r eax 0x43bad20 71019808 ecx 0x43c3abc 71056060 edx 0x2a8e 10894 ebx 0x43c654a 71066954 esp 0xbe1ebfd0 0xbe1ebfd0 ebp 0x0 0x0 esi 0x46573ac 73757612 edi 0xbe9e6d80 -1096913536 eip 0x80a99bb 0x80a99bb <match+5787> eflags 0x4 [ PF ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x0 0 fs 0x0 0 gs 0xb 11 (gdb) list 817 (int)(eptr - md->start_subject); 818 819 flags = (op == OP_SCBRA)? match_cbegroup : 0; 820 do 821 { 822 RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md, 823 ims, eptrb, flags, RM1); 824 if (rrc != MATCH_NOMATCH && 825 (rrc != MATCH_THEN || md->start_match_ptr != ecode)) 826 RRETURN(rrc); (gdb) 827 md->capture_last = save_capture_last; 828 ecode += GET(ecode, 1); 829 } 830 while (*ecode == OP_ALT); 831 832 DPRINTF(("bracket %d failed\n", number)); 833 834 md->offset_vector[offset] = save_offset1; 835 md->offset_vector[offset+1] = save_offset2; 836 md->offset_vector[md->offset_end - number] = save_offset3; (gdb) \q kuba@box:~/src/php-5.4.8$ grep -n -r -e RMATCH ./ (...) kuba@box:~/src/php-5.4.8$ vim ext/pcre/pcrelib/pcre_exec.c /RMATCH (...) line 235: "The original heap-recursive code used longjmp(). However, it seems that this can be very slow on some operating systems. Following a suggestion from Stan Switzer, the use of longjmp() has been abolished, at the cost of having to provide a unique number for each call to RMATCH. There is no way of generating a sequence of numbers at compile time in C. I have given them names, to make them stand out more clearly." (...) "/* These versions of the macros use the stack, as normal. There are debugging versions and production versions. Note that the "rw" argument of RMATCH isn't actually used in this definition. */ #ifndef NO_RECURSE #define REGISTER register #ifdef PCRE_DEBUG #define RMATCH(ra,rb,rc,rd,re,rf,rg,rw) \ " (...) " /* These versions of the macros manage a private stack on the heap. Note that the "rd" argument of RMATCH isn't actually used in this definition. It's the md argument of match(), which never changes. */" No to ja go chyba troche zmieniÅem ;] "register int i; /* Used for loops not involving calls to RMATCH() */" " /* OK, now we can get on with the real code of the function. Recursive calls are specified by the macro RMATCH and RRETURN is used to return. When NO_RECURSE is *not* defined, these just turn into a recursive call to match() and a "return", respectively (possibly with some debugging if PCRE_DEBUG is defined). However, RMATCH isn't like a function call because it's quite a complicated macro. It has to be used in one particular way. This shouldn't, however, impact performance when true recursion is being used. */" 818:833: (...) flags = (op == OP_SCBRA)? match_cbegroup : 0; do { ==> RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md, ims, eptrb, flags, RM1); if (rrc != MATCH_NOMATCH && (rrc != MATCH_THEN || md->start_match_ptr != ecode)) RRETURN(rrc); md->capture_last = save_capture_last; ecode += GET(ecode, 1); } while (*ecode == OP_ALT); DPRINTF(("bracket %d failed\n", number)); (...) ------------------------------------------------------------------------ [2012-11-07 14:26:21] paj...@php.net Increase the stack and the problem should go away. Actually the reproduce script could be reduce to: preg_match("/(\n|.)*/i", $res, $match); with the content of $res and $match being previously set. ------------------------------------------------------------------------ [2012-11-07 14:13:15] jakub dot galczyk at gmail dot com Description: ------------ I was checking one bug in CMS (found by someone else) and accidently there was a SIGSEGV ;] Test script: --------------- Exploit code ('script to test') is here: http://www.exploit-db.com/exploits/15369/ CMS (I saw that we need to have this CMS in /wwwroot) to test: http://www.geardownload.com/webdevelopment/auto-cms-download.html (Below I added a little description grepped from .c file, gdb and valgrind. Expected result: ---------------- No sigsegv? ;) (and shell output from this sploit for autocms wroted by giudinvx) Actual result: -------------- kuba@box:~/src/php-5.4.8$ /usr/local/bin/php -v PHP 5.4.8 (cli) (built: Nov 7 2012 13:36:10) Copyright (c) 1997-2012 The PHP Group Zend Engine v2.4.0, Copyright (c) 1998-2012 Zend Technologies kuba@box:~/src/php-5.4.8$ /usr/local/bin/php ../../public_html/spl.php localhost / Auto CMS <= 1.8 Remote Code Execution Exploit by giudinvx ShellCMD WHATEVERGOESHERE:*:*:* Segmentation fault (core dumped) kuba@box:~/src/php-5.4.8$ ------------------------------------------------------------------------ -- Edit this bug report at https://bugs.php.net/bug.php?id=63455&edit=1