metasploit-framework/unstable-modules/auxiliary/duckduck_password.rb

139 lines
4.1 KiB
Ruby

##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
# The most useful and interesting feature of this module is the way the
# loot file gets appended to, rather than rewritten. Might be use for
# this technique in other modules that gather loot.
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'DuckDuckGo Password Hash Search',
'Description' => %q{
This module scrapes the DuckDuckGo search engine for appearances of the given hash.
When a hash is mentioned on the Internet, it is usually accompanied by the plaintext
version of the password. Good news for the attacker, bad news for the user.
Note that this module almost certainly violates the Terms of Service of DuckDuckGo,
which is a shame, and relegates this to a proof of concept trick until a combination
of search engine, TOS, and a useful API surfaces.
},
'Author' => [ 'todb' ],
'References' =>
[
[ 'URL', 'http://duckduckgo.com/' ] # I heart DDG and feel bad for abusing them :/
]
))
register_options(
[
OptString.new('HASH', [ true, "The password hash to search for", "b963c57010f218edc2cc3c229b5e4d0f"]),
OptBool.new('SSL', [ false, "Use SSL for hash searches", true]) # Just bringing this to the main opts list.
], self.class)
deregister_options('RHOST', 'RPORT', 'VHOST', 'Proxies')
end
def cleanup
datastore['RHOST'] = @old_rhost
datastore['RPORT'] = @old_rport
end
# Save the original rhost/rport in case the user was exploiting something else
def save_rhost
@old_rhost = datastore['RHOST']
@old_rport = datastore['RPORT']
end
def pw_hash
datastore['HASH'].to_s
end
def run
save_rhost()
# Need to set this for send_request_cgi()
datastore['RHOST'] = "duckduckgo.com"
datastore['RPORT'] = datastore['SSL'] ? 443 : 80
loot = ""
uri = "/html/"
res = send_request_cgi({
'method' => 'GET',
'uri' => "/html/",
'vars_get' => {"q" => pw_hash}
}, 25)
if res == nil or res.body == nil
print_error("No response from DuckDuckGo.")
return
end
ddg_res = find_ddg_response(res.body)
if ddg_res
print_good("DuckDuckGo's first listing for '#{pw_hash}' is at #{ddg_res}")
report_hash_found(ddg_res)
else
print_status("No results for #{datastore['HASH']} were found via DuckDuckGo.")
return
end
end
# Instead of one loot file at a time, it's nicer to just append.
def report_hash_found(url)
return unless framework.db.active
loot_header = "Hash,URL"
loot_line = "\"#{pw_hash}\",\"#{url}\"\n"
existing_loot = framework.db.loots.find_by_ltype("internet.hashes")
if existing_loot
append_to_loot(existing_loot,loot_line,url)
else
loot_file = [loot_header,loot_line].join("\n")
p = store_loot("internet.hashes","text/plain",nil,loot_file,"internet_hashes.csv","Internet-Searchable Hashes")
print_status("Saved hash and URL to #{p}")
end
end
def append_to_loot(existing_loot,loot_line,url)
dupe_hash = false
p = existing_loot.path
fh = ::File.open(p, "r+b")
fh.each_line do |line|
if line == loot_line
dupe_hash = true
break
end
end
if dupe_hash
print_status "Discarding duplicate hash '#{pw_hash}' found in #{existing_loot.path}"
fh.close
return
end
# fh.seek(fh.stat.size)
fh.write loot_line
fh.close
existing_loot.updated_at = Time.now.utc
existing_loot.save
print_status("Appended hash and URL to #{p}")
end
def find_ddg_response(html)
first_result = html.match(/<div[^>]+web\-result.*?This is the visible part/m)[0]
return nil unless first_result
url = first_result.match(/href=\x22(http.*)\x22/)[1] rescue nil
return url
end
end