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
parent
0311caf4df
commit
7005216d1f
|
@ -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
|
||||
|
||||
#
|
||||
|
|
|
@ -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)")
|
||||
|
|
Loading…
Reference in New Issue