Class: BetterCap::Network::Servers::DNSD

Inherits:
Object
  • Object
show all
Defined in:
lib/bettercap/network/servers/dnsd.rb

Overview

Simple DNS server class used for DNS spoofing.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hosts_filename = nil, address = '0.0.0.0', port = 5300) ⇒ DNSD

Initialize the DNS server with the specified address and tcp/udp port. The server will load hosts_filename composed by ‘regexp -> ip’ entries to do custom DNS spoofing/resolution.



48
49
50
51
52
53
54
55
56
57
# File 'lib/bettercap/network/servers/dnsd.rb', line 48

def initialize( hosts_filename = nil, address = '0.0.0.0', port = 5300 )
  @port    = port
  @address = address
  @hosts   = hosts_filename
  @server  = nil
  @ifaces  = [
    [:udp, address, port],
    [:tcp, address, port]
  ]
end

Class Method Details

.parse_hosts(filename) ⇒ Object

Parse hosts from filename, example host file:

# *.google.com will point to the attacker’s computer. local .*google.com

# a custom redirection 12.12.12.12 wtf.idontexist.com

Raises:



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/bettercap/network/servers/dnsd.rb', line 114

def self.parse_hosts( filename )
  raise BetterCap::Error, "File '#{filename}' does not exist." unless File.exist?(filename)

  hosts = {}
  File.open(filename).each_with_index do |line,lineno|
    line = line.strip
    # skip empty lines and comments
    next if line.empty? or line[0] == '#'
    if line =~ /^([^\s]+)\s+(.+)$/
      address    = $1
      expression = $2

      if address == 'local'
        address = Context.get.iface.ip.to_s
      end

      raise BetterCap::Error, "Invalid IPv4 address '#{address}' on line #{lineno + 1} of '#{filename}'." \
        unless Network::Validator.is_ip?(address)

      begin
        hosts[ expression ] = address
      rescue RegexpError
        raise BetterCap::Error, "Invalid expression '#{expression}' on line #{lineno + 1} of '#{filename}'."
      end
    end
  end

  hosts
end

Instance Method Details

#add_rule!(exp, addr) ⇒ Object

Add a rule to the DNS resolver at runtime.



60
61
62
63
64
65
66
67
68
69
# File 'lib/bettercap/network/servers/dnsd.rb', line 60

def add_rule!( exp, addr )
  Logger.debug "[#{'DNS'.green}] Adding rule: '#{exp}' -> '#{addr}' ..."

  block = Proc.new do |transaction|
    Logger.info "[#{transaction.options[:peer]} > #{'DNS'.green}] Received request for '#{transaction.question.to_s.yellow}', sending spoofed reply #{addr.yellow} ..."
    transaction.respond!(addr)
  end

  DnsWrapper.get.rules << RubyDNS::RuleBasedServer::Rule.new( [ Regexp.new(exp), Resolv::DNS::Resource::IN::A ], block )
end

#startObject

Start the server.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/bettercap/network/servers/dnsd.rb', line 72

def start
  Logger.info "[#{'DNS'.green}] Starting on #{@address}:#{@port} ..."

  options = {
    :listen => @ifaces,
    :asynchronous => true,
    :server_class => DnsWrapper
  }

  begin
    RubyDNS::run_server( options ) do
      # Suppress RubyDNS logging.
      @logger.level = ::Logger::ERROR
      @upstream ||= RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])

      # Default DNS handler
      otherwise do |transaction|
        Logger.debug "[#{transaction.options[:peer]} > #{'DNS'.green}] Received request for '#{transaction.question.to_s.yellow}' -> upstream DNS"
        transaction.passthrough!(@upstream)
      end
    end

    unless @hosts.nil?
      DNSD.parse_hosts( @hosts ).each do |exp,addr|
        add_rule!( exp, addr )
      end
    end
  rescue Errno::EADDRINUSE
    raise BetterCap::Error, "[DNS] It looks like there's another process listening on #{@address}:#{@port}, please chose a different port."
  end
end

#stopObject

Stop the server.



105
# File 'lib/bettercap/network/servers/dnsd.rb', line 105

def stop; end