[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Off by one buffer overflow in getcwd(3)



-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

This is a demonstration of a "off-by-one" buffer overflow on the heap in
getcwd(3). Told in a script output, so get your popcorn and watch
closely.  Tested on OpenBSD 3.2/macppc.  Found with help of "jafari" on
efnet.


Script started on Mon Jan 3 21:22:53 2005 triton# more mklargedir.c #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h>

int
main(void)
{
~        char buf[512];
~        char lbuf[512];
~        int i;

~        for (i = 0; i < 255; i++) {
~                buf[i] = 'A';
~        }

~        buf[i] = '\0';

~        sprintf(lbuf, "/tmp/%s", buf);

~        mkdir(lbuf, 0755);

~        sprintf(buf, "/tmp/test");
~        mkdir(buf, 0755);

~        sprintf(buf, "/tmp/test2");
~        mkdir(buf, 0755);

~        return 0;
}
triton# ./mklargedir
triton# ls /tmp
.ICE-unix
.X11-unix
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
test
test2
triton# more opendirtest.c
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <stdio.h>

int
main(void)
{
~        DIR *d;
~        struct dirent *de;

~        d = opendir("/tmp");
~        if (d == NULL) {
~                perror("opendir");
~                exit(1);
~        }

~        while ((de = readdir(d)) != NULL) {
~                printf("%s\n", de->d_name);
~        }

~        closedir(d);

~        return 0;
}
triton# ./opendirtest
.
..
.X11-unix
.ICE-unix
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
test
test2
triton# more mkdir.c
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int
main(void)
{
~        int i;

~        chdir("/tmp/test");
~        for (i = 0; i < 254; i++) {
~                mkdir("A", 0755);
~                chdir("A");
~        }

}
triton# ./mkdir
triton# more prog.c
#include <stdio.h>


char * getcwd(char *pt, size_t size);

int
main(void)
{
~        if (getcwd(NULL, -1) == NULL) {
~                perror("getcwd");
~        }
}
triton# diff -u /usr/src/lib/libc/gen/getcwd.c getcwd.c
- --- /usr/src/lib/libc/gen/getcwd.c      Mon May 19 20:14:37 2003
+++ getcwd.c    Wed Dec 29 20:17:52 2004
@@ -175,6 +175,7 @@
~                                        continue;
~                                bcopy(dp->d_name, bup, dp->d_namlen + 1);

+                               printf("%d\n", strlen(up));
~                                /* Save the first error for later. */
~                                if (lstat(up, &s)) {
~                                        if (!save_errno)
triton# cc -o /tmp/prog prog.c getcwd.c
triton# cd /tmp
triton# ls
.ICE-unix
.X11-unix
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
prog
test
test2
triton# mount_null test test2
triton# df
Filesystem                  1K-blocks     Used    Avail Capacity  Mounted on
10.0.0.1:/usr/triton/root    18549738 15211447  2410805    86%    /
192.168.0.1:/usr/triton/usr  18549738 15211447  2410805    86%    /usr
192.168.0.1:/usr/triton/var  18549738 15211447  2410805    86%    /var
192.168.0.1:/usr/triton/etc  18549738 15211447  2410805    86%    /etc
192.168.0.1:/usr/triton/tmp  18549738 15211447  2410805    86%    /tmp
/tmp/test                    18549738 15211447  2410805    86%    test2
triton# cd test2
triton# ls
A
triton# repeat 254 cd A
triton# ls
triton# cp /tmp/prog .
triton# ./prog
774
774
1020
769
770
4
getcwd: No such file or directory
triton# grep 1024 /usr/src/lib/libc/gen/getcwd.c
~                if ((pt = malloc(ptsize = 1024 - 4)) == NULL)
~         * Allocate bytes (1024 - malloc space) for the string of "../"'s.
~        if ((up = malloc(upsize = 1024 - 4)) == NULL)
triton# exit
triton# exit

Script done on Mon Jan  3 21:35:24 2005


The 1020 is the strlen() of array up which means that the buffer to hold this is 1021 bytes in length which includes the trailing NUL byte. What the grep shows is that the array up is malloc'ed to just 1020 bytes. off-by-one.

Here a simple untested patch, comments would probably need rewriting as
well. This also deals with the eup pointer which would otherwise have
resided outside of the up array as well.

- --- /usr/src/lib/libc/gen/getcwd.c      Mon May 19 20:14:37 2003
+++ getcwd.c    Mon Jan  3 22:14:31 2005
@@ -92,7 +92,7 @@
~         * Should always be enough (it's 340 levels).  If it's not,
allocate
~         * as necessary.  Special * case the first stat, it's ".", not
"..".
~         */
- -       if ((up = malloc(upsize = 1024 - 4)) == NULL)
+       if ((up = malloc(upsize = MAXPATHLEN)) == NULL)
~                goto err;
~        eup = up + MAXPATHLEN;
~        bup = up;
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFB2lFt3j5xRv0RIEURAolPAJ0VK4c27ES37myHfFEEnoCJYI9D9ACfVxj/
17ZOLVXE456nBIauGQkEFoU=
=tMC2
-----END PGP SIGNATURE-----