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. # 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, # Returns a list of Net::DNS::Packet (not answers!)
# since it is using the same infrastucture.
# #
def axfr(name,cls=Net::DNS::IN) def axfr(name,cls=Net::DNS::IN)
@logger.info "Requested AXFR transfer, zone #{name} class #{cls}" @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 end
# #

View File

@ -369,8 +369,8 @@ class Metasploit3 < Msf::Auxiliary
begin begin
@res.nameserver=(nssrvip) @res.nameserver=(nssrvip)
zone = [] zone = []
zone = @res.query(target,Net::DNS::AXFR) zone = @res.axfr(target)
if zone.answer.length != 0 if zone.length != 0
print_status("Zone transfer successful") print_status("Zone transfer successful")
report_note(:host => nssrvip, report_note(:host => nssrvip,
:proto => 'udp', :proto => 'udp',
@ -379,80 +379,86 @@ class Metasploit3 < Msf::Auxiliary
:type => 'dns.enum', :type => 'dns.enum',
:data => "Zone transfer successful") :data => "Zone transfer successful")
#Prints each record according to its type #Prints each record according to its type
zone.answer.each do |rr| zone.each do |response|
case rr.type response.answer.each do |rr|
when "A" begin
print_status("Name: #{rr.name} IP address: #{rr.address} Record: A ") case rr.type
report_note(:host => rr.address.to_s, when "A"
:proto => 'udp', print_status("Name: #{rr.name} IP address: #{rr.address} Record: A ")
:sname => 'dns', report_note(:host => rr.address.to_s,
:port => 53 , :proto => 'udp',
:type => 'dns.enum', :sname => 'dns',
:data => "#{rr.address.to_s},#{rr.name},A") :port => 53 ,
when "SOA" :type => 'dns.enum',
print_status("Name: #{rr.mname} Record: SOA") :data => "#{rr.address.to_s},#{rr.name},A")
report_note(:host => nssrvip, when "SOA"
:proto => 'udp', print_status("Name: #{rr.mname} Record: SOA")
:sname => 'dns', report_note(:host => nssrvip,
:port => 53 , :proto => 'udp',
:type => 'dns.enum', :sname => 'dns',
:data => "#{rr.name},SOA") :port => 53 ,
when "MX" :type => 'dns.enum',
print_status("Name: #{rr.exchange} Preference: #{rr.preference} Record: MX") :data => "#{rr.name},SOA")
report_note(:host => nssrvip, when "MX"
:proto => 'udp', print_status("Name: #{rr.exchange} Preference: #{rr.preference} Record: MX")
:sname => 'dns', report_note(:host => nssrvip,
:port => 53 , :proto => 'udp',
:type => 'dns.enum', :sname => 'dns',
:data => "#{rr.exchange},MX") :port => 53 ,
when "CNAME" :type => 'dns.enum',
print_status("Name: #{rr.cname} Record: CNAME") :data => "#{rr.exchange},MX")
report_note(:host => nssrvip, when "CNAME"
:proto => 'udp', print_status("Name: #{rr.cname} Record: CNAME")
:sname => 'dns', report_note(:host => nssrvip,
:port => 53 , :proto => 'udp',
:type => 'dns.enum', :sname => 'dns',
:data => "#{rr.cname},CNAME") :port => 53 ,
when "HINFO" :type => 'dns.enum',
print_status("CPU: #{rr.cpu} OS: #{rr.os} Record: HINFO") :data => "#{rr.cname},CNAME")
report_note(:host => nssrvip, when "HINFO"
:proto => 'udp', print_status("CPU: #{rr.cpu} OS: #{rr.os} Record: HINFO")
:sname => 'dns', report_note(:host => nssrvip,
:port => 53 , :proto => 'udp',
:type => 'dns.enum', :sname => 'dns',
:data => "CPU:#{rr.cpu},OS:#{rr.os},HINFO") :port => 53 ,
when "AAAA" :type => 'dns.enum',
print_status("IPv6 Address: #{rr.address} Record: AAAA") :data => "CPU:#{rr.cpu},OS:#{rr.os},HINFO")
report_note(:host => rr.address.to_s, when "AAAA"
:proto => 'udp', print_status("IPv6 Address: #{rr.address} Record: AAAA")
:sname => 'dns', report_note(:host => rr.address.to_s,
:port => 53 , :proto => 'udp',
:type => 'dns.enum', :sname => 'dns',
:data => "#{rr.address.to_s}, AAAA") :port => 53 ,
when "NS" :type => 'dns.enum',
print_status("Name: #{rr.nsdname} Record: NS") :data => "#{rr.address.to_s}, AAAA")
report_note(:host => nssrvip, when "NS"
:proto => 'udp', print_status("Name: #{rr.nsdname} Record: NS")
:sname => 'dns', report_note(:host => nssrvip,
:port => 53 , :proto => 'udp',
:type => 'dns.enum', :sname => 'dns',
:data => "#{rr.nsdname},NS") :port => 53 ,
when "TXT" :type => 'dns.enum',
print_status("Text: #{rr.inspect}") :data => "#{rr.nsdname},NS")
report_note(:host => nssrvip, when "TXT"
:proto => 'udp', print_status("Text: #{rr.inspect}")
:sname => 'dns', report_note(:host => nssrvip,
:port => 53 , :proto => 'udp',
:type => 'dns.enum', :sname => 'dns',
:data => rr.inspect) :port => 53 ,
when "SRV" :type => 'dns.enum',
print_status("Host: #{rr.host} Port: #{rr.port} Priority: #{rr.priority} Record: SRV") :data => rr.inspect)
report_note(:host => nssrvip, when "SRV"
:proto => 'udp', print_status("Host: #{rr.host} Port: #{rr.port} Priority: #{rr.priority} Record: SRV")
:sname => 'dns', report_note(:host => nssrvip,
:port => 53 , :proto => 'udp',
:type => 'dns.enum', :sname => 'dns',
:data => "#{rr.host},#{rr.port},#{rr.priority},SRV") :port => 53 ,
: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
end end
else else