Lucene search
+L

Siemens SIMATIC S7 Device Detection (COTP)

🗓️ 17 Jun 2016 00:00:00Reported by Copyright (C) 2016 Greenbone AGType 
openvas
 openvas
🔗 plugins.openvas.org👁 1550 Views

Siemens SIMATIC S7 Device Detection (COTP) detects Siemens SIMATIC S7 devices using the Connection-Oriented Transport Protocol (COTP)

Code
# SPDX-FileCopyrightText: 2016 Greenbone AG
# Some text descriptions might be excerpted from (a) referenced
# source(s), and are Copyright (C) by the respective right holder(s).
#
# SPDX-License-Identifier: GPL-2.0-only

if(description)
{
  script_oid("1.3.6.1.4.1.25623.1.0.106099");
  script_version("2023-10-13T05:06:10+0000");
  script_tag(name:"last_modification", value:"2023-10-13 05:06:10 +0000 (Fri, 13 Oct 2023)");
  script_tag(name:"creation_date", value:"2016-06-17 17:08:52 +0700 (Fri, 17 Jun 2016)");
  script_tag(name:"cvss_base", value:"0.0");
  script_tag(name:"cvss_base_vector", value:"AV:N/AC:L/Au:N/C:N/I:N/A:N");

  script_tag(name:"qod_type", value:"remote_banner");

  script_name("Siemens SIMATIC S7 Device Detection (COTP)");

  script_category(ACT_GATHER_INFO);

  script_copyright("Copyright (C) 2016 Greenbone AG");
  script_family("Product detection");
  script_dependencies("find_service.nasl");
  script_require_ports(102);

  script_tag(name:"summary", value:"COTP (Connection-Oriented Transport Protocol) based detection
  of Siemens SIMATIC S7 devices.");

  exit(0);
}

include("byte_func.inc");
include("dump.inc");
include("host_details.inc");
include("http_func.inc");
include("misc_func.inc");
include("port_service_func.inc");

function cotp_send_recv(req, soc) {
  local_var req, soc;

  send(socket: soc, data:req);
  recv = recv(socket: soc, length: 6, min: 6);

  if (strlen(recv) < 6)
    return;

  len = (getword(blob: recv, pos: 2) - 6);

  if (len < 1 || len > 65535)
    return;

  recv += recv(socket: soc, length: len);

  if (strlen(recv) != (len + 6))
    return;

  return recv;
}

function cotp_extract_packet(data) {
  local_var data;

  cotpPacket = substr(data, 7);
  if (hexstr(cotpPacket[1]) == "01" || hexstr(cotpPacket[1]) == "07")
    header_length = 10;
  else
    header_length = 12;

  param_length = getword(blob: cotpPacket, pos: 6);
  return substr(cotpPacket, header_length + param_length);
}

port = 102;

if (!get_port_state(port))
  exit(0);

soc = open_sock_tcp(port);
if (!soc)
  exit(0);

# COTP connection request
connectionReq = raw_string(0x03, 0x00,         # TPKT (version 3) "Emulate" ISO transport services COTP on top of TCP
                           0x00, 0x16,         #   length
                           0x11,               # COTP: length
                           0xe0,               #   PDU Type (CR Connect Request)
                           0x00, 0x00,         #   Destination reference
                           0x00, 0x02,         #   Source reference (can be set by the client)
                           0x00,               #   Class/extended format/flow control
                           0xc1,               #   Parameter code (src-tsap)
                           0x02,               #   Parameter length
                           0x01, 0x00,         #   Source TSAP
                           0xc2,               #   Parameter code (dst-tsap)
                           0x02,               #   Parameter length
                           0x01, 0x02,         #   Destination TSAP
                           0xc0,               #   Parameter code (tpdu-size)
                           0x01,               #   Parameter length
                           0x0a);              #   TPDU size

recv = cotp_send_recv(req: connectionReq, soc: soc);

if (!recv || hexstr(recv[5]) != "d0") {
  # we have to open a new socket
  close(soc);

  soc = open_sock_tcp(port);
  if (!soc)
    exit(0);

  # nb: Try an alternative request
  connectionReq = raw_string(0x03, 0x00, 0x00, 0x16, 0x11, 0xe0, 0x00, 0x00,
                             0x00, 0x05, 0x00, 0xc1, 0x02, 0x01, 0x00, 0xc2,
                             0x02, 0x02, 0x00, 0xc0, 0x01, 0x0a);
  recv = cotp_send_recv(req: connectionReq, soc: soc);

  if (!recv || hexstr(recv[5]) != "d0") {
    close(soc);
    exit(0);
  }
}

negotiatePdu = raw_string(0x03, 0x00, 0x00, 0x19,       # TPKT header
                          0x02,                         # COTP: length
                          0xf0,                         #   PDU Type (DT Data)
                          0x80,                         #   Flags
                          0x32,                         # S7 Communication: Protocol ID
                          0x01,                         #   Header: ROSCTR: Job
                          0x00, 0x00,                   #     Redundancy Identification (Reserved)
                          0x00, 0x00,                   #     Protocol Data Unit Reference
                          0x00, 0x08,                   #     Parameter length
                          0x00, 0x00,                   #     Data length
                          0xf0,                         #   Parameter: Setup communictation
                          0x00,                         #     Reserved
                          0x00, 0x01,                   #     Max AmQ calling
                          0x00, 0x01,                   #     Max AmQ called
                          0x01, 0xe0);                  #     PDU length

recv = cotp_send_recv(req: negotiatePdu, soc: soc);

# nb: S7 Comm response ACK
if (!recv || hexstr(recv[8] != "03")) {
  close(soc);
  exit(0);
}

readModuleID = raw_string(0x03, 0x00, 0x00, 0x21,       # TPKT header
                          0x02, 0xf0, 0x80,             # COTP
                          0x32,                         # S7 Communication
                          0x07,                         #   Header: ROSCTR: Userdata
                          0x00, 0x00,                   #     Redundancy ID
                          0x00, 0x00,                   #     PDU Ref
                          0x00, 0x08,                   #     Param length
                          0x00, 0x08,                   #     Data Length
                          0x00, 0x01, 0x12,             #   Parameter: Parameter header
                          0x04,                         #     length
                          0x11,
                          0x44,                         #     Type: Request / Function Group: CPU functions
                          0x01,                         #     Subfunction: Read SZL
                          0x00,                         #     Sequence number
                          0xff,                         #   Data: Return code: Success
                          0x09,                         #     Transport size
                          0x00, 0x04,                   #     length
                          0x00, 0x11,                   #     SZL-ID  (Module identification)
                          0x00, 0x01);                  #     SZL-Index

recv = cotp_send_recv(req: readModuleID, soc: soc);

if (!recv) {
  close(soc);
  exit(0);
}

dataPacket = cotp_extract_packet(data: recv);

# Return code must be "Success"
if (hexstr(dataPacket[0]) != "ff") {
  close(soc);
  exit(0);
}

version = "unknown";

if (strlen(dataPacket) >= 96) {
  # Version
  ver = hexstr(substr(dataPacket, 93, 95));

  v1 = ver[0] + ver[1];
  v2 = ver[2] + ver[3];
  v3 = ver[4] + ver[5];
  version = hex2dec(xvalue: v1) + "." + hex2dec(xvalue: v2) + "." + hex2dec(xvalue: v3);

  # Module
  module = substr(dataPacket, 14, 32);
  set_kb_item(name: "siemens/simatic_s7/cotp/module", value: module);
}

log_message(port: port, data: "A Siemens SIMATIC S7 service answering to COTP requests seems to be running on this port.");
# nb: Register the service since we can be quite sure that this talks COTP
service_register(port: port, proto: "cotp", ipproto: "tcp", message: "A Siemens SIMATIC S7 service answering to COTP requests seems to be running on this port.");

# Read the component identifications to extract the model
readComponentID = raw_string(0x03, 0x00, 0x00, 0x21, 0x02, 0xf0, 0x80, 0x32,
                             0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
                             0x08, 0x00, 0x01, 0x12, 0x04, 0x11, 0x44, 0x01,
                             0x00, 0xff, 0x09, 0x00, 0x04, 0x00, 0x1c, 0x00,
                             0x01);

recv = cotp_send_recv(req: readComponentID, soc: soc);
close(soc);

model = "unknown";

if (recv) {
  dataPacket = cotp_extract_packet(data: recv);

  if (hexstr(dataPacket[0]) == "ff") {
    dataPacket = substr(dataPacket, 4);

    element_size = getword(blob: dataPacket, pos: 4);
    dataPacket = substr(dataPacket, 8);

    for (i=0; i<strlen(dataPacket); i=i+element_size) {
      element = substr(dataPacket, i, i+element_size);
      if (hexstr(element[1]) == "01") {
        plcName = substr(element, 2);
        mod = eregmatch(pattern: "simatic([ ,]+)?(.*)", string: plcName, icase: TRUE);
        if (mod[2]) {
          model = mod[2];
        }
      }
      else
        if (hexstr(element[1]) == "02") {
          moduleName = substr(element, 2);
          mod = eregmatch(pattern: "((CPU )||(S7-))(.*)", string: moduleName, icase: TRUE);
          if (mod[4]) {
            model = mod[4];
          }
       }
       else if (hexstr(element[1]) == "07") {
         moduleType = substr(element, 2);
         model = moduleType;
         set_kb_item(name: "siemens/simatic_s7/cotp/modtype", value: moduleType);
       }
    }
  }
}

if (version != "unknown") {
  set_kb_item(name: "siemens/simatic_s7/detected", value: TRUE);
  if (model != "unknown") {
    # The extracted model string might included some non-printable characters so normalize it first
    model = bin2string(ddata: model, noprint_replacement: "");
    if (egrep(string: model, pattern: "^(CPU )?3.."))
      model = 300;
    set_kb_item(name: "siemens/simatic_s7/cotp/model", value: model);
  }

  if (version != "unknown")
    set_kb_item(name: "siemens/simatic_s7/cotp/" + port + "/version", value: version);

  set_kb_item(name: "siemens/simatic_s7/cotp/port", value: port);
}

exit(0);

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation