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.
|
||||
|
||||
**FC**
|
||||
|
||||
Optional. If true forces sending flow control packets on all multibyte ISO-TP requests
|
||||
|
||||
## Scenarios
|
||||
|
||||
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 :alive # :nodoc:
|
||||
attr_accessor :api_version
|
||||
attr_accessor :fw_version
|
||||
attr_accessor :hw_version
|
||||
attr_accessor :device_name
|
||||
private
|
||||
attr_accessor :rstream # :nodoc:
|
||||
|
||||
|
|
|
@ -119,11 +119,16 @@ class Automotive < Extension
|
|||
# TODO: Implement sending ISO-TP > 8 bytes
|
||||
data = [ data ] if data.is_a? Integer
|
||||
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
|
||||
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 += "&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))
|
||||
end
|
||||
nil
|
||||
|
|
|
@ -174,6 +174,8 @@ class Console::CommandDispatcher::Automotive
|
|||
data = ''
|
||||
timeout = nil
|
||||
maxpackets = nil
|
||||
flowcontrol = false
|
||||
padding = nil
|
||||
cansend_opts = Rex::Parser::Arguments.new(
|
||||
'-h' => [ false, 'Help Banner' ],
|
||||
'-b' => [ true, 'Target bus'],
|
||||
|
@ -181,6 +183,8 @@ class Console::CommandDispatcher::Automotive
|
|||
'-R' => [ true, 'Return ID'],
|
||||
'-D' => [ true, 'Data packet in Hex (Do not include ISOTP command size)'],
|
||||
'-t' => [ true, 'Timeout value'],
|
||||
'-p' => [ true, 'Padding value, none if not specified'],
|
||||
'-C' => [ false, 'Force flow control'],
|
||||
'-m' => [ true, 'Max packets to receive']
|
||||
)
|
||||
cansend_opts.parse(args) do |opt, _idx, val|
|
||||
|
@ -199,6 +203,10 @@ class Console::CommandDispatcher::Automotive
|
|||
data = val
|
||||
when '-t'
|
||||
timeout = val.to_i
|
||||
when '-p'
|
||||
padding = val
|
||||
when '-C'
|
||||
flowcontrol = true
|
||||
when '-m'
|
||||
maxpackets = val.to_i
|
||||
end
|
||||
|
@ -224,6 +232,8 @@ class Console::CommandDispatcher::Automotive
|
|||
opt = {}
|
||||
opt['TIMEOUT'] = timeout unless timeout.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)
|
||||
if result.key? 'Packets'
|
||||
result['Packets'].each do |pkt|
|
||||
|
|
|
@ -104,6 +104,10 @@ class MetasploitModule < Msf::Auxiliary
|
|||
if self.hw_specialty.has_key? 'rftransceiver'
|
||||
sess.load_rftransceiver if self.hw_specialty['rftransceiver'] == true
|
||||
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
|
||||
|
||||
#
|
||||
|
@ -129,6 +133,18 @@ class MetasploitModule < Msf::Auxiliary
|
|||
if data.key? 'hw_capabilities'
|
||||
self.hw_capabilities = data['hw_capabilities']
|
||||
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
|
||||
|
@ -153,9 +169,17 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
attr_reader :hw_specialty
|
||||
attr_reader :hw_capabilities
|
||||
attr_reader :api_version
|
||||
attr_reader :fw_version
|
||||
attr_reader :hw_version
|
||||
attr_reader :device_name
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :hw_specialty
|
||||
attr_writer :hw_capabilities
|
||||
attr_writer :api_version
|
||||
attr_writer :fw_version
|
||||
attr_writer :hw_version
|
||||
attr_writer :device_name
|
||||
end
|
||||
|
|
|
@ -11,7 +11,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpServer::HTML
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
HWBRIDGE_API_VERSION = "0.0.1"
|
||||
HWBRIDGE_API_VERSION = "0.0.4"
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
|
@ -170,13 +170,24 @@ class MetasploitModule < Msf::Auxiliary
|
|||
# srcid = hex id of the sent packet
|
||||
# dstid = hex id of the return packets
|
||||
# data = string of hex bytes to send
|
||||
# timeout = optional int to timeout on lack of response
|
||||
# maxpkts = max number of packets to recieve
|
||||
def isotp_send_and_wait(bus, srcid, dstid, data, timeout = 2000, maxpkts = 3)
|
||||
# OPT = Options
|
||||
# timeout = optional int to timeout on lack of response
|
||||
# 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["Success"] = false
|
||||
srcid = srcid.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(/../)
|
||||
if bytes.size > 8
|
||||
print_error("Data section currently has to be less than 8 bytes")
|
||||
|
@ -185,6 +196,10 @@ class MetasploitModule < Msf::Auxiliary
|
|||
sz = "%02x" % bytes.size
|
||||
bytes = sz + bytes.join
|
||||
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?
|
||||
`which cansend`
|
||||
unless $?.success?
|
||||
|
@ -193,15 +208,43 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
@can_interfaces.each do |can|
|
||||
if can == bus
|
||||
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
|
||||
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)
|
||||
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
|
||||
|
@ -252,11 +295,12 @@ class MetasploitModule < Msf::Auxiliary
|
|||
elsif request.uri =~ /automotive\/(\w+)\/isotpsend_and_wait\?srcid=(\w+)&dstid=(\w+)&data=(\w+)/
|
||||
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']
|
||||
timeout = 1500
|
||||
maxpkts = 3
|
||||
timeout = $1 if request.uri =~ /&timeout=(\d+)/
|
||||
maxpkts = $1 if request.uri =~ /&maxpkts=(\d+)/
|
||||
send_response_html(cli, isotp_send_and_wait(bus, srcid, dstid, data, timeout, maxpkts).to_json(), { 'Content-Type' => 'application/json' })
|
||||
opt = {}
|
||||
opt['TIMEOUT'] = $1 if request.uri =~ /&timeout=(\d+)/
|
||||
opt['MAXPKTS'] = $1 if request.uri =~ /&maxpkts=(\d+)/
|
||||
opt['PADDING'] = $1 if request.uri =~ /&padding=(\d+)/
|
||||
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
|
||||
send_response_html(cli, not_supported().to_json(), { 'Content-Type' => 'application/json' })
|
||||
end
|
||||
|
|
|
@ -24,6 +24,7 @@ class MetasploitModule < Msf::Post
|
|||
OptInt.new('SRCID', [true, "Module ID to query", 0x7e0]),
|
||||
OptInt.new('DSTID', [false, "Expected reponse ID, defaults to SRCID + 8", 0x7e8]),
|
||||
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]),
|
||||
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
|
||||
opt = {}
|
||||
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)
|
||||
if pids.size == 0
|
||||
print_status("No reported PIDs. You may not be properly connected")
|
||||
|
|
Loading…
Reference in New Issue