This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry.
While auditing janeczku/calibre-web, an open-source web application for browsing, reading, and downloading eBooks stored in a Calibre database, I identified an Insecure Direct Object Reference (IDOR) vulnerability (CWE-284) rated High Severity (CVSS 8.1).
The issue allowed any authenticated user to edit the titles of bookshelf collections owned by other users across the application.
π Bounty milestone
I'm excited to share that this report earned both disclosure and fix bounties on the Huntr bug bounty platform!
Discovery & disclosure bounty: awarded for uncovering and detailing the initial IDOR vulnerability.
Fix bounty: awarded for authoring and submitting the patch directly upstream to resolve the issue in the codebase.
π΅οΈββοΈ Technical deep dive & discovery
The vulnerability existed within the shelf routing logic inside cps/shelf.py (specifically at line 237).
When a user submitted an edit for a shelf, the edit_shelf route queried the database strictly using the shelf_id passed directly from the URL path:
@shelf.route("/shelf/edit/<int:shelf_id>", methods=["GET", "POST"])
@login_required
def edit_shelf(shelf_id):
shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).first()
return create_edit_shelf(shelf, title=_(u"Edit a shelf"), page="shelfed")
Because the application failed to verify whether the logged-in user actually owned the queried shelf_id before passing it to create_edit_shelf(), authorization checks were bypassed completely.
Proof of Concept (PoC)
- Log in as User A.
- Navigate to your own shelf at
/shelf/edit/2. - Intercept the outbound
POSTrequest and modify the path parameter from/shelf/edit/2to/shelf/edit/1(which belongs to User B). - Submit the request. The server accepts the payload and overwrites User B's shelf title.
π οΈ The fix
To remediate the vulnerability, a proper ownership check needed to be enforced before any shelf modification logic executed.
In Commit `c7b057e`, the routing logic was updated to validate that either:
- The currently authenticated user is the owner of the shelf (
shelf.user_id == current_user.id), OR - The shelf is public/the user holds administrative privileges allowing edits.
# Updated authorization check enforcing ownership validation
if shelf.is_public != 1 and shelf.user_id != current_user.id and not current_user.role_admin():
flash(_(u"Sorry you are not allowed to edit this shelf"), category="error")
return redirect(url_for('web.index'))
If an unauthorized user attempts to manipulate the path parameter, the request is denied, an error notification is flashed, and the user is redirected safely.
π‘ Key takeaways
Never trust path parameters for access control: resource identifiers passed via parameters must always be validated against the active session user's permissions backend-side.
Full-cycle security contributions: uncovering security flaws is impactful, but submitting valid code patches to open-source maintainersβand earning a fix bounty along the wayβmakes the open-source ecosystem far more resilient.
Associated links
Repository: janeczku/calibre-web
Fix commit: Commit c7b057e - merged from Ileana's commit
Huntr bounty report: Bounty #458c313f
Top comments (0)