From d05de38207a1775adb6db6c5c3f635fc0c993dcd Mon Sep 17 00:00:00 2001 From: gpotter2 <10530980+gpotter2@users.noreply.github.com> Date: Sun, 12 Mar 2023 13:58:00 +0100 Subject: [PATCH] Several contrib layers cleanups (#3939) --- .config/ci/test.sh | 3 + pyproject.toml | 4 +- scapy/__init__.py | 2 +- scapy/contrib/ubberlogger.py | 119 ----------------------------------- scapy/contrib/wpa_eapol.py | 41 ------------ scapy/layers/eap.py | 106 ++++++++++++++++++++++++++++--- test/scapy/layers/eap.uts | 15 +++++ tox.ini | 14 ++--- 8 files changed, 124 insertions(+), 180 deletions(-) delete mode 100644 scapy/contrib/ubberlogger.py delete mode 100644 scapy/contrib/wpa_eapol.py diff --git a/.config/ci/test.sh b/.config/ci/test.sh index ef365c723a9..30e43fa2a8d 100755 --- a/.config/ci/test.sh +++ b/.config/ci/test.sh @@ -52,6 +52,9 @@ fi if python --version 2>&1 | grep -q PyPy then UT_FLAGS+=" -K not_pypy" + # Code coverage with PyPy makes it very, very slow. Tests work + # but take around 30minutes, so we disable it. + export DISABLE_COVERAGE=" " fi # libpcap diff --git a/pyproject.toml b/pyproject.toml index 382201cdce9..6ac3197697c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,8 +83,8 @@ version = { attr="scapy.VERSION" } concurrency = [ "thread", "multiprocessing" ] source = [ "scapy" ] omit = [ - # Scapy specific paths - "scapy/tools/UTscapy.py", + # Scapy tools + "scapy/tools/", # Scapy external modules "scapy/libs/six.py", "scapy/libs/winpcapy.py", diff --git a/scapy/__init__.py b/scapy/__init__.py index 72f73fd1f66..53cd99229cc 100644 --- a/scapy/__init__.py +++ b/scapy/__init__.py @@ -158,7 +158,7 @@ def _version(): VERSION = __version__ = _version() -_tmp = re.search(r"[0-9.]+", VERSION) +_tmp = re.search(r"([0-9]|\.[0-9])+", VERSION) VERSION_MAIN = _tmp.group() if _tmp is not None else VERSION if __name__ == "__main__": diff --git a/scapy/contrib/ubberlogger.py b/scapy/contrib/ubberlogger.py deleted file mode 100644 index 3fd509ae748..00000000000 --- a/scapy/contrib/ubberlogger.py +++ /dev/null @@ -1,119 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -# This file is part of Scapy -# See https://scapy.net/ for more information - -# Author: Sylvain SARMEJEANNE - -# scapy.contrib.description = Ubberlogger dissectors -# scapy.contrib.status = loads - -from scapy.packet import Packet, bind_layers -from scapy.fields import ByteEnumField, ByteField, IntField, ShortField - -# Syscalls known by Uberlogger -uberlogger_sys_calls = {0: "READ_ID", - 1: "OPEN_ID", - 2: "WRITE_ID", - 3: "CHMOD_ID", - 4: "CHOWN_ID", - 5: "SETUID_ID", - 6: "CHROOT_ID", - 7: "CREATE_MODULE_ID", - 8: "INIT_MODULE_ID", - 9: "DELETE_MODULE_ID", - 10: "CAPSET_ID", - 11: "CAPGET_ID", - 12: "FORK_ID", - 13: "EXECVE_ID"} - -# First part of the header - - -class Uberlogger_honeypot_caract(Packet): - name = "Uberlogger honeypot_caract" - fields_desc = [ByteField("honeypot_id", 0), - ByteField("reserved", 0), - ByteField("os_type_and_version", 0)] - -# Second part of the header - - -class Uberlogger_uber_h(Packet): - name = "Uberlogger uber_h" - fields_desc = [ByteEnumField("syscall_type", 0, uberlogger_sys_calls), - IntField("time_sec", 0), - IntField("time_usec", 0), - IntField("pid", 0), - IntField("uid", 0), - IntField("euid", 0), - IntField("cap_effective", 0), - IntField("cap_inheritable", 0), - IntField("cap_permitted", 0), - IntField("res", 0), - IntField("length", 0)] - -# The 9 following classes are options depending on the syscall type - - -class Uberlogger_capget_data(Packet): - name = "Uberlogger capget_data" - fields_desc = [IntField("target_pid", 0)] - - -class Uberlogger_capset_data(Packet): - name = "Uberlogger capset_data" - fields_desc = [IntField("target_pid", 0), - IntField("effective_cap", 0), - IntField("permitted_cap", 0), - IntField("inheritable_cap", 0)] - - -class Uberlogger_chmod_data(Packet): - name = "Uberlogger chmod_data" - fields_desc = [ShortField("mode", 0)] - - -class Uberlogger_chown_data(Packet): - name = "Uberlogger chown_data" - fields_desc = [IntField("uid", 0), - IntField("gid", 0)] - - -class Uberlogger_open_data(Packet): - name = "Uberlogger open_data" - fields_desc = [IntField("flags", 0), - IntField("mode", 0)] - - -class Uberlogger_read_data(Packet): - name = "Uberlogger read_data" - fields_desc = [IntField("fd", 0), - IntField("count", 0)] - - -class Uberlogger_setuid_data(Packet): - name = "Uberlogger setuid_data" - fields_desc = [IntField("uid", 0)] - - -class Uberlogger_create_module_data(Packet): - name = "Uberlogger create_module_data" - fields_desc = [IntField("size", 0)] - - -class Uberlogger_execve_data(Packet): - name = "Uberlogger execve_data" - fields_desc = [IntField("nbarg", 0)] - - -# Layer bounds for Uberlogger -bind_layers(Uberlogger_honeypot_caract, Uberlogger_uber_h) -bind_layers(Uberlogger_uber_h, Uberlogger_capget_data) -bind_layers(Uberlogger_uber_h, Uberlogger_capset_data) -bind_layers(Uberlogger_uber_h, Uberlogger_chmod_data) -bind_layers(Uberlogger_uber_h, Uberlogger_chown_data) -bind_layers(Uberlogger_uber_h, Uberlogger_open_data) -bind_layers(Uberlogger_uber_h, Uberlogger_read_data) -bind_layers(Uberlogger_uber_h, Uberlogger_setuid_data) -bind_layers(Uberlogger_uber_h, Uberlogger_create_module_data) -bind_layers(Uberlogger_uber_h, Uberlogger_execve_data) diff --git a/scapy/contrib/wpa_eapol.py b/scapy/contrib/wpa_eapol.py deleted file mode 100644 index 77853d12c7e..00000000000 --- a/scapy/contrib/wpa_eapol.py +++ /dev/null @@ -1,41 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -# This file is part of Scapy -# See https://scapy.net/ for more information - -# scapy.contrib.description = WPA EAPOL-KEY -# scapy.contrib.status = loads - -from scapy.packet import Packet, bind_layers -from scapy.fields import ByteField, LenField, ShortField, StrFixedLenField, \ - StrLenField -from scapy.layers.eap import EAPOL - - -class WPA_key(Packet): - name = "WPA_key" - fields_desc = [ByteField("descriptor_type", 1), - ShortField("key_info", 0), - LenField("len", None, "H"), - StrFixedLenField("replay_counter", "", 8), - StrFixedLenField("nonce", "", 32), - StrFixedLenField("key_iv", "", 16), - StrFixedLenField("wpa_key_rsc", "", 8), - StrFixedLenField("wpa_key_id", "", 8), - StrFixedLenField("wpa_key_mic", "", 16), - LenField("wpa_key_length", None, "H"), - StrLenField("wpa_key", "", length_from=lambda pkt:pkt.wpa_key_length)] # noqa: E501 - - def extract_padding(self, s): - tmp_len = self.len - return s[:tmp_len], s[tmp_len:] - - def hashret(self): - return chr(self.type) + self.payload.hashret() - - def answers(self, other): - if isinstance(other, WPA_key): - return 1 - return 0 - - -bind_layers(EAPOL, WPA_key, type=3) diff --git a/scapy/layers/eap.py b/scapy/layers/eap.py index de3707e0591..64579888d1f 100644 --- a/scapy/layers/eap.py +++ b/scapy/layers/eap.py @@ -10,11 +10,35 @@ import struct -from scapy.fields import BitField, ByteField, XByteField,\ - ShortField, IntField, XIntField, ByteEnumField, StrLenField, XStrField,\ - XStrLenField, XStrFixedLenField, LenField, FieldLenField, FieldListField,\ - PacketField, PacketListField, ConditionalField, PadField -from scapy.packet import Packet, Padding, bind_layers +from scapy.fields import ( + BitEnumField, + BitField, + ByteEnumField, + ByteField, + ConditionalField, + FieldLenField, + FieldListField, + IntField, + LenField, + LongField, + PacketField, + PacketListField, + PadField, + ShortField, + StrLenField, + XByteField, + XIntField, + XStrField, + XStrFixedLenField, + XStrLenField, +) +from scapy.packet import ( + Packet, + Padding, + bind_bottom_up, + bind_layers, + bind_top_down, +) from scapy.layers.l2 import SourceMACField, Ether, CookedLinux, GRE, SNAP from scapy.config import conf from scapy.compat import orb, chb @@ -404,6 +428,64 @@ class LEAP(EAP): ] +############################################################################# +# IEEE 802.1X-2010 - EAPOL-Key +############################################################################# + +# sect 11.9 of 802.1X-2010 +# AND sect 12.7.2 of 802.11-2016 + + +class EAPOL_KEY(Packet): + name = "EAPOL_KEY" + fields_desc = [ + ByteEnumField("key_descriptor_type", 1, {1: "RC4", 2: "RSN"}), + # Key Information + BitEnumField("key_descriptor_type_version", 0, 3, { + 1: "HMAC-MD5+ARC4", + 2: "HMAC-SHA1-128+AES-128", + 3: "AES-128-CMAC+AES-128", + }), + BitEnumField("key_type", 0, 1, {0: "Group/SMK", 1: "Pairwise"}), + BitField("res", 0, 2), + BitField("install", 0, 1), + BitField("key_ack", 0, 1), + BitField("has_key_mic", 1, 1), + BitField("secure", 0, 1), + BitField("error", 0, 1), + BitField("request", 0, 1), + BitField("encrypted_key_data", 0, 1), + BitField("smk_message", 0, 1), + BitField("res2", 0, 2), + # + LenField("len", None, "H"), + LongField("key_replay_counter", 0), + XStrFixedLenField("key_nonce", "", 32), + XStrFixedLenField("key_iv", "", 16), + XStrFixedLenField("key_rsc", "", 8), + XStrFixedLenField("key_id", "", 8), + ConditionalField( + XStrFixedLenField("key_mic", "", 16), # XXX size can be 24 + lambda pkt: pkt.has_key_mic + ), + LenField("key_length", None, "H"), + XStrLenField("key", "", + length_from=lambda pkt: pkt.key_length) + ] + + def extract_padding(self, s): + return s[:self.len], s[self.len:] + + def hashret(self): + return struct.pack("!B", self.type) + self.payload.hashret() + + def answers(self, other): + if isinstance(other, EAPOL_KEY) and \ + other.descriptor_type == self.descriptor_type: + return 1 + return 0 + + ############################################################################# # IEEE 802.1X-2010 - MACsec Key Agreement (MKA) protocol ############################################################################# @@ -765,10 +847,14 @@ def extract_padding(self, s): return "", s -bind_layers(Ether, EAPOL, type=34958) -bind_layers(Ether, EAPOL, dst='01:80:c2:00:00:03', type=34958) -bind_layers(CookedLinux, EAPOL, proto=34958) -bind_layers(GRE, EAPOL, proto=34958) +# Bind EAPOL types bind_layers(EAPOL, EAP, type=0) -bind_layers(SNAP, EAPOL, code=34958) +bind_layers(EAPOL, EAPOL_KEY, type=3) bind_layers(EAPOL, MKAPDU, type=5) + +bind_bottom_up(Ether, EAPOL, type=0x888e) +# the reserved IEEE Std 802.1X PAE address +bind_top_down(Ether, EAPOL, dst='01:80:c2:00:00:03', type=0x888e) +bind_layers(CookedLinux, EAPOL, proto=0x888e) +bind_layers(SNAP, EAPOL, code=0x888e) +bind_layers(GRE, EAPOL, proto=0x888e) diff --git a/test/scapy/layers/eap.uts b/test/scapy/layers/eap.uts index 69b5b1b81a0..51616281781 100644 --- a/test/scapy/layers/eap.uts +++ b/test/scapy/layers/eap.uts @@ -52,6 +52,21 @@ assert eapol.type == 0 assert eapol.len == 60 assert eapol.haslayer(EAP_FAST) +############ +############ ++ EAPOL-Key class tests + += EAPOK-Key - over 802.11 - Dissection +s = b'\x08\x02:\x01\x00\xc0\xcab\xa4\xf6\x00"k\xfbI+\x00"k\xfbI+\xa0[\xaa\xaa\x03\x00\x00\x00\x88\x8e\x02\x03\x00u\x02\x00\x8a\x00\x10\x00\x00\x00\x00\x00\x00\x00\x04\x95X{I5\':3\x8f\x90\xb1I\xae\x1f\xd7-"\x82\x1e\\$\xefC=\x83\x97?M\xd6\xdf>\x9b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\xdd\x14\x00\x0f\xac\x04\x03\xca?d\xca\xed\xdd\xef\xf69;\xefX\xd4\x97w' +wifi = Dot11(s) +assert wifi[EAPOL].key_descriptor_type == 2 +assert wifi[EAPOL].key_type == 0 +assert wifi[EAPOL].has_key_mic == 1 +assert wifi[EAPOL].encrypted_key_data == 1 +assert wifi[EAPOL].key_replay_counter == 4 +assert wifi[EAPOL].key_mic == b"\x00" * 16 +assert wifi[EAPOL].key_length == 22 +assert len(wifi[EAPOL].key) == 22 ############ ############ diff --git a/tox.ini b/tox.ini index cd85bd7c808..34aa2a6525e 100644 --- a/tox.ini +++ b/tox.ini @@ -39,13 +39,13 @@ platform = bsd_non_root,bsd_root: darwin|freebsd|openbsd|netbsd windows: win32 commands = - linux_non_root: {envpython} {env:SCAPY_PY_OPTS:-m coverage run} -m scapy.tools.UTscapy -c ./test/configs/linux.utsc -N {posargs} - linux_root: sudo -E {envpython} {env:SCAPY_PY_OPTS:-m coverage run} -m scapy.tools.UTscapy -c ./test/configs/linux.utsc {posargs} - bsd_non_root: {envpython} {env:SCAPY_PY_OPTS:-m coverage run} -m scapy.tools.UTscapy -c test/configs/bsd.utsc -K manufdb -K tshark -N {posargs} - bsd_root: sudo -E {envpython} {env:SCAPY_PY_OPTS:-m coverage run} -m scapy.tools.UTscapy -c test/configs/bsd.utsc -K manufdb -K tshark {posargs} - windows: {envpython} {env:SCAPY_PY_OPTS:-m coverage run} -m scapy.tools.UTscapy -c test/configs/windows.utsc {posargs} - coverage combine - coverage xml -i + linux_non_root: {envpython} {env:DISABLE_COVERAGE:-m coverage run} -m scapy.tools.UTscapy -c ./test/configs/linux.utsc -N {posargs} + linux_root: sudo -E {envpython} {env:DISABLE_COVERAGE:-m coverage run} -m scapy.tools.UTscapy -c ./test/configs/linux.utsc {posargs} + bsd_non_root: {envpython} {env:DISABLE_COVERAGE:-m coverage run} -m scapy.tools.UTscapy -c test/configs/bsd.utsc -K manufdb -K tshark -N {posargs} + bsd_root: sudo -E {envpython} {env:DISABLE_COVERAGE:-m coverage run} -m scapy.tools.UTscapy -c test/configs/bsd.utsc -K manufdb -K tshark {posargs} + windows: {envpython} {env:DISABLE_COVERAGE:-m coverage run} -m scapy.tools.UTscapy -c test/configs/windows.utsc {posargs} + {env:DISABLE_COVERAGE:coverage combine} + {env:DISABLE_COVERAGE:coverage xml -i} # Variants of the main tests