mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
Add 4 scripts from the DINA Community
This commit is contained in:
15
CHANGELOG
15
CHANGELOG
@@ -1,5 +1,20 @@
|
|||||||
#Nmap Changelog ($Id$); -*-text-*-
|
#Nmap Changelog ($Id$); -*-text-*-
|
||||||
|
|
||||||
|
o [NSE] Four new scripts from the DINA community (https://github.com/DINA-community)
|
||||||
|
for querying industrial control systems:
|
||||||
|
|
||||||
|
+ hartip-info reads device information from devices using the Highway
|
||||||
|
Addressable Remote Transducer protocol
|
||||||
|
|
||||||
|
+ iec61850-mms queries devices using Manufacturing Message Specification
|
||||||
|
requests. [Dennis Rösch, Max Helbig]
|
||||||
|
|
||||||
|
+ multicast-profinet-discovery Sends a multicast PROFINET DCP Identify All
|
||||||
|
message and prints the responses. [Stefan Eiwanger, DINA-community]
|
||||||
|
|
||||||
|
+ profinet-cm-lookup queries the DCERPC endpoint mapper exposed via the
|
||||||
|
PNIO-CM service.
|
||||||
|
|
||||||
o Integrated over 2500 service/version detection fingerprints submitted since
|
o Integrated over 2500 service/version detection fingerprints submitted since
|
||||||
June 2020. The signature count went up 1.4% to 12089, including 9 new
|
June 2020. The signature count went up 1.4% to 12089, including 9 new
|
||||||
softmatches. We now detect 1246 protocols, including new additions of grpc,
|
softmatches. We now detect 1246 protocols, including new additions of grpc,
|
||||||
|
|||||||
@@ -16931,7 +16931,7 @@ rarity 8
|
|||||||
ports 123
|
ports 123
|
||||||
Probe UDP DCERPC_CALL q|\x05\0\x0b\x03\x10\0\0\0\x48\0\0\0\x01\0\0\0\xb8\x10\xb8\x10\0\0\0\0\x01\0\0\0\0\0\x01\0\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\xe7\x03\0\0\xfe\xdc\xba\x98\x76\x54\x32\x10\x01\x23\x45\x67\x89\xab\xcd\xef\xe7\x03\0\0|
|
Probe UDP DCERPC_CALL q|\x05\0\x0b\x03\x10\0\0\0\x48\0\0\0\x01\0\0\0\xb8\x10\xb8\x10\0\0\0\0\x01\0\0\0\0\0\x01\0\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\xe7\x03\0\0\xfe\xdc\xba\x98\x76\x54\x32\x10\x01\x23\x45\x67\x89\xab\xcd\xef\xe7\x03\0\0|
|
||||||
rarity 8
|
rarity 8
|
||||||
ports 135,1025-1199
|
ports 135,1025-1199,34964
|
||||||
Probe UDP CIFS_NS_UC q|\x01\x91\0\0\0\x01\0\0\0\0\0\0\x20CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\0\0\x21\0\x01|
|
Probe UDP CIFS_NS_UC q|\x01\x91\0\0\0\x01\0\0\0\0\0\0\x20CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\0\0\x21\0\x01|
|
||||||
rarity 8
|
rarity 8
|
||||||
ports 137
|
ports 137
|
||||||
|
|||||||
1186
nselib/iec61850mms.lua
Executable file
1186
nselib/iec61850mms.lua
Executable file
File diff suppressed because it is too large
Load Diff
304
scripts/hartip-info.nse
Executable file
304
scripts/hartip-info.nse
Executable file
@@ -0,0 +1,304 @@
|
|||||||
|
local shortport = require "shortport"
|
||||||
|
local stdnse = require "stdnse"
|
||||||
|
local string = require "string"
|
||||||
|
local table = require "table"
|
||||||
|
local nmap = require "nmap"
|
||||||
|
local nsedebug = require "nsedebug"
|
||||||
|
|
||||||
|
description = [[
|
||||||
|
This NSE script is used to send a HART-IP packet to a HART device that has TCP 5094 open.
|
||||||
|
The script will establish Session with HART device, then Read Unique Identifier and
|
||||||
|
Read Long Tag packets are sent to parse the required HART device information.
|
||||||
|
Read Sub-Device Identity Summary packet with Sub-Device index 00 01 is sent
|
||||||
|
to request information on Sub-Device, if any available. If the response code
|
||||||
|
differs from 0 (success), the error code is passed as Sub-Device Information.
|
||||||
|
Otherwise, the required Sub-Device information is parsed from response packet.
|
||||||
|
|
||||||
|
Device/Sub-Device Information that is parsed includes Long Tag (user assigned device name),
|
||||||
|
Expanded Device Type, Manufacturer ID, Device ID, Device Revision, Software Revision,
|
||||||
|
HART Protocol Major Revision and Private Label Distributor.
|
||||||
|
|
||||||
|
This script was written based of HART Specifications available at
|
||||||
|
https://www.fieldcommgroup.org/hart-specifications.
|
||||||
|
]]
|
||||||
|
---
|
||||||
|
-- @usage
|
||||||
|
-- nmap <host> -p 5094 --script hartip-info
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- @output
|
||||||
|
--PORT STATE SERVICE
|
||||||
|
--5094/tcp open hart-ip
|
||||||
|
--| hartip-info:
|
||||||
|
--| Device Information:
|
||||||
|
--| IP Address: 172.16.10.90
|
||||||
|
--| Long Tag: ????????????????????????????????
|
||||||
|
--| Expanded Device Type: GW PL ETH/UNI-BUS
|
||||||
|
--| Manufacturer ID: Phoenix Contact
|
||||||
|
--| Device ID: dd4ee3
|
||||||
|
--| Device Revision: 1
|
||||||
|
--| Software Revision: 1
|
||||||
|
--| HART Protocol Major Revision: 7
|
||||||
|
--| Private Label Distributor: Phoenix Contact
|
||||||
|
--| Sub-Device Information:
|
||||||
|
--|_ Error Code: 2
|
||||||
|
-- @xmloutput
|
||||||
|
--<elem key="IP Address">172.16.10.90</elem>
|
||||||
|
--<elem>Long Tag: ????????????????????????????????</elem>
|
||||||
|
--<elem>Expanded Device Type: GW PL ETH/UNI-BUS</elem>
|
||||||
|
--<elem>Manufacturer ID: Phoenix Contact</elem>
|
||||||
|
--<elem>Device ID: dd4ee3</elem>
|
||||||
|
--<elem>Device Revision: 1</elem>
|
||||||
|
--<elem>Software Revision: 1</elem>
|
||||||
|
--<elem>HART Protocol Major Revision: 7</elem>
|
||||||
|
--<elem>Private Label Distributor: Phoenix Contact</elem>
|
||||||
|
|
||||||
|
author = "DINA-community"
|
||||||
|
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
|
||||||
|
categories = {"discovery", "intrusive"}
|
||||||
|
|
||||||
|
-- Function to define the portrule as per nmap standards
|
||||||
|
portrule = shortport.port_or_service(5094, "hart-ip", "tcp")
|
||||||
|
|
||||||
|
-- Table to look up the Product Name based on number-represented Expanded Device Type Code
|
||||||
|
-- Returns "Unknown Device Type" if Expanded Device Type not recognized
|
||||||
|
-- Table data from Common Tables Specification, HCF_SPEC-183, FCG TS20183, Revision 26.0
|
||||||
|
-- 5.1 Table 1. Expanded Device Type Codes
|
||||||
|
|
||||||
|
-- @key expdevtypnum number-represented Device Type Code parsed out of the HART-IP packet
|
||||||
|
local productName = {
|
||||||
|
[4560] = "iTEMP TMT72",
|
||||||
|
[45075] = "GW PL ETH/UNI-BUS",
|
||||||
|
}
|
||||||
|
|
||||||
|
--return device type information
|
||||||
|
local function expdevtyp_lookup(expdevtypnum)
|
||||||
|
return productName[expdevtypnum] or "Unknown Device Type"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Table to look up the Manufacturer Name based on Manufacturer ID
|
||||||
|
-- Returns "Unknown Manufacturer" if Manufacturer ID not recognized
|
||||||
|
-- Table data from Common Tables Specification, HCF_SPEC-183, FCG TS20183, Revision 26.0
|
||||||
|
-- 5.8 Table 8. Manufacturer Identification Codes
|
||||||
|
|
||||||
|
-- @key manidnum number-represented Manufacturer ID parsed out of the HART-IP packet
|
||||||
|
local manufacturerName = {
|
||||||
|
[176] = "Phoenix Contact",
|
||||||
|
}
|
||||||
|
|
||||||
|
--return manufacturer information
|
||||||
|
local function manid_lookup(manidnum)
|
||||||
|
return manufacturerName[manidnum] or "Unknown Manufacturer"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Action Function that is used to run the NSE. This function will send
|
||||||
|
-- the initial query to the host and port that were passed in via nmap.
|
||||||
|
--
|
||||||
|
-- @param host Host that was scanned via nmap
|
||||||
|
-- @param port port that was scanned via nmap
|
||||||
|
action = function(host,port)
|
||||||
|
-- create local vars for socket handling
|
||||||
|
local socket, try, catch, status, err
|
||||||
|
|
||||||
|
-- create new socket
|
||||||
|
socket = nmap.new_socket()
|
||||||
|
|
||||||
|
-- set timeout
|
||||||
|
socket:set_timeout(stdnse.get_timeout(host))
|
||||||
|
|
||||||
|
-- define the catch of the try statement
|
||||||
|
catch = function()
|
||||||
|
socket:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- create new try
|
||||||
|
try = nmap.new_try(catch)
|
||||||
|
|
||||||
|
-- connect to port on host
|
||||||
|
try(socket:connect(host, port))
|
||||||
|
stdnse.debug(1, "#- socket connection established.")
|
||||||
|
|
||||||
|
-- send the initiate session packet
|
||||||
|
-- receive response
|
||||||
|
-- abort if no response
|
||||||
|
local sessInitQuery = stdnse.fromhex("010000000001000D0100004E20")
|
||||||
|
try(socket:send(sessInitQuery))
|
||||||
|
|
||||||
|
local rcvstatus, response = socket:receive()
|
||||||
|
if(rcvstatus == false) then
|
||||||
|
stdnse.debug(1, "#- session initiation with HART device - FAIL.")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
stdnse.debug(1, "#- session initiation with HART device - SUCCESS.")
|
||||||
|
|
||||||
|
-- Command 0 BEGIN --
|
||||||
|
|
||||||
|
-- send the Command 0 Read Unique Idenifier packet
|
||||||
|
-- receive response, abort if no response
|
||||||
|
local cmd0Req = stdnse.fromhex("010003000002000D0280000082")
|
||||||
|
try(socket:send(cmd0Req))
|
||||||
|
|
||||||
|
local rcvstatus, response = socket:receive()
|
||||||
|
if(rcvstatus == false) then
|
||||||
|
stdnse.debug(1, "#- command 0 Read Unique Identifier request - FAIL.")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
stdnse.debug(1, "#- command 0 Read Unique Identifier request - SUCCESS.")
|
||||||
|
|
||||||
|
-- get hart-ip version, message type, message id, response status and sequence number
|
||||||
|
-- abort if no response
|
||||||
|
local _, _, _, res_status, _ = string.unpack(">BBBBI2", response, 1)
|
||||||
|
if (res_status ~= 0) then
|
||||||
|
stdnse.debug(1, "#- command 0 Read Unique Identifier response - FAIL.")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
stdnse.debug(1, "#- command 0 Read Unique Identifier response - SUCCESS.")
|
||||||
|
|
||||||
|
--- unpack device information from Command 0
|
||||||
|
|
||||||
|
-- create table for output and device information
|
||||||
|
local output = stdnse.output_table()
|
||||||
|
local deviceInfo = stdnse.output_table()
|
||||||
|
|
||||||
|
deviceInfo["IP Address"] = host.ip
|
||||||
|
|
||||||
|
-- get expanded device type
|
||||||
|
-- lookup device type number
|
||||||
|
local expDevTypeNum, Index = string.unpack(">I2", response, 16)
|
||||||
|
local expandedDeviceType = expdevtyp_lookup(expDevTypeNum)
|
||||||
|
deviceInfo["Expanded Device Type"] = expandedDeviceType
|
||||||
|
|
||||||
|
-- get master-to-slave minimum preambles
|
||||||
|
local minPreMasterToSlave, Index = string.unpack("B", response, Index)
|
||||||
|
|
||||||
|
-- get HART protocol major revision number
|
||||||
|
local hartProtocolMajorRevision, Index = string.unpack("B", response, Index)
|
||||||
|
deviceInfo["HART Protocol Major Revision"] = hartProtocolMajorRevision
|
||||||
|
|
||||||
|
-- get device revision number
|
||||||
|
local deviceRevision, Index = string.unpack("B",response, Index)
|
||||||
|
deviceInfo["Device Revision"] = deviceRevision
|
||||||
|
|
||||||
|
-- get software revision number
|
||||||
|
local softwareRevision, Index = string.unpack("B",response, Index)
|
||||||
|
deviceInfo["Software Revision"] = softwareRevision
|
||||||
|
|
||||||
|
-- get hardware revision level with physical signaling code and flags
|
||||||
|
local _,flags, Index = string.unpack("BB", response, Index)
|
||||||
|
|
||||||
|
-- get device ID in hex format
|
||||||
|
local deviceID, Index = string.unpack("c3", response, Index)
|
||||||
|
deviceID = stdnse.tohex(deviceID)
|
||||||
|
deviceInfo["Device ID"] = deviceID
|
||||||
|
|
||||||
|
-- get slave-to-master minimum preambles, last device variable code,
|
||||||
|
-- configuration change counter, extended field device status
|
||||||
|
_,_,_,_, Index = string.unpack("BBI2B", response, Index)
|
||||||
|
|
||||||
|
-- get manufacturer ID
|
||||||
|
-- lookup manufacturer id
|
||||||
|
local manufacturerID, Index = string.unpack(">I2", response, Index)
|
||||||
|
manufacturerID = manid_lookup(manufacturerID)
|
||||||
|
deviceInfo["Manufacturer ID"] = manufacturerID
|
||||||
|
|
||||||
|
-- get private label distributor
|
||||||
|
-- lookup manufacturer id
|
||||||
|
local privateLabelDistributor, Index = string.unpack(">I2", response, Index)
|
||||||
|
privateLabelDistributor = manid_lookup(privateLabelDistributor)
|
||||||
|
deviceInfo["Private Label Distributor"] = privateLabelDistributor
|
||||||
|
|
||||||
|
-- get device profile
|
||||||
|
local deviceProfile = string.unpack("B", response, Index)
|
||||||
|
|
||||||
|
-- Command 0 END --
|
||||||
|
|
||||||
|
-- Command 20 BEGIN --
|
||||||
|
|
||||||
|
local longAddress = stdnse.tohex(expDevTypeNum) .. deviceID
|
||||||
|
|
||||||
|
-- send the Command 20 Read Long Tag packet
|
||||||
|
-- receive response, abort if no response
|
||||||
|
local cmd20Req = stdnse.fromhex("010003000003001182" .. longAddress .. "140045")
|
||||||
|
try(socket:send(cmd20Req))
|
||||||
|
|
||||||
|
local rcvstatus, response = socket:receive()
|
||||||
|
if(rcvstatus == false) then
|
||||||
|
stdnse.debug(1, "#- command 20 Read Long Tag request - FAIL.")
|
||||||
|
output['Device Information'] = deviceInfo
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
stdnse.debug(1, "#- command 20 Read Long Tag request - SUCCESS.")
|
||||||
|
|
||||||
|
-- get hart-ip version, message type, message id, response status and sequence number
|
||||||
|
-- abort if no response
|
||||||
|
local _, _, _, res_status, _ = string.unpack(">BBBBI2", response, 1)
|
||||||
|
if (res_status ~= 0) then
|
||||||
|
stdnse.debug(1, "#- command 20 Read Long Tag response - FAIL.")
|
||||||
|
output['Device Information'] = deviceInfo
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
stdnse.debug(1, "#- command 20 Read Long Tag response - SUCCESS.")
|
||||||
|
|
||||||
|
--- unpack device information from Command 20
|
||||||
|
|
||||||
|
-- get device long tag
|
||||||
|
local longTag = string.unpack("c32", response, 19)
|
||||||
|
stdnse.debug(1, "#--- Long Tag = " .. longTag)
|
||||||
|
deviceInfo["Long Tag"] = longTag
|
||||||
|
|
||||||
|
-- Command 20 END --
|
||||||
|
|
||||||
|
--- Command 84 BEGIN
|
||||||
|
|
||||||
|
-- send the Command 84 Read Sub-Device Identity Summary packet for Sub-Device at index 0001
|
||||||
|
-- receive response, abort if no response
|
||||||
|
local subDeviceIndex = "0001"
|
||||||
|
local cmd84Req = stdnse.fromhex("010003000004001382" .. longAddress .. "5402" .. subDeviceIndex .. "06")
|
||||||
|
try(socket:send(cmd84Req))
|
||||||
|
|
||||||
|
local rcvstatus, response = socket:receive()
|
||||||
|
if(rcvstatus == false) then
|
||||||
|
stdnse.debug(1, "#- command 84 Read Sub-Device Identity Summary request - FAIL.")
|
||||||
|
output['Device Information'] = deviceInfo
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
stdnse.debug(1, "#- command 84 Read Sub-Device Identity Summary request - SUCCESS.")
|
||||||
|
|
||||||
|
-- get hart-ip version, message type, message id, response status and sequence number
|
||||||
|
-- abort if no response
|
||||||
|
local _, _, _, res_status, _ = string.unpack(">BBBBI2", response, 1)
|
||||||
|
if (res_status ~= 0) then
|
||||||
|
stdnse.debug(1, "#- command 84 Read Sub-Device Identity Summary response - FAIL.")
|
||||||
|
output['Device Information'] = deviceInfo
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
stdnse.debug(1, "#- command 84 Read Sub-Device Identity Summary response - SUCCESS.")
|
||||||
|
|
||||||
|
--- get sub-device information from Command 84
|
||||||
|
local subDeviceInfo = stdnse.output_table()
|
||||||
|
|
||||||
|
-- get response code
|
||||||
|
-- abort if no success
|
||||||
|
local responseCode = string.unpack("B", response, 17)
|
||||||
|
if (responseCode ~= 0) then
|
||||||
|
stdnse.debug(1, "#- command 84 Read Sub-Device Identity Summary response code %d - FAIL.", responseCode)
|
||||||
|
subDeviceInfo["Error Code"] = responseCode
|
||||||
|
|
||||||
|
output['Device Information'] = deviceInfo
|
||||||
|
output['Sub-Device Information'] = subDeviceInfo
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Command 84 END
|
||||||
|
|
||||||
|
-- close socket
|
||||||
|
socket:close()
|
||||||
|
stdnse.debug(1, "#- socket connection terminated.")
|
||||||
|
|
||||||
|
-- populate output table
|
||||||
|
output['Device Information'] = deviceInfo
|
||||||
|
output['Sub-Device Information'] = subDeviceInfo
|
||||||
|
|
||||||
|
-- return output table to Nmap
|
||||||
|
return output
|
||||||
|
end
|
||||||
380
scripts/iec61850-mms.nse
Executable file
380
scripts/iec61850-mms.nse
Executable file
@@ -0,0 +1,380 @@
|
|||||||
|
local iec61850mms = require "iec61850mms"
|
||||||
|
local nmap = require "nmap"
|
||||||
|
local shortport = require "shortport"
|
||||||
|
local stdnse = require "stdnse"
|
||||||
|
local string = require "string"
|
||||||
|
local table = require "table"
|
||||||
|
|
||||||
|
description = [[
|
||||||
|
Queries a IEC 61850-8-1 MMS server. Sends Initate-Request, Identify-Request and Read-Request to LN0 and LPHD.
|
||||||
|
|
||||||
|
Output contains following attributes:
|
||||||
|
|
||||||
|
* modelName_identify: Identify-Response attribute model_name
|
||||||
|
* vendorName_identify: Identify-Response attribute vendor_name
|
||||||
|
* modelNumber_identify: Identify-Response attribute revision
|
||||||
|
* productFamily: Read-Response attribute 'LLN0$DC$NamPlt$d'
|
||||||
|
* configuration: Read-Response attribute 'LLN0$DC$NamPlt$configRev'
|
||||||
|
* vendorName: Read-Response attribute 'LPHD$DC$PhyNam$vendor' (old: 'LLN0$DC$NamPlt$vendor')
|
||||||
|
* serialNumber: Read-Response attribute 'LPHD$DC$PhyNam$serNum'
|
||||||
|
* modelNumber: Read-Response attribute 'LPHD$DC$PhyNam$model'
|
||||||
|
* firmwareVersion: Read-Response attribute 'LPHD$DC$PhyNam$swRev' (old: 'LLN0$DC$NamPlt$swRev')
|
||||||
|
]]
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @usage
|
||||||
|
-- nmap --script iec61850-mms.nse -p 102 <target>
|
||||||
|
--
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @output
|
||||||
|
-- 102/tcp open iso-tsap
|
||||||
|
--| iec61850_mms.nse:
|
||||||
|
--| modelName_identify: MMS-LITE-80X-001
|
||||||
|
--| productFamily: High End Meter
|
||||||
|
--| vendorName: Schneider Electric
|
||||||
|
--| vendorName_identify: SISCO
|
||||||
|
--| serialNumber: ME-1810A424-02
|
||||||
|
--| modelNumber: 8000
|
||||||
|
--| modelNumber_identify: 6.0000.3
|
||||||
|
--| firmwareVersion: 001.004.003
|
||||||
|
--|_ configuration: 2022-08-19 08:27:20
|
||||||
|
|
||||||
|
|
||||||
|
author = "Dennis Rösch, Max Helbig"
|
||||||
|
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
|
||||||
|
categories = {"discovery", "intrusive", "version"}
|
||||||
|
|
||||||
|
-- Helpers
|
||||||
|
function replaceEmptyStrings(tbl)
|
||||||
|
for key, value in pairs(tbl) do
|
||||||
|
if type(value) == "table" then
|
||||||
|
replaceEmptyStrings(value)
|
||||||
|
elseif type(value) == "string" and value == "" then
|
||||||
|
tbl[key] = "<EMPTY_STRING>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function searchTable(searchString, myTable)
|
||||||
|
local matches = {}
|
||||||
|
local uniqueEntries = {}
|
||||||
|
local extractedPart
|
||||||
|
for i, entry in ipairs(myTable) do
|
||||||
|
if string.find(entry, searchString) then
|
||||||
|
local dollarIndex = string.find(entry, "%$")
|
||||||
|
if not dollarIndex then
|
||||||
|
extractedPart = entry
|
||||||
|
else
|
||||||
|
extractedPart = string.sub(entry, 1, dollarIndex - 1)
|
||||||
|
end
|
||||||
|
if not uniqueEntries[extractedPart] then
|
||||||
|
uniqueEntries[extractedPart] = true
|
||||||
|
table.insert(matches, extractedPart)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return matches
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Rules
|
||||||
|
portrule = shortport.portnumber(102, "iso-tsap")
|
||||||
|
|
||||||
|
-- Actions
|
||||||
|
action = function(host, port)
|
||||||
|
local timeout = 500
|
||||||
|
|
||||||
|
local status, recv
|
||||||
|
local output = {}
|
||||||
|
local socket = nmap.new_socket()
|
||||||
|
|
||||||
|
local decoder = iec61850mms.MMSDecoder:new()
|
||||||
|
local encoder = iec61850mms.MMSEncoder:new()
|
||||||
|
local query = iec61850mms.MMSQueries:new()
|
||||||
|
|
||||||
|
socket:set_timeout(timeout)
|
||||||
|
|
||||||
|
stdnse.debug(2, "Connecting to host")
|
||||||
|
status, recv = socket:connect(host, port, "tcp")
|
||||||
|
if not status then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
stdnse.debug(2, "Connected")
|
||||||
|
|
||||||
|
stdnse.debug(2, "Sending CR_TPDU")
|
||||||
|
local CR_TPDU = "\x03\x00\x00\x16\x11\xe0\x00\x00\x00\x01\x00\xc1\x02\x00\x00\xc2\x02\x00\x01\xc0\x01\x0a"
|
||||||
|
status = socket:send( CR_TPDU )
|
||||||
|
if not status then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
status, recv = socket:receive_bytes(1024)
|
||||||
|
stdnse.debug(2, "Response recieved")
|
||||||
|
stdnse.debug(3, "cr_tpdu: %s", stdnse.tohex(recv) )
|
||||||
|
|
||||||
|
|
||||||
|
local MMS_INITIATE = "\x03\x00\x00\xd3\x02\xf0\x80\x0d\xca\x05\x06\x13\x01\x00\x16\x01\x02\x14\x02\x00\x02\x33\x02" ..
|
||||||
|
"\x00\x01\x34\x02\x00\x01\xc1\xb4\x31\x81\xb1\xa0\x03\x80\x01\x01" ..
|
||||||
|
"\xa2\x81\xa9\x81\x04\x00\x00\x00\x01\x82\x04\x00\x00\x00\x01\xa4" ..
|
||||||
|
"\x23\x30\x0f\x02\x01\x01\x06\x04\x52\x01\x00\x01\x30\x04\x06\x02" ..
|
||||||
|
"\x51\x01\x30\x10\x02\x01\x03\x06\x05\x28\xca\x22\x02\x01\x30\x04" ..
|
||||||
|
"\x06\x02\x51\x01\x61\x76\x30\x74\x02\x01\x01\xa0\x6f\x60\x6d\xa1" ..
|
||||||
|
"\x07\x06\x05\x28\xca\x22\x02\x03\xa2\x07\x06\x05\x29\x01\x87\x67" ..
|
||||||
|
"\x01\xa3\x03\x02\x01\x0c\xa4\x03\x02\x01\x00\xa5\x03\x02\x01\x00" ..
|
||||||
|
"\xa6\x06\x06\x04\x29\x01\x87\x67\xa7\x03\x02\x01\x0c\xa8\x03\x02" ..
|
||||||
|
"\x01\x00\xa9\x03\x02\x01\x00\xbe\x33\x28\x31\x06\x02\x51\x01\x02" ..
|
||||||
|
"\x01\x03\xa0\x28\xa8\x26\x80\x03\x00\xfd\xe8\x81\x01\x0a\x82\x01" ..
|
||||||
|
"\x0a\x83\x01\x05\xa4\x16\x80\x01\x01\x81\x03\x05\xf1\x00\x82\x0c" ..
|
||||||
|
"\x03\xee\x1c\x00\x00\x00\x00\x00\x00\x00\xed\x18"
|
||||||
|
|
||||||
|
stdnse.debug(2, "Sending MMS initiate")
|
||||||
|
status = socket:send( MMS_INITIATE )
|
||||||
|
if not status then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
status, recv = socket:receive_bytes(1024)
|
||||||
|
stdnse.debug(2, "Response recieved")
|
||||||
|
stdnse.debug(3, "mms_initiate: %s", stdnse.tohex(recv) )
|
||||||
|
|
||||||
|
local MMS_IDENTIFY = "\x03\x00\x00\x1b\x02\xf0\x80\x01\x00\x01\x00\x61\x0e\x30\x0c\x02" ..
|
||||||
|
"\x01\x03\xa0\x07\xa0\x05\x02\x01\x01\x82\x00"
|
||||||
|
|
||||||
|
stdnse.debug(2, "Sending MMS identify")
|
||||||
|
status = socket:send( MMS_IDENTIFY )
|
||||||
|
if not status then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
status, recv = socket:receive_bytes(2048)
|
||||||
|
stdnse.debug(2, "Response recieved")
|
||||||
|
stdnse.debug(3, "mms_identify: %s", stdnse.tohex(recv) )
|
||||||
|
|
||||||
|
local output = stdnse.output_table()
|
||||||
|
|
||||||
|
if ( status and recv ) then
|
||||||
|
local mmsIdentstruct = decoder:unpackAndDecode(recv)
|
||||||
|
if not mmsIdentstruct then
|
||||||
|
stdnse.debug(1, "error while decoding")
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
replaceEmptyStrings(mmsIdentstruct)
|
||||||
|
|
||||||
|
local vendor_name = mmsIdentstruct.confirmed_ResponsePDU.identify.vendorName
|
||||||
|
local model_name = mmsIdentstruct.confirmed_ResponsePDU.identify.modelName
|
||||||
|
local revision = mmsIdentstruct.confirmed_ResponsePDU.identify.revision
|
||||||
|
|
||||||
|
stdnse.debug(1, "vendor_name: %s", vendor_name )
|
||||||
|
stdnse.debug(1, "model_name: %s", model_name )
|
||||||
|
stdnse.debug(1, "revision: %s", revision )
|
||||||
|
output["modelName_identify"] = model_name
|
||||||
|
output["vendorName_identify"] = vendor_name
|
||||||
|
output["modelNumber_identify"] = revision
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local invokeID = 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
local vmd_NameList_Struct = query:nameList(invokeID)
|
||||||
|
local MMS_GETNAMELIST_vmdspecific = encoder:packmmsInTPKT(encoder:mmsPDU(vmd_NameList_Struct))
|
||||||
|
stdnse.debug(2, "Sending MMS getNameList (vmdSpecific)")
|
||||||
|
status = socket:send( MMS_GETNAMELIST_vmdspecific )
|
||||||
|
if not status then
|
||||||
|
stdnse.debug(1, "error while sending MMS getNameList (vmdSpecific)")
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
status, recv = socket:receive_bytes(1024)
|
||||||
|
stdnse.debug(2, "Response recieved")
|
||||||
|
stdnse.debug(3, "mms_getnamelist: %s", stdnse.tohex(recv) )
|
||||||
|
|
||||||
|
local vmd_names
|
||||||
|
if ( status and recv ) then
|
||||||
|
local mmsNLTab = decoder:unpackAndDecode(recv)
|
||||||
|
if not mmsNLTab then
|
||||||
|
stdnse.debug(1, "error while decoding")
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
vmd_names = mmsNLTab.confirmed_ResponsePDU.getNameList.listOfIdentifier
|
||||||
|
stdnse.debug(1, "found %d vmdNames", #vmd_names )
|
||||||
|
for i, v in ipairs(vmd_names) do
|
||||||
|
stdnse.debug(1, "vmd_name %d: %s", i, v )
|
||||||
|
end
|
||||||
|
else
|
||||||
|
stdnse.debug(1, "error while processing MMS getNameList (vmdSpecific) response")
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
-- reading complete vmdspecific NameList
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
local matches
|
||||||
|
local vmd_name
|
||||||
|
stdnse.debug(2, "Start reading complete NameList")
|
||||||
|
for i, v in ipairs(vmd_names) do
|
||||||
|
local morefollows = true
|
||||||
|
local continueAfter = ""
|
||||||
|
local allIdentifiers = {}
|
||||||
|
stdnse.debug(2, "get NameList for vmdName %s", v)
|
||||||
|
while morefollows do
|
||||||
|
local mmsStruct = query:nameList(invokeID, v, continueAfter)
|
||||||
|
local sendString = encoder:packmmsInTPKT(encoder:mmsPDU(mmsStruct))
|
||||||
|
stdnse.debug(2, "Sending getNameList request")
|
||||||
|
status = socket:send( sendString )
|
||||||
|
if not status then
|
||||||
|
stdnse.debug(1, "error sending request")
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
status, recv = socket:receive_bytes(100000)
|
||||||
|
stdnse.debug(2, "Response recieved")
|
||||||
|
stdnse.debug(3, "mms_getnamelist recv: %s", stdnse.tohex(recv) )
|
||||||
|
if ( status and recv ) then
|
||||||
|
local recv_Struct = decoder:unpackAndDecode(recv)
|
||||||
|
if not recv_Struct then
|
||||||
|
stdnse.debug(1, "error while decoding")
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
local identifier = recv_Struct.confirmed_ResponsePDU.getNameList.listOfIdentifier
|
||||||
|
for i, v in ipairs(identifier) do table.insert(allIdentifiers, v) end
|
||||||
|
if #identifier > 100 then
|
||||||
|
stdnse.debug(1, "Response contains more then 100 identifiers")
|
||||||
|
stdnse.debug(2, "Just got %d identifiers", #identifier)
|
||||||
|
end
|
||||||
|
|
||||||
|
morefollows = recv_Struct.confirmed_ResponsePDU.getNameList.moreFollows
|
||||||
|
if morefollows then
|
||||||
|
continueAfter = identifier[#identifier]
|
||||||
|
stdnse.debug(2, "More identifiers availible!")
|
||||||
|
end
|
||||||
|
|
||||||
|
invokeID = invokeID + 1
|
||||||
|
else
|
||||||
|
stdnse.debug(1, "error while processing MMS getNameList response")
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
stdnse.debug(2, "Reading complete NameList done")
|
||||||
|
|
||||||
|
stdnse.debug(2, "Searching for LPHD in %d identifiers", #allIdentifiers)
|
||||||
|
matches = searchTable("LPHD", allIdentifiers)
|
||||||
|
if #matches >= 1 then
|
||||||
|
vmd_name = v
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end -- for loop
|
||||||
|
stdnse.debug(2, "Searching done: found %d unique entrys", #matches)
|
||||||
|
|
||||||
|
|
||||||
|
if #matches == 0 then
|
||||||
|
stdnse.debug(1, "No Logical Node contains LPHD")
|
||||||
|
end
|
||||||
|
|
||||||
|
if #matches > 1 then
|
||||||
|
stdnse.debug(1, "Found more then one Node")
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
local attributes = {
|
||||||
|
'LLN0$DC$NamPlt$d',
|
||||||
|
'LLN0$DC$NamPlt$configRev'
|
||||||
|
}
|
||||||
|
|
||||||
|
local Node_Ready = false
|
||||||
|
local node
|
||||||
|
if #matches == 1 then
|
||||||
|
node = matches[1]
|
||||||
|
Node_Ready = true
|
||||||
|
stdnse.debug(2, "Node is: %s", node)
|
||||||
|
table.insert(attributes, node .. '$DC$PhyNam$vendor')
|
||||||
|
table.insert(attributes, node .. '$DC$PhyNam$serNum')
|
||||||
|
table.insert(attributes, node .. '$DC$PhyNam$model')
|
||||||
|
table.insert(attributes, node .. '$DC$PhyNam$swRev')
|
||||||
|
end
|
||||||
|
|
||||||
|
local mmsRequest = query:askfor(invokeID, vmd_name, attributes)
|
||||||
|
local MMS_READREQUEST = encoder:packmmsInTPKT(mmsRequest)
|
||||||
|
|
||||||
|
stdnse.debug(2, "Sending MMS readRequest")
|
||||||
|
status = socket:send( MMS_READREQUEST )
|
||||||
|
if not status then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
status, recv = socket:receive_bytes(1024)
|
||||||
|
stdnse.debug(2, "Response recieved")
|
||||||
|
stdnse.debug(3, "mms_read: %s", stdnse.tohex(recv) )
|
||||||
|
|
||||||
|
local mmsstruct
|
||||||
|
if ( status and recv ) then
|
||||||
|
mmsstruct = decoder:unpackAndDecode(recv)
|
||||||
|
if not mmsstruct then
|
||||||
|
stdnse.debug(1, "error while decoding")
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
replaceEmptyStrings(mmsstruct)
|
||||||
|
else
|
||||||
|
stdnse.debug(1, "error while processing MMS getNameList response")
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
local mmsoutput
|
||||||
|
local attNum = #attributes
|
||||||
|
local rplNum = #mmsstruct.confirmed_ResponsePDU.Read_Response.listOfAccessResult
|
||||||
|
if rplNum == attNum then
|
||||||
|
mmsoutput = mmsstruct.confirmed_ResponsePDU.Read_Response.listOfAccessResult
|
||||||
|
else
|
||||||
|
|
||||||
|
stdnse.debug(2,"\nReply from Host %s at port %d was not compliant with standard", host["ip"], port["number"])
|
||||||
|
stdnse.debug(2,"Request for %d attributes has been replied with %d values", attNum, rplNum)
|
||||||
|
stdnse.debug(2,"attempting individual queries...\n")
|
||||||
|
mmsoutput = {}
|
||||||
|
for i = 1, attNum do
|
||||||
|
local mmsRequest = query:askfor(i, vmd_name, attributes[i])
|
||||||
|
local MMS_READREQUEST = encoder:packmmsInTPKT(mmsRequest)
|
||||||
|
|
||||||
|
status = socket:send( MMS_READREQUEST )
|
||||||
|
if not status then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
status, recv = socket:receive_bytes(1024)
|
||||||
|
stdnse.debug(1, "mms_read recv: %s", stdnse.tohex(recv) )
|
||||||
|
|
||||||
|
if ( status and recv ) then
|
||||||
|
local mmsstruct = decoder:unpackAndDecode(recv)
|
||||||
|
if not mmsstruct then
|
||||||
|
stdnse.debug(1, "error while decoding")
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
replaceEmptyStrings(mmsstruct)
|
||||||
|
table.insert(mmsoutput, {})
|
||||||
|
mmsoutput[i][1] = mmsstruct.confirmed_ResponsePDU.Read_Response.listOfAccessResult[1][1]
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- create table for output
|
||||||
|
output["productFamily"] = mmsoutput[1][1]
|
||||||
|
output["configuration"] = mmsoutput[2][1]
|
||||||
|
|
||||||
|
if Node_Ready then
|
||||||
|
output["vendorName"] = mmsoutput[3][1]
|
||||||
|
output["serialNumber"] = mmsoutput[4][1]
|
||||||
|
output["modelNumber"] = mmsoutput[5][1]
|
||||||
|
output["firmwareVersion"] = mmsoutput[6][1]
|
||||||
|
else
|
||||||
|
output["vendorName"] = "<NO_LPHD_FOUND>"
|
||||||
|
output["serialNumber"] = "<NO_LPHD_FOUND>"
|
||||||
|
output["modelNumber"] = "<NO_LPHD_FOUND>"
|
||||||
|
output["firmwareVersion"] = "<NO_LPHD_FOUND>"
|
||||||
|
end
|
||||||
|
return output
|
||||||
|
|
||||||
|
end
|
||||||
439
scripts/multicast-profinet-discovery.nse
Executable file
439
scripts/multicast-profinet-discovery.nse
Executable file
@@ -0,0 +1,439 @@
|
|||||||
|
local coroutine = require "coroutine"
|
||||||
|
local nmap = require "nmap"
|
||||||
|
local stdnse = require "stdnse"
|
||||||
|
local string = require "string"
|
||||||
|
local table = require "table"
|
||||||
|
local packet = require "packet"
|
||||||
|
local ipOps = require "ipOps"
|
||||||
|
|
||||||
|
|
||||||
|
description = [[
|
||||||
|
Sends a multicast PROFINET DCP Identify All message and prints the responses.
|
||||||
|
|
||||||
|
Reference:
|
||||||
|
* https://profinetuniversity.com/naming-addressing/profinet-dcp/
|
||||||
|
]]
|
||||||
|
|
||||||
|
---@output
|
||||||
|
--multicast-profinet-discovery:
|
||||||
|
--| devices:
|
||||||
|
--|
|
||||||
|
--| ip_addr: 10.253.81.37
|
||||||
|
--| mac_addr: 00:0E:8C:C9:41:15
|
||||||
|
--| subnetmask: 255.255.255.0
|
||||||
|
--| vendorId: 002A
|
||||||
|
--| deviceId: 0105
|
||||||
|
--| vendorvalue: S7-300
|
||||||
|
--| deviceRole: 00
|
||||||
|
--| nameOfStation: pn-io
|
||||||
|
--|
|
||||||
|
--| ip_addr: 10.253.81.26
|
||||||
|
--| mac_addr: AC:64:17:2C:C9:46
|
||||||
|
--| subnetmask: 255.255.255.0
|
||||||
|
--| vendorId: 002A
|
||||||
|
--| deviceId: 0404
|
||||||
|
--| vendorvalue: SIMATIC-HMI
|
||||||
|
--| deviceRole: 00
|
||||||
|
--|_ nameOfStation: xd134xbvisu.profinetxaschnittstellexb103b2
|
||||||
|
|
||||||
|
author = "Stefan Eiwanger, DINA-community"
|
||||||
|
license = "BSD-2-Clause Plus Patent License. For further details, please refer https://spdx.org/licenses/BSD-2-Clause-Patent.html"
|
||||||
|
categories = {"discovery","info", "safe"}
|
||||||
|
|
||||||
|
prerule = function()
|
||||||
|
if not nmap.is_privileged() then
|
||||||
|
stdnse.debug(1, "Nmap is NOT running as privileged.")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local pn_dcp_multicast = "01:0e:cf:00:00:00"
|
||||||
|
|
||||||
|
|
||||||
|
-- generate raw profinet identify all message
|
||||||
|
--@param iface interface table containing mac address
|
||||||
|
--@return eth_packet ethernet packet for sending over socket
|
||||||
|
build_eth_frame= function(iface)
|
||||||
|
|
||||||
|
stdnse.debug(1, "Build packet for dcp identify all call.")
|
||||||
|
stdnse.debug(1, "Interface: " .. iface.device)
|
||||||
|
local pn_dcp_size = 46 -- min size of ethernet packet
|
||||||
|
local eth_packet
|
||||||
|
local src_mac = iface.mac
|
||||||
|
|
||||||
|
|
||||||
|
local dest_mac = packet.mactobin(pn_dcp_multicast)
|
||||||
|
local eth_proto = string.pack("I2", 0x9288)
|
||||||
|
|
||||||
|
-- pn-dcp request frame : [FrameID | ServiceID | ServiceType | Xid | ResponseDelay | DCPDataLength | Option | Suboption ]
|
||||||
|
local blockData = string.pack("I2BBI4I2I2BB", 0xfefe, 0x05,0x00,0x10000010, 0x0400, 0x0400,0xff, 0xff)
|
||||||
|
local padbyte = string.pack("B", 0x00)
|
||||||
|
|
||||||
|
-- build the packet
|
||||||
|
eth_packet = dest_mac .. src_mac .. eth_proto .. blockData
|
||||||
|
local length = string.len(eth_packet)
|
||||||
|
|
||||||
|
-- fill the rest of the packet with 0x00 till ethernet min size is reached
|
||||||
|
local padding = string.rep(padbyte, (pn_dcp_size-length))
|
||||||
|
eth_packet = eth_packet .. padding
|
||||||
|
return eth_packet
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- extract data from incoming dcp packets and store them into a table
|
||||||
|
--@param eth_data ethernet part of the recieved packet
|
||||||
|
--@param pn_data profinet part of the recieved packet == ethernet packetload
|
||||||
|
--@return device table with all extraced data from the pn_dcp
|
||||||
|
parse_pndcp = function(eth_data, pn_data)
|
||||||
|
stdnse.debug(1, "Start parsing of answer")
|
||||||
|
local pos = 7 -- start after the destination mac address (host)
|
||||||
|
local deviceMacAddress
|
||||||
|
local deviceRoleInterpretation = {}
|
||||||
|
deviceRoleInterpretation [0] = "PNIO Device"
|
||||||
|
deviceRoleInterpretation [1] = "PNIO Controller"
|
||||||
|
deviceRoleInterpretation [2] = "PNIO Multidevice"
|
||||||
|
deviceRoleInterpretation [3] = "PNIO Supervisor"
|
||||||
|
|
||||||
|
-- extract device mac address
|
||||||
|
local mac
|
||||||
|
mac, pos = string.unpack("c6", eth_data, pos)
|
||||||
|
deviceMacAddress = stdnse.format_mac(mac)
|
||||||
|
|
||||||
|
stdnse.debug(1, "Device MAC address: %s", deviceMacAddress)
|
||||||
|
|
||||||
|
-- check if the packet is a request
|
||||||
|
local serviceType
|
||||||
|
serviceType= string.unpack("B", pn_data, 4)
|
||||||
|
stdnse.debug(1, "Servicetype %x", serviceType)
|
||||||
|
if (serviceType == 0) then return end
|
||||||
|
|
||||||
|
|
||||||
|
-- start extrating data from pn_dcp_response -- start with 1
|
||||||
|
pos = 11
|
||||||
|
|
||||||
|
local gesDCPDataLength = ""
|
||||||
|
gesDCPDataLength, pos = string.unpack(">I2", pn_data, pos)
|
||||||
|
stdnse.debug(1,"DCP Datalength of full packet: %d", gesDCPDataLength)
|
||||||
|
|
||||||
|
-- extract data from DCP block
|
||||||
|
local option, suboption
|
||||||
|
local IP, deviceVendorValue, deviceRole, deviceId, nameofstation, dcpDatalength, subnetmask, standardGateway, vendorId = "", "", "", "", "", "", "", "", ""
|
||||||
|
stdnse.debug(1, "Start extracting data from DCP block")
|
||||||
|
while(pos < gesDCPDataLength) do
|
||||||
|
|
||||||
|
-- Option IP, suboption IP
|
||||||
|
option, suboption, pos = string.unpack("BB", pn_data, pos)
|
||||||
|
|
||||||
|
local dcpDataLength, _
|
||||||
|
if option == 1 then -- IP
|
||||||
|
if(suboption == 2) then
|
||||||
|
stdnse.debug(1, "Option IP, suboption IP")
|
||||||
|
|
||||||
|
-- DCP block length
|
||||||
|
dcpDataLength, pos = string.unpack(">I2", pn_data, pos)
|
||||||
|
--stdnse.debug(1,"* DCP Datalength of IP/IP %d", dcpDataLength)
|
||||||
|
|
||||||
|
-- block info
|
||||||
|
_, pos = string.unpack(">I2", pn_data, pos)
|
||||||
|
|
||||||
|
local dword = ""
|
||||||
|
-- IP
|
||||||
|
dword, pos = string.unpack(">I4", pn_data, pos)
|
||||||
|
IP = ipOps.fromdword(dword)
|
||||||
|
stdnse.debug(1, "* IP address: %s", IP)
|
||||||
|
|
||||||
|
-- subnetmask
|
||||||
|
dword, pos = string.unpack(">I4", pn_data, pos)
|
||||||
|
subnetmask = ipOps.fromdword(dword)
|
||||||
|
stdnse.debug(1, "* Subnetmask: %s", subnetmask)
|
||||||
|
|
||||||
|
-- standard gateway
|
||||||
|
dword, pos = string.unpack(">I4", pn_data, pos)
|
||||||
|
standardGateway = ipOps.fromdword(dword)
|
||||||
|
stdnse.debug(1, "* Default gateway: %s", standardGateway)
|
||||||
|
|
||||||
|
--[[if dcpDataLength%2 ~= 0 then
|
||||||
|
pos = pos +1 -- add padding
|
||||||
|
end
|
||||||
|
--]]
|
||||||
|
else
|
||||||
|
stdnse.debug(1, "Option IP, suboption something else: %d", suboption)
|
||||||
|
|
||||||
|
-- DCP block length
|
||||||
|
dcpDataLength, pos = string.unpack(">I2", pn_data, pos)
|
||||||
|
--stdnse.debug(1, "* DCP datalength of IP/else: %d", dcpDataLength)
|
||||||
|
|
||||||
|
if dcpDataLength%2 ~= 0 then
|
||||||
|
pos = pos +1 -- add padding
|
||||||
|
stdnse.debug(1, "dcpDatalength was odd, add padding +1 to pos")
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
elseif option == 2 then -- device properties
|
||||||
|
if suboption == 1 then-- deviceVendorValue manufacturer specific option
|
||||||
|
stdnse.debug(1, "Option device properties, suboption manufacturer specific")
|
||||||
|
|
||||||
|
-- DCP block length
|
||||||
|
dcpDataLength, pos = string.unpack(">I2", pn_data, pos)
|
||||||
|
--stdnse.debug(1,"* DCP Datalength of device properties/manufacturer specific %d", dcpDataLength)
|
||||||
|
|
||||||
|
-- block info
|
||||||
|
_, pos = string.unpack(">I2", pn_data, pos)
|
||||||
|
|
||||||
|
-- device vendor
|
||||||
|
deviceVendorValue, pos = string.unpack("c" .. (dcpDataLength - 2) ,pn_data, pos)
|
||||||
|
stdnse.debug(1, "* Device Vendor: %s", deviceVendorValue)
|
||||||
|
|
||||||
|
if dcpDataLength%2 ~= 0 then
|
||||||
|
stdnse.debug(1, "dcpDatalength was odd, add padding +1 to pos")
|
||||||
|
pos = pos +1 -- add padding
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif suboption == 2 then -- nameofstation
|
||||||
|
stdnse.debug(1, "Option device properties, suboption name of station")
|
||||||
|
|
||||||
|
-- DCP block length
|
||||||
|
dcpDataLength, pos = string.unpack(">I2", pn_data, pos)
|
||||||
|
--stdnse.debug(1,"* DCP Datalength of device properties/name of station %d", dcpDataLength)
|
||||||
|
|
||||||
|
-- block info
|
||||||
|
_, pos = string.unpack(">I2", pn_data, pos)
|
||||||
|
|
||||||
|
-- name of station
|
||||||
|
nameofstation, pos = string.unpack("c" .. (dcpDataLength - 2) ,pn_data, pos)
|
||||||
|
stdnse.debug(1, "* Name Of Station: %s", nameofstation)
|
||||||
|
|
||||||
|
if dcpDataLength%2 ~= 0 then
|
||||||
|
stdnse.debug(1, "dcpDatalength was odd, add padding +1 to pos")
|
||||||
|
pos = pos +1 -- add padding
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif suboption == 3 then -- device id, vendor Id
|
||||||
|
stdnse.debug(1, "Option device properties, suboption device ID")
|
||||||
|
|
||||||
|
-- DCP block length
|
||||||
|
dcpDataLength, pos = string.unpack(">I2", pn_data, pos)
|
||||||
|
--stdnse.debug(1,"* DCP Datalength of device properties/device ID %d", dcpDataLength)
|
||||||
|
|
||||||
|
-- block info
|
||||||
|
_, pos = string.unpack(">I2", pn_data, pos)
|
||||||
|
|
||||||
|
-- vendor ID
|
||||||
|
local tmpvendorId, tmpdeviceId = "", ""
|
||||||
|
tmpvendorId, pos = string.unpack("c2", pn_data, pos)
|
||||||
|
vendorId = stdnse.tohex(tmpvendorId)
|
||||||
|
vendorId = "0x" .. vendorId
|
||||||
|
stdnse.debug(1, "* Vendor ID: %s", vendorId)
|
||||||
|
|
||||||
|
-- device ID
|
||||||
|
tmpdeviceId, pos = string.unpack("c2", pn_data, pos)
|
||||||
|
deviceId = stdnse.tohex(tmpdeviceId)
|
||||||
|
deviceId = "0x" .. deviceId
|
||||||
|
stdnse.debug(1, "* Device ID: %s", deviceId)
|
||||||
|
|
||||||
|
elseif suboption == 4 then -- device role
|
||||||
|
stdnse.debug(1, "Option device properties, suboption device role")
|
||||||
|
|
||||||
|
-- DCP block length
|
||||||
|
dcpDataLength, pos = string.unpack(">I2", pn_data, pos)
|
||||||
|
--stdnse.debug(1,"* DCP Datalength of device properties/device role %d", dcpDataLength)
|
||||||
|
|
||||||
|
-- block info
|
||||||
|
_, pos = string.unpack(">I2", pn_data, pos)
|
||||||
|
|
||||||
|
-- device role
|
||||||
|
deviceRole, pos = string.unpack("B", pn_data, pos)
|
||||||
|
deviceRole = deviceRoleInterpretation[deviceRole] .. ' 0x0' .. deviceRole
|
||||||
|
stdnse.debug(1, "* Device Role: %s", deviceRole)
|
||||||
|
|
||||||
|
-- reserved
|
||||||
|
_, pos = string.unpack("B", pn_data, pos)
|
||||||
|
else
|
||||||
|
stdnse.debug(1, "Option device properties, suboption something else: %d", suboption)
|
||||||
|
|
||||||
|
-- DCP block length
|
||||||
|
dcpDataLength, pos = string.unpack(">I2", pn_data, pos)
|
||||||
|
--stdnse.debug(1,"* DCP Datalength of device properties/device role %d", dcpDataLength)
|
||||||
|
|
||||||
|
pos = pos + dcpDataLength
|
||||||
|
if dcpDataLength%2 ~= 0 then
|
||||||
|
stdnse.debug(2, "dcpDatalength was odd, add padding +1 to pos")
|
||||||
|
pos = pos +1 -- add padding
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
else
|
||||||
|
stdnse.debug(1, "Option something else: %d", option)
|
||||||
|
|
||||||
|
-- DCP block length
|
||||||
|
dcpDataLength, pos = string.unpack(">I2", pn_data, pos)
|
||||||
|
--stdnse.debug(1,"* DCP Datalength of device properties/device role %d", dcpDataLength)
|
||||||
|
|
||||||
|
pos = pos + dcpDataLength
|
||||||
|
if dcpDataLength%2 ~= 0 then
|
||||||
|
stdnse.debug(1, "dcpDatalength was odd, add padding +1 to pos")
|
||||||
|
pos = pos +1 -- add padding
|
||||||
|
end
|
||||||
|
|
||||||
|
end -- close if
|
||||||
|
|
||||||
|
end -- close while
|
||||||
|
|
||||||
|
-- store data into table
|
||||||
|
local device = stdnse.output_table()
|
||||||
|
device.ip_addr = IP
|
||||||
|
device.mac_addr = deviceMacAddress
|
||||||
|
device.subnetmask = subnetmask
|
||||||
|
device.vendorId = vendorId
|
||||||
|
device.deviceId = deviceId
|
||||||
|
device.vendorvalue = deviceVendorValue
|
||||||
|
device.deviceRole = deviceRole
|
||||||
|
device.nameOfStation = nameofstation
|
||||||
|
|
||||||
|
stdnse.debug(1, "End of parsing\n")
|
||||||
|
|
||||||
|
return device
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get all possible interfaces
|
||||||
|
--@param link type of interface e.g. "ethernet"
|
||||||
|
--@param up status of the interface
|
||||||
|
--@return result table with all interfaces which match the given requirements
|
||||||
|
getInterfaces = function(link, up)
|
||||||
|
if( not(nmap.list_interfaces) ) then return end
|
||||||
|
local interfaces, err = nmap.list_interfaces()
|
||||||
|
local result = {}
|
||||||
|
|
||||||
|
if ( not(err) ) then
|
||||||
|
for _, iface in ipairs(interfaces) do
|
||||||
|
if ( iface.link == link and
|
||||||
|
iface.up == up and
|
||||||
|
iface.mac ) then
|
||||||
|
if #result == 0 then
|
||||||
|
table.insert(result, iface)
|
||||||
|
else
|
||||||
|
local exists = false
|
||||||
|
for _, intface in ipairs(result) do
|
||||||
|
if intface.mac == iface.mac then
|
||||||
|
exists = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not exists then
|
||||||
|
table.insert(result, iface)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- helpfunction for thread call
|
||||||
|
--@param iface interface table
|
||||||
|
--@param pn_dcp ethernet dcp packet to send
|
||||||
|
--@param devices table for results
|
||||||
|
--@return devices, table with devices which answered to the dcp identify all call
|
||||||
|
discoverThread = function(iface, pn_dcp, devices)
|
||||||
|
local condvar = nmap.condvar(devices)
|
||||||
|
local dnet = nmap.new_dnet()
|
||||||
|
local pcap_s = nmap.new_socket()
|
||||||
|
pcap_s:set_timeout(2000)
|
||||||
|
dnet:ethernet_open(iface.device)
|
||||||
|
pcap_s:pcap_open(iface.device, 256, false, "ether proto 0x8892")
|
||||||
|
|
||||||
|
local status, ethData, length, pn_data
|
||||||
|
|
||||||
|
dnet:ethernet_send(pn_dcp) -- send the frame
|
||||||
|
|
||||||
|
status = true
|
||||||
|
while status do
|
||||||
|
status, length, ethData, pn_data = pcap_s:pcap_receive()
|
||||||
|
|
||||||
|
if(status) then
|
||||||
|
devices[#devices + 1] = parse_pndcp(ethData, pn_data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
dnet:ethernet_close(iface.device); -- close the sender
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pcap_s:close(iface.device)
|
||||||
|
condvar "signal"
|
||||||
|
return devices
|
||||||
|
end
|
||||||
|
|
||||||
|
-- main fuction
|
||||||
|
--@return 0 if no devices were found
|
||||||
|
--@return output_tab table for nmap to show the gathered information
|
||||||
|
action = function()
|
||||||
|
local interface_e = nmap.get_interface()
|
||||||
|
local interfaces = {}
|
||||||
|
|
||||||
|
local output_tab = stdnse.output_table()
|
||||||
|
output_tab.devices = {}
|
||||||
|
|
||||||
|
-- check interface parameter
|
||||||
|
|
||||||
|
local dnet = nmap.new_dnet()
|
||||||
|
local pcap_s = nmap.new_socket()
|
||||||
|
pcap_s:set_timeout(4000)
|
||||||
|
|
||||||
|
|
||||||
|
if(interface_e) then -- interface supplied with -e
|
||||||
|
local iface = nmap.get_interface_info(interface_e)
|
||||||
|
if not (iface and iface.link == 'ethernet') then
|
||||||
|
stdnse.debug(1, "%s not supported with %s", iface, SCRIPT_NAME)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
table.insert(interfaces, iface)
|
||||||
|
else -- discover interfaces
|
||||||
|
interfaces = getInterfaces("ethernet", "up")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check if at least one interface is available
|
||||||
|
if #interfaces == 0 then
|
||||||
|
print("No interfaces found")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get the frame we want to send
|
||||||
|
|
||||||
|
|
||||||
|
local threads = {}
|
||||||
|
|
||||||
|
local condvar = nmap.condvar(output_tab.devices)
|
||||||
|
|
||||||
|
|
||||||
|
for _, iface in ipairs(interfaces) do
|
||||||
|
local pn_dcp = build_eth_frame(iface)
|
||||||
|
--print(iface.device)
|
||||||
|
|
||||||
|
local co = stdnse.new_thread(discoverThread, iface, pn_dcp, output_tab.devices)
|
||||||
|
threads[co] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- wait for all threads to finish sniffing
|
||||||
|
repeat
|
||||||
|
for thread in pairs(threads) do
|
||||||
|
if coroutine.status(thread) == "dead" then
|
||||||
|
threads[thread] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if ( next(threads) ) then
|
||||||
|
condvar "wait"
|
||||||
|
end
|
||||||
|
until next(threads) == nil
|
||||||
|
|
||||||
|
-- check the output if something is doubled there
|
||||||
|
if #output_tab.devices == 0 then
|
||||||
|
print("No profinet devices in the subnet")
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return output_tab
|
||||||
|
|
||||||
|
end
|
||||||
166
scripts/profinet-cm-lookup.nse
Executable file
166
scripts/profinet-cm-lookup.nse
Executable file
@@ -0,0 +1,166 @@
|
|||||||
|
local nmap = require "nmap"
|
||||||
|
local stdnse = require "stdnse"
|
||||||
|
local shortport = require "shortport"
|
||||||
|
local string = require "string"
|
||||||
|
|
||||||
|
description = [[
|
||||||
|
Sends a DCERPC EPM Lookup Request to PROFINET devices. the DCE/RPC Endpoint Mapper (EPM) targeting Profinet Devices.
|
||||||
|
|
||||||
|
Profinet Devices support the udp-based PNIO-CM protocol under port 34964.
|
||||||
|
PNIO-CM uses DCE/RPC as its underlying protocol.
|
||||||
|
|
||||||
|
|
||||||
|
Profinet Devices support a DCE/RPC UUID Entity under the UUID variant
|
||||||
|
'dea00001-6c97-11d1-8271-00a02442df7d'. This script sends the Lookup Request for this UUID.
|
||||||
|
|
||||||
|
References:
|
||||||
|
* https://rt-labs.com/docs/p-net/profinet_details.html#dce-rpc-uuid-entities
|
||||||
|
* https://wiki.wireshark.org/EPM
|
||||||
|
]]
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @usage nmap -sU <target_ip> -p 34964 --script profinet-cm-lookup
|
||||||
|
---
|
||||||
|
-- @output
|
||||||
|
--PORT STATE SERVICE REASON
|
||||||
|
--34964/udp open|filtered profinet-cm no-response
|
||||||
|
--| profinet-cm-lookup:
|
||||||
|
--| ipAddress: 192.168.10.12
|
||||||
|
--| annotationOffset: 0
|
||||||
|
--| annotationLength: 64
|
||||||
|
--|_ annotation: S7-1500 6ES7 672-5DC01-0YA0 0 V 2 1 7
|
||||||
|
-- @xmloutput
|
||||||
|
--<elem key="ipAddress">192.168.10.12</elem>
|
||||||
|
--<elem key="annotationOffset">0</elem>
|
||||||
|
--<elem key="annotationLength">64</elem>
|
||||||
|
--<elem key="annotation">S7-1500 6ES7 672-5DC01-0YA0 0 V 2 1 7</elem>
|
||||||
|
|
||||||
|
categories = {"discovery", "intrusive"}
|
||||||
|
author = "DINA-community"
|
||||||
|
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
|
||||||
|
|
||||||
|
local EPM_UDP_PORT = 34964
|
||||||
|
|
||||||
|
local DCE_RPC_REQUEST = string.char(
|
||||||
|
0x04,0x00,0x20,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x83,0xaf,0xe1,0x1f,0x5d,0xc9,0x11,
|
||||||
|
0x91,0xa4,0x08,0x00,0x2b,0x14,0xa0,0xfa,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,
|
||||||
|
0x01,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,
|
||||||
|
0x0c,0x00,0x00,0x00,0x02,0x00,0xff,0xff,0xff,0xff,0x4c,0x00,0x00,0x00,0x00,0x00)
|
||||||
|
|
||||||
|
local EPM_Lookup = string.char(
|
||||||
|
0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0xa0,0xde,
|
||||||
|
0x97,0x6c,0xd1,0x11,0x82,0x71,0x00,0xa0,0x24,0x42,0xdf,0x7d,0x01,0x00,0x00,0x00,
|
||||||
|
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00)
|
||||||
|
|
||||||
|
|
||||||
|
-- The Rules
|
||||||
|
portrule = shortport.port_or_service(34964, "profinet-cm", "udp")
|
||||||
|
if not nmap.is_privileged() then
|
||||||
|
stdnse.debug(1, "Nmap is NOT running as privileged.")
|
||||||
|
portrule = nil
|
||||||
|
prerule = function() return false end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- The Action
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Parses the EPM Lookup Response extracting the annotation field containing the
|
||||||
|
-- product name and the article number of the scanned PNIO Device
|
||||||
|
---
|
||||||
|
parse_response = function(host, port, layer3)
|
||||||
|
-- print raw bytes of reponse
|
||||||
|
stdnse.debug(2, "Raw hex: %s", stdnse.tohex(layer3))
|
||||||
|
|
||||||
|
-- parse byte order/ endianness
|
||||||
|
local order_tmp = string.unpack('B', layer3, 33)
|
||||||
|
local order = order_tmp >> 4
|
||||||
|
local format_prefix = order == 0 and ">" or "<"
|
||||||
|
|
||||||
|
stdnse.debug(1, "little_endian: " .. tostring(order))
|
||||||
|
|
||||||
|
-- parse annotationOffset
|
||||||
|
local annotationOffset = string.unpack("I4", layer3, 165)
|
||||||
|
stdnse.debug(1, "annotationOffset 0x%s", stdnse.tohex(annotationOffset))
|
||||||
|
|
||||||
|
-- parse annotationLength
|
||||||
|
local annotation_length_format = string.format("%si4", format_prefix)
|
||||||
|
stdnse.debug(1, annotation_length_format)
|
||||||
|
local annotationLength = string.unpack(annotation_length_format, layer3, 169)
|
||||||
|
stdnse.debug(1, "annotationLength " .. annotationLength)
|
||||||
|
|
||||||
|
-- parse annotation
|
||||||
|
local annotation_format = string.format("c%d", annotationLength)
|
||||||
|
local annotation = string.unpack(annotation_format, layer3, 173)
|
||||||
|
stdnse.debug(1, "annotation: " .. annotation)
|
||||||
|
|
||||||
|
-- create table for output
|
||||||
|
local output = stdnse.output_table()
|
||||||
|
output["ipAddress"] = host.ip
|
||||||
|
output["annotationOffset"] = annotationOffset
|
||||||
|
output["annotationLength"] = annotationLength
|
||||||
|
output["annotation"] = annotation
|
||||||
|
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Sends the udp payload and parses the response
|
||||||
|
lookup_request = function(host, port, payload, timeout)
|
||||||
|
local socket, try, catch
|
||||||
|
|
||||||
|
-- create a new udp socket for sending the lookup request
|
||||||
|
local socket = nmap.new_socket("udp")
|
||||||
|
|
||||||
|
-- create a socket for receiving incoming data
|
||||||
|
-- 'socket:receive()'' alone won't suffice as the UDP port of
|
||||||
|
-- the scanned device can be selected arbitrarily
|
||||||
|
local pcap = nmap.new_socket()
|
||||||
|
|
||||||
|
-- set timeout
|
||||||
|
socket:set_timeout(tonumber(timeout))
|
||||||
|
|
||||||
|
catch = function()
|
||||||
|
pcap:close()
|
||||||
|
socket:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- create new try
|
||||||
|
try = nmap.new_try(catch)
|
||||||
|
|
||||||
|
-- connect to port on host for sending payload
|
||||||
|
try(socket:connect(host.ip, port["number"], "udp"))
|
||||||
|
|
||||||
|
local status, lhost, lport, rhost, rport = socket:get_info()
|
||||||
|
|
||||||
|
if status then
|
||||||
|
-- configuration for pcap:pcap_receive()
|
||||||
|
pcap:pcap_open(host.interface, 1500, false, "udp dst port " .. lport .. " and src host " .. host.ip)
|
||||||
|
pcap:set_timeout(host.times.timeout * 1000)
|
||||||
|
|
||||||
|
-- send lookup packet with PNIO Interface UUID
|
||||||
|
try(socket:send(payload))
|
||||||
|
|
||||||
|
-- receive response
|
||||||
|
local status_rec, len, _, layer3 = pcap:pcap_receive()
|
||||||
|
|
||||||
|
-- when successful, set port state to "open" and parse response
|
||||||
|
if status_rec and len > 200 then
|
||||||
|
nmap.set_port_state(host, port, "open")
|
||||||
|
return parse_response(host, port, layer3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- close sockets
|
||||||
|
pcap:close()
|
||||||
|
socket:close()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- MAIN
|
||||||
|
action = function(host, port)
|
||||||
|
local payload = DCE_RPC_REQUEST .. EPM_Lookup
|
||||||
|
local timeout = stdnse.get_timeout(host)
|
||||||
|
return lookup_request(host, port, payload, timeout)
|
||||||
|
end
|
||||||
@@ -145,6 +145,7 @@ Entry { filename = "hadoop-jobtracker-info.nse", categories = { "default", "disc
|
|||||||
Entry { filename = "hadoop-namenode-info.nse", categories = { "default", "discovery", "safe", } }
|
Entry { filename = "hadoop-namenode-info.nse", categories = { "default", "discovery", "safe", } }
|
||||||
Entry { filename = "hadoop-secondary-namenode-info.nse", categories = { "default", "discovery", "safe", } }
|
Entry { filename = "hadoop-secondary-namenode-info.nse", categories = { "default", "discovery", "safe", } }
|
||||||
Entry { filename = "hadoop-tasktracker-info.nse", categories = { "default", "discovery", "safe", } }
|
Entry { filename = "hadoop-tasktracker-info.nse", categories = { "default", "discovery", "safe", } }
|
||||||
|
Entry { filename = "hartip-info.nse", categories = { "discovery", "intrusive", } }
|
||||||
Entry { filename = "hbase-master-info.nse", categories = { "default", "discovery", "safe", } }
|
Entry { filename = "hbase-master-info.nse", categories = { "default", "discovery", "safe", } }
|
||||||
Entry { filename = "hbase-region-info.nse", categories = { "default", "discovery", "safe", } }
|
Entry { filename = "hbase-region-info.nse", categories = { "default", "discovery", "safe", } }
|
||||||
Entry { filename = "hddtemp-info.nse", categories = { "default", "discovery", "safe", } }
|
Entry { filename = "hddtemp-info.nse", categories = { "default", "discovery", "safe", } }
|
||||||
@@ -291,6 +292,7 @@ Entry { filename = "iax2-brute.nse", categories = { "brute", "intrusive", } }
|
|||||||
Entry { filename = "iax2-version.nse", categories = { "version", } }
|
Entry { filename = "iax2-version.nse", categories = { "version", } }
|
||||||
Entry { filename = "icap-info.nse", categories = { "discovery", "safe", } }
|
Entry { filename = "icap-info.nse", categories = { "discovery", "safe", } }
|
||||||
Entry { filename = "iec-identify.nse", categories = { "discovery", "intrusive", } }
|
Entry { filename = "iec-identify.nse", categories = { "discovery", "intrusive", } }
|
||||||
|
Entry { filename = "iec61850-mms.nse", categories = { "discovery", "intrusive", "version", } }
|
||||||
Entry { filename = "ike-version.nse", categories = { "default", "discovery", "safe", "version", } }
|
Entry { filename = "ike-version.nse", categories = { "default", "discovery", "safe", "version", } }
|
||||||
Entry { filename = "imap-brute.nse", categories = { "brute", "intrusive", } }
|
Entry { filename = "imap-brute.nse", categories = { "brute", "intrusive", } }
|
||||||
Entry { filename = "imap-capabilities.nse", categories = { "default", "safe", } }
|
Entry { filename = "imap-capabilities.nse", categories = { "default", "safe", } }
|
||||||
@@ -367,6 +369,7 @@ Entry { filename = "ms-sql-tables.nse", categories = { "discovery", "safe", } }
|
|||||||
Entry { filename = "ms-sql-xp-cmdshell.nse", categories = { "intrusive", } }
|
Entry { filename = "ms-sql-xp-cmdshell.nse", categories = { "intrusive", } }
|
||||||
Entry { filename = "msrpc-enum.nse", categories = { "discovery", "safe", } }
|
Entry { filename = "msrpc-enum.nse", categories = { "discovery", "safe", } }
|
||||||
Entry { filename = "mtrace.nse", categories = { "broadcast", "discovery", "safe", } }
|
Entry { filename = "mtrace.nse", categories = { "broadcast", "discovery", "safe", } }
|
||||||
|
Entry { filename = "multicast-profinet-discovery.nse", categories = { "discovery", "info", "safe", } }
|
||||||
Entry { filename = "murmur-version.nse", categories = { "version", } }
|
Entry { filename = "murmur-version.nse", categories = { "version", } }
|
||||||
Entry { filename = "mysql-audit.nse", categories = { "discovery", "safe", } }
|
Entry { filename = "mysql-audit.nse", categories = { "discovery", "safe", } }
|
||||||
Entry { filename = "mysql-brute.nse", categories = { "brute", "intrusive", } }
|
Entry { filename = "mysql-brute.nse", categories = { "brute", "intrusive", } }
|
||||||
@@ -429,6 +432,7 @@ Entry { filename = "pop3-capabilities.nse", categories = { "default", "discovery
|
|||||||
Entry { filename = "pop3-ntlm-info.nse", categories = { "default", "discovery", "safe", } }
|
Entry { filename = "pop3-ntlm-info.nse", categories = { "default", "discovery", "safe", } }
|
||||||
Entry { filename = "port-states.nse", categories = { "safe", } }
|
Entry { filename = "port-states.nse", categories = { "safe", } }
|
||||||
Entry { filename = "pptp-version.nse", categories = { "version", } }
|
Entry { filename = "pptp-version.nse", categories = { "version", } }
|
||||||
|
Entry { filename = "profinet-cm-lookup.nse", categories = { "discovery", "intrusive", } }
|
||||||
Entry { filename = "puppet-naivesigning.nse", categories = { "intrusive", "vuln", } }
|
Entry { filename = "puppet-naivesigning.nse", categories = { "intrusive", "vuln", } }
|
||||||
Entry { filename = "qconn-exec.nse", categories = { "exploit", "intrusive", "vuln", } }
|
Entry { filename = "qconn-exec.nse", categories = { "exploit", "intrusive", "vuln", } }
|
||||||
Entry { filename = "qscan.nse", categories = { "discovery", "safe", } }
|
Entry { filename = "qscan.nse", categories = { "discovery", "safe", } }
|
||||||
|
|||||||
Reference in New Issue
Block a user