198 lines
6.6 KiB
Lua

local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
description = [[
This NSE script is used to send a FINS packet to a remote device. The script
will send a Controller Data Read Command 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.
]]
---
-- @usage
-- nmap --script omron-info -sU -p 9600 <host>
--
-- @output
-- 9600/tcp open OMRON FINS
-- | omron-info:
-- | Controller Model: CJ2M-CPU32 02.01
-- | Controller Version: 02.01
-- | For System Use:
-- | Program Area Size: 20
-- | IOM size: 23
-- | No. DM Words: 32768
-- | Timer/Counter: 8
-- | Expansion DM Size: 1
-- | No. of steps/transitions: 0
-- | Kind of Memory Card: 0
-- |_ Memory Card Size: 0
-- @xmloutput
-- <elem key="Controller Model">CS1G_CPU44H 03.00</elem>
-- <elem key="Controller Version">03.00</elem>
-- <elem key="For System Use"></elem>
-- <elem key="Program Area Size">20</elem>
-- <elem key="IOM size">23</elem>
-- <elem key="No. DM Words">32768</elem>
-- <elem key="Timer/Counter">8</elem>
-- <elem key="Expansion DM Size">1</elem>
-- <elem key="No. of steps/transitions">0</elem>
-- <elem key="Kind of Memory Card">0</elem>
-- <elem key="Memory Card Size">0</elem>
author = "Stephen Hilt (Digital Bond)"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"discovery", "version"}
--
-- Function to define the portrule as per nmap standards
--
--
portrule = shortport.version_port_or_service(9600, "fins", {"tcp", "udp"})
---
-- Function to set the nmap output for the host, if a valid OMRON FINS packet
-- is received then the output will show that the port is open instead of
-- <code>open|filtered</code>
--
-- @param host Host that was passed in via nmap
-- @param port port that FINS is running on (Default UDP/9600)
function set_nmap(host, port)
--set port Open
port.state = "open"
-- set version name to OMRON FINS
port.version.name = "fins"
nmap.set_port_version(host, port)
nmap.set_port_state(host, port, "open")
end
local memcard = {
[0] = "No Memory Card",
[1] = "SPRAM",
[2] = "EPROM",
[3] = "EEPROM"
}
function memory_card(value)
local mem_card = memcard[value] or "Unknown Memory Card Type"
return mem_card
end
---
-- send_udp is a function that is used to run send the appropriate traffic to
-- the omron devices via UDP
--
-- @param socket Socket that is passed in from Action
function send_udp(socket)
local controller_data_read = stdnse.fromhex( "800002000000006300ef050100")
-- send Request Information Packet
socket:send(controller_data_read)
local rcvstatus, response = socket:receive()
return response
end
---
-- send_tcp is a function that is used to run send the appropriate traffic to
-- the omron devices via TCP
--
-- @param socket Socket that is passed in from Action
function send_tcp(socket)
-- this is the request address command
local req_addr = stdnse.fromhex( "46494e530000000c000000000000000000000000")
-- TCP requires a network address that is revived from the first request,
-- The read controller data these two strings will be joined with the address
local controller_data_read = stdnse.fromhex("46494e5300000015000000020000000080000200")
local controller_data_read2 = stdnse.fromhex("000000ef050501")
-- send Request Information Packet
socket:send(req_addr)
local rcvstatus, response = socket:receive()
local header = string.byte(response, 1)
if(header == 0x46) then
local address = string.byte(response, 24)
local controller_data = ("%s%c%s%c"):format(controller_data_read, address, controller_data_read2, 0x00)
-- send the read controller data request
socket:send(controller_data)
local rcvstatus, response = socket:receive()
return response
end
return "ERROR"
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 FINS supported device.
--
-- @param host Host that was scanned via nmap
-- @param port port that was scanned via nmap
action = function(host,port)
-- create table for output
local output = stdnse.output_table()
-- create new socket
local socket = nmap.new_socket()
local catch = function()
socket:close()
end
-- create new try
local try = nmap.new_try(catch)
-- connect to port on host
try(socket:connect(host, port))
-- init response var
local response = ""
-- set offset to 0, this will mean its UDP
local offset = 0
-- check to see if the protocol is TCP, if it is set offset to 16
-- and perform the tcp_send function
if (port.protocol == "tcp")then
offset = 16
response = send_tcp(socket)
-- else its udp and call the send_udp function
else
response = send_udp(socket)
end
-- unpack the first byte for checking that it was a valid response
local header = string.unpack("B", response, 1)
if(header == 0xc0 or header == 0xc1 or header == 0x46) then
set_nmap(host, port)
local response_code = string.unpack("<I2", response, 13 + offset)
-- test for a few of the error codes I saw when testing the script
if(response_code == 2081) then
output["Response Code"] = "Data cannot be changed (0x2108)"
elseif(response_code == 290) then
output["Response Code"] = "The mode is wrong (executing) (0x2201)"
-- if a successful response code then
elseif(response_code == 0) then
-- parse information from response
output["Response Code"] = "Normal completion (0x0000)"
output["Controller Model"] = string.unpack("z", response,15 + offset)
output["Controller Version"] = string.unpack("z", response, 35 + offset)
output["For System Use"] = string.unpack("z", response, 55 + offset)
local pos
output["Program Area Size"], pos = string.unpack(">I2", response, 95 + offset)
output["IOM size"], pos = string.unpack("B", response, pos)
output["No. DM Words"], pos = string.unpack(">I2", response, pos)
output["Timer/Counter"], pos = string.unpack("B", response, pos)
output["Expansion DM Size"], pos = string.unpack("B", response, pos)
output["No. of steps/transitions"], pos = string.unpack(">I2", response, pos)
local mem_card_type
mem_card_type, pos = string.unpack("B", response, pos)
output["Kind of Memory Card"] = memory_card(mem_card_type)
output["Memory Card Size"], pos = string.unpack(">I2", response, pos)
else
output["Response Code"] = "Unknown Response Code"
end
socket:close()
return output
else
socket:close()
return nil
end
end