├── .gitattributes ├── .gitignore ├── FlaskRedirectorProtector.py ├── LICENSE ├── README.md ├── blacklist.txt └── files └── payloads.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | db.sqlite3 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # Jupyter Notebook 76 | .ipynb_checkpoints 77 | 78 | # IPython 79 | profile_default/ 80 | ipython_config.py 81 | 82 | # pyenv 83 | .python-version 84 | 85 | # pipenv 86 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 87 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 88 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 89 | # install all needed dependencies. 90 | #Pipfile.lock 91 | 92 | # celery beat schedule file 93 | celerybeat-schedule 94 | 95 | # SageMath parsed files 96 | *.sage.py 97 | 98 | # Environments 99 | .env 100 | .venv 101 | env/ 102 | venv/ 103 | ENV/ 104 | env.bak/ 105 | venv.bak/ 106 | 107 | # Spyder project settings 108 | .spyderproject 109 | .spyproject 110 | 111 | # Rope project settings 112 | .ropeproject 113 | 114 | # mkdocs documentation 115 | /site 116 | 117 | # mypy 118 | .mypy_cache/ 119 | .dmypy.json 120 | dmypy.json 121 | 122 | # Pyre type checker 123 | .pyre/ 124 | -------------------------------------------------------------------------------- /FlaskRedirectorProtector.py: -------------------------------------------------------------------------------- 1 | 2 | from flask import Flask, render_template, redirect, make_response, jsonify, request, send_from_directory 3 | import flask 4 | import requests 5 | import argparse 6 | import os 7 | 8 | app = Flask(__name__) 9 | 10 | method_requests_mapping = { 11 | 'GET': requests.get, 12 | 'HEAD': requests.head, 13 | 'POST': requests.post, 14 | 'PUT': requests.put, 15 | 'DELETE': requests.delete, 16 | 'PATCH': requests.patch, 17 | 'OPTIONS': requests.options, 18 | } 19 | 20 | @app.before_request 21 | def log_request(): 22 | # Logging 23 | ip = flask.request.remote_addr 24 | request = flask.request.base_url 25 | ua = flask.request.headers.get('User-Agent') 26 | with open("access.log", "a") as log: 27 | log.write("ip: " + ip + "," + " User-Agent: " + ua + "," + " Request: " + request) 28 | log.write('\n') 29 | 30 | 31 | @app.route("/files/") 32 | def fileserve(filename): 33 | ua = flask.request.headers.get('User-Agent') 34 | if useragent_whitelist is not None: 35 | if useragent_whitelist in ua: 36 | if os.path.isfile(directory + "/" + filename): 37 | return send_from_directory(directory=directory, filename=filename) 38 | else: 39 | return redirect(redirect_url, code=302) 40 | elif useragent_blacklist is True: 41 | with open('blacklist.txt','r') as f: 42 | for x in f: 43 | x = x.rstrip() 44 | if ua == x: 45 | return redirect(redirect_url, code=302) 46 | else: 47 | if os.path.isfile(directory + "/" + filename): 48 | return send_from_directory(directory=directory, filename=filename) 49 | else: 50 | return redirect(redirect_url, code=302) 51 | else: 52 | return redirect(redirect_url, code=302) 53 | 54 | @app.route('/', defaults={'path': ''}) 55 | @app.route('/', methods=method_requests_mapping.keys()) 56 | def teamserver(path): 57 | 58 | if headerkey in flask.request.headers.get(header): 59 | url = teamserver + path 60 | requests_function = method_requests_mapping[flask.request.method] 61 | ip = flask.request.remote_addr 62 | request = flask.request.base_url 63 | ua = flask.request.headers.get('User-Agent') 64 | with open("access.log", "a") as log: 65 | log.write("Secret Cookie Requested from: " + ip) 66 | log.write('\n') 67 | request = requests_function(url, stream=True, params=flask.request.args) 68 | response = flask.Response(flask.stream_with_context(request.iter_content()), 69 | content_type=request.headers['content-type'], 70 | status=request.status_code) 71 | response.headers['Access-Control-Allow-Origin'] = '*' 72 | return response 73 | else: 74 | return redirect(redirect_url, code=302) 75 | 76 | 77 | 78 | if __name__ == '__main__': 79 | parser = argparse.ArgumentParser(description="Protect your redirector/teamserver") 80 | parser.add_argument('--teamserver', help="Teamserver to forward to. Example: http://ip:port/", required=False) 81 | parser.add_argument('--host', help="Host to listen on.", default="0.0.0.0", required=True) 82 | parser.add_argument('--port', help="Port to listen on.", required=True) 83 | parser.add_argument('--header', help="Header to allow traffic. Example: 'X-Aspnet-Version'", required=False) 84 | parser.add_argument('--headerkey', help="Header key. Example: '1.5'", required=False) 85 | parser.add_argument('--redirect_url', help="Redirect URL.", required=True) 86 | parser.add_argument('--serve_payloads', action='store_true', help="Switch: Serve Payloads from files folder", required=False) 87 | parser.add_argument('--directory', help="Custom payload directory", default="files", required=False) 88 | parser.add_argument('--useragent_whitelist', help="Custom Useragent to allow.",required=False) 89 | parser.add_argument('--useragent_blacklist', action='store_true', help="Custom Useragent file to disallow.", required=False) 90 | args = parser.parse_args() 91 | port = int(args.port) 92 | useragent_whitelist = args.useragent_whitelist 93 | useragent_blacklist = args.useragent_blacklist 94 | serve_payloads = args.serve_payloads 95 | directory = args.directory 96 | redirect_url = args.redirect_url 97 | teamserver = args.teamserver 98 | headerkey = args.headerkey 99 | header = args.header 100 | host = args.host 101 | app.debug = False 102 | app.run(host=host,port=port) 103 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, rvrsh3ll 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlaskRedirectorProtector 2 | Protect your servers with a secret header 3 | 4 | I previously blogged about hardening your azure domain front with a secret header. 5 | The same may be accomplished for standard redirectors as well. 6 | Reference: https://medium.com/@rvrsh3ll/hardening-your-azure-domain-front-7423b5ab4f64 7 | 8 | ## Examples: 9 | 10 | ### Allow traffic using a secret header 11 | 12 | python3 FlaskRedirectorProtector.py --port 80 --redirect_url https://www.google.com --teamserver "http://host:80/" --headerkey "1.5" --header "X-Aspnet-Version" 13 | 14 | curl -H "X-Aspnet-Version: 1.5" http://redirector/testing 15 | 16 | ### Allow traffic to a payload based on useragent 17 | python3 FlaskRedirectorProtector.py --port 8080 --serve_payloads --directory "files" --redirect_url https://www.google.com --useragent_whitelist "Chrome" --host "0.0.0.0" 18 | 19 | ### Disallow traffic to a payload based on useragent in blacklist.txt 20 | python3 FlaskRedirectorProtector.py --port 8080 --serve_payloads --directory "files" --redirect_url https://www.google.com --useragent_blacklist --host "0.0.0.0" 21 | 22 | -------------------------------------------------------------------------------- /blacklist.txt: -------------------------------------------------------------------------------- 1 | Googlebot/2.1 (+http://www.googlebot.com/bot.html) 2 | Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm) 3 | Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp) 4 | curl/7.67.0 5 | Wget/1.20.3 (linux-gnu) -------------------------------------------------------------------------------- /files/payloads.txt: -------------------------------------------------------------------------------- 1 | Add payloads to this folder and remove payloads.txt 2 | --------------------------------------------------------------------------------