PageRenderTime 66ms CodeModel.GetById 19ms RepoModel.GetById 12ms app.codeStats 0ms

/modules/auxiliary/server/fakedns.rb

https://gitlab.com/0072016/metasploit-framework-rapid7
Ruby | 284 lines | 208 code | 43 blank | 33 comment | 24 complexity | ae6c770cd65681f2b2ec439d7cdbe6a1 MD5 | raw file
  1. ##
  2. # This module requires Metasploit: http://metasploit.com/download
  3. # Current source: https://github.com/rapid7/metasploit-framework
  4. ##
  5. require 'msf/core'
  6. require 'resolv'
  7. class MetasploitModule < Msf::Auxiliary
  8. include Msf::Auxiliary::Report
  9. def initialize
  10. super(
  11. 'Name' => 'Fake DNS Service',
  12. 'Description' => %q{
  13. This module provides a DNS service that redirects
  14. all queries to a particular address.
  15. },
  16. 'Author' => ['ddz', 'hdm', 'fozavci'],
  17. 'License' => MSF_LICENSE,
  18. 'Actions' =>
  19. [
  20. [ 'Service' ]
  21. ],
  22. 'PassiveActions' =>
  23. [
  24. 'Service'
  25. ],
  26. 'DefaultAction' => 'Service'
  27. )
  28. register_options(
  29. [
  30. OptAddress.new('SRVHOST', [ true, "The local host to listen on.", '0.0.0.0' ]),
  31. OptPort.new('SRVPORT', [ true, "The local port to listen on.", 53 ]),
  32. OptAddress.new('TARGETHOST', [ false, "The address that all names should resolve to", nil ]),
  33. OptString.new('TARGETDOMAIN', [ true, "The list of target domain names we want to fully resolve (BYPASS) or fake resolve (FAKE)", 'www.google.com']),
  34. OptEnum.new('TARGETACTION', [ true, "Action for TARGETDOMAIN", "BYPASS", %w{FAKE BYPASS}]),
  35. ], self.class)
  36. register_advanced_options(
  37. [
  38. OptPort.new('RR_SRV_PORT', [ false, "The port field in the SRV response when FAKE", 5060]),
  39. OptBool.new('LogConsole', [ false, "Determines whether to log all request to the console", true]),
  40. OptBool.new('LogDatabase', [ false, "Determines whether to log all request to the database", false]),
  41. ], self.class)
  42. end
  43. def target_host(addr = nil)
  44. target = datastore['TARGETHOST']
  45. if target.blank?
  46. if addr
  47. ::Rex::Socket.source_address(addr)
  48. else
  49. nil
  50. end
  51. else
  52. ::Rex::Socket.resolv_to_dotted(target)
  53. end
  54. end
  55. def run
  56. @port = datastore['SRVPORT'].to_i
  57. @log_console = false
  58. @log_database = false
  59. if datastore['LogConsole']
  60. @log_console = true
  61. end
  62. if datastore['LogDatabase']
  63. @log_database = true
  64. end
  65. # MacOS X workaround
  66. ::Socket.do_not_reverse_lookup = true
  67. print_status("DNS server initializing")
  68. @sock = ::UDPSocket.new()
  69. @sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1)
  70. @sock.bind(datastore['SRVHOST'], @port)
  71. @run = true
  72. @domain_target_list = datastore['TARGETDOMAIN'].split
  73. @bypass = ( datastore['TARGETACTION'].upcase == "BYPASS" )
  74. print_status("DNS server started")
  75. begin
  76. while @run
  77. @error_resolving = false
  78. packet, addr = @sock.recvfrom(65535)
  79. src_addr = addr[3]
  80. @requestor = addr
  81. next if packet.length == 0
  82. request = Resolv::DNS::Message.decode(packet)
  83. next unless request.qr == 0
  84. #
  85. # XXX: Track request IDs by requesting IP address and port
  86. #
  87. # Windows XP SP1a: UDP source port constant,
  88. # sequential IDs since boot time
  89. # Windows XP SP2: Randomized IDs
  90. #
  91. # Debian 3.1: Static source port (32906) until timeout,
  92. # randomized IDs
  93. #
  94. lst = []
  95. request.each_question {|name, typeclass|
  96. # Identify potential domain exceptions
  97. @match_target = false
  98. @match_name = name.to_s
  99. @domain_target_list.each do |ex|
  100. escaped = Regexp.escape(ex).gsub('\*','.*?')
  101. regex = Regexp.new "^#{escaped}$", Regexp::IGNORECASE
  102. if ( name.to_s =~ regex )
  103. @match_target = true
  104. @match_name = ex
  105. end
  106. end
  107. tc_s = typeclass.to_s().gsub(/^Resolv::DNS::Resource::/, "")
  108. request.qr = 1
  109. request.ra = 1
  110. lst << "#{tc_s} #{name}"
  111. case tc_s
  112. when 'IN::A'
  113. # Special fingerprinting name lookups:
  114. #
  115. # _isatap -> XP SP = 0
  116. # isatap.localdomain -> XP SP >= 1
  117. # teredo.ipv6.microsoft.com -> XP SP >= 2
  118. #
  119. # time.windows.com -> windows ???
  120. # wpad.localdomain -> windows ???
  121. #
  122. # <hostname> SOA -> windows XP self hostname lookup
  123. #
  124. answer = Resolv::DNS::Resource::IN::A.new(target_host(src_addr))
  125. if (@match_target and not @bypass) or (not @match_target and @bypass)
  126. # Resolve FAKE response
  127. if (@log_console)
  128. print_status("DNS target domain #{@match_name} found; Returning fake A records for #{name}")
  129. end
  130. else
  131. # Resolve the exception domain
  132. begin
  133. ip = Resolv::DNS.new().getaddress(name).to_s
  134. answer = Resolv::DNS::Resource::IN::A.new( ip )
  135. rescue ::Exception => e
  136. @error_resolving = true
  137. next
  138. end
  139. if (@log_console)
  140. print_status("DNS bypass domain #{@match_name} found; Returning real A records for #{name}")
  141. end
  142. end
  143. request.add_answer(name, 60, answer)
  144. when 'IN::MX'
  145. mx = Resolv::DNS::Resource::IN::MX.new(10, Resolv::DNS::Name.create("mail.#{name}"))
  146. ns = Resolv::DNS::Resource::IN::NS.new(Resolv::DNS::Name.create("dns.#{name}"))
  147. ar = Resolv::DNS::Resource::IN::A.new(target_host(src_addr))
  148. request.add_answer(name, 60, mx)
  149. request.add_authority(name, 60, ns)
  150. request.add_additional(Resolv::DNS::Name.create("mail.#{name}"), 60, ar)
  151. when 'IN::NS'
  152. ns = Resolv::DNS::Resource::IN::NS.new(Resolv::DNS::Name.create("dns.#{name}"))
  153. ar = Resolv::DNS::Resource::IN::A.new(target_host(src_addr))
  154. request.add_answer(name, 60, ns)
  155. request.add_additional(name, 60, ar)
  156. when 'IN::SRV'
  157. if @bypass || !@match_target
  158. if @log_console
  159. print_status("DNS bypass domain #{@match_name} found; Returning real SRV records for #{name}")
  160. end
  161. # if we are in bypass mode or we are in fake mode but the target didn't match,
  162. # just return the real response RRs
  163. resources = Resolv::DNS.new().getresources(Resolv::DNS::Name.create(name), Resolv::DNS::Resource::IN::SRV)
  164. if resources.empty?
  165. @error_resolving = true
  166. print_error("Unable to resolve SRV record for #{name} -- skipping")
  167. next
  168. end
  169. resources.each do |resource|
  170. host = resource.target
  171. port = resource.port.to_i
  172. weight = resource.weight.to_i
  173. priority = resource.priority.to_i
  174. ttl = resource.ttl.to_i
  175. request.add_answer(
  176. name,
  177. ttl,
  178. Resolv::DNS::Resource::IN::SRV.new(priority, weight, port, Resolv::DNS::Name.create(host))
  179. )
  180. end
  181. else
  182. if @log_console
  183. print_status("DNS target domain #{@match_name} found; Returning fake SRV records for #{name}")
  184. # Prepare the FAKE response
  185. request.add_answer(
  186. name,
  187. 10,
  188. Resolv::DNS::Resource::IN::SRV.new(5, 0, datastore['RR_SRV_PORT'], Resolv::DNS::Name.create(name))
  189. )
  190. request.add_additional(Resolv::DNS::Name.create(name), 60, Resolv::DNS::Resource::IN::A.new(target_host(src_addr)))
  191. end
  192. end
  193. when 'IN::PTR'
  194. soa = Resolv::DNS::Resource::IN::SOA.new(
  195. Resolv::DNS::Name.create("ns.internet.com"),
  196. Resolv::DNS::Name.create("root.internet.com"),
  197. 1,
  198. 3600,
  199. 3600,
  200. 3600,
  201. 3600
  202. )
  203. ans = Resolv::DNS::Resource::IN::PTR.new(
  204. Resolv::DNS::Name.create("www")
  205. )
  206. request.add_answer(name, 60, ans)
  207. request.add_authority(name, 60, soa)
  208. else
  209. lst << "UNKNOWN #{tc_s}"
  210. end
  211. }
  212. if(@log_console)
  213. if(@error_resolving)
  214. print_error("XID #{request.id} (#{lst.join(", ")}) - Error resolving")
  215. else
  216. print_status("XID #{request.id} (#{lst.join(", ")})")
  217. end
  218. end
  219. if(@log_database)
  220. report_note(
  221. :host => addr[3],
  222. :type => "dns_lookup",
  223. :data => "#{addr[3]}:#{addr[1]} XID #{request.id} (#{lst.join(", ")})"
  224. ) if lst.length > 0
  225. end
  226. @sock.send(request.encode(), 0, addr[3], addr[1])
  227. end
  228. rescue ::Exception => e
  229. print_error("fakedns: #{e.class} #{e} #{e.backtrace}")
  230. # Make sure the socket gets closed on exit
  231. ensure
  232. @sock.close
  233. end
  234. end
  235. def print_error(msg)
  236. @requestor ? super("%s:%p - DNS - %s" % [@requestor[3], @requestor[1], msg]) : super(msg)
  237. end
  238. def print_status(msg)
  239. @requestor ? super("%s:%p - DNS - %s" % [@requestor[3], @requestor[1], msg]) : super(msg)
  240. end
  241. end