95 lines
3.1 KiB
Ruby
95 lines
3.1 KiB
Ruby
# -*- coding: binary -*-
|
|
|
|
require 'rex/proto/sip/response'
|
|
|
|
module Msf
|
|
# SIP protocol support
|
|
module Exploit::Remote::SIP
|
|
# Parses +response+, extracts useful metdata and then reports on it.
|
|
# Returns true iff the response was a valid SIP response
|
|
def report_response(response, rhost, proto, desired_headers = %w(User-Agent Server Allow))
|
|
endpoint = "#{rhost}:#{rport} #{proto}"
|
|
begin
|
|
options_response = Rex::Proto::SIP::Response.parse(response)
|
|
rescue ArgumentError => e
|
|
vprint_error("#{endpoint} is not SIP: #{e}")
|
|
return false
|
|
end
|
|
|
|
# Extracted headers, stored as a hash where the key is the header name
|
|
# and the value is a list of all values seen for the header, covering the
|
|
# case where the same header value is seen multiple times
|
|
extracted_headers = {}
|
|
unless desired_headers.nil? || desired_headers.empty?
|
|
desired_headers.each do |desired_header|
|
|
next unless (found_header = options_response.header(desired_header))
|
|
extracted_headers[desired_header] ||= []
|
|
extracted_headers[desired_header] |= found_header
|
|
end
|
|
end
|
|
|
|
# Create a SIP OPTIONS fingerprint hash
|
|
fprint = {
|
|
'code' => options_response.code,
|
|
'message' => options_response.message
|
|
}
|
|
|
|
# compact the header values, append the header information to the
|
|
# fingerprint hash
|
|
extracted_headers.each_pair do |k,v|
|
|
value = v.join(',')
|
|
extracted_headers[k] = value
|
|
fprint['header_' + k.gsub('-', '_').downcase] = value
|
|
end
|
|
|
|
# Create a summary of the response
|
|
status = options_response.status_line.dup
|
|
unless extracted_headers.keys.empty?
|
|
status << ": #{extracted_headers}"
|
|
end
|
|
|
|
# Report the service with the status information
|
|
report_service(
|
|
host: rhost,
|
|
port: rport,
|
|
proto: proto.downcase,
|
|
name: 'sip',
|
|
info: status
|
|
)
|
|
|
|
# Report the fingerprint information
|
|
report_note(
|
|
host: rhost,
|
|
port: rport,
|
|
proto: proto.downcase,
|
|
type: "sip.options.fingerprint",
|
|
data: fprint
|
|
)
|
|
|
|
# Display the actual result to the user
|
|
print_status(endpoint + " " + status)
|
|
true
|
|
end
|
|
|
|
def create_probe(ip, proto)
|
|
suser = Rex::Text.rand_text_alphanumeric(rand(8) + 1)
|
|
shost = Rex::Socket.source_address(ip)
|
|
src = "#{shost}:#{datastore['RPORT']}"
|
|
|
|
data = "OPTIONS sip:#{datastore['TO']}@#{ip} SIP/2.0\r\n"
|
|
data << "Via: SIP/2.0/#{proto.upcase} #{src};branch=z9hG4bK.#{format('%.8x', rand(0x100000000))};rport;alias\r\n"
|
|
data << "From: sip:#{suser}@#{src};tag=70c00e8c\r\n"
|
|
data << "To: sip:#{datastore['TO']}@#{ip}\r\n"
|
|
data << "Call-ID: #{rand(0x100000000)}@#{shost}\r\n"
|
|
data << "CSeq: 1 OPTIONS\r\n"
|
|
data << "Contact: sip:#{suser}@#{src}\r\n"
|
|
data << "Max-Forwards: 20\r\n"
|
|
data << "User-Agent: #{suser}\r\n"
|
|
data << "Accept: application/sdp\r\n"
|
|
data << "Content-Length: 0\r\n"
|
|
data << "\r\n"
|
|
data
|
|
end
|
|
end
|
|
end
|