home *** CD-ROM | disk | FTP | other *** search
-
- ---[ Phrack Magazine Volume 7, Issue 51 September 01, 1997, article 10 of 17
-
-
- -------------------------[ Scanning for RPC Services
-
-
- --------[ halflife <halflife@infonexus.com>
-
-
- Remote Procedure Language is a specification for letting procedures be
- executable on remote machines. It is defined in rfc1831. It has a number of
- good traits, and if you run SunOS or Solaris, you are almost required to make
- use of it to some degree.
-
- Unfortunately, there are vulnerabilities in some RPC services that have
- caused many machines to be penetrated. Many administrators block access to
- portmapper (port 111) in an effort to deny external users access to their weak
- RPC services.
-
- Unfortunately, this is completely inadequate. This article details how
- trivial it is to do a scan for specific RPC program numbers. The scan can be
- performed relatively quickly, and in many cases will not be logged.
-
- First, a little information about RPC itself; when I refer to RPC, I am only
- referring to ONC RPC, and not DCE RPC. RPC is a query/reply-based system. You
- send an initial query with the program number you are interested in, the
- procedure number, any arguments, authentication, and other needed parameters.
- In response, you get whatever the procedure returns, and some indication of
- the reason for the failure if it failed.
-
- Since RPC was designed to be portable, all arguments must be translated into
- XDR. XDR is a data encoding language that superficially reminds me a little
- bit of Pascal (at least, as far as strings are concerned). If you want more
- information on XDR, it is defined in rfc1832.
-
- As you probably surmised by now, RPC programs are made up of various
- procedures. There is one procedure that always exists, it is procedure 0.
- This procedure accepts no arguments, and it does not return any value (think
- void rpcping(void)). This is how we will determine if a given port holds a
- given program, we will call the ping procedure!
-
- So now we have a basic idea on how to determine if a given port is running
- a given RPC program number. Next we need to determine which UDP ports are
- listening. This can be done a number of ways, but the way I am using is
- to connect() to the port and try write data. If nothing is there, we
- will (hopefully) get a PORT_UNREACH error in errno, in which case we know
- there is nothing on that port.
-
- In the given code, we do a udp scan, and for every listening udp port, we
- try to query the ping procedure of the program number we are scanning for.
- If we get a positive response, the program number we are looking for exists
- on that port and we exit.
-
- <++> RPCscan/Makefile
- CC=gcc
- PROGNAME=rpcscan
- CFLAGS=-c
-
- build: checkrpc.o main.o rpcserv.o udpcheck.o
- $(CC) -o $(PROGNAME) checkrpc.o main.o rpcserv.o udpcheck.o
-
- checkrpc.o:
- $(CC) $(CFLAGS) checkrpc.c
-
- main.o:
- $(CC) $(CFLAGS) main.c
-
- rpcserv.o:
- $(CC) $(CFLAGS) rpcserv.c
-
- udpcheck.o:
- $(CC) $(CFLAGS) udpcheck.c
-
- clean:
- rm -f *.o $(PROGNAME)
- <-->
- <++> RPCscan/checkrpc.c
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/time.h>
- #include <sys/socket.h>
- #include <rpc/rpc.h>
- #include <netdb.h>
-
- extern struct sockaddr_in *saddr;
-
- int
- check_rpc_service(long program)
- {
- int sock = RPC_ANYSOCK;
- CLIENT *client;
- struct timeval timeout;
- enum clnt_stat cstat;
-
- timeout.tv_sec = 10;
- timeout.tv_usec = 0;
- client = clntudp_create(saddr, program, 1, timeout, &sock);
- if(!client)
- return -1;
- timeout.tv_sec = 10;
- timeout.tv_usec = 0;
- cstat = RPC_TIMEDOUT;
- cstat = clnt_call(client, 0, xdr_void, NULL, xdr_void, NULL, timeout);
- if(cstat == RPC_TIMEDOUT)
- {
- timeout.tv_sec = 10;
- timeout.tv_usec = 0;
- cstat = clnt_call(client, 0, xdr_void, NULL, xdr_void, NULL, timeout);
- }
- clnt_destroy(client);
- close(sock);
- if(cstat == RPC_SUCCESS)
- return 1;
- else if(cstat == RPC_PROGVERSMISMATCH)
- return 1;
- else return 0;
- }
- <-->
- <++> RPCscan/main.c
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
-
- int check_udp_port(char *, u_short);
- int check_rpc_service(long);
- long get_rpc_prog_number(char *);
- #define HIGH_PORT 5000
- #define LOW_PORT 512
-
- main(int argc, char **argv)
- {
- int i,j;
- long prog;
- if(argc != 3)
- {
- fprintf(stderr, "%s host program\n", argv[0]);
- exit(0);
- }
- prog = get_rpc_prog_number(argv[2]);
- if(prog == -1)
- {
- fprintf(stderr, "invalid rpc program number\n");
- exit(0);
- }
- printf("Scanning %s for program %d\n", argv[1], prog);
- for(i=LOW_PORT;i <= HIGH_PORT;i++)
- {
- if(check_udp_port(argv[1], i) > 0)
- {
- if(check_rpc_service(prog) == 1)
- {
- printf("%s is on port %u\n", argv[2], i);
- exit(0);
- }
- }
- }
- }
- <-->
- <++> RPCscan/rpcserv.c
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <netdb.h>
- #include <ctype.h>
- #include <rpc/rpc.h>
-
- long
- get_rpc_prog_number(char *progname)
- {
- struct rpcent *r;
- int i=0;
-
- while(progname[i] != '\0')
- {
- if(!isdigit(progname[i]))
- {
- setrpcent(1);
- r = getrpcbyname(progname);
- endrpcent();
- if(!r)
- return -1;
- else return r->r_number;
- }
- i++;
- }
- return atoi(progname);
- }
- <-->
- <++> RPCscan/udpcheck.c
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <netdb.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/param.h>
- #include <sys/time.h>
- #include <sys/errno.h>
- extern int h_errno;
-
- struct sockaddr_in *saddr = NULL;
-
- int
- check_udp_port(char *hostname, u_short port)
- {
- int s, i, sr;
- struct hostent *he;
- fd_set rset;
- struct timeval tv;
-
- if(!saddr)
- {
- saddr = malloc(sizeof(struct sockaddr_in));
- if(!saddr) return -1;
-
- saddr->sin_family = AF_INET;
- saddr->sin_addr.s_addr = inet_addr(hostname);
- if(saddr->sin_addr.s_addr == INADDR_NONE)
- {
- sethostent(1);
- he = gethostbyname(hostname);
- if(!he)
- {
- herror("gethostbyname");
- exit(1);
- }
- if(he->h_length <= sizeof(saddr->sin_addr.s_addr))
- bcopy(he->h_addr, &saddr->sin_addr.s_addr, he->h_length);
- else
- bcopy(he->h_addr, &saddr->sin_addr.s_addr, sizeof(saddr->sin_addr.s_addr));
- endhostent();
- }
- }
- saddr->sin_port = htons(port);
- s = socket(AF_INET, SOCK_DGRAM, 0);
- if(s < 0)
- {
- perror("socket");
- return -1;
- }
- i = connect(s, (struct sockaddr *)saddr, sizeof(struct sockaddr_in));
- if(i < 0)
- {
- perror("connect");
- return -1;
- }
- for(i=0;i < 3;i++)
- {
- write(s, "", 1);
- FD_ZERO(&rset);
- FD_SET(s, &rset);
- tv.tv_sec = 5;
- tv.tv_usec = 0;
- sr = select(s+1, &rset, NULL, NULL, &tv);
- if(sr != 1)
- continue;
- if(read(s, &sr, sizeof(sr)) < 1)
- {
- close(s);
- return 0;
- }
- else
- {
- close(s);
- return 1;
- }
- }
- close(s);
- return 1;
- }
- <-->
-
-
- ----[ EOF
-