PyFu Subscribe

Setup Environment to Practice PyFu Examples

PyFu Labs

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.baseUrlPath so 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.