Skip to content

Latest commit

 

History

History
196 lines (141 loc) · 7.38 KB

File metadata and controls

196 lines (141 loc) · 7.38 KB

Flask

A lightweight WSGI web framework for quickly building HTTP servers, APIs, and web apps — invaluable for standing up callback listeners, test targets, and tooling dashboards.

Overview

Flask is a "micro" framework: minimal core, add what you need. A few lines give you a routable HTTP server with request parsing, JSON responses, and templating. In offensive and defensive work it's the fastest way to spin up an exfil/callback listener, a deliberately vulnerable app for practice, a webhook receiver, or a small dashboard over your tooling — all on infrastructure you control.

Installation

pip install Flask

Basic Usage

from flask import Flask, jsonify, request

app = Flask(__name__)


@app.route("/health")
def health():
    return jsonify(status="ok")


@app.route("/callback", methods=["POST"])
def callback():
    app.logger.info("callback from %s", request.remote_addr)
    return jsonify(received=True), 200


if __name__ == "__main__":
    # Bind to loopback only; debug must stay off
    app.run(host="127.0.0.1", port=5000, debug=False)

In security work Flask is most often used for small listeners: a callback receiver, a mock API, or a deliberately vulnerable target for lab exercises.

Important APIs

API Purpose
Flask(__name__) The application object
@app.route(rule, methods=[...]) Register a handler
request.args / .form / .json Query, form, and JSON input
request.headers, .remote_addr, .method Request metadata
jsonify(**kwargs) JSON response with the right content type
make_response(body, status, headers) Full control over the response
abort(code) Raise an HTTP error
render_template(name, **ctx) Jinja2 template with autoescaping on
app.logger Application logger
@app.errorhandler(code) Custom error responses
app.run(host, port, debug) Development server only

Warning

app.run() is a development server. Production requires a WSGI server such as Gunicorn or uWSGI behind a reverse proxy.

Example

A minimal callback/collector listener (for authorized labs you control):

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/beacon", methods=["POST"])
def beacon():
    data = request.get_json(silent=True) or {}
    print(f"[+] check-in from {request.remote_addr}: {data}")
    return jsonify(status="ok")

if __name__ == "__main__":
    app.run(host="127.0.0.1", port=8000)

Output

 * Running on http://127.0.0.1:8000
[+] check-in from 127.0.0.1: {'host': 'lab-agent-01', 'user': 'tester'}
127.0.0.1 - - [18/Jul/2026 10:15:02] "POST /beacon HTTP/1.1" 200 -

A small JSON API endpoint:

from flask import Flask, jsonify, request

app = Flask(__name__)
HOSTS = {"192.168.56.10": ["ssh", "http"], "192.168.56.11": ["smb"]}

@app.get("/api/hosts")
def hosts():
    return jsonify(HOSTS)

@app.get("/api/host/<ip>")
def host(ip):
    return jsonify(services=HOSTS.get(ip, [])), (200 if ip in HOSTS else 404)

if __name__ == "__main__":
    app.run(port=8000)

Output

$ curl 127.0.0.1:8000/api/host/192.168.56.10
{"services":["ssh","http"]}

Render a simple results dashboard with a template:

from flask import Flask, render_template_string

app = Flask(__name__)
TEMPLATE = """
<h1>Scan Results</h1>
<ul>{% for h, up in results.items() %}
  <li>{{ h }} — {{ 'UP' if up else 'down' }}</li>
{% endfor %}</ul>
"""

@app.get("/")
def dashboard():
    results = {"192.168.56.10": True, "192.168.56.11": False}
    return render_template_string(TEMPLATE, results=results)

if __name__ == "__main__":
    app.run(port=8000)

Output

Scan Results
 - 192.168.56.10 — UP
 - 192.168.56.11 — down

Security Use Cases

  • Callback / collector listeners — receive check-ins, webhooks, or exfil in authorized lab and red-team infrastructure you own.
  • Vulnerable test targets — build deliberately weak endpoints to practice and demonstrate SQLi, XSS, SSRF, and IDOR safely.
  • Tooling dashboards & APIs — expose scan results and controls to a team over a small internal web app.
  • Webhook receivers — accept callbacks from CI, monitoring, or third-party services during automation.
  • Blue-team honeytokens — serve tracking endpoints/beacon URLs to detect credential or link misuse.

Warning

Flask's built-in server is for development only. Never bind a listener to 0.0.0.0 on an untrusted network without authentication, and use gunicorn/uwsgi behind a proxy for anything long-lived.

Common Mistakes

  • Leaving debug=True — this exposes the Werkzeug interactive debugger, which allows arbitrary code execution by anyone who can reach it.
  • Binding to 0.0.0.0 on a shared or untrusted network, publishing the listener to everyone.
  • Using app.run() in production instead of a real WSGI server.
  • Building HTML with f-strings instead of render_template(), losing Jinja2's autoescaping and creating XSS.
  • Using render_template_string() with user input — server-side template injection leading to code execution.
  • Hardcoding SECRET_KEY in source, which lets anyone forge session cookies.
  • Trusting request.remote_addr behind a proxy — it is the proxy's address unless ProxyFix is configured.
  • Returning raw exception text to the client, disclosing internals.

Security Considerations

[!warning] Authorized use only Run listeners only on hosts and networks you control. Bind to 127.0.0.1 unless you deliberately intend to expose the service.

  • debug=True is remote code execution. The Werkzeug debugger offers an interactive Python console on unhandled exceptions. Never enable it on any reachable interface — this is the single most damaging Flask misconfiguration.
  • Intentionally vulnerable lab apps must be isolated. If you build one for exercises, keep it on a host-only network with no route to production or the internet.
  • Treat every request field as untrusted. Query parameters, form data, JSON bodies, headers, and cookies are all attacker-controlled.
  • Keep autoescaping on. Use render_template(); never concatenate user input into markup, and never pass it to render_template_string().
  • Load SECRET_KEY from the environment and generate it with [[Secrets-Module|secrets]].
  • A callback listener receives attacker-influenced data during an engagement — log it, but never execute, deserialise, or interpolate it into a command.
  • Add security headers and TLS at the reverse proxy for anything beyond a lab.

Best Practices

  • Keep debug=True off outside a private lab — the debugger allows arbitrary code execution.
  • Validate and escape all request input; use render_template autoescaping, never string-concatenate HTML/SQL.
  • Bind to 127.0.0.1 unless you deliberately need external access, and add auth when you do.
  • Use jsonify (not manual str(dict)) so responses have correct content types.
  • Front production deployments with a real WSGI server and TLS.

References

Related Topics

  • [[FastAPI]] — async, type-hinted alternative for API-heavy tooling
  • [[requests]] — the client that talks to your Flask endpoints
  • [[Readme|Python for Security Professionals]] — course home