Skip to content

Commit f80dab3

Browse files
gijzelaerrclaude
andcommitted
fix: parse session challenge as USINT array at attribute 303
The PLC sends the 20-byte challenge as a USINT array (flags=0x10, type=0x02) at attribute 303, not as a BLOB. Confirmed from xBiggs's S7-1200 FW v4.2.2 debug logs. The previous heuristic looking for a BLOB never matched. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 43d5df5 commit f80dab3

1 file changed

Lines changed: 17 additions & 14 deletions

File tree

s7/connection.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -867,28 +867,31 @@ def _parse_create_object_response(self, payload: bytes) -> None:
867867
offset += 2
868868
offset = self._skip_typed_value(payload, offset, datatype, _flags)
869869

870-
else:
870+
elif attr_id == 303 and self._session_challenge is None:
871+
# ServerSessionChallenge: 20-byte USINT array
871872
if offset + 2 > len(payload):
872873
break
873874
_flags = payload[offset]
874875
datatype = payload[offset + 1]
875876
offset += 2
876-
# Capture 20-byte BLOB/array as the session challenge
877-
if datatype == DataType.BLOB and self._session_challenge is None:
878-
value_start = offset
879-
offset = self._skip_typed_value(payload, value_start, datatype, _flags)
880-
blob_length = offset - value_start
881-
if blob_length > 0:
882-
length, consumed = decode_uint32_vlq(payload, value_start)
883-
blob_data = bytes(payload[value_start + consumed : value_start + consumed + length])
884-
if len(blob_data) == 20:
885-
self._session_challenge = blob_data
886-
logger.info(
887-
f"Session challenge captured (attr {attr_id}, {len(blob_data)} bytes): {blob_data.hex()}"
888-
)
877+
is_array = bool(_flags & 0x10)
878+
if is_array and datatype == DataType.USINT:
879+
count, consumed = decode_uint32_vlq(payload, offset)
880+
offset += consumed
881+
self._session_challenge = bytes(payload[offset : offset + count])
882+
offset += count
883+
logger.info(f"Session challenge captured ({count} bytes): {self._session_challenge.hex()}")
889884
else:
890885
offset = self._skip_typed_value(payload, offset, datatype, _flags)
891886

887+
else:
888+
if offset + 2 > len(payload):
889+
break
890+
_flags = payload[offset]
891+
datatype = payload[offset + 1]
892+
offset += 2
893+
offset = self._skip_typed_value(payload, offset, datatype, _flags)
894+
892895
elif tag == ElementID.START_OF_OBJECT:
893896
offset += 1
894897
# Skip RelationId (4 bytes fixed) + ClassId (VLQ) + ClassFlags (VLQ) + AttributeId (VLQ)

0 commit comments

Comments
 (0)