home *** CD-ROM | disk | FTP | other *** search
/ The Hacker's Encyclopedia 1998 / hackers_encyclopedia.iso / zines / phrack2 / phrack52.007 < prev    next >
Encoding:
Text File  |  2003-06-11  |  16.5 KB  |  552 lines

  1. ---[  Phrack Magazine   Volume 8, Issue 52 January 26, 1998, article 07 of 20
  2.  
  3.  
  4. -------------------------[  Linux Ping Daemon
  5.  
  6.  
  7. --------[  route|daemon9 <route@infonexus.com>
  8.  
  9.  
  10.  
  11.  
  12. ----[  Introduction and Impetus
  13.  
  14.  
  15.     I have an idea.  How about we rip ICMP_ECHO support from the kernel?  How
  16. about we employ a userland daemon that controls ICMP_ECHO reflection via TCP
  17. wrapper access control?  (Actually, this idea was originally (c) Asriel, who
  18. did the 44BSD version.  http://www.enteract.com/~tqbf/goodies.html.  He just
  19. asked me to do the linux version.)
  20.  
  21.     The bastard son of this idea is pingd.  A cute userland daemon that 
  22. handles all ICMP_ECHO and ICMP_ECHOREPLY traffic.  The engine is simple.  A
  23. raw ICMP socket under Linux gets a copy of every ICMP datagram delivered to 
  24. the IP module (assuming the IP datagram is destined for an interface on that
  25. host).  We simply remove support of ICMP_ECHO processing from the kernel and
  26. erect a userland daemon with a raw ICMP socket to handle these packets.
  27.  
  28.     Once we have the packet, we do some basic sanity checks such as packet
  29. type and code, and packet size.  Next, we pass the packet to the authentication
  30. mechanism where it is checked against the access control list.  If the packet
  31. is allowed, we send a response, otherwise we drop it on the floor.
  32.  
  33.     The rule for this project was primarily security and then efficiency.  The
  34. next version will have an option to send ICMP_HOST_UNREACH to an offending
  35. host.  I may also at some point add some hooks for some sort of payload
  36. content analysis (read: LOKI detection) but for now, pingd stands as is.
  37.  
  38.  
  39. ----[  Compilation and Installation
  40.  
  41.  
  42. i.  You will need libwrap and libnet.  Libwrap comes with Wieste Venema's Tcp
  43.     wrapper package and is available from ftp://ftp.win.tue.nl/pub/security/.
  44.     The libnet networking library is available from: 
  45.     http://www.infonexus.com/~daemon9/Projects/libnet.tar.gz.
  46.  
  47. ii. Build and install both libraries according to their respective instructions.
  48.  
  49. 1.  Build the program and apply the kernel patch.
  50.  
  51.         `make all` OR (`make pingd` AND `make patch`)
  52.  
  53. 1a. Recompile your kernel.  It is NOT necessary to make {config, dep, clean}.
  54.     It is only necessary to:
  55.     
  56.         `make; make install`
  57.  
  58.     (or the equivalent).
  59.  
  60. 2.  Test the daemon.  Ensure that there are no wrapper entries in the 
  61.     /etc/hosts.{deny, allow} and start the daemon in debug mode.
  62.  
  63.         `./pingd -d1` and then `ping 0`
  64.  
  65. 3.  Edit your TCP wrapper access control files.  Simply add a new service
  66.     (ping) and the IP addresses you want to allow or deny:
  67.  
  68.         `cat >> /etc/hosts.deny`
  69.           ping : evil.com
  70.  
  71.         ^D
  72.  
  73. 4.  Install the program and add it to your /etc/rc.d/rc/local:
  74.  
  75.         `make install`
  76.  
  77.  
  78. ----[  Empirical Data
  79.  
  80.  
  81.     This is slower then doing it in the kernel.  Especially on localhost.  How
  82. about that.  Remotely, the RTT's are about .7 - .9 ms longer with a concise
  83. /etc/hosts.{allow,deny}.  This is the price you pay for a more secure
  84. implementation.  All the hosts are on the same 10MB network, with
  85. approximately the same speed NICs.
  86.  
  87.  
  88.     The following Linux machine has a normal kernel-based ICMP_ECHO reflector
  89.     mechanism:
  90.  
  91. resentment:~/# ping 192.168.2.34
  92. PING 192.168.2.34 (192.168.2.34): 56 data bytes
  93. 64 bytes from 192.168.2.34: icmp_seq=0 ttl=64 time=0.8 ms
  94. 64 bytes from 192.168.2.34: icmp_seq=1 ttl=64 time=0.6 ms
  95. 64 bytes from 192.168.2.34: icmp_seq=2 ttl=64 time=0.8 ms
  96.  
  97. --- 192.168.2.34 ping statistics ---
  98. 3 packets transmitted, 3 packets received, 0% packet loss
  99. round-trip min/avg/max = 0.6/0.7/0.8 ms
  100.  
  101.  
  102.     This machine is running pingd compiled with DLOG (and has no kernel
  103.     ICMP_ECHO support):
  104.  
  105. resentment:~/# ping 192.168.2.35
  106. PING 192.168.2.35 (192.168.2.35): 56 data bytes
  107. 64 bytes from 192.168.2.35: icmp_seq=0 ttl=64 time=1.5 ms
  108. 64 bytes from 192.168.2.35: icmp_seq=1 ttl=64 time=1.4 ms
  109. 64 bytes from 192.168.2.35: icmp_seq=2 ttl=64 time=1.3 ms
  110.  
  111. --- 192.168.2.35 ping statistics ---
  112. 3 packets transmitted, 3 packets received, 0% packet loss
  113. round-trip min/avg/max = 1.3/1.4/1.5 ms
  114.  
  115.  
  116.     Stress-test of the same host (not recommended to do with debugging on):
  117.  
  118. torment# /sbin/ping -f -c 10000 192.168.2.35
  119. PING 192.168.2.35 (192.168.2.35): 56 data bytes
  120. ............................................................................
  121. --- 192.168.2.35 ping statistics ---
  122. 10088 packets transmitted, 10000 packets received, 0% packet loss
  123. round-trip min/avg/max = 0.985/36.790/86.075 ms
  124.  
  125. resentment:~# ping -f -c 10000 192.168.2.35
  126. PING 192.168.2.35 (192.168.2.35): 56 data bytes
  127. ..
  128. --- 192.168.2.35 ping statistics ---
  129. 10001 packets transmitted, 10000 packets received, 0% packet loss
  130. round-trip min/avg/max = 1.0/1.2/17.4 ms
  131.  
  132.  
  133.     An example of the wrapper log:
  134.  
  135. Jan 16 18:23:03 shattered pingd: started: 997
  136. Jan 16 18:24:52 shattered pingd: ICMP_ECHO allowed by wrapper
  137. (64 bytes from 192.168.2.38)
  138. Jan 16 18:24:54 shattered last message repeated 2 times
  139. Jan 16 18:26:50 shattered pingd: ICMP_ECHO allowed by wrapper
  140. (64 bytes from 192.168.2.37)
  141. Jan 16 18:26:58 shattered last message repeated 10087 times
  142. Jan 16 18:30:09 shattered pingd: ICMP_ECHO allowed by wrapper
  143. (64 bytes from 192.168.2.38)
  144. Jan 16 18:30:19 shattered last message repeated 10000 times
  145. Jan 16 18:47:30 shattered pingd: ICMP_ECHO denied by wrapper
  146. (64 bytes from 192.168.2.34)
  147. Jan 16 18:47:32 shattered last message repeated 2 times
  148. Jan 16 18:48:16 shattered pingd: packet too large
  149. (10008 bytes from 192.168.2.38)
  150. Jan 16 18:48:17 shattered last message repeated 2 times
  151.  
  152.  
  153. ----[  The code
  154.  
  155.  
  156. <++> Pingd/Makefile
  157. # linux pingd Makefile
  158. # daemon9|route <route@infonexus.com>
  159.  
  160. #   Define this if you want syslog logging of ICMP_ECHO traffic.  This slows
  161. #   slow down daemon response time a bit.
  162. #   default: enabled.
  163. DEFINES     =   -DLOG
  164.  
  165. CC          =   gcc
  166. VER         =   0.1
  167. NETSRC      =   /usr/src/linux/net/ipv4
  168. INSTALL_LOC =   /usr/sbin
  169. PINGD       =   pingd
  170. LIBS        =   -lnet -lwrap
  171. DEFINES     +=   -D__BSD_SOURCE
  172. CFLAGS      =   -O3 -funroll-loops -fomit-frame-pointer -pipe -m486 -Wall
  173. OBJECTS     =   pingd.o
  174.  
  175. .c.o:
  176.     $(CC) $(CFLAGS) $(DEFINES) -c $< -o $@
  177.  
  178. pingd:  $(OBJECTS)
  179.     $(CC) $(CFLAGS) $(OBJECTS) -o pingd $(LIBS)
  180.     strip pingd
  181.  
  182. all: patch pingd
  183.  
  184. patch:
  185.     @(/usr/bin/patch -d $(NETSRC) < patchfile)
  186.     @(echo "Patchfile installed")
  187.     @(echo "You must now recompile your kernel")
  188.     @(echo "")
  189.  
  190. install: pingd
  191.     (install -m755 $(PINGD) $(INSTALL_LOC))
  192.     (echo ""                              >> /etc/rc.d/rc.local)
  193.     (echo "echo \"Starting ping daemon\"" >> /etc/rc.d/rc.local)
  194.     (echo "$(INSTALL_LOC)/$(PINGD)"       >> /etc/rc.d/rc.local)
  195.  
  196. dist:   clean
  197.     @(cd ..; rm pingd-$(VER).tgz; tar cvzf pingd-$(VER).tgz Pingd/)
  198.  
  199. clean:
  200.     rm -f *.o core pingd
  201. # EOF
  202. <-->
  203. <++> Pingd/pingd.h
  204. /*
  205.  *  $Id$
  206.  *
  207.  *  Linux pingd sourcefile
  208.  *  pingd.h - function prototypes, global data structures, and macros
  209.  *  Copyright (c) 1998 by daemon9|route (route@infonexus.com)
  210.  *
  211.  *
  212.  *
  213.  */
  214.  
  215. #ifndef _PINGD_H
  216. #define _PINGD_H
  217.  
  218. #include <stdio.h>
  219. #include <stdlib.h>
  220. #include <string.h>
  221. #include <unistd.h>
  222. #include <netinet/in.h>
  223. #include <netinet/ip.h>
  224. #include <netinet/ip_icmp.h>
  225. #include <pwd.h>
  226. #include <syslog.h>
  227. #include <sys/types.h>
  228. #include <sys/socket.h>
  229. #include <libnet.h>
  230.  
  231. #define NOBODY          "nobody"        /* Nobody pwnam */
  232. #define STRING_UNKNOWN  "unknown"       /* From tcpd.h */
  233. #define HEADER_MATERIAL 28              /* ICMP == 8 bytes, IP == 20 bytes */
  234. #define MAX_PAYLOAD     8096            /* Out of thin air */
  235.  
  236. struct icmp_packet
  237. {
  238.     struct ip iph;
  239.     struct icmphdr icmph;
  240.     u_char payload[MAX_PAYLOAD];
  241. };
  242.  
  243.  
  244. /* F U N C T I O N    P R O T O T Y P E S */
  245.  
  246.  
  247. void
  248. usage(
  249.     char *                  /* pointer to argv[0] */
  250.     );
  251.  
  252. int                         /* 1 if the packet is allowed, 0 if denied */
  253. verify(
  254.     struct icmp_packet *    /* pointer to the ICMP packet in question */
  255.     );
  256.  
  257. void
  258. icmp_reflect(
  259.     struct icmp_packet *,   /* pointer to the ICMP packet in question */
  260.     int                     /* socket file descriptor */
  261.     );
  262.  
  263. int                         /* 1 if access is granted, 0 if denied */
  264. hosts_ctl(
  265.     char *,                 /* daemon name */
  266.     char *,                 /* client name (canonical) */
  267.     char *,                 /* client address (dots 'n' decimals) */
  268.     char *                  /* client user (unused) */
  269.     );
  270.  
  271. #endif  /* _PINGD_H */
  272.  
  273. /* EOF */
  274. <-->
  275. <++> Pingd/pingd.c
  276. /*
  277.  *  $Id$
  278.  *
  279.  *  Linux pingd sourcefile
  280.  *  ping.c - main sourcefile
  281.  *  Copyright (c) 1998 by daemon9|route <route@infonexus.com>
  282.  *
  283.  *  
  284.  *
  285.  *  $Log$
  286.  */
  287.  
  288. #include "pingd.h"
  289.  
  290. int d           = 0;                /* Debuging level (defaults off) */
  291. int max_packet  = 1024;             /* Maximum packet size (default) */
  292.  
  293. int
  294. main(int argc, char **argv)
  295. {
  296.     int sock_fd, c;
  297.     struct icmp_packet i_pack;
  298.     struct passwd *pwd_p;
  299.  
  300.     /*
  301.      *  Make sure we have UID 0.
  302.      */
  303.     if (geteuid() || getuid())
  304.     {
  305.         fprintf(stderr, "Inadequate privledges\n");
  306.         exit(1);
  307.     }
  308.  
  309.     /*
  310.      *  Open a raw ICMP socket and set IP_HDRINCL.
  311.      */
  312.     if ((sock_fd = open_raw_sock(IPPROTO_ICMP)) == -1)
  313.     {
  314.         perror("socket allocation");
  315.         exit(1);
  316.     }
  317.  
  318.     /*
  319.      *  Now that we have the raw socket, we no longer need root privledges
  320.      *  so we drop our UID to nobody.
  321.      */
  322.     if (!(pwd_p = getpwnam(NOBODY))) 
  323.     {
  324.         fprintf(stderr, "Can't get pwnam info on nobody");
  325.         exit(1);
  326.     }
  327.     else if (setuid(pwd_p->pw_uid) == -1)
  328.     {
  329.         perror("Can't drop privledges");
  330.         exit(1);
  331.     }
  332.  
  333.     while((c = getopt(argc, argv, "d:s:")) != EOF)
  334.     {
  335.         switch (c)
  336.         {
  337.             case 'd':
  338.                 d = atoi(optarg);
  339.                 break;
  340.  
  341.             case 's':
  342.                 max_packet = atoi(optarg);
  343.                 break;
  344.  
  345.             default:
  346.                 usage(argv[0]);
  347.         }
  348.     }
  349.  
  350.     if (!d) daemon();
  351.     if (d) fprintf(stderr, "Max packetsize of %d bytes\n", max_packet);
  352.  
  353. #ifdef  LOG
  354.     openlog("pingd", 0, 0);
  355.     syslog(LOG_DAEMON|LOG_INFO, "started: %d", getpid());
  356. #endif  /* LOG */
  357.     /*
  358.      *  We're powered up.  From here on out, everything should run swimmingly.
  359.      */
  360.     for (;;)
  361.     {
  362.         bzero(&i_pack, sizeof(i_pack));
  363.         c = recv(sock_fd, (struct icmp_packet *)&i_pack, sizeof(i_pack), 0);
  364.         if (c == -1)
  365.         {
  366.             if (d) fprintf(stderr, "truncated read: %s", strerror(errno));
  367.             continue;
  368.         }
  369.  
  370.         /*
  371.          *  Make sure packet isn't too small or too big.
  372.          */
  373.         if (c < HEADER_MATERIAL || c > max_packet)
  374.         {
  375. #ifdef  LOG
  376.             syslog(
  377.                     LOG_DAEMON|LOG_INFO,
  378.                     "bad packet size (%d bytes from %s)",
  379.                     ntohs(i_pack.iph.ip_len) - sizeof(i_pack.iph),
  380.                     host_lookup(i_pack.iph.ip_src.s_addr));
  381. #endif  /* LOG */
  382.             continue;
  383.         }
  384.  
  385.         /*
  386.          *  We only want ICMP_ECHO packets.
  387.          */
  388.         if (i_pack.icmph.type != ICMP_ECHO) continue;
  389.         else if (d)
  390.                 fprintf(stderr,
  391.                 "%d byte ICMP_ECHO from %s\n",
  392.                 ntohs(i_pack.iph.ip_len) - sizeof(i_pack.iph),
  393.                 host_lookup(i_pack.iph.ip_src.s_addr));
  394.  
  395.         /*
  396.          *  Pass packet to the access control mechanism.
  397.          */
  398.         if (!verify(&i_pack))
  399.         {
  400. #ifdef  LOG
  401.             syslog(
  402.                     LOG_DAEMON|LOG_INFO,
  403.                     "ICMP_ECHO denied by wrapper (%d bytes from %s)",
  404.                     ntohs(i_pack.iph.ip_len) - sizeof(i_pack.iph),
  405.                     host_lookup(i_pack.iph.ip_src.s_addr));
  406. #endif  /* LOG */
  407.         }
  408.         else 
  409.         {
  410. #ifdef  LOG
  411.             syslog(
  412.                     LOG_DAEMON|LOG_INFO,
  413.                     "ICMP_ECHO allowed by wrapper (%d bytes from %s)",
  414.                     ntohs(i_pack.iph.ip_len) - sizeof(i_pack.iph),
  415.                     host_lookup(i_pack.iph.ip_src.s_addr));
  416. #endif  /* LOG */
  417.             icmp_reflect(&i_pack, sock_fd);
  418.         }
  419.     }
  420. }
  421.  
  422.  
  423. void
  424. icmp_reflect(struct icmp_packet *p_ptr, int sock_fd)
  425. {
  426.     int c;
  427.     u_long tmp;
  428.     struct sockaddr_in sin;
  429.  
  430.     bzero((struct sockaddr_in *)&sin, sizeof(sin));
  431.     /*
  432.      *  Formulate ICMP_ECHOREPLY response packet.  All we do change the
  433.      *  packet type and flip the IP addresses.  This avoids a copy.
  434.      */
  435.     tmp = p_ptr->iph.ip_dst.s_addr;
  436.     p_ptr->iph.ip_dst.s_addr = p_ptr->iph.ip_src.s_addr;
  437.     p_ptr->iph.ip_src.s_addr = tmp;
  438.     p_ptr->icmph.type        = ICMP_ECHOREPLY;
  439.     p_ptr->icmph.checksum    = 0;
  440.     p_ptr->icmph.checksum    =
  441.                         ip_check((u_short *)&p_ptr->icmph,
  442.                         ntohs(p_ptr->iph.ip_len) - sizeof(struct ip));
  443.     sin.sin_family      = AF_INET;
  444.     sin.sin_addr.s_addr = p_ptr->iph.ip_dst.s_addr; 
  445.  
  446.     c = sendto(sock_fd,
  447.             (struct icmp_packet *)p_ptr,
  448.             ntohs(p_ptr->iph.ip_len),
  449.             0,
  450.             (struct sockaddr *) &sin, sizeof(sin));
  451.  
  452.     if (c != ntohs(p_ptr->iph.ip_len))
  453.     {
  454.         if (d) perror("truncated write");
  455.         return;
  456.     }
  457.     else if (d) fprintf(stderr, "ICMP_ECHOREPLY sent\n");
  458. }
  459.  
  460.  
  461. int
  462. verify(struct icmp_packet *p_ptr)
  463. {
  464.     if (!hosts_ctl("ping", 
  465.                     host_lookup(p_ptr->iph.ip_src.s_addr),
  466.                     host_lookup(p_ptr->iph.ip_src.s_addr),
  467.                     STRING_UNKNOWN))
  468.         return (0);
  469.  
  470.     else return (1);
  471. }
  472.  
  473.  
  474. void
  475. usage(char *argv0)
  476. {
  477.     fprintf(stderr, "usage: %s [-d 1|0 ] [-s maxpacketsize] \n",argv0);
  478.     exit(0);
  479. }
  480.  
  481.  
  482. /* EOF */
  483. <-->
  484. <++> Pingd/patchfile
  485. --- /usr/src/linux/net/ipv4/icmp.c.original    Sat Jan 10 11:10:36 1998
  486. +++ /usr/src/linux/net/ipv4/icmp.c    Sat Jan 10 11:19:23 1998
  487. @@ -42,7 +42,8 @@
  488.   *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  489.   *    Willy Konynenberg    :    Transparent proxy adapted to new
  490.   *                    socket hash code.
  491. - *
  492. + *              route           :       1.10.98:  ICMP_ECHO / ICMP_ECHOREQUEST
  493. + *                                      support into userland.
  494.   *
  495.   * RFC1122 (Host Requirements -- Comm. Layer) Status:
  496.   * (boy, are there a lot of rules for ICMP)
  497. @@ -882,28 +883,6 @@
  498.        kfree_skb(skb, FREE_READ);
  499.  }
  500.  
  501. -/*
  502. - *    Handle ICMP_ECHO ("ping") requests. 
  503. - *
  504. - *    RFC 1122: 3.2.2.6 MUST have an echo server that answers ICMP echo requests.
  505. - *    RFC 1122: 3.2.2.6 Data received in the ICMP_ECHO request MUST be included in the reply.
  506. - *    RFC 1812: 4.3.3.6 SHOULD have a config option for silently ignoring echo requests, MUST have default=NOT.
  507. - *    See also WRT handling of options once they are done and working.
  508. - */
  509. -static void icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len)
  510. -{
  511. -#ifndef CONFIG_IP_IGNORE_ECHO_REQUESTS
  512. -    struct icmp_bxm icmp_param;
  513. -    icmp_param.icmph=*icmph;
  514. -    icmp_param.icmph.type=ICMP_ECHOREPLY;
  515. -    icmp_param.data_ptr=(icmph+1);
  516. -    icmp_param.data_len=len;
  517. -    if (ip_options_echo(&icmp_param.replyopts, NULL, daddr, saddr, skb)==0)
  518. -        icmp_build_xmit(&icmp_param, daddr, saddr, skb->ip_hdr->tos);
  519. -#endif
  520. -    kfree_skb(skb, FREE_READ);
  521. -}
  522.  
  523.  /*
  524.   *    Handle ICMP Timestamp requests. 
  525. @@ -1144,8 +1123,8 @@
  526.   */
  527.   
  528.  static struct icmp_control icmp_pointers[19] = {
  529. -/* ECHO REPLY (0) */
  530. - { &icmp_statistics.IcmpOutEchoReps, &icmp_statistics.IcmpInEchoReps, icmp_discard, 0, NULL },
  531. +/* ECHO REPLY (0) - Disabled, we now do ICMP_ECHOREQUEST in userland */
  532. + { &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, NULL },
  533.   { &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, NULL },
  534.   { &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, NULL },
  535.  /* DEST UNREACH (3) */
  536. @@ -1156,8 +1135,8 @@
  537.   { &icmp_statistics.IcmpOutRedirects, &icmp_statistics.IcmpInRedirects, icmp_redirect, 1, &xrl_redirect },
  538.   { &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, NULL },
  539.   { &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, NULL },
  540. -/* ECHO (8) */
  541. - { &icmp_statistics.IcmpOutEchos, &icmp_statistics.IcmpInEchos, icmp_echo, 0, NULL },
  542. +/* ECHO (8) - Disabled, we now do ICMP_ECHOREQUEST in userland */
  543. + { &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, NULL },
  544.   { &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, NULL },
  545.   { &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, NULL },
  546.  /* TIME EXCEEDED (11) */
  547. <-->
  548.  
  549. ----[  EOF
  550.  
  551.