2013-12-26 12:00:39 +00:00
|
|
|
##
|
|
|
|
# This module requires Metasploit: http//metasploit.com/download
|
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
|
|
##
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
require 'uri'
|
|
|
|
|
|
|
|
class Metasploit3 < Msf::Auxiliary
|
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
|
|
include Msf::Auxiliary::Report
|
|
|
|
|
|
|
|
def initialize
|
|
|
|
super(
|
|
|
|
'Name' => 'IBM Lotus Sametime Version Enumeration',
|
|
|
|
'Description' => %q{
|
|
|
|
This module scans an IBM Lotus Sametime web interface to enumerate
|
|
|
|
the version and configuration information.
|
|
|
|
},
|
|
|
|
'Author' =>
|
|
|
|
[
|
|
|
|
'kicks4kittens' # Metasploit module
|
|
|
|
],
|
|
|
|
'License' => MSF_LICENSE
|
|
|
|
)
|
|
|
|
register_options(
|
|
|
|
[
|
|
|
|
OptString.new('TARGETURI', [ true, "The path to the Sametime Server", '/']),
|
|
|
|
OptBool.new('QuerySametimeProxy', [ true, "Automatically query Sametime proxy if found", true]),
|
|
|
|
OptBool.new('ShowVersions', [ true, "Display Version information from server", true]),
|
|
|
|
OptBool.new('ShowConfig', [ true, "Display Config information from server", true]),
|
|
|
|
OptBool.new('ShowAPIVersions', [ true, "Display API Version information from server", false])
|
|
|
|
], self.class)
|
|
|
|
|
|
|
|
register_advanced_options(
|
|
|
|
[
|
|
|
|
OptBool.new('StoreConfigs', [ true, "Store JSON configs to loot", true])
|
|
|
|
], self.class)
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
def check_url(tpath, url, stproxy_rhost='')
|
|
|
|
|
|
|
|
if stproxy_rhost.empty?
|
|
|
|
checked_host = datastore['RHOST']
|
|
|
|
vprint_status("Requesting \"#{checked_host}:#{rport}#{normalize_uri(tpath, url)}\"")
|
|
|
|
res = send_request_cgi({
|
|
|
|
'uri' => normalize_uri(tpath, url),
|
|
|
|
'method' => 'GET'
|
|
|
|
})
|
|
|
|
else
|
|
|
|
checked_host = stproxy_rhost
|
|
|
|
# make request with provided stproxy rhost
|
|
|
|
vprint_status("Requesting \"#{checked_host}:#{rport}#{normalize_uri(tpath, url)}\"")
|
|
|
|
res = send_request_cgi({
|
|
|
|
'uri' => normalize_uri(tpath, url),
|
|
|
|
'method' => 'GET',
|
|
|
|
'rhost' => stproxy_rhost, # connect to Sametime Proxy
|
|
|
|
'vhost' => stproxy_rhost # set appropriate VHOST
|
|
|
|
})
|
2013-12-26 12:00:39 +00:00
|
|
|
end
|
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
if not res
|
|
|
|
print_status("#{checked_host}:#{rport} - Did not respond")
|
|
|
|
return
|
|
|
|
elsif res.code == 403
|
|
|
|
print_status("#{checked_host}:#{rport} - Access Denied #{res.code} #{res.message}")
|
|
|
|
return
|
|
|
|
elsif not res.code == 200
|
|
|
|
print_error("#{checked_host}:#{rport} - Unexpected Response code (#{res.code}) received from server")
|
|
|
|
return
|
|
|
|
end
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
if url.include?('WebAVServlet')
|
|
|
|
# special handler for WebAVServlet as body is JSON regardless of content-type
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
begin
|
|
|
|
res_json = JSON.parse(res.body)
|
|
|
|
rescue JSON::ParserError
|
|
|
|
print_error("Unable to parse JSON response")
|
|
|
|
end
|
|
|
|
extract_webavservlet_data(res_json)
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
elsif res['content-type'].include?("text/plain") or res['content-type'].include?("text/html")
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
res.body.each_line do | response_line |
|
|
|
|
extract_response_data(response_line, url)
|
|
|
|
end
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
elsif res['content-type'].include?("text/json") or res['content-type'].include?("text/javaScript")
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
begin
|
|
|
|
res_json = JSON.parse(res.body)
|
|
|
|
rescue JSON::ParserError
|
|
|
|
print_error("Unable to parse JSON response")
|
|
|
|
end
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
# store configuration files as loot
|
|
|
|
store_config(tpath, url, res_json, checked_host) if datastore['StoreConfigs']
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
extract_json_data(res_json)
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
end
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
end
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
def extract_webavservlet_data(res_json)
|
|
|
|
# extract data from WebAVServlet
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
# stwebav/WebAVServlet --> WebPlayer information
|
|
|
|
if res_json['Softphone']
|
|
|
|
@version_info['version']['Softphone'] = res_json['Softphone']
|
|
|
|
end
|
|
|
|
if res_json['WebPlayer']
|
|
|
|
@version_info['version']['WebPlayer'] = res_json['WebPlayer']
|
|
|
|
end
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def extract_response_data(response_line, url)
|
|
|
|
# extract data from response
|
|
|
|
|
|
|
|
case response_line
|
|
|
|
# stmeetings/about.jsp --> Sametime Server version string
|
|
|
|
when /lotusBuild">Release (.+?)<\/td>/i
|
|
|
|
# lotus build version
|
|
|
|
@version_info['version']['sametimeVersion'] = $1.chomp
|
|
|
|
# serverversion.properties --> API Version information
|
|
|
|
when /^meeting=(.*)$/i
|
|
|
|
# meeting api version
|
|
|
|
@version_info['api']['meeting'] = $1.chomp
|
|
|
|
when /^appshare=(.*)$/i
|
|
|
|
# appshare api version
|
|
|
|
@version_info['api']['appshare'] = $1.chomp
|
|
|
|
when /^docshare=(.*)$/i
|
|
|
|
# docshare api version
|
|
|
|
@version_info['api']['docshare'] = $1.chomp
|
|
|
|
when /^rtc4web=(.*)$/i
|
|
|
|
# rtc4web api version
|
|
|
|
@version_info['api']['rtc4web'] = $1.chomp
|
|
|
|
when /^roomapi=(.*)$/i
|
|
|
|
# room api version
|
|
|
|
@version_info['api']['roomapi'] = $1.chomp
|
|
|
|
when /^recordings=(.*)$/i
|
|
|
|
# recording api version
|
|
|
|
@version_info['api']['recordings'] = $1.chomp
|
|
|
|
when /^audio=(.*)$/i
|
|
|
|
# audio api version
|
|
|
|
@version_info['api']['audio'] = $1.chomp
|
|
|
|
when /^video=(.*)$/i
|
|
|
|
# video api version
|
|
|
|
@version_info['api']['video'] = $1.chomp
|
|
|
|
# rtc/buildinfo.txt --> Server Build version
|
|
|
|
when /^(\d{8}-\d+)$/
|
|
|
|
if url.include?('buildinfo.txt')
|
|
|
|
# buildinfo version
|
|
|
|
@version_info['version']['buildinfo'] = $1.chomp
|
|
|
|
end
|
|
|
|
# stwebclient/i18nStrings.jsp --> Sametime Server version string
|
|
|
|
when /aboutBoxProductTitle":"(.*?)",/i
|
|
|
|
if not @version_info['version']['sametimeVersion']
|
|
|
|
# sametime version
|
|
|
|
@version_info['version']['sametimeVersion'] = $1.chomp
|
|
|
|
end
|
2013-12-26 12:00:39 +00:00
|
|
|
end
|
2014-01-15 12:53:14 +00:00
|
|
|
end
|
2013-12-26 12:00:39 +00:00
|
|
|
|
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
def extract_json_data(res_json)
|
|
|
|
# extract data from JSON response
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
# stwebclient/communityserver --> Community server address
|
|
|
|
if res_json['communityRef']
|
|
|
|
@version_info['conf']['communityRef'] = res_json['communityRef']
|
|
|
|
end
|
|
|
|
if res_json['anonymousEnabled']
|
|
|
|
@version_info['conf']['communityref_anonymousEnabled'] = res_json['anonymousEnabled']
|
|
|
|
# stmeetings/configuration --> Sametime configuration
|
|
|
|
end
|
|
|
|
if res_json['calinteg.enabled']
|
|
|
|
@version_info['conf']['calinteg.enabled'] = res_json['calinteg.enabled']
|
|
|
|
end
|
|
|
|
if res_json['docshare.fileio.codebase']
|
|
|
|
@version_info['conf']['docshare.fileio.codebase'] = res_json['docshare.fileio.codebase']
|
|
|
|
end
|
|
|
|
if res_json['docshare.native.codebase']
|
|
|
|
@version_info['conf']['docshare.native.codebase'] = res_json['docshare.native.codebase']
|
|
|
|
end
|
|
|
|
if res_json['docshare.remote.url']
|
|
|
|
@version_info['conf']['docshare.remote.url'] = res_json['docshare.remote.url']
|
|
|
|
end
|
|
|
|
if res_json['meetingroom.allowGuestAccess']
|
|
|
|
if res_json['meetingroom.allowGuestAccess'] == "1"
|
|
|
|
@version_info['conf']['meetingroom.allowGuestAccess'] = "true"
|
|
|
|
else
|
|
|
|
@version_info['conf']['meetingroom.allowGuestAccess'] = "false"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if res_json['meetingroomcenter.allowGuestAccess']
|
|
|
|
if res_json['meetingroomcenter.allowGuestAccess'] == "1"
|
|
|
|
@version_info['conf']['meetingroomcenter.allowGuestAccess'] = "true"
|
|
|
|
else
|
|
|
|
@version_info['conf']['meetingroomcenter.allowGuestAccess'] = "false"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if res_json['meetingroomcenter.customLoginPage']
|
|
|
|
@version_info['conf']['meetingroomcenter.customLoginPage'] = res_json['meetingroomcenter.customLoginPage']
|
|
|
|
end
|
|
|
|
if res_json['meetingroomcenter.enforceCSRFToken']
|
|
|
|
@version_info['conf']['meetingroomcenter.enforceCSRFToken'] = res_json['meetingroomcenter.enforceCSRFToken']
|
|
|
|
end
|
|
|
|
if res_json['meetingroomcenter.enforceHiddenRooms']
|
|
|
|
@version_info['conf']['meetingroomcenter.enforceHiddenRooms'] = res_json['meetingroomcenter.enforceHiddenRooms']
|
|
|
|
end
|
|
|
|
if res_json['meetingroomcenter.passwords']
|
|
|
|
@version_info['conf']['meetingroomcenter.passwords'] = res_json['meetingroomcenter.passwords']
|
|
|
|
end
|
|
|
|
if res_json['meetingserver.statistics.jmx.enabled']
|
|
|
|
@version_info['conf']['meetingserver.statistics.jmx.enabled'] = res_json['meetingserver.statistics.jmx.enabled']
|
|
|
|
end
|
|
|
|
if res_json['rtc4web.enforceNonce']
|
|
|
|
@version_info['conf']['rtc4web.enforceNonce'] = res_json['rtc4web.enforceNonce']
|
|
|
|
end
|
|
|
|
if res_json['userInfoRedirect']
|
|
|
|
@version_info['conf']['userInfoRedirect'] = res_json['userInfoRedirect']
|
|
|
|
end
|
|
|
|
if res_json['userInfoUrlTemplate']
|
|
|
|
@version_info['conf']['userInfoUrlTemplatee'] = res_json['userInfoUrlTemplate']
|
|
|
|
end
|
|
|
|
if res_json['meetingroomcenter.stProxyAddress']
|
|
|
|
@version_info['conf']['meetingroomcenter.stProxyAddress'] = res_json['meetingroomcenter.stProxyAddress']
|
|
|
|
end
|
|
|
|
if res_json['meetingroomcenter.stProxySSLAddress']
|
|
|
|
@version_info['conf']['meetingroomcenter.stProxySSLAddress'] = res_json['meetingroomcenter.stProxySSLAddress']
|
|
|
|
end
|
|
|
|
# stwebclient/communityserver --> Sametime Community server name
|
|
|
|
if res_json['communityRef']
|
|
|
|
@version_info['conf']['communityRef'] = res_json['communityRef']
|
|
|
|
@version_info['conf']['anonymousEnabled'] = res_json['anonymousEnabled']
|
2013-12-26 12:00:39 +00:00
|
|
|
end
|
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
end
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
def report
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
if @version_info['version']['sametimeVersion']
|
|
|
|
print_line()
|
|
|
|
print_good("#{@version_info['version']['sametimeVersion']} Detected (#{peer})")
|
|
|
|
else
|
|
|
|
print_line()
|
|
|
|
print_status("#{peer} - IBM Lotus Sametime information")
|
|
|
|
end
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
# configure tables
|
|
|
|
version_tbl = Msf::Ui::Console::Table.new(
|
|
|
|
Msf::Ui::Console::Table::Style::Default,
|
|
|
|
'Header' => "IBM Lotus Sametime Information [Version]",
|
|
|
|
'Prefix' => "",
|
|
|
|
'Indent' => 1,
|
|
|
|
'Columns' =>
|
|
|
|
[
|
|
|
|
"Component",
|
|
|
|
"Version"
|
|
|
|
])
|
|
|
|
|
|
|
|
conf_tbl = Msf::Ui::Console::Table.new(
|
|
|
|
Msf::Ui::Console::Table::Style::Default,
|
|
|
|
'Header' => "IBM Lotus Sametime Information [Config]",
|
|
|
|
'Prefix' => "",
|
|
|
|
'Indent' => 1,
|
|
|
|
'Columns' =>
|
|
|
|
[
|
|
|
|
"Key",
|
|
|
|
"Value"
|
|
|
|
])
|
|
|
|
|
|
|
|
api_tbl = Msf::Ui::Console::Table.new(
|
|
|
|
Msf::Ui::Console::Table::Style::Default,
|
|
|
|
'Header' => "IBM Lotus Sametime Information [API]",
|
|
|
|
'Prefix' => "",
|
|
|
|
'Indent' => 1,
|
|
|
|
'Columns' =>
|
|
|
|
[
|
|
|
|
"API",
|
|
|
|
"Version"
|
|
|
|
])
|
|
|
|
|
|
|
|
# populate tables
|
|
|
|
@version_info['version'].each do | line |
|
|
|
|
version_tbl << [ line[0], line[1] ]
|
|
|
|
end
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
@version_info['conf'].each do | line |
|
|
|
|
conf_tbl << [ line[0], line[1] ]
|
|
|
|
end
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
@version_info['api'].each do | line |
|
|
|
|
api_tbl << [ line[0], line[1] ]
|
|
|
|
end
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
# display tables
|
|
|
|
print_good("#{version_tbl.to_s}") if not version_tbl.to_s.empty? and datastore['ShowVersions']
|
|
|
|
print_good("#{api_tbl.to_s}") if not api_tbl.to_s.empty? and datastore['ShowAPIVersions']
|
|
|
|
print_good("#{conf_tbl.to_s}") if not conf_tbl.to_s.empty? and datastore['ShowConfig']
|
|
|
|
|
|
|
|
# report_note
|
|
|
|
if @version_info['version']['sametimeVersion']
|
|
|
|
report_note(
|
|
|
|
:host => datastore['rhost'],
|
|
|
|
:port => datastore['rport'],
|
|
|
|
:proto => 'http',
|
|
|
|
:ntype => 'ibm_lotus_sametime_version',
|
|
|
|
:data => @version_info['version']['sametimeVersion']
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def store_config(tpath, url, config_to_store, checked_host)
|
|
|
|
# store configuration as loot
|
|
|
|
|
|
|
|
if not config_to_store.empty?
|
|
|
|
loot = store_loot(
|
|
|
|
"ibm_lotus_sametime_configuration_" + url,
|
|
|
|
"text/json",
|
|
|
|
datastore['rhost'],
|
|
|
|
config_to_store,
|
|
|
|
".json"
|
|
|
|
)
|
|
|
|
|
|
|
|
print_good("#{checked_host} - IBM Lotus Sametime Configuration data stored as loot")
|
|
|
|
print_status("#{checked_host}#{normalize_uri(tpath, url)}\n => #{loot}")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def run
|
|
|
|
|
|
|
|
# create storage for extracted information+
|
|
|
|
@version_info = {}
|
|
|
|
@version_info['version'] = {}
|
|
|
|
@version_info['conf'] = {}
|
|
|
|
@version_info['api'] = {}
|
|
|
|
|
|
|
|
tpath = normalize_uri(target_uri.path)
|
|
|
|
|
|
|
|
sametime_urls = [
|
|
|
|
'/stmeetings/about.jsp',
|
|
|
|
'/stmeetings/serverversion.properties',
|
|
|
|
'/rtc/buildinfo.txt',
|
|
|
|
'/stmeetings/configuration?format=json&verbose=true'
|
|
|
|
]
|
|
|
|
|
|
|
|
sametime_proxy_urls = [
|
|
|
|
'/stwebclient/i18nStrings.jsp',
|
|
|
|
'/stwebclient/communityserver',
|
|
|
|
'/stwebav/WebAVServlet?Name=WebPlayerVersion'
|
|
|
|
]
|
|
|
|
|
|
|
|
print_status("#{peer} - Checking IBM Lotus Sametime Server")
|
|
|
|
sametime_urls.each do | url |
|
|
|
|
check_url(tpath, url)
|
|
|
|
end
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
if @version_info['conf']['meetingroomcenter.stProxyAddress'] or @version_info['conf']['meetingroomcenter.stProxySSLAddress']
|
|
|
|
# check Sametime proxy if configured to do so
|
|
|
|
if datastore['QuerySametimeProxy']
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
if @version_info['conf']['meetingroomcenter.stProxySSLAddress'] and datastore['SSL']
|
|
|
|
# keep using SSL
|
|
|
|
stproxy_rhost = URI(@version_info['conf']['meetingroomcenter.stProxySSLAddress']).host
|
|
|
|
vprint_status("Testing discovered Sametime proxy address for further data #{stproxy_rhost}")
|
|
|
|
else
|
|
|
|
stproxy_rhost = URI(@version_info['conf']['meetingroomcenter.stProxyAddress']).host
|
|
|
|
vprint_status("Testing discovered Sametime proxy address for further data #{stproxy_rhost}")
|
|
|
|
end
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
print_good("#{peer} - Sametime Proxy address discovered #{stproxy_rhost}")
|
2013-12-26 12:00:39 +00:00
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
sametime_proxy_urls.each do | url |
|
|
|
|
check_url(tpath, url, stproxy_rhost)
|
2013-12-26 12:00:39 +00:00
|
|
|
end
|
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
else
|
|
|
|
print_status("#{peer} - Sametime Proxy address discovered #{stproxy_rhost}, but checks disabled")
|
|
|
|
end
|
2013-12-26 12:00:39 +00:00
|
|
|
end
|
|
|
|
|
2014-01-15 12:53:14 +00:00
|
|
|
report unless @version_info.empty?
|
|
|
|
|
|
|
|
end
|
|
|
|
|
2013-12-26 12:00:39 +00:00
|
|
|
end
|