> your AI agent picks dependencies from memory; give it dated facts — try starlog.dev ↗ vet your agent's deps ↗ vibe-coding is fine. vibe-importing isn’t. — try starlog.dev ↗ vibe-importing isn’t fine ↗ your agent has never seen your private packages — try starlog.dev ↗ facts for private packages ↗ a linter for the dependencies your AI agent picks — try starlog.dev ↗ a linter for agent deps ↗

Back to Articles

Otto-Support: A Deliberately Broken MCP Server That Teaches AI Security the Hard Way

[ View on GitHub ]

Otto-Support: A Deliberately Broken MCP Server That Teaches AI Security the Hard Way

Hook

What happens when you give Claude or GPT-4 access to your customer support backend? Otto-support shows you—by letting AI agents hack their way from anonymous user to admin through 19 progressively dangerous tools.

Context

The Model Context Protocol (MCP) is Anthropic's standardized interface for connecting AI assistants to external tools and data sources. As developers rush to give LLMs access to databases, APIs, and internal systems, we're recreating every authentication and authorization mistake from the last 20 years of web development—except now the attacker is an AI that can enumerate endpoints, craft payloads, and escalate privileges at machine speed.

BishopFox's otto-support addresses this emerging threat landscape by doing something counterintuitive: building a deliberately vulnerable MCP server that simulates a customer support system. Instead of preaching security best practices through documentation, it creates a hands-on CTF environment where security researchers can watch AI assistants exploit real MCP implementations. The project demonstrates how prompt injection, credential leakage, and role-based access control failures combine when you plug an LLM into backend infrastructure. It's OWASP Juice Shop for the AI agent era.

Technical Insight

Otto-support implements a 4-tier privilege escalation ladder using the mcp-go library. When the MCP server starts, it exposes only a handful of tools to unauthenticated users. As the AI assistant (or human operator) discovers credentials embedded in responses or error messages, new tools dynamically appear. The progression flows: unauthenticated → user → support → admin, with each tier unlocking increasingly sensitive operations.

The architecture orchestrates multiple mock backend services on localhost ports. A payment gateway (port 8081), customer API (8082), metadata service (8083), and session signer (8084) all coordinate through a shared SQLite database. Each service contains intentional vulnerabilities—SQL injection points, hardcoded credentials, insecure session handling—that mirror real-world security failures. The MCP server acts as the orchestration layer, translating AI assistant tool calls into HTTP requests against these vulnerable backends.

Here's how the role-based tool exposure works in practice:

func (s *Server) getAvailableTools(authLevel AuthLevel) []mcp.Tool {
    tools := []mcp.Tool{
        {
            Name: "search_tickets",
            Description: "Search support tickets by customer email",
            InputSchema: searchTicketSchema,
        },
    }
    
    if authLevel >= USER {
        tools = append(tools, mcp.Tool{
            Name: "view_ticket_details",
            Description: "View full ticket details including internal notes",
            InputSchema: ticketDetailsSchema,
        })
    }
    
    if authLevel >= SUPPORT {
        tools = append(tools, mcp.Tool{
            Name: "update_customer_metadata",
            Description: "Modify customer account metadata",
            InputSchema: metadataSchema,
        })
    }
    
    if authLevel >= ADMIN {
        tools = append(tools, mcp.Tool{
            Name: "execute_sql_query",
            Description: "Direct database access for reporting",
            InputSchema: sqlQuerySchema,
        })
    }
    
    return tools
}

The genius of this design is that AI assistants naturally probe for additional capabilities. When Claude executes search_tickets and receives a response containing a JWT or API key, it will attempt to use that credential in subsequent calls. The MCP protocol's tool discovery mechanism (tools/list) returns different results based on the current authentication context, creating a gamified progression that mirrors real penetration testing.

The project includes an 'offline' mode that's particularly clever for exploit development. Instead of burning API credits with OpenAI or Claude while iterating on attack scripts, you can run deterministic exploits against the vulnerable server:

# Run with Claude
otto-support --backend claude --api-key sk-ant-xxx

# Run with offline mode for scripted testing
otto-support --backend offline --script exploit.json

The offline mode accepts JSON files containing pre-scripted tool invocations, letting security researchers develop reliable exploits without LLM non-determinism. This is critical for CTF environments where you need reproducible solutions.

Validation is handled through a built-in flags system. As the AI assistant (or human attacker) escalates privileges and exfiltrates data, they encounter flags—hashes or secrets that prove successful exploitation. Running otto-support flags shows which objectives you've completed:

$ otto-support flags
[✓] Flag 1: USER_CREDENTIAL_LEAK (user tier access)
[✓] Flag 2: SUPPORT_SESSION_FORGE (support tier access)
[✗] Flag 3: ADMIN_SQL_INJECTION (admin tier access)
[✗] Flag 4: PAYMENT_DATA_EXFIL (sensitive data extraction)

This structured approach transforms an educational security tool into a measurable training exercise. You're not just poking at vulnerabilities randomly—you're following a progression that teaches specific attack patterns relevant to MCP implementations.

Gotcha

The most obvious limitation is right in the project description: this should never, ever touch production infrastructure. Otto-support is vulnerable by design, with hardcoded credentials, SQL injection vectors, and privilege escalation paths deliberately embedded. Running it on a network-accessible port or pointing it at real databases would be catastrophic. It's a security training ground, not a framework.

The second limitation is specificity. Otto-support teaches MCP security through 19 pre-defined tools modeling a customer support system. If you're building a different type of MCP server—say, one for financial transactions or medical records—the specific vulnerabilities won't map directly. You'll learn general principles (authentication token leakage, RBAC failures, injection attacks), but the exploit paths are hardcoded around this particular scenario. It's not a generalized vulnerable MCP framework you can customize with your own business logic. Additionally, the learning curve is steep: you need to understand both the MCP protocol specification and AI assistant behavior patterns. Someone coming from traditional web pentesting will find the AI-driven exploitation model unfamiliar, while ML engineers might struggle with the protocol internals.

Verdict

Use if: You're a security researcher studying AI agent vulnerabilities, a penetration tester adding MCP assessments to your service offerings, or a developer building MCP servers who wants to understand attack patterns before making the same mistakes. It's perfect for training exercises, CTF competitions, or demonstrating to management why 'just give the AI database access' is a terrible idea. Skip if: You need a production MCP server (use mark3labs/mcp-go directly), aren't specifically focused on security research, or want a general-purpose AI tooling framework. This is explicitly a vulnerable-by-design training platform that teaches through controlled exploitation, not a foundation for building secure applications.