Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup IP fragmentation, TCP session and TLS sessions #4082

Merged
merged 13 commits into from
Sep 19, 2023
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
Loading