Land #6220, adds ATG client module
commit
a9e8ab785e
|
@ -0,0 +1,258 @@
|
|||
##
|
||||
# encoding: utf-8
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Exploit::Remote::Tcp
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Veeder-Root Automatic Tank Gauge (ATG) Administrative Client',
|
||||
'Description' => %q{
|
||||
This module acts as a simplistic administrative client for interfacing
|
||||
with Veeder-Root Automatic Tank Gauges (ATGs) or other devices speaking
|
||||
the TLS-250 and TLS-350 protocols. This has been tested against
|
||||
GasPot, a honeypot meant to simulate ATGs; it has not been tested
|
||||
against anything else, so use at your own risk.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Jon Hart <jon_hart[at]rapid7.com>' # original metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'https://community.rapid7.com/community/infosec/blog/2015/01/22/the-internet-of-gas-station-tank-gauges'],
|
||||
['URL', 'http://www.trendmicro.com/vinfo/us/security/news/cybercrime-and-digital-threats/the-gaspot-experiment'],
|
||||
['URL', 'https://github.com/sjhilt/GasPot'],
|
||||
['URL', 'http://www.veeder.com/us/automatic-tank-gauge-atg-consoles'],
|
||||
['URL', 'http://www.chipkin.com/files/liz/576013-635.pdf'],
|
||||
['URL', 'http://www.veeder.com/gold/download.cfm?doc_id=6227']
|
||||
],
|
||||
'DefaultAction' => 'INVENTORY',
|
||||
'Actions' =>
|
||||
[
|
||||
[ 'ALARM',
|
||||
{
|
||||
'Description' => 'I30200 Sensor alarm history (untested)',
|
||||
'TLS-350_CMD' => "\x01I30200"
|
||||
}
|
||||
],
|
||||
[ 'ALARM_RESET',
|
||||
{
|
||||
'Description' => 'IS00300 Remote alarm reset (untested)',
|
||||
'TLS-350_CMD' => "\x01IS00300"
|
||||
}
|
||||
],
|
||||
[ 'DELIVERY',
|
||||
{
|
||||
'Description' => 'I20200 Delivery report',
|
||||
'TLS-350_CMD' => "\x01I20200"
|
||||
}
|
||||
],
|
||||
[ 'INVENTORY',
|
||||
{
|
||||
'Description' => '200/I20100 In-tank inventory report',
|
||||
'TLS-250_CMD' => "\x01200",
|
||||
'TLS-350_CMD' => "\x01I20100"
|
||||
}
|
||||
],
|
||||
[ 'LEAK',
|
||||
{
|
||||
'Description' => 'I20300 Leak report',
|
||||
'TLS-350_CMD' => "\x01I20300"
|
||||
}
|
||||
],
|
||||
[ 'RELAY',
|
||||
{
|
||||
'Description' => 'I40600 Relay status (untested)',
|
||||
'TLS-350_CMD' => "\x01I40600"
|
||||
}
|
||||
],
|
||||
[ 'RESET',
|
||||
{
|
||||
'Description' => 'IS00100 Reset (untested)',
|
||||
'TLS-350_CMD' => "\x01IS00100"
|
||||
}
|
||||
],
|
||||
[ 'CLEAR_RESET',
|
||||
{
|
||||
'Description' => 'IS00200 Clear Reset Flag (untested)',
|
||||
'TLS-350_CMD' => "\x01IS00200"
|
||||
}
|
||||
],
|
||||
[ 'SENSOR',
|
||||
{
|
||||
'Description' => 'I30100 Sensor status (untested)',
|
||||
'TLS-350_CMD' => "\x01I30100"
|
||||
}
|
||||
],
|
||||
[ 'SENSOR_DIAG',
|
||||
{
|
||||
'Description' => 'IB0100 Sensor diagnostics (untested)',
|
||||
'TLS-350_CMD' => "\x01IB0100"
|
||||
}
|
||||
],
|
||||
[ 'SHIFT',
|
||||
{
|
||||
'Description' => 'I20400 Shift report',
|
||||
'TLS-350_CMD' => "\x01I20400"
|
||||
}
|
||||
],
|
||||
[ 'SET_TANK_NAME',
|
||||
{
|
||||
'Description' => 'S602 set tank name (use TANK_NUMBER and TANK_NAME options)',
|
||||
'TLS-350_CMD' => "\x01S602"
|
||||
}
|
||||
],
|
||||
# [ 'SET_TIME',
|
||||
# {
|
||||
# 'Description' => 'S50100 Set time of day (use TIME option) (untested)',
|
||||
# 'TLS-350_CMD' => "\x01S50100"
|
||||
# }
|
||||
# ],
|
||||
[ 'STATUS',
|
||||
{
|
||||
'Description' => 'I20500 In-tank status report',
|
||||
'TLS-350_CMD' => "\x01I20500"
|
||||
}
|
||||
],
|
||||
[ 'SYSTEM_STATUS',
|
||||
{
|
||||
'Description' => 'I10100 System status report (untested)',
|
||||
'TLS-350_CMD' => "\x01I10100"
|
||||
}
|
||||
],
|
||||
[ 'TANK_ALARM',
|
||||
{
|
||||
'Description' => 'I20600 Tank alarm history (untested)',
|
||||
'TLS-350_CMD' => "\x01I20600"
|
||||
}
|
||||
],
|
||||
[ 'TANK_DIAG',
|
||||
{
|
||||
'Description' => 'IA0100 Tank diagnostics (untested)',
|
||||
'TLS-350_CMD' => "\x01IA0100"
|
||||
}
|
||||
],
|
||||
[ 'VERSION',
|
||||
{
|
||||
'Description' => 'Version information',
|
||||
'TLS-250_CMD' => "\x01980",
|
||||
'TLS-350_CMD' => "\x01I90200"
|
||||
}
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(10001),
|
||||
OptInt.new('TANK_NUMBER', [false, 'The tank number to operate on (use with SET_TANK_NAME, 0 to change all)', 1]),
|
||||
OptString.new('TANK_NAME', [false, 'The tank name to set (use with SET_TANK_NAME, defaults to random)'])
|
||||
]
|
||||
)
|
||||
deregister_options('SSL', 'SSLCipher', 'SSLVerifyMode', 'SSLVersion')
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptEnum.new('PROTOCOL', [true, 'The Veeder-Root TLS protocol to speak', 'TLS-350', %w(TLS-350 TLS-250)]),
|
||||
OptInt.new('TIMEOUT', [true, 'Time in seconds to wait for responses to our probes', 5])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def setup
|
||||
# ensure that the specified command is implemented for the desired version of the TLS protocol
|
||||
unless action.opts.keys.include?(protocol_opt_name)
|
||||
fail_with(Failure::BadConfig, "#{action.name} not defined for #{protocol}")
|
||||
end
|
||||
|
||||
# ensure that the tank number is set for the commands that need it
|
||||
if action.name == 'SET_TANK_NAME' && (tank_number < 0 || tank_number > 99)
|
||||
fail_with(Failure::BadConfig, "TANK_NUMBER #{tank_number} is invalid")
|
||||
end
|
||||
|
||||
unless timeout > 0
|
||||
fail_with(Failure::BadConfig, "Invalid timeout #{timeout} -- must be > 0")
|
||||
end
|
||||
end
|
||||
|
||||
def get_response(request)
|
||||
sock.put(request)
|
||||
response = sock.get_once(-1, timeout)
|
||||
response
|
||||
end
|
||||
|
||||
def peer
|
||||
"#{rhost}:#{rport}"
|
||||
end
|
||||
|
||||
def protocol
|
||||
datastore['PROTOCOL']
|
||||
end
|
||||
|
||||
def protocol_opt_name
|
||||
protocol + '_CMD'
|
||||
end
|
||||
|
||||
def tank_name
|
||||
@tank_name ||= (datastore['TANK_NAME'] ? datastore['TANK_NAME'] : Rex::Text.rand_text_alpha(16))
|
||||
end
|
||||
|
||||
def tank_number
|
||||
datastore['TANK_NUMBER']
|
||||
end
|
||||
|
||||
def time
|
||||
if datastore['TIME']
|
||||
Time.parse(datastore['TIME']).to_i
|
||||
else
|
||||
Time.now.to_i
|
||||
end
|
||||
end
|
||||
|
||||
def timeout
|
||||
datastore['TIMEOUT']
|
||||
end
|
||||
|
||||
def run_host(_host)
|
||||
begin
|
||||
connect
|
||||
case action.name
|
||||
when 'SET_TANK_NAME'
|
||||
# send the set tank name command to change the tank name(s)
|
||||
if tank_number == 0
|
||||
vprint_status("#{peer} -- setting all tank names to #{tank_name}")
|
||||
else
|
||||
vprint_status("#{peer} -- setting tank ##{tank_number}'s name to #{tank_name}")
|
||||
end
|
||||
request = "#{action.opts[protocol_opt_name]}#{format('%02d', tank_number)}#{tank_name}\n"
|
||||
sock.put(request)
|
||||
# reconnect
|
||||
disconnect
|
||||
connect
|
||||
# send an inventory probe to show that it succeeded
|
||||
inventory_probe = "#{actions.find { |a| a.name == 'INVENTORY' }.opts[protocol_opt_name]}\n"
|
||||
inventory_response = get_response(inventory_probe)
|
||||
message = "#{peer} #{protocol} #{action.opts['Description']}:\n#{inventory_response}"
|
||||
if inventory_response.include?(tank_name)
|
||||
print_good message
|
||||
else
|
||||
print_warning message
|
||||
end
|
||||
else
|
||||
response = get_response("#{action.opts[protocol_opt_name]}\n")
|
||||
print_good("#{peer} #{protocol} #{action.opts['Description']}:\n#{response}")
|
||||
end
|
||||
ensure
|
||||
disconnect
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue