add a new endpoint returning a devious cat

This commit is contained in:
Stefan Kempinger 2026-02-03 12:27:54 +01:00
parent 38e46a3c50
commit e1e805adea
3 changed files with 27 additions and 7 deletions

View file

@ -4,9 +4,8 @@ After=network.target
[Service] [Service]
Type=simple Type=simple
WorkingDirectory=/home/kemp/ins/teaching/workshop-c-and-c-server WorkingDirectory=/root/workshop-c-and-c
# Run the requested command. This uses /usr/bin/env to locate `uv` in PATH. ExecStart=/usr/bin/python3 main.py
ExecStart=/usr/bin/env uv run main.py
# Ensure common system paths are available when using /usr/bin/env # Ensure common system paths are available when using /usr/bin/env
Environment=PATH=/usr/local/bin:/usr/bin:/bin Environment=PATH=/usr/local/bin:/usr/bin:/bin
Environment=PYTHONUNBUFFERED=1 Environment=PYTHONUNBUFFERED=1

BIN
cat.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

29
main.py
View file

@ -1,10 +1,12 @@
from fastapi import FastAPI, Request, Response import base64
from fastapi.responses import HTMLResponse
import threading import threading
from datetime import datetime from datetime import datetime
import base64 from pathlib import Path
from typing import Dict, List
import uvicorn 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)") app = FastAPI(title="Tiny C2 (Workshop)")
@ -16,6 +18,7 @@ _lock = threading.Lock()
# A 1x1 transparent PNG (base64) used as a static beacon image. # A 1x1 transparent PNG (base64) used as a static beacon image.
_1x1_png_b64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C/HgAGgwJ/lK3Q6wAAAABJRU5ErkJggg==" _1x1_png_b64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C/HgAGgwJ/lK3Q6wAAAABJRU5ErkJggg=="
_IMAGE_BYTES = base64.b64decode(_1x1_png_b64) _IMAGE_BYTES = base64.b64decode(_1x1_png_b64)
_CAT_PATH = Path(__file__).resolve().parent / "cat.jpg"
def _record_session(value: str, ip: str) -> None: 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) 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) @app.get("/sessions", response_class=HTMLResponse)
async def sessions_list(): async def sessions_list():
""" """