Search code examples
pythonscapy

Scapy layers: dissection/decoding of layers goes wrong


I'm trying to implement a protocol (S7Comm). This protocol contains several layers (it is an ISO on TCP protocol, where the payload of the ISO on TCP is the S7Comm telegram). The code I'm using is based on the s7scan (https://github.com/klsecservices/s7scan) and is based on Python 2. I've already migrated the code to Python 3. (Using Python 3.11 and Scapy 2.5.0)

When connecting to the server, everything goes fine, but when receiving a message back, Scapy decodes the message wrong. I've tried to change the bind_layers() to match the message, but that goes from bad to worse. The data string I receive is correct (all bytes are present and at the correct place and even have the correct values).

Scapy decodes the message as follows:

cotp._sendrcv(): received answer  b'\x03\x00\x00\x1b\x02\xf0\x802\x03\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\xf0\x00\x00\x01\x00\x01\x01\xe0'
s7.connect(): Received answer in s7.py is  TPKT / COTP / COTP_TCP_Data / S7COMM / Raw

While it should be

cotp._sendrcv(): received answer  b'\x03\x00\x00\x1b\x02\xf0\x802\x03\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\xf0\x00\x00\x01\x00\x01\x01\xe0'
s7.connect(): Received answer in s7.py is  TPKT / COTP_TCP_Data / S7COMM

For each part of the protocol (TPKT, COTP, and S7Comm) there are separate .py files. In the COTP.py file, for each message type a separate class is defined and used in the bind_layers:

bind_layers(CLNP, COTP)
bind_layers(TPKT, COTP)
bind_layers(COTP, COTP_TCP_ConnectRequest, pdu_type=0xe0, length=17)
bind_layers(COTP, COTP_TCP_ConnectConfirm, pdu_type=0xd0)
bind_layers(COTP, COTP_LLC_ConnectRequest, pdu_type=0xe1, length=0x1F)
bind_layers(COTP, COTP_LLC_ConnectConfirm, pdu_type=0xd1)
bind_layers(COTP, COTP_DataAcknowledgement, pdu_type=0x60, length=0x09)
bind_layers(COTP, COTP_LLC_Data, pdu_type=0xf0, length=0x07)
bind_layers(COTP, COTP_TCP_Data, pdu_type=0xf0, length=0x02)
bind_layers(COTP, COTP_DisconnectRequest, pdu_type=0x80, length=0x06)
bind_layers(COTP, COTP_DisconnectConfirm, pdu_type=0xC0)

Sending (and receiving) goes with this code:

           self._send(packet)
            answer = self._socket_.recv(4096)
            print("cotp._sendrcv(): received answer ", answer)
            if answer == None:
                return None
            else:
                return [TPKT(answer)]

Scapy decodes the returnvalue as TPKT / COTP / COTP_TCP_Data / S7COMM / Raw.

I've already tried to add a new 'bind_layers':

bind_layers(TPKT, COTP_TCP_Data, pdu_type=0xf0, length=0x02)

and other things to force a 'casting' to the correct decoding.

What am I doing wrong here?


Solution

  • I've found the problem. There I was converting the code from Python 2 to Python 3, it was a converting error. Within the old code, it didn't matter if the data was a string or an array of bytes. Within Python 3 it does matter. Converting from V2 to V3 attempt 3 did the trick, here I did focus on the right conversions of the data (string/byte).