Skip to content

enipTCP: fix list reply item counts and lengths - #5251

Open
bIackr0se wants to merge 1 commit into
secdev:masterfrom
bIackr0se:fix/enip-list-reply-lengths
Open

bIackr0se wants to merge 1 commit into
secdev:masterfrom
bIackr0se:fix/enip-list-reply-lengths

Conversation

@bIackr0se

Copy link
Copy Markdown
Contributor

Description

The ListServices, ListIdentity and ListInterfaces replies in enipTCP.py encode itemCount (and the ListInterfaces itemLength) big-endian, while EtherNet/IP is little-endian. Their counts and lengths also default to 0 instead of None, so scapy never computes them. A device reply with one item dissects as itemCount=256, and a built ListIdentity reply has itemCount, itemLength and productNameLength set to 0. The fix encodes these fields little-endian, computes them when they are left unset, and adds extract_padding so that a list with more than one item dissects into separate items.

from scapy.contrib.enipTCP import *
p = ENIPTCP(bytes.fromhex(  # ListIdentity reply of a cpppo 5.2.5 server
    "63003c0000000000000000000000000000000000000000000100"
    "0c00360001000002af120000000000000000000000000100"
    "0e003600140b60311a066c0014313735362d4c36312f42204c4f47495835353631ff"))
print(p.itemCount)
r = ENIPListIdentity(bytes(ENIPListIdentity(items=[ENIPListIdentityItem(productName=b"PLC-01")])))
print(r.itemCount, r.items[0].productName)

Before:

256
0 b''

After:

1
1 b'PLC-01'

A reply built this way (with ENIPTCP(commandId=0x63, length=None)) is read by pycomm3 CIPDriver.list_identity() and by tshark with the right product name, which they do not find on master.

Before the packet is built, itemCount and itemLength now read None instead of 0. The default-value tests check the built bytes instead.

This message was written with the help of AI (Claude Opus 5.5).

ListServices, ListIdentity and ListInterfaces encoded itemCount (and the
ListInterfaces itemLength) big-endian, so a device reply with one item
dissected as itemCount=256. The counts and lengths also defaulted to 0,
so a built reply wrote 0 for itemCount, itemLength and
productNameLength, and peers (and scapy itself) read an empty product
name. Items after the first were dissected as Raw.

Encode them little-endian, compute them when left unset, and split the
items of a list.

AI-Assisted: yes (Claude Opus 5.5, GPT-6.1 Sol)
@codecov

codecov Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.42%. Comparing base (770ab0f) to head (e1d9011).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #5251   +/-   ##
=======================================
  Coverage   80.41%   80.42%           
=======================================
  Files         376      376           
  Lines       98405    98414    +9     
=======================================
+ Hits        79137    79149   +12     
+ Misses      19268    19265    -3     
Files with missing lines Coverage Δ
scapy/contrib/enipTCP.py 98.63% <100.00%> (+0.19%) ⬆️

... and 5 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant