Skip to content

Commit

Permalink
Cleanup IP fragmentation, TCP session and TLS sessions (#4082)
Browse files Browse the repository at this point in the history
  • Loading branch information
gpotter2 authored Sep 19, 2023
1 parent f311149 commit 219f2fe
Show file tree
Hide file tree
Showing 51 changed files with 707 additions and 514 deletions.
4 changes: 2 additions & 2 deletions doc/scapy/layers/automotive.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1158,7 +1158,7 @@ then casted to ``UDS`` objects through the ``basecls`` parameter
Usage example::

with PcapReader("test/contrib/automotive/ecu_trace.pcap") as sock:
udsmsgs = sniff(session=ISOTPSession, session_kwargs={"use_ext_addr":False, "basecls":UDS}, count=50, opened_socket=sock)
udsmsgs = sniff(session=ISOTPSession(use_ext_addr=False, basecls=UDS), count=50, opened_socket=sock)


ecu = Ecu()
Expand All @@ -1183,7 +1183,7 @@ Usage example::
session = EcuSession()

with PcapReader("test/contrib/automotive/ecu_trace.pcap") as sock:
udsmsgs = sniff(session=ISOTPSession, session_kwargs={"supersession": session, "use_ext_addr":False, "basecls":UDS}, count=50, opened_socket=sock)
udsmsgs = sniff(session=ISOTPSession(use_ext_addr=False, basecls=UDS, supersession=session)), count=50, opened_socket=sock)

ecu = session.ecu
print(ecu.log)
Expand Down
3 changes: 1 addition & 2 deletions doc/scapy/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -783,9 +783,8 @@ Those sessions can be used using the ``session=`` parameter of ``sniff()``. Exam

.. note::
To implement your own Session class, in order to support another flow-based protocol, start by copying a sample from `scapy/sessions.py <https://github.com/secdev/scapy/blob/master/scapy/sessions.py>`_
Your custom ``Session`` class only needs to extend the :py:class:`~scapy.sessions.DefaultSession` class, and implement a ``on_packet_received`` function, such as in the example.
Your custom ``Session`` class only needs to extend the :py:class:`~scapy.sessions.DefaultSession` class, and implement a ``process`` or a ``recv`` function, such as in the examples.

.. note:: Would you need it, you can use: ``class TLS_over_TCP(TLSSession, TCPSession): pass`` to sniff TLS packets that are defragmented.

How to use TCPSession to defragment TCP packets
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
4 changes: 2 additions & 2 deletions scapy/arch/bpf/supersocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,9 +425,9 @@ def nonblock_recv(self):

class L3bpfSocket(L2bpfSocket):

def recv(self, x=BPF_BUFFER_LENGTH):
def recv(self, x=BPF_BUFFER_LENGTH, **kwargs):
"""Receive on layer 3"""
r = SuperSocket.recv(self, x)
r = SuperSocket.recv(self, x, **kwargs)
if r:
r.payload.time = r.time
return r.payload
Expand Down
9 changes: 5 additions & 4 deletions scapy/arch/libpcap.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,14 @@
import scapy.consts

from typing import (
cast,
Any,
Dict,
List,
NoReturn,
Optional,
Tuple,
Type,
cast,
)

if not scapy.consts.WINDOWS:
Expand Down Expand Up @@ -571,9 +572,9 @@ def send(self, x):
class L3pcapSocket(L2pcapSocket):
desc = "read/write packets at layer 3 using only libpcap"

def recv(self, x=MTU):
# type: (int) -> Optional[Packet]
r = L2pcapSocket.recv(self, x)
def recv(self, x=MTU, **kwargs):
# type: (int, **Any) -> Optional[Packet]
r = L2pcapSocket.recv(self, x, **kwargs)
if r:
r.payload.time = r.time
return r.payload
Expand Down
6 changes: 3 additions & 3 deletions scapy/arch/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -593,9 +593,9 @@ def send(self, x):
class L3PacketSocket(L2Socket):
desc = "read/write packets at layer 3 using Linux PF_PACKET sockets"

def recv(self, x=MTU):
# type: (int) -> Optional[Packet]
pkt = SuperSocket.recv(self, x)
def recv(self, x=MTU, **kwargs):
# type: (int, **Any) -> Optional[Packet]
pkt = SuperSocket.recv(self, x, **kwargs)
if pkt and self.lvl == 2:
pkt.payload.time = pkt.time
return pkt.payload
Expand Down
8 changes: 5 additions & 3 deletions scapy/automaton.py
Original file line number Diff line number Diff line change
Expand Up @@ -636,11 +636,13 @@ def fileno(self):
# type: () -> int
return self.spb.fileno()

def recv(self, n=MTU):
# type: (Optional[int]) -> Any
# note: _ATMT_supersocket may return bytes in certain cases, which
# is expected. We cheat on typing.
def recv(self, n=MTU, **kwargs): # type: ignore
# type: (int, **Any) -> Any
r = self.spb.recv(n)
if self.proto is not None and r is not None:
r = self.proto(r)
r = self.proto(r, **kwargs)
return r

def close(self):
Expand Down
20 changes: 19 additions & 1 deletion scapy/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,13 @@ def _iface_changer(attr, val, old):
return val # type: ignore


def _reset_tls_nss_keys(attr, val, old):
# type: (str, Any, Any) -> Any
"""Reset conf.tls_nss_keys when conf.tls_nss_filename changes"""
conf.tls_nss_keys = None
return val


class Conf(ConfClass):
"""
This object contains the configuration of Scapy.
Expand Down Expand Up @@ -775,7 +782,8 @@ class Conf(ConfClass):
filter = ""
#: when 1, store received packet that are not matched into `debug.recv`
debug_match = False
#: When 1, print some TLS session secrets when they are computed.
#: When 1, print some TLS session secrets when they are computed, and
#: warn about the session recognition.
debug_tls = False
wepkey = ""
#: holds the Scapy interface list and manager
Expand Down Expand Up @@ -901,6 +909,16 @@ class Conf(ConfClass):
#: a safety mechanism: the maximum amount of items included in a PacketListField
#: or a FieldListField
max_list_count = 100
#: When the TLS module is loaded (not by default), the following turns on sessions
tls_session_enable = False
#: Filename containing NSS Keys Log
tls_nss_filename = Interceptor(
"tls_nss_filename",
None,
_reset_tls_nss_keys
)
#: Dictionary containing parsed NSS Keys
tls_nss_keys = None

def __getattribute__(self, attr):
# type: (str) -> Any
Expand Down
13 changes: 7 additions & 6 deletions scapy/contrib/automotive/bmw/hsfz.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from scapy.data import MTU

from typing import (
Any,
Optional,
Tuple,
Type,
Expand Down Expand Up @@ -88,8 +89,8 @@ def __init__(self, ip='127.0.0.1', port=6801):
StreamSocket.__init__(self, s, HSFZ)
self.buffer = b""

def recv(self, x=MTU):
# type: (int) -> Optional[Packet]
def recv(self, x=MTU, **kwargs):
# type: (int, **Any) -> Optional[Packet]
if self.buffer:
len_data = self.buffer[:4]
else:
Expand All @@ -104,7 +105,7 @@ def recv(self, x=MTU):
if len(self.buffer) != len_int:
return None

pkt = self.basecls(self.buffer) # type: Packet
pkt = self.basecls(self.buffer, **kwargs) # type: Packet
self.buffer = b""
return pkt

Expand Down Expand Up @@ -141,11 +142,11 @@ def send(self, x):
self.close()
return 0

def recv(self, x=MTU):
# type: (int) -> Optional[Packet]
def recv(self, x=MTU, **kwargs):
# type: (int, **Any) -> Optional[Packet]
pkt = super(UDS_HSFZSocket, self).recv(x)
if pkt:
return self.outputcls(bytes(pkt.payload))
return self.outputcls(bytes(pkt.payload), **kwargs)
else:
return pkt

Expand Down
13 changes: 7 additions & 6 deletions scapy/contrib/automotive/doip.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from scapy.data import MTU

from typing import (
Any,
Union,
Tuple,
Optional,
Expand Down Expand Up @@ -294,8 +295,8 @@ def __init__(self, ip='127.0.0.1', port=13400, activate_routing=True,
self._activate_routing(
source_address, target_address, activation_type, reserved_oem)

def recv(self, x=MTU):
# type: (int) -> Optional[Packet]
def recv(self, x=MTU, **kwargs):
# type: (int, **Any) -> Optional[Packet]
if self.buffer:
len_data = self.buffer[:8]
else:
Expand All @@ -310,7 +311,7 @@ def recv(self, x=MTU):
if len(self.buffer) != len_int:
return None

pkt = self.basecls(self.buffer) # type: Packet
pkt = self.basecls(self.buffer, **kwargs) # type: Packet
self.buffer = b""
return pkt

Expand Down Expand Up @@ -407,9 +408,9 @@ def send(self, x):

return super(UDS_DoIPSocket, self).send(pkt)

def recv(self, x=MTU):
# type: (int) -> Optional[Packet]
pkt = super(UDS_DoIPSocket, self).recv(x)
def recv(self, x=MTU, **kwargs):
# type: (int, **Any) -> Optional[Packet]
pkt = super(UDS_DoIPSocket, self).recv(x, **kwargs)
if pkt and pkt.payload_type == 0x8001:
return pkt.payload
else:
Expand Down
9 changes: 4 additions & 5 deletions scapy/contrib/automotive/ecu.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,17 +469,16 @@ class EcuSession(DefaultSession):
"""
def __init__(self, *args, **kwargs):
# type: (Any, Any) -> None
DefaultSession.__init__(self, *args, **kwargs)
self.ecu = Ecu(logging=kwargs.pop("logging", True),
verbose=kwargs.pop("verbose", True),
store_supported_responses=kwargs.pop("store_supported_responses", True)) # noqa: E501
super(EcuSession, self).__init__(*args, **kwargs)

def on_packet_received(self, pkt):
# type: (Optional[Packet]) -> None
def process(self, pkt: Packet) -> Optional[Packet]:
if not pkt:
return
return None
self.ecu.update(pkt)
DefaultSession.on_packet_received(self, pkt)
return pkt


class EcuResponse:
Expand Down
7 changes: 4 additions & 3 deletions scapy/contrib/isotp/isotp_native_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

# Typing imports
from typing import (
Any,
Optional,
Union,
Tuple,
Expand Down Expand Up @@ -387,9 +388,9 @@ def recv_raw(self, x=0xffff):
ts = get_last_packet_timestamp(self.ins)
return self.basecls, pkt, ts

def recv(self, x=0xffff):
# type: (int) -> Optional[Packet]
msg = SuperSocket.recv(self, x)
def recv(self, x=0xffff, **kwargs):
# type: (int, **Any) -> Optional[Packet]
msg = SuperSocket.recv(self, x, **kwargs)
if msg is None:
return msg

Expand Down
6 changes: 3 additions & 3 deletions scapy/contrib/isotp/isotp_soft_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,9 @@ def recv_raw(self, x=0xffff):
return self.basecls, tup[0], float(tup[1])
return self.basecls, None, None

def recv(self, x=0xffff):
# type: (int) -> Optional[Packet]
msg = super(ISOTPSoftSocket, self).recv(x)
def recv(self, x=0xffff, **kwargs):
# type: (int, **Any) -> Optional[Packet]
msg = super(ISOTPSoftSocket, self).recv(x, **kwargs)
if msg is None:
return None

Expand Down
22 changes: 14 additions & 8 deletions scapy/contrib/isotp/isotp_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@
from scapy.utils import EDecimal
from scapy.packet import Packet
from scapy.sessions import DefaultSession
from scapy.supersocket import SuperSocket
from scapy.contrib.isotp.isotp_packet import ISOTP, N_PCI_CF, N_PCI_SF, \
N_PCI_FF, N_PCI_FC

# Typing imports
from typing import (
cast,
Iterable,
Iterator,
Optional,
Union,
List,
Expand Down Expand Up @@ -336,20 +339,23 @@ class ISOTPSession(DefaultSession):

def __init__(self, *args, **kwargs):
# type: (Any, Any) -> None
super(ISOTPSession, self).__init__(*args, **kwargs)
self.m = ISOTPMessageBuilder(
use_ext_address=kwargs.pop("use_ext_address", None),
rx_id=kwargs.pop("rx_id", None),
basecls=kwargs.pop("basecls", ISOTP))
super(ISOTPSession, self).__init__(*args, **kwargs)

def on_packet_received(self, pkt):
# type: (Optional[Packet]) -> None
def recv(self, sock: SuperSocket) -> Iterator[Packet]:
"""
Will be called by sniff() to ask for a packet
"""
pkt = sock.recv()
if not pkt:
return
self.m.feed(pkt)
while len(self.m) > 0:
rcvd = self.m.pop()
if self._supersession:
self._supersession.on_packet_received(rcvd)
else:
super(ISOTPSession, self).on_packet_received(rcvd)
rcvd = cast(Optional[Packet], self.m.pop())
if rcvd:
rcvd = self.process(rcvd)
if rcvd:
yield rcvd
14 changes: 9 additions & 5 deletions scapy/layers/dcerpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@
EPacketListField,
)

# Typing imports
from typing import (
Optional,
)


# DCE/RPC Packet
DCE_RPC_TYPE = {
Expand Down Expand Up @@ -1895,12 +1900,11 @@ def _process_dcerpc_packet(self, pkt):
pkt = self._parse_with_opnum(pkt, opnum, opts)
return pkt

def on_packet_received(self, pkt):
def process(self, pkt: Packet) -> Optional[Packet]:
if DceRpc5 in pkt:
return super(DceRpcSession, self).on_packet_received(
self._process_dcerpc_packet(pkt)
)
return super(DceRpcSession, self).on_packet_received(pkt)
return self._process_dcerpc_packet(pkt)
else:
return pkt


# --- TODO cleanup below
Expand Down
Loading

0 comments on commit 219f2fe

Please sign in to comment.