CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/admin/atg/atg_client.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Auxiliary
7
include Msf::Auxiliary::Report
8
include Msf::Exploit::Remote::Tcp
9
include Msf::Auxiliary::Scanner
10
11
def initialize
12
super(
13
'Name' => 'Veeder-Root Automatic Tank Gauge (ATG) Administrative Client',
14
'Description' => %q{
15
This module acts as a simplistic administrative client for interfacing
16
with Veeder-Root Automatic Tank Gauges (ATGs) or other devices speaking
17
the TLS-250 and TLS-350 protocols. This has been tested against
18
GasPot and Conpot, both honeypots meant to simulate ATGs; it has not
19
been tested against anything else, so use at your own risk.
20
},
21
'Author' =>
22
[
23
'Jon Hart <jon_hart[at]rapid7.com>' # original metasploit module
24
],
25
'License' => MSF_LICENSE,
26
'References' =>
27
[
28
['URL', 'https://www.rapid7.com/blog/post/2015/01/22/the-internet-of-gas-station-tank-gauges/'],
29
['URL', 'https://www.trendmicro.com/vinfo/us/security/news/cybercrime-and-digital-threats/the-gaspot-experiment'],
30
['URL', 'https://github.com/sjhilt/GasPot'],
31
['URL', 'https://github.com/mushorg/conpot'],
32
['URL', 'https://www.veeder.com/us/automatic-tank-gauge-atg-consoles'],
33
['URL', 'https://cdn.chipkin.com/files/liz/576013-635.pdf'],
34
['URL', 'https://docs.veeder.com/gold/download.cfm?doc_id=6227']
35
],
36
'DefaultAction' => 'INVENTORY',
37
'Actions' =>
38
[
39
[ 'ALARM',
40
{
41
'Description' => 'I30200 Sensor alarm history (untested)',
42
'TLS-350_CMD' => "\x01I30200"
43
}
44
],
45
[ 'ALARM_RESET',
46
{
47
'Description' => 'IS00300 Remote alarm reset (untested)',
48
'TLS-350_CMD' => "\x01IS00300"
49
}
50
],
51
[ 'DELIVERY',
52
{
53
'Description' => 'I20200 Delivery report',
54
'TLS-350_CMD' => "\x01I20200"
55
}
56
],
57
[ 'INVENTORY',
58
{
59
'Description' => '200/I20100 In-tank inventory report',
60
'TLS-250_CMD' => "\x01200",
61
'TLS-350_CMD' => "\x01I20100"
62
}
63
],
64
[ 'LEAK',
65
{
66
'Description' => 'I20300 Leak report',
67
'TLS-350_CMD' => "\x01I20300"
68
}
69
],
70
[ 'RELAY',
71
{
72
'Description' => 'I40600 Relay status (untested)',
73
'TLS-350_CMD' => "\x01I40600"
74
}
75
],
76
[ 'RESET',
77
{
78
'Description' => 'IS00100 Reset (untested)',
79
'TLS-350_CMD' => "\x01IS00100"
80
}
81
],
82
[ 'CLEAR_RESET',
83
{
84
'Description' => 'IS00200 Clear Reset Flag (untested)',
85
'TLS-350_CMD' => "\x01IS00200"
86
}
87
],
88
[ 'SENSOR',
89
{
90
'Description' => 'I30100 Sensor status (untested)',
91
'TLS-350_CMD' => "\x01I30100"
92
}
93
],
94
[ 'SENSOR_DIAG',
95
{
96
'Description' => 'IB0100 Sensor diagnostics (untested)',
97
'TLS-350_CMD' => "\x01IB0100"
98
}
99
],
100
[ 'SHIFT',
101
{
102
'Description' => 'I20400 Shift report',
103
'TLS-350_CMD' => "\x01I20400"
104
}
105
],
106
[ 'SET_TANK_NAME',
107
{
108
'Description' => 'S602 set tank name (use TANK_NUMBER and TANK_NAME options)',
109
'TLS-350_CMD' => "\x01S602"
110
}
111
],
112
# [ 'SET_TIME',
113
# {
114
# 'Description' => 'S50100 Set time of day (use TIME option) (untested)',
115
# 'TLS-350_CMD' => "\x01S50100"
116
# }
117
# ],
118
[ 'STATUS',
119
{
120
'Description' => 'I20500 In-tank status report',
121
'TLS-350_CMD' => "\x01I20500"
122
}
123
],
124
[ 'SYSTEM_STATUS',
125
{
126
'Description' => 'I10100 System status report (untested)',
127
'TLS-350_CMD' => "\x01I10100"
128
}
129
],
130
[ 'TANK_ALARM',
131
{
132
'Description' => 'I20600 Tank alarm history (untested)',
133
'TLS-350_CMD' => "\x01I20600"
134
}
135
],
136
[ 'TANK_DIAG',
137
{
138
'Description' => 'IA0100 Tank diagnostics (untested)',
139
'TLS-350_CMD' => "\x01IA0100"
140
}
141
],
142
[ 'VERSION',
143
{
144
'Description' => 'Version information',
145
'TLS-250_CMD' => "\x01980",
146
'TLS-350_CMD' => "\x01I90200"
147
}
148
]
149
]
150
)
151
152
register_options(
153
[
154
Opt::RPORT(10001),
155
OptInt.new('TANK_NUMBER', [false, 'The tank number to operate on (use with SET_TANK_NAME, 0 to change all)', 1]),
156
OptString.new('TANK_NAME', [false, 'The tank name to set (use with SET_TANK_NAME, defaults to random)'])
157
]
158
)
159
deregister_options('SSL', 'SSLCipher', 'SSLVerifyMode', 'SSLVersion')
160
161
register_advanced_options(
162
[
163
OptEnum.new('PROTOCOL', [true, 'The Veeder-Root TLS protocol to speak', 'TLS-350', %w(TLS-350 TLS-250)]),
164
OptInt.new('TIMEOUT', [true, 'Time in seconds to wait for responses to our probes', 5])
165
]
166
)
167
end
168
169
def setup
170
# ensure that the specified command is implemented for the desired version of the TLS protocol
171
unless action.opts.keys.include?(protocol_opt_name)
172
fail_with(Failure::BadConfig, "#{action.name} not defined for #{protocol}")
173
end
174
175
# ensure that the tank number is set for the commands that need it
176
if action.name == 'SET_TANK_NAME' && (tank_number < 0 || tank_number > 99)
177
fail_with(Failure::BadConfig, "TANK_NUMBER #{tank_number} is invalid")
178
end
179
180
unless timeout > 0
181
fail_with(Failure::BadConfig, "Invalid timeout #{timeout} -- must be > 0")
182
end
183
end
184
185
def get_response(request)
186
sock.put(request)
187
response = sock.get_once(-1, timeout)
188
response.strip!
189
response += " (command not understood)" if response == "9999FF1B"
190
response
191
end
192
193
def protocol
194
datastore['PROTOCOL']
195
end
196
197
def protocol_opt_name
198
protocol + '_CMD'
199
end
200
201
def tank_name
202
@tank_name ||= (datastore['TANK_NAME'] ? datastore['TANK_NAME'] : Rex::Text.rand_text_alpha(16))
203
end
204
205
def tank_number
206
datastore['TANK_NUMBER']
207
end
208
209
def time
210
if datastore['TIME']
211
Time.parse(datastore['TIME']).to_i
212
else
213
Time.now.to_i
214
end
215
end
216
217
def timeout
218
datastore['TIMEOUT']
219
end
220
221
def run_host(_host)
222
begin
223
connect
224
case action.name
225
when 'SET_TANK_NAME'
226
# send the set tank name command to change the tank name(s)
227
if tank_number == 0
228
vprint_status("Setting all tank names to #{tank_name}")
229
else
230
vprint_status("Setting tank ##{tank_number}'s name to #{tank_name}")
231
end
232
request = "#{action.opts[protocol_opt_name]}#{format('%02d', tank_number)}#{tank_name}\n"
233
sock.put(request)
234
# reconnect
235
disconnect
236
connect
237
# send an inventory probe to show that it succeeded
238
inventory_probe = "#{actions.find { |a| a.name == 'INVENTORY' }.opts[protocol_opt_name]}\n"
239
inventory_response = get_response(inventory_probe)
240
message = "#{protocol} #{action.opts['Description']}:\n#{inventory_response}"
241
if inventory_response.include?(tank_name)
242
print_good message
243
else
244
print_warning message
245
end
246
else
247
response = get_response("#{action.opts[protocol_opt_name]}\n")
248
print_good("#{protocol} #{action.opts['Description']}:")
249
print_line(response)
250
end
251
ensure
252
disconnect
253
end
254
end
255
end
256
257