[prev in list] [next in list] [prev in thread] [next in thread] 

List:       openbsd-bugs
Subject:    Invalid pointer dereferences in ldconfig (prebind.c)
From:       Alejandro Hernandez <alejandro.hernandez () ioactive ! co ! uk>
Date:       2014-10-16 22:54:04
Message-ID: 13E61BCA7787794E89BDF39B8DE40C024CD02D1195 () ioaexchange ! ioactive ! local
[Download RAW message or body]

Hi,

I'd like to report some minor bugs in ldconfig -P (prebind). These are in \
src/libexec/ld.so/ldconfig/prebind.c and are reached only if the -P switch is \
enabled. These were found in http://mirrors.gigenet.com/pub/OpenBSD/5.5/src.tar.gz \
and tested in OpenBSD 5.5 i386.

Taken from $man ldconfig:

"     -P     Create and append prebind information to all executables found in
             the specified directories, and also all shared libraries which
             are required by those executables.

PREBINDING
     Prebinding is loosely based on an earlier concept called Prelinking,
     which also accelerated ld.so(1) performance but simultaneously impaired
     address space randomization.  When prebinding information is added to
     libraries and programs using -P, program startup can be significantly
     improved because ld.so(1) can initialize the shared library environment
     much faster.  Prebinding information adds a small amount of data to the
     end of each specified program and associated shared libraries."


1.- Inside load_elf(), the field ehdr->e_phoff is used as an offset to create a \
memory pointer (phdr). If e_phoff contains a big offset the application will fail: \
struct elf_object * load_file(const char *filename, int objtype)
{
    ...
        if (objtype == OBJTYPE_EXE) {
                if (ehdr->e_type != ET_EXEC)
                        goto done;

                note_found = 0;

                phdr = (Elf_Phdr *)((char *)buf + ehdr->e_phoff);
                for (i = 0; i < ehdr->e_phnum; i++) {
                        if (phdr[i].p_type == PT_NOTE) {
                                note_found = elf_check_note(buf,&phdr[i]);
                                break;
                        }
                }
                if (note_found == 0)
                        goto done; /* no OpenBSD note found */
        }
    ...
}

1.1.- PoC
$ uname -rm
5.5 i386
$ ldconfig -Pv /home/nitr0us/foo55_H/
...
processing /home/nitr0us/foo55_H/orc_1734
/home/nitr0us/foo55_H/orc_1734: wrong arch
processing /home/nitr0us/foo55_H/orc_0553
processing /home/nitr0us/foo55_H/orc_1879
processing /home/nitr0us/foo55_H/orc_0829
Segmentation fault (core dumped)
$ readelf -h /home/nitr0us/foo55_H/orc_0829 | grep "Start of program headers"
readelf: Error: Unable to read in 0x140 bytes of program headers
  Start of program headers:          1145324612 (bytes into file)
$


2.- It seems the next ones have been patched in 5.5 because I couldn't reproduce the \
PoCs that worked in 5.2, however the bugs seems still to be present in the \
src.tar.gz. In the code above, there's a for loop based on ehdr->e_phnum. If a \
malformed ELF with a big e_phnum is analyzed, the application will fail.

2.1.- PoC
-bash-4.2$ uname -rm
5.2 i386
-bash-4.2$ ldconfig -vP .
loading dir .
processing ./foo52
processing ./e_phnum
Segmentation fault (core dumped)
-bash-4.2$ readelf -h e_phnum | grep "Number of program headers"
readelf: Error: Unable to read in 0x1fffe0 bytes of program headers
  Number of program headers:         65535

If the ELF header is not currupt, elf_check_note() will be called if the current \
p_type is PT_NOTE (Note information), then ldconfig tries to access to the content of \
a pointer plus the p_offset element of the current program header, therefore, if \
p_offset holds a big value, it will fail. /*
 * check if the given executable header on an ELF executable
 * has the proper OpenBSD note on the file if it is not present
 * binaries will be skipped.
 */
int
elf_check_note(void *buf, Elf_Phdr *phdr)
{
        u_long address;
        u_int *pint;
        char *osname;

        address = phdr->p_offset;
        pint = (u_int *)((char *)buf + address);
        osname = (char *)buf + address + sizeof(*pint) * 3;

        if (pint[0] == 8 /* OpenBSD\0 */ &&
            pint[1] == 4 /* ??? */ &&
            pint[2] == 1 /* type_osversion */ &&
            strcmp("OpenBSD", osname) == 0)
                return 1;

        return 0;
}

2.2.- PoC
-bash-4.2$ uname -rm
5.2 i386
-bash-4.2$ ldconfig -vP prebind
loading dir prebind/
processing prebind/foo52
processing prebind/p_offset
Segmentation fault (core dumped)
-bash-4.2$ readelf -lW prebind/p_offset | grep NOTE
  NOTE           0x41414141 0x1c0001a8 0x1c0001a8 0x00018 0x00018 R   0x4
-bash-4.2$


Regards.

Alejandro Hernandez
Senior Security Consultant

IOActive, Ltd
Mobile: (+521) 55 2515 7446
http://www.ioactive.com<http://www.ioactive.com/>
alejandro.hernandez@ioactive.co.uk<mailto:alejandro.hernandez@ioactive.co.uk>
"Using our past to secure your future"
The Americas | EMEA | AsiaPac


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic