From e1e805adea82f0efd4e336b69f041b6a0941b51f Mon Sep 17 00:00:00 2001 From: Stefan Kempinger Date: Tue, 3 Feb 2026 12:27:54 +0100 Subject: [PATCH] add a new endpoint returning a devious cat --- c2.service | 5 ++--- cat.jpg | Bin 0 -> 11611 bytes main.py | 29 +++++++++++++++++++++++++---- 3 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 cat.jpg diff --git a/c2.service b/c2.service index 7aace1b..79422ac 100644 --- a/c2.service +++ b/c2.service @@ -4,9 +4,8 @@ After=network.target [Service] Type=simple -WorkingDirectory=/home/kemp/ins/teaching/workshop-c-and-c-server -# Run the requested command. This uses /usr/bin/env to locate `uv` in PATH. -ExecStart=/usr/bin/env uv run main.py +WorkingDirectory=/root/workshop-c-and-c +ExecStart=/usr/bin/python3 main.py # Ensure common system paths are available when using /usr/bin/env Environment=PATH=/usr/local/bin:/usr/bin:/bin Environment=PYTHONUNBUFFERED=1 diff --git a/cat.jpg b/cat.jpg new file mode 100644 index 0000000000000000000000000000000000000000..188fedbb2438d847dcc17054170ad504075e1ea9 GIT binary patch literal 11611 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 Ae*gdg literal 0 HcmV?d00001 diff --git a/main.py b/main.py index 22e3e84..5fc089a 100644 --- a/main.py +++ b/main.py @@ -1,10 +1,12 @@ -from fastapi import FastAPI, Request, Response -from fastapi.responses import HTMLResponse +import base64 import threading from datetime import datetime -import base64 +from pathlib import Path +from typing import Dict, List + import uvicorn -from typing import List, Dict +from fastapi import FastAPI, Request, Response +from fastapi.responses import FileResponse, HTMLResponse app = FastAPI(title="Tiny C2 (Workshop)") @@ -16,6 +18,7 @@ _lock = threading.Lock() # A 1x1 transparent PNG (base64) used as a static beacon image. _1x1_png_b64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C/HgAGgwJ/lK3Q6wAAAABJRU5ErkJggg==" _IMAGE_BYTES = base64.b64decode(_1x1_png_b64) +_CAT_PATH = Path(__file__).resolve().parent / "cat.jpg" def _record_session(value: str, ip: str) -> None: @@ -50,6 +53,24 @@ async def beacon(session: str, request: Request): return Response(content=_IMAGE_BYTES, media_type="image/png", headers=headers) +@app.get("/cat/{session}", response_class=Response) +async def cat_image(session: str, request: Request): + """ + Serves the local cat.jpg file as a static image. + """ + client_ip = request.client.host if request.client else "unknown" + # session is provided by the URL path; FastAPI will have decoded percent-encoding + if session: + _record_session(session, client_ip) + + headers = { + "Cache-Control": "no-cache, no-store, must-revalidate", + "Pragma": "no-cache", + "Expires": "0", + } + return FileResponse(_CAT_PATH, media_type="image/jpeg", headers=headers) + + @app.get("/sessions", response_class=HTMLResponse) async def sessions_list(): """