[HackerNotes Ep. 148] MCP Hacking Guide

In this episode: Justin gives us a crash course on Model Context Protocol.

Hacker TL;DR

  • MCP is a plain JSON-RPC control channel, easy to read/fake and typically carried over stdio, streamable HTTP, or HTTP+SSE. Local stdio agents are easy to spoof for fast PoCs.

  • The initialize response includes an instructions string that may be merged into the client’s system prompt, prime prompt-injection/context-poisoning surface.

  • Clients can advertise roots (filesystem roots) during init and respond to roots/list, quick way to enumerate exposed local paths.

  • resources/list + resources/read(uri) (including templatized URIs) is basically arbitrary file-read/path-traversal as a service; the spec even lists git://, submodule/symlink/hooks add serious RCE primitives.

  • Tool identity is messy (name, title, annotation.title + fuzzy namespaces). The UI label can differ from the invoked identifier, enabling shadowing/spoofing, collisions, and weird type/DOS behavior.

With ThreatLocker® Network Control, you regain complete control over network access, no matter where your employees connect from. While traditional firewalls rely on static ACLs that require manual reconfiguration for every new location, our Zero Trust network protection solution provides your organization with:

  • ✓ Direct connections between clients and servers

  • ✓ A centrally managed firewall for every endpoint and server

  • ✓ Dynamic access control that adapts automatically

  • ✓ Granular policies by IP, user, device, or keyword

  • ✓ Automatic port closure and invisibility to unauthorized devices

Start controlling your network—wherever your team works.

HACKERNOTES;

Model Context Protocol

rez0 and gr3pme are both traveling/at a LHE right now, so this week we got a Justin solo episode!

He came in prepared, his notes were so solid that I decided to build the HackerNotes straight from them, just adding some context and explanations where it felt needed.

What you’ll read below is basically Justin’s raw notes + my own attempt to make sense of them, which means it might sound a little more chaotic than usual. Think of it as me learning this stuff in real time while trying to decode Justin’s brain. hahahah

If you really want to get the most out of this one, I recommend listening to the episode while reading. The notes alone can be a bit cryptic, and hearing Justin explain things fills in a lot of blanks. It’s a short episode, so pairing them makes for a great deep dive into MCPs.

Anyway, hope you enjoy it, this was a fun one to write!

1- Architecture

  • Both SHOULD be MCP Servers of sorts - listening for events, etc

  • Communication over JSONRPC

  • Done over: stdio, Streamable HTTP, older HTTP+SSE transport

MCP is a simple JSON-RPC control channel between two roles: the client and the server that supplies context, files, tools, prompts. Because messages are plain JSON, it’s easy to read, craft, or fake traffic, so we can quickly prototype a malicious server/client.

The protocol commonly runs over three low-friction transports: stdio, streamable HTTP, and HTTP+SSE. Stdio is dangerous because it’s local and invisible to network monitors, a malicious process or wrapper can spoof messages easily. Streamed HTTP/SSE add network attack surfaces but they still allow servers to push commands or data that clients may auto-apply. In short: human-readable messages + easy transports = fast PoCs and a big attack surface if clients accept server inputs without strict validation or human confirmation.

2- Authentication

  • “MCP clients and authorization servers SHOULD support the OAuth 2.0 Dynamic Client Registration Protocol”

In MCP, authentication is usually handled through OAuth 2.0, and the spec recommends support for Dynamic Client Registration, a feature that lets clients register themselves with the authorization server on the fly. But if not locked down, a malicious client can register redirect URIs pointing to attacker controlled domains or to local endpoinds.

3- Initialization

  • MCP Client & MCP Server (Sometimes MCP Proxy)

  • Initialization handshake

  • Client → Intialize → Server

    • Protocol version

    • Capabilities

      • Roots, sampling, elicitation

        • listChanged - does it support notifications for object changes?

    • Client Info

  • Server -> Initialization response -> Client

    • protocolVersion

    • Capabilities

      • Logging

      • Prompts

      • Resources

      • Tools

    • ServerInfo

    • Instructions ( MAY be integrated into system prompt?)

Flow: Client → initialize (protocolVersion, capabilities, clientInfo) → Server → initialize response (protocolVersion, capabilities, serverInfo + instructions). It’s very interesting that instructions is a freeform string the server returns and may be merged into the client’s system prompt, that makes it a high-value prompt-injection/tool-context poisoning surface.

Capabilities include roots, sampling, elicitation, the three core client capabilities Justin called out, and the server-side ones like tools, resources, logging, prompts, and utilities. Each capability can carry subscribe/listChanged flags to indicate notification support. Note: some implementations advertise capabilities but only do a minimal request/response pattern, so test for real behaviour rather than trusting the declared list.

4- MCP Client Objects

  • Roots

    • The Model Context Protocol (MCP) provides a standardized way for clients to expose filesystem “roots” to servers'

      • Clients that support roots MUST declare the roots capability during initialization:

    • Server → roots/list → Client

    • All roots must have file://

Clients can expose filesystem “roots.” If declared in initialization, server can call roots/list and learn exposed roots or path leaks. This is an easy local-file enumeration primitive.

  • Sampling

    • Allow the MCP Server to query the LLM and create messages for the user

    • Ideas: Malicious MCP server, Resource Exhaustion, invisible prompt smuggling

Allows the MCP server to ask the LLM to generate messages. If the client auto-processes sampling output (or has poor UI/approval), a malicious server can inject prompts, call tools, trigger resource fetches, or cause resource exhaustion by chained actions. The spec warns UX should include a human-in-the-loop but implementations may skip/auto-accept. Watch for invisible/unicode prompt smuggling.

  • Elicitation

    • Allows the MCP Server to request information from the user

    • There will certainly be a no-user-interaction Elicitation request that results in PII leak to malicious MCP server for user

    • Available actions for that are accept, decline, cancel

Server can request data from user/client. Actions: accept, decline, cancel. Dangerous when clients auto-respond from stored environment/config, this is a funny direct PII/secret exfil vector. Spec explicitly forbids sensitive requests… Yeah right.

5- MCP Server

  • Tools

    • Expose tool definitions to the user and give ability to call

    • Done via

      • Client → tool/list → server

      • Client → tool/call → server

    • Tool Object:

      • Name, title, description, inputSchema, outputSchema, annotation

      • Annotations:

        • Title

      • Tools MAY return structured or unstructured content

        • Unstructured: text, image (base64 data), audio (base64 data), resource links (uri&mimetype)

          • Each of these has annotations as well

    • Footguns:

      • Tool names are very ambiguous

        • Namespaces?

        • Name? Title? annotation → title?

        • Tool collisions → DOS?

    • Attacking the client:

      • Tool hijacking/shadowing - using different name fields or description

      • Tool

    • Is confirmation implemented as a tool? Then not mando

    • For Proxies - can you causes a DOS because of strong typing on various types of fields for LLM providers?

  • Resources

    • The Model Context Protocol (MCP) provides a standardized way for servers to expose resources to clients.

      • Awesome when attacking servers

    • Client → resources/list → Server

    • Client → resources/read with URI as param (file:///) → Server

      • Arbitrary file read as a service

    • Also, templatized resources under resources/templates/list which allow for URI Templates (providing “variables” to the URI path - PATH TRAVERSAL AS A SERVICEEE)

    • File schemes:

      • https:// (SHOULD - SSRF AS A SERVICE - not exactly - but nice for client-side attacks)

      • file://

      • git:// (RCE via submodule recursion as a SERVICE)

Server advertises tools via tool/list with fields name, title, description, inputSchema, outputSchema, and annotation. Tools may return structured (JSON) or unstructured (text, base64 image/audio, resource links with URI+mimetype) outputs.

Tool identity is messy and exploitable: servers expose multiple label fields (name, title, annotation.title) and namespaces are fuzzy, so the string the UI shows can differ from the string the client actually invokes. That’s an attack surface, spoof a friendly title while the invoked name performs something else, create near-duplicate names to force resolution races, or use confusable encodings/aliases to break routing and type checks.

Short checklist: force the client to tool/list and compare displayed vs invoked identifiers; send tools with mismatched title/name and observe which identifier the client calls; try collision/alias patterns and unicode confusables to see how resolution fails; and check whether confirmations are implemented as callable tools (if consent is a tool, the server can try to automate approvals). These primitives unlock reliable shadowing, spoofing, and surprising client behaviour.

Resources are exposed via resources/list / resources/read(uri) and support schemes like file://, https://, and git://. Templatized URIs let servers synthesize paths, almost like arbitrary read / path-traversal as a service. git:// is particularly risky (submodule recursion, symlink/write tricks, repo hooks → potential RCE).

  • Leaving off Utilities and Prompts as an exercise to the listener

We’ve reached the end of this week’s HackerNotes and we know that there might be some mistakes or info missing here and there. Honestly this week’s episode was a challenge for both Justin and me.

So, please, leave us some feedback over on Discord! The #pod-talk and #corrections channels are there for everyone to talk about things they’ve seen on the pod, give feedback and tell us about things we missed.

Thank you, I hope you guys enjoyed it and learned something new.

See y’all next week!