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