Hack Monty

Welcome. This is a honeypot. The server behind it executes whatever Python you POST to /run/ inside pydantic monty, our language-level sandbox.

There is a secret on this machine. Your job is to find it — by escaping the sandbox. If you do, we'll pay you $5,000.

Blog post about this.

Also look at the API docs. (or Redoc)

Grab the hackmonty.py CLI to run code here from your terminal.

View the traces in Pydantic Logfire to see what how your code is executed (and everyone else's).

Monty Python

Bounty rules & how to participate

WARNING

Anyone can join the Pydantic Logfire project to view all requests to hackmonty.com. All http headers (including IP and User-Agent etc.) are collected for requests to /run/ and /run/{snapshot_id}/.

Bounty rules (I strongly suggest you read all the rules before participating!)

Bounty amounts

Most importantly:

What we'll pay the full bounty for:

What we may pay a partial bounty for (amount to be decided at our discretion):

What we will not pay a bounty for, but would still appreciate:

What we will not pay a bounty for and strongly discourage (please don't do this!):

How to participate

The goal is to read one of the secrets on this machine: either the contents of /app/secret.txt or the value of the SECRET environment variable. Both are set in production and not reachable from a well-behaved sandboxed program.

Submitting code

POST Python source to /run/:

curl -X POST https://hackmonty.com/run/ \
  -H 'content-type: application/json' \
  -d '{"code": "print(1 + 1)"}'

The response is a JSON snapshot. Monty pauses whenever sandboxed code needs something from the outside world — a function call, a name lookup, or a future — and returns a snapshot describing the pause. The server resolves OS-related snapshots (datetime.now, date.today, os.environ, os.getenv) internally against a fake environment, so those never reach you; everything else does.

Resuming

POST the resume payload to /run/{snapshot_id}/. The kind in the body must match the snapshot's kind or you'll get a 400. A single program will typically require many resumes before it either completes or surrenders the secret.

See the Swagger docs or Redoc docs for the full request and response schemas.

Request secret

To let us confirm it was really you who found the secret, you may want to include a User header on your requests. The header value should be the SHA-256 hex digest of some unique secret only you know (a random string, passphrase, UUID — anything). Keep the plaintext to yourself until you report the find; we'll check that its SHA-256 matches a User header we recorded from the winning run and that nobody else beat you to it.

USER_HASH=$(printf 'my-secret-passphrase' | shasum -a 256 | awk '{print $1}')
curl -X POST https://hackmonty.com/run/ \
  -H 'content-type: application/json' \
  -H "User: $USER_HASH" \
  -d '{"code": "print(1 + 1)"}'

The CLI at hackmonty.py accepts a --user-secret flag that takes your plaintext secret and sends its SHA-256 as the header on every request:

uv run hackmonty.py --user-secret 'my-secret-passphrase' --code 'print(1 + 1)'

Who we can pay

We'd love contributions from any developer, anywhere — but we can only pay the bounty if you have a bank account in a country approved by GitHub Sponsors, and our bank (Mercury) is able to make a transfer to it. If you find something and we can't legally pay you, we'll still credit you publicly and sort out some swag — but please check the list before you spend a week on this expecting a cheque.

Reporting a find

If you've found a vulnerability that you think is worth paying the bounty for, please submit this form.