[ norboten ]
mcp · lab mcp-03

The Token for Someone Else

· about 35 minutes · runs on ubuntu-26.04-claude · unrated

An unrated lab. It runs on your machine with no account and no network, and everything about it — the faults, the checks, the hints and the reference solution — is in the repository. An attempt on it is recorded on your profile and never moves a rating: only rated labs do. Rated and unrated labs.

The briefing

The weekly fleet report, ~/bin/inventory-report, has Claude Code, in ~/inventory-report, ask the inventory MCP server at http://127.0.0.1:8931/mcp for the host list. The server wants a bearer token signed by this machine's identity provider, lab-idp; the job reads its token from ~/.config/inventory/env. The server's settings are in /etc/inventory-mcp/config.json, and sudo inventory-mcp restart makes it read them again.

The report works. An audit found out why: the job's token is a copy of the billing sync's, and the inventory server takes it — along with any other token the provider ever signed, expired or not. One leaked token opens every service that trusts the provider.

This lab runs in a container, as learner with sudo. claude here is the real Claude Code 2.1.270, talking to a scripted model on this machine; it connects to the server and calls it for real. The job's JSON result is ~/inventory-report.json.

What is expected, and graded — the grader mints tokens of its own with lab-idp:

  1. A token issued for another service is refused.
  2. A token past its expiry is refused.
  3. The job runs on a token issued for the inventory server, and gets the host list.

What is graded

This lab runs in a container — Docker or Podman, no VM — so there is no boot to survive: the checks run once, against the state you left.

CheckObjective
01_a_token_for_another_service_is_refusedMake an MCP server accept only tokens issued for it
02_an_expired_token_is_refusedMake an MCP server accept only tokens issued for it
03_the_job_uses_its_own_tokenGive an MCP client a token for the server it talks to

Start it

  1. 2Labs
  2. mcp-03select this one
  3. udownload it
  4. sstart the machine

Reading

Where the lab's hints send you, level by level, as you ask for them (h, then l opens a journal section in the TUI).

Theory for this lab

3 questions on the same topic, in the lab's Theory tab. They never affect the lab's grade. Here they are, to answer in place:

Question 1

An MCP server verifies that bearer tokens are signed by the company's identity provider and not expired, and nothing else. What can an attacker who stole the billing API's token do?

Question 2

What does this print — the audience of a JWT, read without its signing key?

import base64, json
claims = {"iss": "https://idp.lab", "aud": "https://billing.example/api", "sub": "sync"}
part = base64.urlsafe_b64encode(json.dumps(claims).encode()).rstrip(b"=").decode()
token = "eyJhbGciOiJIUzI1NiJ9." + part + ".c2ln"
payload = token.split(".")[1]
print(json.loads(base64.urlsafe_b64decode(payload + "=" * (-len(payload) % 4)))["aud"])
Question 3

A Claude Code job's remote MCP server starts answering 401 to its token. What does the job's model see?