summaryrefslogtreecommitdiffstats
path: root/scripts/pcworx-info.nse
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/pcworx-info.nse')
-rw-r--r--scripts/pcworx-info.nse111
1 files changed, 111 insertions, 0 deletions
diff --git a/scripts/pcworx-info.nse b/scripts/pcworx-info.nse
new file mode 100644
index 0000000..4b5f7ad
--- /dev/null
+++ b/scripts/pcworx-info.nse
@@ -0,0 +1,111 @@
+local string = require "string"
+local nmap = require "nmap"
+local shortport = require "shortport"
+local stdnse = require "stdnse"
+
+description = [[
+This NSE script will query and parse pcworx protocol to a remote PLC.
+The script will send a initial request packets and once a response is received,
+it validates that it was a proper response to the command that was sent, and then
+will parse out the data. PCWorx is a protocol and Program by Phoenix Contact.
+
+
+http://digitalbond.com
+]]
+---
+-- @usage
+-- nmap --script pcworx-info -p 1962 <host>
+--
+--
+-- @output
+--| pcworx-info:
+--| PLC Type: ILC 330 ETH
+--| Model Number: 2737193
+--| Firmware Version: 3.95T
+--| Firmware Date: Mar 2 2012
+--|_ Firmware Time: 09:39:02
+
+--
+--
+-- @xmloutput
+--<elem key="PLC Type">ILC 330 ETH</elem>
+--<elem key="Model Number">2737193</elem>
+--<elem key="Firmware Version">3.95T</elem>
+--<elem key="Firmware Date">Mar 2 2012</elem>
+--<elem key="Firmware Time">09:39:02</elem>
+
+author = "Stephen Hilt (Digital Bond)"
+license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
+categories = {"discovery"}
+
+portrule = shortport.port_or_service(1962, "pcworx", "tcp")
+
+-- Safely extract a zero-terminated string if the blob is long enough
+-- Returns nil if it is not.
+local function get_string(blob, offset)
+ if #blob >= offset then
+ return string.unpack("z", blob, offset)
+ end
+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. The initial response is parsed to determine if host
+-- is a pcworx Protocol device. If it is then more actions are taken to gather extra information.
+--
+-- @param host Host that was scanned via nmap
+-- @param port port that was scanned via nmap
+action = function(host,port)
+ local init_comms = "\x01\x01\0\x1a\0\0\0\0x\x80\0\x03\0\x0cIBETH01N0_M\0"
+
+ -- create table for output
+ local output = stdnse.output_table()
+
+ -- create new socket
+ local socket = nmap.new_socket()
+ -- define the catch of the try statement
+ local catch = function()
+ socket:close()
+ end
+ local try = nmap.new_try(catch)
+
+ try(socket:connect(host, port))
+ try(socket:send(init_comms))
+ local response = try(socket:receive())
+
+ if not response:match("^\x81") then
+ stdnse.debug1("Unexpected or unknown PCWorx message.")
+ return nil
+ end
+ -- pcworx has a session ID that is generated by the PLC
+ -- This will pull the SID so we can communicate further to the PLC
+ local sid = string.sub(response, 18, 18)
+ local init_comms2 = "\x01\x05\0\x16\0\x01\0\0\x78\x80\0" .. sid .. "\0\0\0\x06\0\x04\x02\x95\0\0"
+ try(socket:send(init_comms2))
+ -- receive response
+ response = try(socket:receive())
+ -- TODO: verify this
+
+ -- this is the request that will pull all the information from the PLC
+ local req_info = "\x01\x06\0\x0e\0\x02\0\0\0\0\0" .. sid .. "\x04\0"
+ try(socket:send(req_info))
+ -- receive response
+ response = try(socket:receive())
+
+ -- if the response starts with 0x81 then we will continue
+ if not response:match("^\x81") then
+ stdnse.debug1("Unexpected or unknown PCWorx message.")
+ socket:close()
+ return nil
+ end
+
+ -- create output table with proper data
+ output["PLC Type"] = get_string(response, 31)
+ output["Model Number"] = get_string(response, 153)
+ output["Firmware Version"] = get_string(response, 67)
+ output["Firmware Date"] = get_string(response, 80)
+ output["Firmware Time"] = get_string(response, 92)
+
+ -- close socket and return output table
+ socket:close()
+ return output
+end