Fix axfr support for auxiliary/gather/enum_dns

AXFR support in net-dns is broken. This fixes it, and makes the
requisite modifications to enum_dns module. Basic problem is that AXFR
responses consist of a chain of DNS replies, not a single reply with
multiple answers. Previously, only the first of these replies, the SOA
record, was returned. Also added some exception handling to avoid
problems like #483.
unstable
Daniel Miller 2012-08-16 20:40:24 -05:00
parent 0311caf4df
commit 7005216d1f
2 changed files with 180 additions and 79 deletions

View File

@ -995,12 +995,107 @@ module Net # :nodoc:
#
# Performs a zone transfer for the zone passed as a parameter.
#
# It is actually only a wrapper to a send with type set as Net::DNS::AXFR,
# since it is using the same infrastucture.
# Returns a list of Net::DNS::Packet (not answers!)
#
def axfr(name,cls=Net::DNS::IN)
@logger.info "Requested AXFR transfer, zone #{name} class #{cls}"
send(name,Net::DNS::AXFR,cls)
if @config[:nameservers].size == 0
raise Resolver::Error, "No nameservers specified!"
end
method = :query_tcp
packet = make_query_packet(name, Net::DNS::AXFR, cls)
# Store packet_data for performance improvements,
# so methods don't keep on calling Packet#data
packet_data = packet.data
packet_size = packet_data.size
if @raw
@logger.warn "AXFR query, switching to TCP over RAW socket"
method = :send_raw_tcp
else
@logger.warn "AXFR query, switching to TCP"
method = :query_tcp
end
answers = []
length = [packet_data.size].pack("n")
@config[:nameservers].each do |ns|
begin
socket = Socket.new(Socket::AF_INET,Socket::SOCK_STREAM,0)
socket.bind(Socket.pack_sockaddr_in(@config[:source_port],@config[:source_address].to_s))
sockaddr = Socket.pack_sockaddr_in(@config[:port],ns.to_s)
@config[:tcp_timeout].timeout do
catch "next nameserver" do
socket.connect(sockaddr)
@logger.info "Contacting nameserver #{ns} port #{@config[:port]}"
socket.write(length+packet_data)
soa = 0
while soa < 2 do
buffer = ""
ans = socket.recv(Net::DNS::INT16SZ)
if ans.size == 0
if soa == 1
break #No records in zone
else
@logger.warn "Couldn't recv from nameserver #{ns}, trying next."
throw "next nameserver"
end
end
len = ans.unpack("n")[0]
@logger.info "Receiving #{len} bytes..."
if len == 0
@logger.warn "Receiving 0 length packet from nameserver #{ns}, trying next."
throw "next nameserver"
end
while (buffer.size < len)
left = len - buffer.size
temp,from = socket.recvfrom(left)
buffer += temp
end
unless buffer.size == len
@logger.warn "Malformed packet from nameserver #{ns}, trying next."
throw "next nameserver"
end
@logger.info "Received #{buffer.size} bytes from #{ns.to_s}:#{@config[:port].to_s}"
begin
response = Net::DNS::Packet.parse(buffer,["",@config[:port],ns.to_s,ns.to_s])
if response.answer[0].type == "SOA"
soa += 1
end
answers << response
rescue NameError => e
@logger.warn "Error parsing axfr response: #{e.message}"
end
end
if soa == 2
answers.pop #Remove duplicate SOA
end
end
end
rescue TimeoutError
@logger.warn "Nameserver #{ns} not responding within TCP timeout, trying next one"
answers = []
next
ensure
socket.close
end
end
unless answers
message = "No response from nameservers list"
@logger.fatal(message)
raise NoResponseError, message
end
return answers
end
#

View File

@ -369,8 +369,8 @@ class Metasploit3 < Msf::Auxiliary
begin
@res.nameserver=(nssrvip)
zone = []
zone = @res.query(target,Net::DNS::AXFR)
if zone.answer.length != 0
zone = @res.axfr(target)
if zone.length != 0
print_status("Zone transfer successful")
report_note(:host => nssrvip,
:proto => 'udp',
@ -379,7 +379,9 @@ class Metasploit3 < Msf::Auxiliary
:type => 'dns.enum',
:data => "Zone transfer successful")
#Prints each record according to its type
zone.answer.each do |rr|
zone.each do |response|
response.answer.each do |rr|
begin
case rr.type
when "A"
print_status("Name: #{rr.name} IP address: #{rr.address} Record: A ")
@ -454,6 +456,10 @@ class Metasploit3 < Msf::Auxiliary
:type => 'dns.enum',
:data => "#{rr.host},#{rr.port},#{rr.priority},SRV")
end
rescue ActiveRecord::RecordInvalid
#Do nothing. Probably tried to store :host => 127.0.0.1
end
end
end
else
print_error("Zone transfer failed (length was zero)")