-
Notifications
You must be signed in to change notification settings - Fork 88
/
Copy pathiec-identify.nse
executable file
·135 lines (116 loc) · 4.39 KB
/
iec-identify.nse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
description = [[
Attemts to check tcp/2404 port supporting IEC 60870-5-104 ICS protocol.
]]
---
-- @usage
-- nmap -Pn -n -d --script iec-identify.nse --script-args='iec-identify.timeout=500' -p 2404 <host>
--
-- @args iec-identify.timeout
-- Set the timeout in milliseconds. The default value is 500.
--
-- @output
-- PORT STATE SERVICE REASON
-- 2404/tcp open IEC 60870-5-104 syn-ack
-- | iec-identify:
-- | testfr sent / recv: 680443000000 / 680483000000
-- | startdt sent / recv: 680407000000 / 68040b000000
-- | c_ic_na_1 sent / recv: 680e0000000064010600ffff00000000 / 680e0000020064014700ffff00000014
-- |_ asdu address: 65535
--
-- Version 0.1
--
---
author = "Aleksandr Timorin"
copyright = "Aleksandr Timorin"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"discovery", "intrusive"}
local shortport = require("shortport")
local bin = require("bin")
local comm = require("comm")
local stdnse = require("stdnse")
portrule = shortport.portnumber(2404, "tcp")
local function hex2str(str)
local x = {}
local char_int
for y in str:gmatch('(..)') do
char_int = tonumber(y, 16)
if char_int>=32 and char_int<=126 then
x[#x+1] = string.char( char_int )
else
x[#x+1] = y
end
end
return table.concat( x )
end
action = function(host, port)
local timeout = stdnse.get_script_args("iec-identify.timeout")
timeout = tonumber(timeout) or 500
local asdu_address
local pos
local status, recv
local output = {}
local socket = nmap.new_socket()
socket:set_timeout(timeout)
-- attempt to connect tp 2404 tcp port
stdnse.print_debug(1, "try to connect to port 2404" )
status, result = socket:connect(host, port, "tcp")
--stdnse.print_debug(1, "connect status %s", status )
if not status then
return nil
end
-- send TESTFR command
local TESTFR = string.char(0x68, 0x04, 0x43, 0x00, 0x00, 0x00)
status = socket:send( TESTFR )
stdnse.print_debug(1, "testfr status %s", status )
if not status then
return nil
end
-- receive TESTFR answer
status, recv = socket:receive_bytes(1024)
stdnse.print_debug(1, "testfr recv: %s", stdnse.tohex(recv) )
--table.insert(output, string.format("testfr sent / recv: %s / %s", hex2str( stdnse.tohex(TESTFR)), hex2str( stdnse.tohex(recv))))
table.insert(output, string.format("testfr sent / recv: %s / %s", stdnse.tohex(TESTFR), stdnse.tohex(recv)))
-- send STARTDT command
local STARTDT = string.char(0x68, 0x04, 0x07, 0x00, 0x00, 0x00)
status = socket:send( STARTDT )
if not status then
return nil
end
-- receive STARTDT answer
status, recv = socket:receive_bytes(0)
stdnse.print_debug(1, "startd recv len: %d", #recv )
stdnse.print_debug(1, "startdt recv: %s", stdnse.tohex(recv) )
--table.insert(output, string.format("startdt sent / recv: %s / %s", hex2str( stdnse.tohex(STARTDT)), hex2str( stdnse.tohex(recv))))
table.insert(output, string.format("startdt sent / recv: %s / %s", stdnse.tohex(STARTDT), stdnse.tohex(recv)))
-- if received 2 packets - STARTDT con + ME_EI_NA_1 Init -> full length should be 6+6+10 bytes
if #recv == 22 then
pos, asdu_address = bin.unpack("<S", recv, 17 )
else
-- send C_IC_NA_1 command
local C_IC_NA_1_broadcast = string.char(0x68, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x64, 0x01, 0x06, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x14)
status = socket:send( C_IC_NA_1_broadcast )
stdnse.print_debug(1, "c_ic_na_1 status %s", status )
if not status then
return nil
end
-- receive C_IC_NA_1 answer
status, recv = socket:receive_bytes(0)
stdnse.print_debug(1, "c_ic_na_1 recv len: %d", #recv )
stdnse.print_debug(1, "c_ic_na_1 recv: %s", stdnse.tohex(recv) )
table.insert(output, string.format("c_ic_na_1 sent / recv: %s / %s", stdnse.tohex(C_IC_NA_1_broadcast), stdnse.tohex(recv)))
if #recv == 16 then
pos, asdu_address = bin.unpack("<S", recv, 11 )
end
end
if asdu_address then
table.insert(output, string.format("asdu address: %d", asdu_address))
end
if(#output == 4 and asdu_address) then
port.version.name = "IEC 60870-5-104"
nmap.set_port_state(host, port, "open")
nmap.set_port_version(host, port, "hardmatched")
return stdnse.format_output(true, output)
else
return nil
end
end