1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-10 17:59:04 +00:00

Documentation and whitespace in modbus-discover.nse.

This commit is contained in:
david
2010-12-16 08:59:18 +00:00
parent ad40df2c9b
commit 41a81e1789

View File

@@ -1,5 +1,15 @@
description = [[ description = [[
Attempts to find valid sid for tcp modbus server and try to get device info. Enumerates Modbus slave ids (sids) and gets their device information.
Modbus is one of the popular SCADA protocols. This script does Modbus device
information disclosure. It tries to find legal sids (slave ids) of Modbus
devices and to get additional information about the vendor and firmware. This
script is improvement of modscan python utility written by Mark Bristow.
Information about MODBUS protocol and security issues:
* MODBUS application protocol specification: http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf
* Defcon 16 Modscan presentation: https://www.defcon.org/images/defcon-16/dc16-presentations/defcon-16-bristow.pdf
* Modscan utility is hosted at google code: http://code.google.com/p/modscan/
]] ]]
--- ---
@@ -16,22 +26,9 @@ description = [[
-- | SLAVE ID DATA: \xFA\xFFPM710PowerMeter -- | SLAVE ID DATA: \xFA\xFFPM710PowerMeter
-- | DEVICE IDENTIFICATION: Schneider Electric PM710 v03.110 -- | DEVICE IDENTIFICATION: Schneider Electric PM710 v03.110
-- |_ Positive error response for sid = 0x96 (GATEWAY TARGET DEVICE FAILED TO RESPONSE) -- |_ Positive error response for sid = 0x96 (GATEWAY TARGET DEVICE FAILED TO RESPONSE)
--
-- Version 0.2 - /12.12.10/ - script cleanup -- Version 0.2 - /12.12.10/ - script cleanup
-- Version 0.3 - /13.12.10/ - several bugfixes -- Version 0.3 - /13.12.10/ - several bugfixes
--
-- MODBUS is one of the popular SCADA protocols.
-- This script intended for modbus device information disclosure.
-- It tries to find legal sid (slave id) of modbus device(s) and
-- if success try to get additional information about vendor and firmware.
-- This script is improvement of modscan python utility written by
-- Mark Bristow.
--
-- Information about MODBUS protocol and security issues:
-- * MODBUS application protocol specification: http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf
-- * Defcon 16 Modscan presentation: https://www.defcon.org/images/defcon-16/dc16-presentations/defcon-16-bristow.pdf
-- * Modscan utility is hosted at google code: http://code.google.com/p/modscan/
---
author = "Alexander Rudakov" author = "Alexander Rudakov"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html" license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
@@ -45,58 +42,58 @@ require "shortport"
portrule = shortport.portnumber(502, "tcp") portrule = shortport.portnumber(502, "tcp")
local form_rsid = function(sid, functionId, data) local form_rsid = function(sid, functionId, data)
local payload_len = 2 local payload_len = 2
if ( #data > 0 ) then if ( #data > 0 ) then
payload_len = payload_len + #data payload_len = payload_len + #data
end end
return bin.pack('CCCCC', 0x00, 0x00, 0x00, 0x00, 0x00) .. bin.pack('C', payload_len) .. bin.pack('C', sid) .. bin.pack('C', functionId) .. data return bin.pack('CCCCC', 0x00, 0x00, 0x00, 0x00, 0x00) .. bin.pack('C', payload_len) .. bin.pack('C', sid) .. bin.pack('C', functionId) .. data
end end
discover_device_id_recursive = function(host, port, sid, start_id) discover_device_id_recursive = function(host, port, sid, start_id)
local rsid = form_rsid(sid, 0x2B, bin.pack('H', "0E 01")..bin.pack('C', start_id)) local rsid = form_rsid(sid, 0x2B, bin.pack('H', "0E 01")..bin.pack('C', start_id))
local status, result = comm.exchange(host, port, rsid) local status, result = comm.exchange(host, port, rsid)
local objects_table = {} local objects_table = {}
if ( status and (#result >= 8)) then if ( status and (#result >= 8)) then
local ret_code = string.byte(result, 8) local ret_code = string.byte(result, 8)
if ( ret_code == 0x2B and #result >= 15 ) then if ( ret_code == 0x2B and #result >= 15 ) then
local more_follows = string.byte(result, 12) local more_follows = string.byte(result, 12)
local next_object_id = string.byte(result, 13) local next_object_id = string.byte(result, 13)
local number_of_objects = string.byte(result, 14) local number_of_objects = string.byte(result, 14)
stdnse.print_debug(1, ("more = 0x%x, next_id = 0x%x, obj_number = 0x%x"):format(more_follows, next_object_id, number_of_objects)) stdnse.print_debug(1, ("more = 0x%x, next_id = 0x%x, obj_number = 0x%x"):format(more_follows, next_object_id, number_of_objects))
local offset = 15 local offset = 15
for i = start_id, (number_of_objects - 1) do for i = start_id, (number_of_objects - 1) do
local object_id = string.byte(result, offset) local object_id = string.byte(result, offset)
local object_len = string.byte(result, offset + 1) local object_len = string.byte(result, offset + 1)
-- error data format -- -- error data format --
if object_len == nil then break end if object_len == nil then break end
local object_value = string.sub(result, offset + 2, offset + 1 + object_len) local object_value = string.sub(result, offset + 2, offset + 1 + object_len)
stdnse.print_debug(1, ("Object id = 0x%x, value = %s"):format(object_id, object_value)) stdnse.print_debug(1, ("Object id = 0x%x, value = %s"):format(object_id, object_value))
table.insert(objects_table, object_id + 1, object_value) table.insert(objects_table, object_id + 1, object_value)
offset = offset + 2 + object_len offset = offset + 2 + object_len
end end
if ( more_follows == 0xFF and next_object_id ~= 0x00 ) then if ( more_follows == 0xFF and next_object_id ~= 0x00 ) then
stdnse.print_debug(1, "Has more objects") stdnse.print_debug(1, "Has more objects")
local recursive_table = discover_device_id_recursive(host, port, sid, next_object_id) local recursive_table = discover_device_id_recursive(host, port, sid, next_object_id)
for k,v in pairs(recursive_table) do for k,v in pairs(recursive_table) do
table.insert(objects_table, k, v) table.insert(objects_table, k, v)
end end
end end
end end
end end
return objects_table return objects_table
end end
local discover_device_id = function(host, port, sid) local discover_device_id = function(host, port, sid)
return discover_device_id_recursive(host, port, sid, 0x0) return discover_device_id_recursive(host, port, sid, 0x0)
end end
local form_device_id_string = function(device_table) local form_device_id_string = function(device_table)
local ret_string = "DEVICE IDENTIFICATION: " local ret_string = "DEVICE IDENTIFICATION: "
for i = 1, #device_table do for i = 1, #device_table do
if ( device_table[i] ~= nil ) then if ( device_table[i] ~= nil ) then
ret_string = ret_string..device_table[i] ret_string = ret_string..device_table[i]
if ( i < #device_table ) then ret_string = ret_string.." " end if ( i < #device_table ) then ret_string = ret_string.." " end
end end
end end
return ret_string return ret_string
end end
@@ -121,52 +118,52 @@ modbus_exception_codes = {
} }
action = function(host, port) action = function(host, port)
local aggressive = false -- stop on first founded sid local aggressive = false -- stop on first founded sid
if (nmap.registry.args['modbus-discover.aggressive']) then
aggressive = nmap.registry.args['modbus-discover.aggressive']
end
local opts = {timeout=2000}
local results = {}
for sid = 1, 246 do
stdnse.print_debug(3, "Sending command with sid = %d", sid)
rsid = form_rsid(sid, 0x11, "")
local status, result = comm.exchange(host, port, rsid, opts) if (nmap.registry.args['modbus-discover.aggressive']) then
if ( status and (#result >= 8) ) then aggressive = nmap.registry.args['modbus-discover.aggressive']
local ret_code = string.byte(result, 8) end
if ( ret_code == (0x11) or ret_code == (0x11 + 128) ) then
local sid_table = {}
if ret_code == (0x11) then
table.insert(results, ("Positive response for sid = 0x%x"):format(sid))
local slave_id = extract_slave_id(result)
if ( slave_id ~= nil ) then table.insert(sid_table, "SLAVE ID DATA: "..slave_id) end
elseif ret_code == (0x11 + 128) then
local exception_code = string.byte(result, 9)
local exception_string = modbus_exception_codes[exception_code]
if ( exception_string == nil ) then exception_string = "UNKNOWN EXCEPTION" end
table.insert(results, ("Positive error response for sid = 0x%x (%s)"):format(sid, exception_string))
end
local device_table = discover_device_id(host, port, sid) local opts = {timeout=2000}
if ( #device_table > 0 ) then local results = {}
table.insert(sid_table, form_device_id_string(device_table))
end
if ( #sid_table > 0 ) then
table.insert(results, sid_table)
end
if ( not aggressive ) then break end
end
end
end
if ( #results > 0 ) then for sid = 1, 246 do
port.state = "open" stdnse.print_debug(3, "Sending command with sid = %d", sid)
port.version.name = "modbus" rsid = form_rsid(sid, 0x11, "")
nmap.set_port_version(host, port, "hardmatched")
end local status, result = comm.exchange(host, port, rsid, opts)
if ( status and (#result >= 8) ) then
return stdnse.format_output(true, results) local ret_code = string.byte(result, 8)
if ( ret_code == (0x11) or ret_code == (0x11 + 128) ) then
local sid_table = {}
if ret_code == (0x11) then
table.insert(results, ("Positive response for sid = 0x%x"):format(sid))
local slave_id = extract_slave_id(result)
if ( slave_id ~= nil ) then table.insert(sid_table, "SLAVE ID DATA: "..slave_id) end
elseif ret_code == (0x11 + 128) then
local exception_code = string.byte(result, 9)
local exception_string = modbus_exception_codes[exception_code]
if ( exception_string == nil ) then exception_string = "UNKNOWN EXCEPTION" end
table.insert(results, ("Positive error response for sid = 0x%x (%s)"):format(sid, exception_string))
end
local device_table = discover_device_id(host, port, sid)
if ( #device_table > 0 ) then
table.insert(sid_table, form_device_id_string(device_table))
end
if ( #sid_table > 0 ) then
table.insert(results, sid_table)
end
if ( not aggressive ) then break end
end
end
end
if ( #results > 0 ) then
port.state = "open"
port.version.name = "modbus"
nmap.set_port_version(host, port, "hardmatched")
end
return stdnse.format_output(true, results)
end end