Setup Environment to Practice PyFu Examples
PyFu ships with a simple and practical lab environment for practicing Python exploitation techniques. All examples demonstrated throughout PyFu were tested on Ubuntu 24.04 with Python 3.12 to ensure consistency across the learning material.
The lab currently ships 45 containerized apps, all intentionally vulnerable or built to demonstrate a framework behaviour, broken down as 23 Flask, 19 FastAPI, 2 Streamlit, and 1 FastMCP, plus a collection of standalone generic-py-fu scripts that cover the language-level techniques that are not web applications (pickle, shelve, and YAML deserialization, eval/exec, object-graph and sandbox-escape chains, import-system abuse, command-execution sinks, path traversal, introspection, and bytecode).
In the official PyFu GitHub repository every example is organized in a consistent structure alongside a ready-to-use docker-compose.yml file that spins up the full environment.
This setup leverages Traefik as a reverse proxy to route traffic to the appropriate vulnerable containers.
Before running the labs, you’ll need to configure your system’s /etc/hosts file to resolve the pyfu.local domain to your local machine.
This can be done by adding the following entry to /etc/hosts:
127.0.0.1 pyfu.local
With this setup in place, you can clone the PyFu repository and bring up the full lab using the following commands:
git clone https://github.com/mhaskar/PyFuLabs
cd PyFu/PyFuLabs
docker compose up -d --build
You can also build and run a single example by naming its service, for instance:
docker compose up -d --build flask-ssti
Lab Components
Traefik
Acts as the central entry point for all HTTP traffic. Routes incoming requests based on the request path and forwards them to the appropriate Docker containers. Provides a built-in dashboard (http://pyfu.local:9999) for monitoring routes and services.
Flask-Fu Services
Contains Flask applications exposed under a route prefix /flask-fu/<app-name>. Traefik strips the prefix, so each app serves at / internally on port 5000.
Example
http://pyfu.local/flask-fu/hello-world
FastAPI-Fu Services
Contains FastAPI applications exposed under a route prefix /fastapi-fu/<app-name>. Traefik strips the prefix, so each app serves at / internally on port 8000.
Example
http://pyfu.local/fastapi-fu/hello-world
Streamlit-Fu Services
Contains Streamlit applications exposed under a route prefix /streamlit-fu/<app-name> on internal port 8501.
Example
http://pyfu.local/streamlit-fu/hello-world
Streamlit apps keep their path prefix (Traefik does not strip it) because each app sets
--server.baseUrlPathso its WebSocket and static assets resolve correctly behind the proxy.
MCP-Fu Services
Contains FastMCP servers exposed under a route prefix /mcp-fu/<app-name> on internal port 8000. FastMCP belongs in the lab because the Model Context Protocol is increasingly served over an HTTP transport, which makes an MCP server an ordinary network-reachable web target with the same attack surface as any other API, an unauthenticated listener, exposed tools, and reachable resources. Traefik strips the prefix and the server serves the MCP endpoint at /mcp/.
Example
http://pyfu.local/mcp-fu/fastmcp-no-auth/mcp/
Generic-Py-Fu Scripts
Not every example is a web application. Pure language-behaviour demonstrations (pickle, shelve, and YAML deserialization, eval/exec, object-graph and sandbox-escape chains, import-system abuse, command-execution sinks, path traversal, introspection, bytecode, JWT basics) live under generic-py-fu/ as standalone scripts.
Because several of these intentionally execute commands, write .pth files, or perform pickle RCE, the recommended way to run them is the isolated sandbox container (a single image on a pinned CPython, with no network and the third-party deps preinstalled). It uses the tools profile, so it is not started by docker compose up; invoke a script on demand:
docker compose run --rm generic-py-fu python3 object-model/subclasses-walk.py
They also still run directly on the host if you prefer (python3 generic-py-fu/<path>.py). See generic-py-fu/README.md for the full index.
Lab Inventory
Every containerized app maps to a handbook page; the full page-to-artifact matrix lives in PyFuLabs/LAB_PLAN.md.
Flask-Fu (23)
hello-world multiple-routes flask-request
flask-decorators flask-blueprints flask-templates
flask-session flask-jwt flask-path-traversal
flask-debug-pin flask-os-command-injection
flask-business-logic flask-broken-access-control
flask-dev-header-bypass flask-ssti flask-jinja2-ssti
flask-ssti-ai flask-prompt-injection flask-sqli
flask-xxe flask-ssrf flask-requests-ssrf
flask-pandas-rce
FastAPI-Fu (19)
hello-world fastapi-router fastapi-middleware
fastapi-dependency-injection fastapi-di-auth-bypass
fastapi-broken-access-control fastapi-pydantic
fastapi-basic-auth fastapi-jwt fastapi-docs-exposure
fastapi-dynamic-invocation fastapi-business-logic
fastapi-pickle-auth-bypass fastapi-jwt-hardcoded-secret
fastapi-broken-jwt fastapi-jwt-alg-none fastapi-jwt-weak-key
fastapi-jwt-algo-confusion fastapi-jwt-jku-injection
Streamlit-Fu (2)
hello-world streamlit-security
MCP-Fu (1)
fastmcp-no-auth
Generic-Py-Fu (scripts)
command-injection deserialization (pickle, shelve, yaml) dynamic-eval
path-traversal object-model import-abuse sandbox-escape
internals introspection jwt XXE
Universal App Structure
Every containerized example follows the same layout, which is what makes services interchangeable and lets Traefik route them automatically:
<tier>-fu/<app-name>/
├── app.py # the application (intentionally vulnerable for attack labs)
├── client_poc.py # attacker enumeration/exploit client (MCP apps only)
├── Dockerfile # python:3.12-slim base
├── requirements.txt # pip dependencies
├── README.md # the handbook page it maps to + exploit steps
└── templates/ # only when the app renders HTML
Flask apps listen on internal port 5000, FastAPI and FastMCP on 8000, and Streamlit on 8501; Traefik maps all of them under http://pyfu.local. Each app’s README.md records the exact handbook page it implements and ready-to-paste exploitation commands.
Modify a Code Example
This lab design provides complete isolation for every vulnerable service, allowing you to easily modify, test, and redeploy any application without having to worry about:
-
Port conflicts between services.
-
Traefik routing configurations.
-
Network-level access and exposure.
Each vulnerable app is fully encapsulated in its own folder, with its own Dockerfile and isolated container. This design allows for extremely fast iteration cycles when working on exploitation examples, writing new code, or modifying existing vulnerability scenarios.
To modify any of the services “examples”, you simply navigate to the corresponding service directory for example the flask-fu/flask-path-traversal/ folder, make the necessary changes to the source code in app.py file, and then rebuild the container for that specific service.
Once the modifications are complete, you can rebuild and redeploy just that container using:
docker compose up -d --build flask-path-traversal
This rebuilds the image and restarts the container so your changes take effect immediately, without touching any other service.