Overrun
Overrun's change-order endpoint authorises by parent project, but loads the child object by global id alone. Players who change one digit in the URL read change orders that belong to other PMs.
The Scenario
Overrun's contract change-order module shipped two sprints ago. The endpoint that opens a single change order is mounted under a project route — and the middleware on that route only checks that the calling PM owns the project. The handler then loads the change order by its global record id without re-checking that the change order actually belongs to that project. The bug has been live for weeks. The internal notes on a confidential change order at Harbour Point are about to be read by the wrong people.
Challenge Intel
Synopsis
Sub-resource IDOR — the change-order detail endpoint authorises the URL's project_id but never verifies that the requested change_order actually belongs to that project.
What It Is
Each project has a Change Orders page listing the COs filed against it. Each CO has a sequential global record id (1, 2, 3, …). The route /projects/:project_id/change-orders/:co_id checks that the caller owns :project_id, but loads the CO by :co_id alone — so any authenticated PM can read any CO by changing the :co_id segment. New self-signup accounts get a starter project whose COs occupy the next free ids (e.g. 7, 8, 9), making the gap to lower ids visible. The flag is embedded in the internal_notes field of CO-0004 (Harbour Point Mixed Use, project_id=2).
Who It's For
Players who want to practice the most common real-world IDOR pattern: parent-child URL routes that authorise the parent but never re-verify the child.
Skills You'll Practice
- Recognising sub-resource routes vulnerable to ID enumeration
- Reading sequential numeric ids in URLs and inferring gaps
- Manually walking a small id space by hand to discover scoped data
- Distinguishing a parent-ownership check from a child-ownership check
What You'll Gain
- Why authorising the URL prefix is not the same as authorising the resource
- Why sub-resources need their own scope check, not just inheritance from the parent
- Defense: every layer of the path that names a resource needs its own ownership predicate