How We Discovered a Critical Vulnerability Chain in the Dutch OV-Chipkaart System
— and Why It Could Have Cost Hundreds of Thousands of Euros

ReactiveZero delivers advanced penetration testing, red teaming, threat hunting, and digital forensics — led by OSCP, CEH, and CHFI-certified experts. We help organizations neutralize threats across IT, OT, IoT, and cloud environments.
⚠️ Disclaimer
All vulnerabilities discussed in this post have been responsibly disclosed and remediated by the OV-chipkaart team. The attack chain outlined here is no longer valid and cannot be exploited. This blog post is for educational purposes and to promote better security practices in public systems.
🔍 Introduction
In October 2022, we conducted a deep-dive investigation into the Dutch public transportation card system — the OV-chipkaart — and uncovered a chain of vulnerabilities that, when combined, could have led to massive financial abuse of the refund system for anonymous cards. By exploiting missing rate limits, weak client-side validations, and misconfigured email services, we demonstrated how an attacker could systematically withdraw up to €50 per card, potentially resulting in hundreds of thousands of euros in unauthorized refunds.
This post outlines the vulnerabilities we discovered, how they were chained together, and the potential real-world consequences had they not been addressed.
🧩 The Vulnerability Chain
1. Client-Side OV-chipkaart Number Validation

The OV-chipkaart site performed chipkaart number validation entirely client-side, using JavaScript. This allowed me to script requests to check for valid OV card numbers without server-side enforcement or throttling.
Using a custom script (chipkaart-checker.py), we was able to verify active OV-chipkaart numbers at scale through:

This essentially allowed enumeration of all active cards within a known numeric range.
2. No CAPTCHA or Request Limiting on Expiration Checks
The expiration date of each card could be guessed using brute force techniques on multiple endpoints, including:

There were no CAPTCHAs, no IP rate limiting, and no backend validation to stop automated guessing. Combining this with valid card numbers enabled precise profiling of expired cards eligible for refunds.
3. Harvestable CSRF Tokens
While some endpoints used CSRF tokens, they were embedded in static HTML as hidden input fields, easily retrievable via script on every page load. This made token reuse and automated form submission trivial.
4. Identifying Anonymous vs Personal OV Cards

Interestingly, the site’s response behavior was different based on card type. Anonymous cards prompted suggestions to upgrade to personal cards, while personal ones simply displayed the balance.

This allowed an attacker to identify anonymous cards, which are eligible for remote refund without physically owning them.
5. Misconfigured Email Service
The refund reminder service at:
https://www.ov-chipkaart.nl/kopie-van-saldo-terugvragen/ov-chipkaart-e-mailservice-3.htm
was completely open, accepting all card numbers (anonymous or personal), without CAPTCHA or CSRF protection. It could be abused to validate card status, and even spam or flood user inboxes.
💣 Impact: Financial Abuse at Scale
According to OV’s own documentation, anonymous cards that expired less than one year ago with less than €50 in credit are eligible for online refunds without requiring the physical card.
By combining:
Valid card enumeration
Expiration date brute forcing
Anonymous card detection
An attacker could script mass refund requests for thousands of expired anonymous cards. Assuming just 10,000 cards with unused balances below €50, the potential loss could exceed:
€500,000
This wasn’t just theoretical. we provided working proof-of-concept scripts (chipkaart-checker.py, expiration-checker.py) and responsibly disclosed them.
✅ Resolution
I'm happy to confirm that after coordinated disclosure with the responsible parties, all identified vulnerabilities have been patched, and proper mitigations including CAPTCHA, backend validation, and request limits have been deployed.
🧠 Key Takeaways
Never trust client-side validation — always validate sensitive data on the server.
Rate limits and CAPTCHA are not optional — especially on endpoints handling sensitive queries or financial actions.
Minimal information disclosure can cascade — what looks like a minor UI difference can lead to a serious security chain.
Security through obscurity fails — publicly available ranges, lack of CSRF protections, and weak authentication create systemic risk.
🔐 Responsible Disclosure
All findings were submitted to the OV-chipkaart team and handled professionally. I'm grateful they took the matter seriously and worked promptly to resolve the issues.
If you're a security researcher or developer working on public-facing infrastructure, let this be a reminder: it’s rarely one vulnerability — it's the chain that breaks you.

