From 3d593d0df188f06132dfa560a7d264d999dc4a64 Mon Sep 17 00:00:00 2001 From: Mutti Kocbayindiran Date: Tue, 21 Feb 2023 01:10:35 -0800 Subject: [PATCH 1/4] Ensure that packet is set to p param to type of bytes. In certain situations where bgp fields for open messages where being set, this function was initializing packet variable to a str and returning. Fix bgp path_attrib length field. Should be type byte, not int. Adding fuzz() support to bgp open and update. Allow for BGPFieldLenField len to be fuzzed, but fixed when recalled. --- scapy/contrib/bgp.py | 62 ++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/scapy/contrib/bgp.py b/scapy/contrib/bgp.py index af988cf08df..180695f47f7 100644 --- a/scapy/contrib/bgp.py +++ b/scapy/contrib/bgp.py @@ -12,6 +12,7 @@ import struct import re import socket +from typing import Any from scapy import pton_ntop from scapy.packet import Packet, Packet_metaclass, bind_layers @@ -27,6 +28,7 @@ from scapy.config import conf, ConfClass from scapy.compat import orb, chb from scapy.error import log_runtime +from scapy.volatile import RandShort, RandString, RandByte, RandIP, RandNum # @@ -117,25 +119,50 @@ def i2m(self, pkt, i): """"Internal" (IP as bytes, mask as int) to "machine" representation.""" mask, ip = i - ip = socket.inet_aton(ip) + ip = socket.inet_aton(str(ip)) return struct.pack(">B", mask) + ip[:self.mask2iplen(mask)] def addfield(self, pkt, s, val): return s + self.i2m(pkt, val) def getfield(self, pkt, s): + '''pkt: BGPNLRI_IPv4 + s: bytes byte 0: network prefix, byte 1+: network + e.g. + s = b'\x18\x03\x03\x03' 24/3.3.3 + ''' length = self.mask2iplen(orb(s[0])) + 1 return s[length:], self.m2i(pkt, s[:length]) def m2i(self, pkt, m): + '''TODO: Need to figure out a way to prevent inet_ntoa errors when it comes + to fuzzing exercises.''' mask = orb(m[0]) mask2iplen_res = self.mask2iplen(mask) - ip = b"".join(m[i + 1:i + 2] if i < mask2iplen_res else b"\x00" for i in range(4)) # noqa: E501 + + ip = b"".join( + m[i + 1:i + 2] if i < mask2iplen_res and m[i + 1:i + 2] else b"\x00" for i in range(4)) + # Pad network address with 0's to make valid ip address return (mask, socket.inet_ntoa(ip)) + def randval(self): + # TODO: replace IPField with RandString() + return (RandNetMask(0, 32), RandIP()) + + +class RandNetMask(RandNum): + def __init__(self, min=0, max=2**8 - 1): + # type: (int, int) -> None + """ + :param int min: Minimum range for random value. + :param int max: Maximum range for random value. + """ + RandNum.__init__(self, min, max) + class BGPFieldIPv6(Field): """IPv6 Field (CIDR)""" + # TODO: Add support for fuzz() def mask2iplen(self, mask): """Get the IP field mask length (in bytes).""" @@ -449,7 +476,7 @@ class BGPHeader(Packet): 0xffffffffffffffffffffffffffffffff, 0x80 ), - ShortField("len", None), + ShortField("len", 0), # default = 0, enables field for fuzz() ByteEnumField("type", 4, _bgp_message_types) ] @@ -485,7 +512,7 @@ def _bgp_dispatcher(payload): cls = _get_cls("BGPHeader", conf.raw_layer) else: - if len(payload) >= _BGP_HEADER_SIZE and\ + if len(payload) >= _BGP_HEADER_SIZE and \ payload[:16] == _BGP_HEADER_MARKER: # Get BGP message type @@ -633,7 +660,7 @@ class _BGPCapability_metaclass(_BGPCap_metaclass, Packet_metaclass): pass -class BGPCapability(Packet, metaclass=_BGPCapability_metaclass): +class BGPCapability(six.with_metaclass(_BGPCapability_metaclass, Packet)): # type: ignore """ Generic BGP capability. """ @@ -677,7 +704,7 @@ class BGPCapGeneric(BGPCapability): fields_desc = [ ByteEnumField("code", 0, _capabilities), FieldLenField("length", None, fmt="B", length_of="cap_data"), - StrLenField("cap_data", '', + StrLenField("cap_data", b'', length_from=lambda p: p.length, max_length=255), ] @@ -888,7 +915,6 @@ class BGPAuthenticationInformation(Packet): # Optional Parameter. # - class BGPOptParamPacketListField(PacketListField): """ PacketListField handling the optional parameters (OPEN message). @@ -896,6 +922,7 @@ class BGPOptParamPacketListField(PacketListField): def getfield(self, pkt, s): lst = [] + ret = b'' length = 0 if self.length_from is not None: @@ -954,7 +981,7 @@ def post_build(self, p, pay): length = len(p) - \ 2 # parameter type (1 byte) - parameter length (1 byte) packet = p[:1] + chb(length) - if (self.param_type == 2 and self.param_value is not None) or\ + if (self.param_type == 2 and self.param_value is not None) or \ (self.param_type == 1 and self.authentication_data is not None): # noqa: E501 packet = packet + p[2:] @@ -1789,8 +1816,9 @@ def __init__(self, name, default, enum_from=None): self.enum_from = enum_from def i2repr(self, pkt, i): - enum = self.enum_from(pkt) - return enum.get(i, i) + if self.enum_from: + enum = self.enum_from(pkt) + return enum.get(i, i) class BGPPAExtCommunity(Packet): @@ -2127,7 +2155,7 @@ def post_build(self, p, pay): if extended_length: packet = packet + p[2:4] else: - packet = packet + p[2] + packet = packet + p[2].to_bytes(1, byteorder='big') else: if extended_length: packet = packet + struct.pack("!H", length) @@ -2157,9 +2185,9 @@ class BGPUpdate(BGP): name = "UPDATE" fields_desc = [ - FieldLenField( + BGPFieldLenField( "withdrawn_routes_len", - None, + 0, length_of="withdrawn_routes", fmt="!H" ), @@ -2169,9 +2197,9 @@ class BGPUpdate(BGP): "IPv4", length_from=lambda p: p.withdrawn_routes_len ), - FieldLenField( + BGPFieldLenField( "path_attr_len", - None, + 0, length_of="path_attr", fmt="!H" ), @@ -2185,8 +2213,10 @@ class BGPUpdate(BGP): ] def post_build(self, p, pay): + # type: (bytes, bytes) -> bytes subpacklen = lambda p: len(p) - packet = "" + packet = p + if self.withdrawn_routes_len is None: wl = sum(map(subpacklen, self.withdrawn_routes)) packet = p[:0] + struct.pack("!H", wl) + p[2:] From 67e03f40dfd6f84e4a79c230267f118a8213e39d Mon Sep 17 00:00:00 2001 From: Mutti Date: Sun, 5 Mar 2023 13:55:04 -0800 Subject: [PATCH 2/4] Update bgp.py --- scapy/contrib/bgp.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scapy/contrib/bgp.py b/scapy/contrib/bgp.py index 180695f47f7..95b68984fc7 100644 --- a/scapy/contrib/bgp.py +++ b/scapy/contrib/bgp.py @@ -12,7 +12,6 @@ import struct import re import socket -from typing import Any from scapy import pton_ntop from scapy.packet import Packet, Packet_metaclass, bind_layers From 6cc34d845eebea2421f3bc7c640791fd2d3bbae8 Mon Sep 17 00:00:00 2001 From: Mutti Date: Sun, 5 Mar 2023 13:59:12 -0800 Subject: [PATCH 3/4] Update scapy/contrib/bgp.py Co-authored-by: gpotter2 <10530980+gpotter2@users.noreply.github.com> --- scapy/contrib/bgp.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scapy/contrib/bgp.py b/scapy/contrib/bgp.py index 95b68984fc7..7725e27100a 100644 --- a/scapy/contrib/bgp.py +++ b/scapy/contrib/bgp.py @@ -125,11 +125,13 @@ def addfield(self, pkt, s, val): return s + self.i2m(pkt, val) def getfield(self, pkt, s): - '''pkt: BGPNLRI_IPv4 - s: bytes byte 0: network prefix, byte 1+: network - e.g. + """ + :param pkt: BGPNLRI_IPv4 + :param s: bytes byte 0: network prefix, byte 1+: network + + example: s = b'\x18\x03\x03\x03' 24/3.3.3 - ''' + """" length = self.mask2iplen(orb(s[0])) + 1 return s[length:], self.m2i(pkt, s[:length]) From 9ba88adf94be5679d0dbbee76ff77e343437471e Mon Sep 17 00:00:00 2001 From: Mutti Date: Wed, 7 Feb 2024 20:59:59 -0800 Subject: [PATCH 4/4] Update bgp.py Removed extra double quote comments --- scapy/contrib/bgp.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scapy/contrib/bgp.py b/scapy/contrib/bgp.py index 7725e27100a..ea8469bd630 100644 --- a/scapy/contrib/bgp.py +++ b/scapy/contrib/bgp.py @@ -102,7 +102,7 @@ def h2i(self, pkt, h): return int(mask), ip def i2h(self, pkt, i): - """"Internal" representation to "human" representation + """Internal" representation to "human" representation (x.x.x.x/y).""" mask, ip = i return ip + "/" + str(mask) @@ -115,7 +115,7 @@ def i2len(self, pkt, i): return self.mask2iplen(mask) + 1 def i2m(self, pkt, i): - """"Internal" (IP as bytes, mask as int) to "machine" + """Internal" (IP as bytes, mask as int) to "machine" representation.""" mask, ip = i ip = socket.inet_aton(str(ip)) @@ -131,7 +131,7 @@ def getfield(self, pkt, s): example: s = b'\x18\x03\x03\x03' 24/3.3.3 - """" + """ length = self.mask2iplen(orb(s[0])) + 1 return s[length:], self.m2i(pkt, s[:length]) @@ -175,7 +175,7 @@ def h2i(self, pkt, h): return int(mask), ip def i2h(self, pkt, i): - """"Internal" representation to "human" representation.""" + """Internal" representation to "human" representation.""" mask, ip = i return ip + "/" + str(mask) @@ -187,7 +187,7 @@ def i2len(self, pkt, i): return self.mask2iplen(mask) + 1 def i2m(self, pkt, i): - """"Internal" (IP as bytes, mask as int) to "machine" representation.""" # noqa: E501 + """Internal" (IP as bytes, mask as int) to "machine" representation.""" # noqa: E501 mask, ip = i ip = pton_ntop.inet_pton(socket.AF_INET6, ip) return struct.pack(">B", mask) + ip[:self.mask2iplen(mask)]