Skip to main content

Command Palette

Search for a command to run...

How We Discovered a Critical Vulnerability Chain in the Dutch OV-Chipkaart System

— and Why It Could Have Cost Hundreds of Thousands of Euros

Updated
•4 min read•View as Markdown
How We Discovered a Critical Vulnerability Chain in the Dutch OV-Chipkaart System
R

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.