5
5
# http://metasploit.com/framework/
6
6
##
7
7
8
-
9
- ## MODBUS/TCP scanner to find correct Unit_ID/StationID
10
-
11
8
require 'msf/core'
12
9
13
10
class Metasploit3 < Msf ::Auxiliary
14
11
include Msf ::Exploit ::Remote ::Tcp
15
12
include Msf ::Auxiliary ::Fuzzer
16
13
17
-
18
14
def initialize ( info = { } )
19
15
super ( update_info ( info ,
20
- 'Name' => 'Modbus_findunitID ' ,
16
+ 'Name' => 'Modbus Unit ID and Station ID Enumerator ' ,
21
17
'Description' => %q{
22
- This module sends a command (0x04, read input register) to modbus endpoint.
18
+ Modbus is a cleartext protocol used in common SCADA systems, developed
19
+ originally as a serial-line (RS232) async protocol, and later transformed
20
+ to IP, which is called ModbusTCP. default tcpport is 502.
21
+
22
+ This module sends a command (0x04, read input register) to the modbus endpoint.
23
23
If this command is sent to the correct unit-id, it returns with the same funcion-id.
24
24
if not, it should be added 0x80, so that it sys 0x84, and an exception-code follows
25
- which do not interest us. This does not always happen, but at least the first 4 bytes
26
- in the return-packet should be exact the same as what was sent.
27
- You can change port, ip and the scan-range for unit-id.
28
- There is also added a value - BENICE - to make the scanner sleep a second or more
29
- between probes. We have seen installations where scanning too many too fast workes like a DoS.
25
+ which do not interest us. This does not always happen, but at least the first 4
26
+ bytes in the return-packet should be exact the same as what was sent.
27
+
28
+ You can change port, ip and the scan-range for unit-id. There is also added a
29
+ value - BENICE - to make the scanner sleep a second or more between probes. We
30
+ have seen installations where scanning too many too fast workes like a DoS.
30
31
} ,
31
32
'References' =>
32
33
[
@@ -35,9 +36,9 @@ def initialize(info = {})
35
36
] ,
36
37
'Author' => [ 'EsMnemon <esm[at]mnemonic.no>' ] ,
37
38
'License' => MSF_LICENSE ,
38
- 'DisclosureDate' => 'Oct 28 2012' ,
39
- 'Version' => '$Revision: 0001 $'
39
+ 'DisclosureDate' => 'Oct 28 2012'
40
40
) )
41
+
41
42
register_options (
42
43
[
43
44
Opt ::RPORT ( 502 ) ,
@@ -47,60 +48,62 @@ def initialize(info = {})
47
48
OptInt . new ( 'TIMEOUT' , [ true , 'Timeout for the network probe, 0 means no timeout' , 2 ] )
48
49
] , self . class )
49
50
end
51
+
50
52
def run
51
- start = "\x21 \x00 \x00 \x00 \x00 \x06 "
52
- theend = "\x04 \x00 \x01 \x00 \x00 "
53
- noll = "\x00 "
54
- # between, \01..\0ff (1-255)
55
- unless ( 1 ..255 ) . include? datastore [ 'UNIT_ID_FROM' ]
56
- print_status ( "unit ID must be between 1 and 254 adjusting UNIT_ID_FROM to 1" )
57
- datastore [ 'UNIT_ID_FROM' ] = 1
58
- end
59
- unless ( 1 ..255 ) . include? datastore [ 'UNIT_ID_TO' ]
60
- print_status ( "unit ID must be between #{ datastore [ 'UNIT_ID_FROM' ] } and 255, adjusting UNIT_ID_TO to #{ datastore [ 'UNIT_ID_FROM' ] } " )
61
- datastore [ 'UNIT_ID_TO' ] = datastore [ 'UNIT_ID_FROM' ]
62
- end
63
- if datastore [ 'UNIT_ID_FROM' ] > datastore [ 'UNIT_ID_TO' ] then
64
- print_status ( "UNIT_ID_TO is less than UNIT_ID_FROM, setting them equal" )
65
- datastore [ 'UNIT_ID_TO' ] = datastore [ 'UNIT_ID_FROM' ]
66
- end
53
+ start = "\x21 \x00 \x00 \x00 \x00 \x06 "
54
+ theend = "\x04 \x00 \x01 \x00 \x00 "
55
+ noll = "\x00 "
56
+ # between, \01..\0ff (1-255)
57
+ unless ( 1 ..255 ) . include? datastore [ 'UNIT_ID_FROM' ]
58
+ print_status ( "unit ID must be between 1 and 254 adjusting UNIT_ID_FROM to 1" )
59
+ datastore [ 'UNIT_ID_FROM' ] = 1
60
+ end
67
61
68
- datastore [ 'UNIT_ID_FROM' ] . upto ( datastore [ 'UNIT_ID_TO' ] ) do |counter |
69
- sploit = start
70
- sploit +=[ counter ] . pack ( "C" )
71
- sploit +=theend
72
- select ( nil , nil , nil , datastore [ 'BENICE' ] )
73
- connect ( )
74
- sock . put ( sploit )
75
- #debug: print_status("sent to unit_id #{counter} ")
76
- data = sock . get_once ( 12 , datastore [ 'TIMEOUT' ] )
77
- if ( data . nil? )
78
- data = noll +noll +noll +noll
62
+ unless ( 1 ..255 ) . include? datastore [ 'UNIT_ID_TO' ]
63
+ print_status ( "Unit ID must be between #{ datastore [ 'UNIT_ID_FROM' ] } and 255" )
64
+ print_warning ( "Adjusting UNIT_ID_TO to #{ datastore [ 'UNIT_ID_FROM' ] } " )
65
+ datastore [ 'UNIT_ID_TO' ] = datastore [ 'UNIT_ID_FROM' ]
79
66
end
80
- if data [ 0 , 4 ] == "\x21 \x00 \x00 \x00 " #return of the same trans-id+proto-id
81
- print_good ( "Received: correct MODBUS/TCP from stationID #{ counter } " )
82
- else
83
- print_status ( "Received: incorrect/none data from stationID #{ counter } (probably not in use)" )
67
+
68
+ if datastore [ 'UNIT_ID_FROM' ] > datastore [ 'UNIT_ID_TO' ] then
69
+ print_warning ( "UNIT_ID_TO is less than UNIT_ID_FROM, setting them equal" )
70
+ datastore [ 'UNIT_ID_TO' ] = datastore [ 'UNIT_ID_FROM' ]
71
+ end
72
+
73
+ datastore [ 'UNIT_ID_FROM' ] . upto ( datastore [ 'UNIT_ID_TO' ] ) do |counter |
74
+ sploit = start
75
+ sploit += [ counter ] . pack ( "C" )
76
+ sploit += theend
77
+ select ( nil , nil , nil , datastore [ 'BENICE' ] )
78
+ connect ( )
79
+ sock . put ( sploit )
80
+
81
+ data = sock . get_once ( 12 , datastore [ 'TIMEOUT' ] )
82
+ if ( data . nil? )
83
+ data = noll +noll +noll +noll
84
+ end
85
+
86
+ if data [ 0 , 4 ] == "\x21 \x00 \x00 \x00 " #return of the same trans-id+proto-id
87
+ print_good ( "Received: correct MODBUS/TCP from stationID #{ counter } " )
88
+ else
89
+ print_status ( "Received: incorrect/none data from stationID #{ counter } (probably not in use)" )
90
+ end
91
+
92
+ disconnect ( )
84
93
end
85
- disconnect ( )
86
- end
87
94
end
88
95
end
89
96
90
97
91
98
=begin
92
-
93
- Modbus is a cleartext protocol used in common SCADA systems, developed
94
- originally as a serial-line (RS232) async protocol, and later transformed
95
- to IP, which is called ModbusTCP. default tcpport is 502.
96
-
97
- This client is developed and tested against a SAIA PCD1.M2 system
98
- http://www.saia-pcd.com/en/products/plc/pcd-overview/Pages/pcd1-m2.aspx
99
- and a modbus/tcp PLC simulator from plcsimulator.org
100
- and the Modbus SLAVE from http://www.modbustools.com/
101
-
102
- Mission is to find Unit-ID/stationID of the modbus-endpoint,
103
- RHOST=IP of the modbus-service (PLC)
104
- RPORT=Usually 502
99
+ For testing purposes:
105
100
101
+ This client is developed and tested against a SAIA PCD1.M2 system
102
+ http://www.saia-pcd.com/en/products/plc/pcd-overview/Pages/pcd1-m2.aspx
103
+ and a modbus/tcp PLC simulator from plcsimulator.org
104
+ and the Modbus SLAVE from http://www.modbustools.com/
105
+
106
+ Mission is to find Unit-ID/stationID of the modbus-endpoint:
107
+ RHOST=IP of the modbus-service (PLC)
108
+ RPORT=Usually 502
106
109
=end
0 commit comments