commit
e651bc1205
|
@ -30,6 +30,10 @@ PIDs to ASCII.
|
||||||
|
|
||||||
Optional byte-value to use for padding all CAN bus packets to an 8-byte length. Padding is disabled by default.
|
Optional byte-value to use for padding all CAN bus packets to an 8-byte length. Padding is disabled by default.
|
||||||
|
|
||||||
|
**FC**
|
||||||
|
|
||||||
|
Optional. If true forces sending flow control packets on all multibyte ISO-TP requests
|
||||||
|
|
||||||
## Scenarios
|
## Scenarios
|
||||||
|
|
||||||
Given a standard vehicle ECU that is connected to can2 of the HWBridge device:
|
Given a standard vehicle ECU that is connected to can2 of the HWBridge device:
|
||||||
|
|
|
@ -196,6 +196,10 @@ class HWBridge < Rex::Post::HWBridge::Client
|
||||||
|
|
||||||
attr_accessor :console # :nodoc:
|
attr_accessor :console # :nodoc:
|
||||||
attr_accessor :alive # :nodoc:
|
attr_accessor :alive # :nodoc:
|
||||||
|
attr_accessor :api_version
|
||||||
|
attr_accessor :fw_version
|
||||||
|
attr_accessor :hw_version
|
||||||
|
attr_accessor :device_name
|
||||||
private
|
private
|
||||||
attr_accessor :rstream # :nodoc:
|
attr_accessor :rstream # :nodoc:
|
||||||
|
|
||||||
|
|
|
@ -119,11 +119,16 @@ class Automotive < Extension
|
||||||
# TODO: Implement sending ISO-TP > 8 bytes
|
# TODO: Implement sending ISO-TP > 8 bytes
|
||||||
data = [ data ] if data.is_a? Integer
|
data = [ data ] if data.is_a? Integer
|
||||||
if data.size < 8
|
if data.size < 8
|
||||||
|
# Padding is handled differently after 0.0.3
|
||||||
|
if Gem::Version.new(client.api_version) < Gem::Version.new('0.0.4')
|
||||||
data = padd_packet(data, opt['PADDING']) if opt.key? 'PADDING'
|
data = padd_packet(data, opt['PADDING']) if opt.key? 'PADDING'
|
||||||
|
end
|
||||||
data = array2hex(data).join
|
data = array2hex(data).join
|
||||||
request_str = "/automotive/#{bus}/isotpsend_and_wait?srcid=#{src_id}&dstid=#{dst_id}&data=#{data}"
|
request_str = "/automotive/#{bus}/isotpsend_and_wait?srcid=#{src_id}&dstid=#{dst_id}&data=#{data}"
|
||||||
request_str += "&timeout=#{opt['TIMEOUT']}" if opt.key? "TIMEOUT"
|
request_str += "&timeout=#{opt['TIMEOUT']}" if opt.key? "TIMEOUT"
|
||||||
request_str += "&maxpkts=#{opt['MAXPKTS']}" if opt.key? "MAXPKTS"
|
request_str += "&maxpkts=#{opt['MAXPKTS']}" if opt.key? "MAXPKTS"
|
||||||
|
request_str += "&padding=#{opt['PADDING']}" if opt.key? "PADDING" # Won't hurt to use in older versions
|
||||||
|
request_str += "&fc=#{opt['FC']}" if opt.key? "FC" # Force flow control
|
||||||
return check_for_errors(client.send_request(request_str))
|
return check_for_errors(client.send_request(request_str))
|
||||||
end
|
end
|
||||||
nil
|
nil
|
||||||
|
|
|
@ -174,6 +174,8 @@ class Console::CommandDispatcher::Automotive
|
||||||
data = ''
|
data = ''
|
||||||
timeout = nil
|
timeout = nil
|
||||||
maxpackets = nil
|
maxpackets = nil
|
||||||
|
flowcontrol = false
|
||||||
|
padding = nil
|
||||||
cansend_opts = Rex::Parser::Arguments.new(
|
cansend_opts = Rex::Parser::Arguments.new(
|
||||||
'-h' => [ false, 'Help Banner' ],
|
'-h' => [ false, 'Help Banner' ],
|
||||||
'-b' => [ true, 'Target bus'],
|
'-b' => [ true, 'Target bus'],
|
||||||
|
@ -181,6 +183,8 @@ class Console::CommandDispatcher::Automotive
|
||||||
'-R' => [ true, 'Return ID'],
|
'-R' => [ true, 'Return ID'],
|
||||||
'-D' => [ true, 'Data packet in Hex (Do not include ISOTP command size)'],
|
'-D' => [ true, 'Data packet in Hex (Do not include ISOTP command size)'],
|
||||||
'-t' => [ true, 'Timeout value'],
|
'-t' => [ true, 'Timeout value'],
|
||||||
|
'-p' => [ true, 'Padding value, none if not specified'],
|
||||||
|
'-C' => [ false, 'Force flow control'],
|
||||||
'-m' => [ true, 'Max packets to receive']
|
'-m' => [ true, 'Max packets to receive']
|
||||||
)
|
)
|
||||||
cansend_opts.parse(args) do |opt, _idx, val|
|
cansend_opts.parse(args) do |opt, _idx, val|
|
||||||
|
@ -199,6 +203,10 @@ class Console::CommandDispatcher::Automotive
|
||||||
data = val
|
data = val
|
||||||
when '-t'
|
when '-t'
|
||||||
timeout = val.to_i
|
timeout = val.to_i
|
||||||
|
when '-p'
|
||||||
|
padding = val
|
||||||
|
when '-C'
|
||||||
|
flowcontrol = true
|
||||||
when '-m'
|
when '-m'
|
||||||
maxpackets = val.to_i
|
maxpackets = val.to_i
|
||||||
end
|
end
|
||||||
|
@ -224,6 +232,8 @@ class Console::CommandDispatcher::Automotive
|
||||||
opt = {}
|
opt = {}
|
||||||
opt['TIMEOUT'] = timeout unless timeout.nil?
|
opt['TIMEOUT'] = timeout unless timeout.nil?
|
||||||
opt['MAXPKTS'] = maxpackets unless maxpackets.nil?
|
opt['MAXPKTS'] = maxpackets unless maxpackets.nil?
|
||||||
|
opt['PADDING'] = padding unless padding.nil?
|
||||||
|
opt['FC'] = true unless flowcontrol == false
|
||||||
result = client.automotive.send_isotp_and_wait_for_response(bus, id, ret, bytes, opt)
|
result = client.automotive.send_isotp_and_wait_for_response(bus, id, ret, bytes, opt)
|
||||||
if result.key? 'Packets'
|
if result.key? 'Packets'
|
||||||
result['Packets'].each do |pkt|
|
result['Packets'].each do |pkt|
|
||||||
|
|
|
@ -104,6 +104,10 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
if self.hw_specialty.has_key? 'rftransceiver'
|
if self.hw_specialty.has_key? 'rftransceiver'
|
||||||
sess.load_rftransceiver if self.hw_specialty['rftransceiver'] == true
|
sess.load_rftransceiver if self.hw_specialty['rftransceiver'] == true
|
||||||
end
|
end
|
||||||
|
sess.api_version = self.api_version if self.api_version
|
||||||
|
sess.fw_version = self.fw_version if self.fw_version
|
||||||
|
sess.hw_version = self.hw_version if self.hw_version
|
||||||
|
sess.device_name = self.device_name if self.device_name
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -129,6 +133,18 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
if data.key? 'hw_capabilities'
|
if data.key? 'hw_capabilities'
|
||||||
self.hw_capabilities = data['hw_capabilities']
|
self.hw_capabilities = data['hw_capabilities']
|
||||||
end
|
end
|
||||||
|
if data.key? 'api_version'
|
||||||
|
self.api_version = data['api_version']
|
||||||
|
end
|
||||||
|
if data.key? 'fw_version'
|
||||||
|
self.fw_version = data['fw_version']
|
||||||
|
end
|
||||||
|
if data.key? 'hw_vesrion'
|
||||||
|
self.hw_version = data['hw_version']
|
||||||
|
end
|
||||||
|
if data.key? 'device_name'
|
||||||
|
self.device_name = data['device_name']
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -153,9 +169,17 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
|
|
||||||
attr_reader :hw_specialty
|
attr_reader :hw_specialty
|
||||||
attr_reader :hw_capabilities
|
attr_reader :hw_capabilities
|
||||||
|
attr_reader :api_version
|
||||||
|
attr_reader :fw_version
|
||||||
|
attr_reader :hw_version
|
||||||
|
attr_reader :device_name
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
attr_writer :hw_specialty
|
attr_writer :hw_specialty
|
||||||
attr_writer :hw_capabilities
|
attr_writer :hw_capabilities
|
||||||
|
attr_writer :api_version
|
||||||
|
attr_writer :fw_version
|
||||||
|
attr_writer :hw_version
|
||||||
|
attr_writer :device_name
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,7 +11,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
include Msf::Exploit::Remote::HttpServer::HTML
|
include Msf::Exploit::Remote::HttpServer::HTML
|
||||||
include Msf::Auxiliary::Report
|
include Msf::Auxiliary::Report
|
||||||
|
|
||||||
HWBRIDGE_API_VERSION = "0.0.1"
|
HWBRIDGE_API_VERSION = "0.0.4"
|
||||||
|
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super(update_info(info,
|
super(update_info(info,
|
||||||
|
@ -170,13 +170,24 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
# srcid = hex id of the sent packet
|
# srcid = hex id of the sent packet
|
||||||
# dstid = hex id of the return packets
|
# dstid = hex id of the return packets
|
||||||
# data = string of hex bytes to send
|
# data = string of hex bytes to send
|
||||||
|
# OPT = Options
|
||||||
# timeout = optional int to timeout on lack of response
|
# timeout = optional int to timeout on lack of response
|
||||||
# maxpkts = max number of packets to recieve
|
# maxpkts = max number of packets to recieve
|
||||||
def isotp_send_and_wait(bus, srcid, dstid, data, timeout = 2000, maxpkts = 3)
|
# padding = append bytes to end of packet (Doesn't increase reported ISO-TP size)
|
||||||
|
# fc = flow control, if true forces flow control packets
|
||||||
|
def isotp_send_and_wait(bus, srcid, dstid, data, opt = {})
|
||||||
result = {}
|
result = {}
|
||||||
result["Success"] = false
|
result["Success"] = false
|
||||||
srcid = srcid.to_i(16).to_s(16)
|
srcid = srcid.to_i(16).to_s(16)
|
||||||
dstid = dstid.to_i(16).to_s(16)
|
dstid = dstid.to_i(16).to_s(16)
|
||||||
|
timeout = 2000
|
||||||
|
maxpkts = 3
|
||||||
|
flowcontrol = nil
|
||||||
|
padding = nil
|
||||||
|
timeout = opt['TIMEOUT'] if opt.key? 'TIMEOUT'
|
||||||
|
maxpkts = opt['MAXPKTS'] if opt.key? 'MAXPKTS'
|
||||||
|
padding = opt['PADDING'] if opt.key? 'PADDING'
|
||||||
|
flowcontrol = opt['FC'] if opt.key? 'FC'
|
||||||
bytes = data.scan(/../)
|
bytes = data.scan(/../)
|
||||||
if bytes.size > 8
|
if bytes.size > 8
|
||||||
print_error("Data section currently has to be less than 8 bytes")
|
print_error("Data section currently has to be less than 8 bytes")
|
||||||
|
@ -185,6 +196,10 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
sz = "%02x" % bytes.size
|
sz = "%02x" % bytes.size
|
||||||
bytes = sz + bytes.join
|
bytes = sz + bytes.join
|
||||||
end
|
end
|
||||||
|
if padding && bytes.size < 16 # 16 == 8 bytes because of ascii size
|
||||||
|
padding = "%02x" % padding.to_i
|
||||||
|
bytes += ([ padding ] * (16 - bytes.size)).join
|
||||||
|
end
|
||||||
# Should we ever require isotpsend for this?
|
# Should we ever require isotpsend for this?
|
||||||
`which cansend`
|
`which cansend`
|
||||||
unless $?.success?
|
unless $?.success?
|
||||||
|
@ -193,6 +208,33 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
end
|
end
|
||||||
@can_interfaces.each do |can|
|
@can_interfaces.each do |can|
|
||||||
if can == bus
|
if can == bus
|
||||||
|
if flowcontrol
|
||||||
|
candump(bus, dstid, timeout, 1)
|
||||||
|
system("cansend #{bus} #{srcid}##{bytes}")
|
||||||
|
@packets_sent += 1
|
||||||
|
@last_sent = Time.now.to_i
|
||||||
|
result["Success"] = true if $?.success?
|
||||||
|
result["Packets"] = []
|
||||||
|
$candump_sniffer.join
|
||||||
|
unless @pkt_response.empty?
|
||||||
|
result = @pkt_response
|
||||||
|
if result.key?("Packets") && result["Packets"].size > 0 && result["Packets"][0].key?("DATA")
|
||||||
|
if result["Packets"][0]["DATA"][0] == "10"
|
||||||
|
system("cansend #{bus} #{srcid}#3000000000000000")
|
||||||
|
candump(bus, dstid, timeout, maxpkts)
|
||||||
|
@packets_sent += 1
|
||||||
|
@last_sent = Time.now.to_i
|
||||||
|
$candump_sniffer.join
|
||||||
|
unless @pkt_response.empty?
|
||||||
|
if @pkt_response.key?("Packets") && @pkt_response["Packets"].size > 0
|
||||||
|
result["Packets"] += @pkt_response["Packets"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
candump(bus, dstid, timeout, maxpkts)
|
candump(bus, dstid, timeout, maxpkts)
|
||||||
system("cansend #{bus} #{srcid}##{bytes}")
|
system("cansend #{bus} #{srcid}##{bytes}")
|
||||||
@packets_sent += 1
|
@packets_sent += 1
|
||||||
|
@ -205,6 +247,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
result
|
result
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -252,11 +295,12 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
elsif request.uri =~ /automotive\/(\w+)\/isotpsend_and_wait\?srcid=(\w+)&dstid=(\w+)&data=(\w+)/
|
elsif request.uri =~ /automotive\/(\w+)\/isotpsend_and_wait\?srcid=(\w+)&dstid=(\w+)&data=(\w+)/
|
||||||
bus = $1; srcid = $2; dstid = $3; data = $4
|
bus = $1; srcid = $2; dstid = $3; data = $4
|
||||||
print_status("Request to send ISO-TP packet and wait for response #{srcid}##{data} => #{dstid}") if datastore['VERBOSE']
|
print_status("Request to send ISO-TP packet and wait for response #{srcid}##{data} => #{dstid}") if datastore['VERBOSE']
|
||||||
timeout = 1500
|
opt = {}
|
||||||
maxpkts = 3
|
opt['TIMEOUT'] = $1 if request.uri =~ /&timeout=(\d+)/
|
||||||
timeout = $1 if request.uri =~ /&timeout=(\d+)/
|
opt['MAXPKTS'] = $1 if request.uri =~ /&maxpkts=(\d+)/
|
||||||
maxpkts = $1 if request.uri =~ /&maxpkts=(\d+)/
|
opt['PADDING'] = $1 if request.uri =~ /&padding=(\d+)/
|
||||||
send_response_html(cli, isotp_send_and_wait(bus, srcid, dstid, data, timeout, maxpkts).to_json(), { 'Content-Type' => 'application/json' })
|
opt['FC'] = true if request.uri =~ /&fc=true/i
|
||||||
|
send_response_html(cli, isotp_send_and_wait(bus, srcid, dstid, data, opt).to_json(), { 'Content-Type' => 'application/json' })
|
||||||
else
|
else
|
||||||
send_response_html(cli, not_supported().to_json(), { 'Content-Type' => 'application/json' })
|
send_response_html(cli, not_supported().to_json(), { 'Content-Type' => 'application/json' })
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,6 +24,7 @@ class MetasploitModule < Msf::Post
|
||||||
OptInt.new('SRCID', [true, "Module ID to query", 0x7e0]),
|
OptInt.new('SRCID', [true, "Module ID to query", 0x7e0]),
|
||||||
OptInt.new('DSTID', [false, "Expected reponse ID, defaults to SRCID + 8", 0x7e8]),
|
OptInt.new('DSTID', [false, "Expected reponse ID, defaults to SRCID + 8", 0x7e8]),
|
||||||
OptInt.new('PADDING', [false, "Optinal end of packet padding", nil]),
|
OptInt.new('PADDING', [false, "Optinal end of packet padding", nil]),
|
||||||
|
OptBool.new('FC', [false, "Optinal forces flow control", nil]),
|
||||||
OptBool.new('CLEAR_DTCS', [false, "Clear any DTCs and reset MIL if errors are present", false]),
|
OptBool.new('CLEAR_DTCS', [false, "Clear any DTCs and reset MIL if errors are present", false]),
|
||||||
OptString.new('CANBUS', [false, "CAN Bus to perform scan on, defaults to connected bus", nil])
|
OptString.new('CANBUS', [false, "CAN Bus to perform scan on, defaults to connected bus", nil])
|
||||||
])
|
])
|
||||||
|
@ -33,6 +34,7 @@ class MetasploitModule < Msf::Post
|
||||||
def run
|
def run
|
||||||
opt = {}
|
opt = {}
|
||||||
opt['PADDING'] = datastore["PADDING"] if datastore["PADDING"]
|
opt['PADDING'] = datastore["PADDING"] if datastore["PADDING"]
|
||||||
|
opt['FC'] = datastore['FC'] if datastore['FC']
|
||||||
pids = get_current_data_pids(datastore["CANBUS"], datastore["SRCID"], datastore["DSTID"], opt)
|
pids = get_current_data_pids(datastore["CANBUS"], datastore["SRCID"], datastore["DSTID"], opt)
|
||||||
if pids.size == 0
|
if pids.size == 0
|
||||||
print_status("No reported PIDs. You may not be properly connected")
|
print_status("No reported PIDs. You may not be properly connected")
|
||||||
|
|
Loading…
Reference in New Issue