Land #8951, Hwbridge auto padding fix and flowcontrol

bug/bundler_fix 4.16.7
Pearce Barry 2017-09-15 08:33:17 -05:00
commit e651bc1205
No known key found for this signature in database
GPG Key ID: 0916F4DEA5C5DE0A
7 changed files with 112 additions and 19 deletions

View File

@ -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:

View File

@ -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:

View File

@ -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
data = padd_packet(data, opt['PADDING']) if opt.key? 'PADDING' # 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'
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

View File

@ -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|

View File

@ -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

View File

@ -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
# timeout = optional int to timeout on lack of response # OPT = Options
# maxpkts = max number of packets to recieve # timeout = optional int to timeout on lack of response
def isotp_send_and_wait(bus, srcid, dstid, data, timeout = 2000, maxpkts = 3) # maxpkts = max number of packets to recieve
# 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,15 +208,43 @@ class MetasploitModule < Msf::Auxiliary
end end
@can_interfaces.each do |can| @can_interfaces.each do |can|
if can == bus if can == bus
candump(bus, dstid, timeout, maxpkts) if flowcontrol
system("cansend #{bus} #{srcid}##{bytes}") candump(bus, dstid, timeout, 1)
@packets_sent += 1 system("cansend #{bus} #{srcid}##{bytes}")
@last_sent = Time.now.to_i @packets_sent += 1
result["Success"] = true if $?.success? @last_sent = Time.now.to_i
result["Packets"] = [] result["Success"] = true if $?.success?
$candump_sniffer.join result["Packets"] = []
unless @pkt_response.empty? $candump_sniffer.join
result = @pkt_response 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)
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
end
end end
end end
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

View File

@ -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")