July 17, 2026
I Funded a Stranger’s Bank Card With My Own Money; and That’s Exactly the Problem
A hands-on walkthrough of Broken Object Level Authorization (BOLA) on VulnBank

By Muhammad Usman Faridi
5 min read
A hands-on walkthrough of Broken Object Level Authorization (BOLA) on VulnBank
There's a moment in every appsec learner's journey where a vulnerability stops being a bullet point on the OWASP API Top 10 and starts being something you actually did. For me, that moment was watching one user's card get funded by another user's session — no exploit chain, no payload, just a number in a URL that should never have worked.
This is the walkthrough of how I found (and rigorously confirmed) a Broken Object Level Authorization vulnerability in VulnBank, an intentionally vulnerable banking application built for security training.
What Is BOLA, Actually?
Broken Object Level Authorization sits at #1 on the OWASP API Security Top 10 (API 1: 2023), and for good reason — it's common, trivial to exploit, and quietly devastating.
The core idea in one sentence: the server correctly checks who you are, but never checks what you're allowed to touch.
Any API endpoint that takes an object identifier — a card_id, account_number, order_id — needs to answer two separate questions:
- Authentication: is this a valid, logged-in user?
- Authorization: should _this _specific user be allowed to access this specific object?
BOLA is what happens when an API nails question one and skips question two entirely. Usually it's one missing clause in a query.
The vulnerable version:
SELECT * FROM cards WHERE id = :card_idSELECT * FROM cards WHERE id = :card_idThe fixed version:
SELECT * FROM cards WHERE id = :card_id AND user_id = :authenticated_user_idSELECT * FROM cards WHERE id = :card_id AND user_id = :authenticated_user_idThat's genuinely the whole difference and because it never breaks anything during normal use (your own IDs always belong to you), it hides in plain sight until someone deliberately tries an ID that isn't theirs.
So that's exactly what I did — with two accounts, on purpose, so I could prove it beyond doubt rather than just suspect it.
Setting the Stage: Two Users, Two Cards
Testing BOLA against yourself proves nothing — you always have legitimate access to your own resources. So I set up two separate accounts to simulate a real attacker/victim scenario.
User 1 — Jhonny
- I created a virtual card with a $2,500 limit.
- I then funded it with $80 from the main balance.
With the funding request captured in Burp Suite, I sent it to Repeater for closer inspection, this is the request whose card_id parameter would become the centerpiece of the whole test.
User 2 — Alex
Same setup:
- A fresh virtual card of $2,500 limit.
- Funded with $100.
- And the same treatment — captured the request and sent it to Repeater.
Two accounts, two cards, two independent funding requests sitting side by side. Now the real test could begin.
Step One: Does the App Even Check Who You Are?
Before hunting for authorization flaws, I checked the basics. I stripped the session cookie and Authorization header from a funding request entirely and sent it.
401 Unauthorized: "Token is missing."
Good! The server clearly enforces authentication. That ruled out the simplest failure mode and pointed straight at the real question: does it check which authenticated user is making the request, or just that one is?
Step Two: The Swap
This is the actual test, and it's almost anticlimactic in how simple it is.
I took Jhonny's valid token and used it to fund Alex's card:
200 OK. The card funded successfully with Jhonny's session authorizing a change to Alex's card.
Then I reversed it, Alex's token, aimed at Jhonny's card:
200 OK again. Same result, opposite direction.
Neither request was rejected. The server verified that a valid token was present but never verified that the token holder actually owned the card they were funding. It simply processed whatever card_id showed up in the URL, against whichever authenticated user happened to be making the call.
Why This Isn't "Just a Feature"
The first pushback any BOLA finding gets is: "Couldn't this just be an intentional transfer feature?"
It's a fair question, and worth addressing directly.
The answer is NO, for a few concrete reasons:
- There was no recipient search, no username/email lookup, no way to intentionally select another user through the interface.
- Neither Jhonny nor Alex received any notification or gave any consent.
- The card IDs used were never exposed to either user by the application itself, they were reached only by directly editing a request in Burp, not by anything the UI ever presented as selectable.
- Both requests used each user's own main balance and own token throughout, nothing about the flow resembled a designed transfer mechanism.
A designed feature has guardrails: consent steps, recipient verification, fraud checks. This had none of that, because it was never meant to be reachable in the first place.
The Fix
The remediation here is almost anticlimactic given the impact. This isn't a hard problem to solve, just an easy one to forget:
- Every object-level query needs an explicit ownership check tied to the authenticated session:
WHERE card_id = ? AND user_id = ? - Better yet, enforce this centrally, an authorization layer or middleware that every object-fetching endpoint routes through, rather than relying on each developer to remember it per-endpoint
- Make cross-account testing a standard part of QA and code review: test with two different authenticated accounts against each other's objects, not just each account against its own.
The Takeaway
BOLA doesn't require exotic tooling or deep exploit development. It requires one thing: noticing that an ID in a URL is just a number, and asking whether the server actually checked if you were allowed to use it.
In this case, it hadn't. Two independent accounts, each fully authenticated, could reach into each other's resources without so much as a warning.
Authentication tells a server who is asking. Authorization is the separate and often forgotten question of what they're allowed to ask for?. Every API needs both, and it's worth checking, endpoint by endpoint, that yours actually has them.
This testing was performed against VulnBank, an intentionally vulnerable application built for security education and training purposes.