From Cookie to CVE: Finding an IDOR in a WordPress Quiz Plugin
The Beginning
When I started doing WordPress bug bounty, I had no checklist, no methodology, and no idea what I was doing. I just picked quiz plugins and started clicking.
That’s how I found CVE-2025-10493 — an unauthenticated IDOR in the Chained Quiz plugin. It wasn’t fancy. It didn’t require a PhD in cryptography or years of experience. It just required noticing one suspicious cookie and asking a simple question: “What happens if I change this?”
This isn’t a story about being brilliant. It’s about being curious and a little bit stubborn. If you’re new to security research and feel like everyone else knows something you don’t, this one’s for you.
Why Quiz Plugins?
I come from a penetration testing background, so I’m used to testing web applications hands-on. When I started looking at WordPress plugins, I didn’t try to hunt for some exotic vulnerability class. I wanted something tangible — something I could see, interact with, and break.
Quiz and form plugins felt right. They handle user input, store data, and process submissions. They’re real, functional, and people actually use them. My approach was simple: install it locally, use it like a normal user, watch every request in Burp, and ask “What happens if I do something unexpected?”
Meet the Chained Quiz Plugin
The Chained Quiz plugin lets WordPress site owners create interactive quizzes with conditional logic. Users answer questions, the plugin tracks their progress, and a final result is calculated. Standard stuff. But the interesting part isn’t what it does — it’s how it identifies who’s taking the quiz.
The Cookie That Caught My Attention
While testing the plugin, I kept Burp Suite open and watched every request. This is a habit I picked up early on — if you’re not watching the traffic, you’re testing blind.
During quiz submission, I noticed something in the request to admin-ajax.php:
Cookie: chained_completion_id1=1
That number looked sequential. When I submitted another quiz, it became 2. Then 3. It was clearly tied to a database identifier.
Here’s the full request:
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: localhost
Cookie: chained_completion_id1=1
Content-Type: application/x-www-form-urlencoded
answer=0&question_id=1&quiz_id=1&post_id=117&question_type=radio&points=0&action=chainedquiz_ajax&chainedquiz_action=answer&total_questions=1
At that moment, a very simple thought crossed my mind:
“What if this value belongs to someone else?”
I wasn’t trying to be clever. I just knew that trusting client-controlled identifiers without validation is a classic security mistake. I’d seen it in PortSwigger labs. I’d seen it during pentests. And now I was seeing it in a WordPress plugin.
Testing the Assumption
All quiz submissions went through the same endpoint: /wp-admin/admin-ajax.php
I tested the functionality first as a logged-in subscriber, then without authentication — and it still worked. This raised an important question: “What actually ties a quiz attempt to a specific user or session?”
The answer, it turned out, was just the cookie. There was no session validation. No IP check. No token verification. The server blindly trusted whatever chained_completion_id the client sent.
I started experimenting with the request:
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: localhost
Cookie: chained_completion_id1=1
Content-Type: application/x-www-form-urlencoded
answer=0&question_id=1&quiz_id=1&post_id=117&question_type=radio&points=0&action=chainedquiz_ajax&chainedquiz_action=answer&total_questions=1
- Changed the
answervalue → it was accepted - Changed the
pointsvalue → it was accepted - Changed the
chained_completion_idin the cookie → it was accepted
Since this was a local setup, I could directly check the database. When I modified the cookie to reference another quiz attempt, the stored data was overwritten.
The server wasn’t checking who created that quiz attempt, whether the current user had permission to modify it, or whether the session matched. It just trusted the cookie.
Proof of Concept
Here’s how the attack works:
| Step | Actor | Action | Result |
|---|---|---|---|
| 1 | Victim | Completes quiz | Cookie: chained_completion_id1=2 |
| 2 | Attacker | Starts own quiz | Cookie: chained_completion_id1=1 |
| 3 | Attacker | Intercepts request, changes cookie to =2 | Modifies victim’s quiz data |
| 4 | Server | Processes request without validation | Victim’s quiz results overwritten ❌ |
The attacker can also modify other parameters like answer or points to manipulate the final score or results.
Full technical PoC available on Exploit-DB:
https://www.exploit-db.com/exploits/52464
Impact
This is an unauthenticated Insecure Direct Object Reference (IDOR). An attacker can:
- Overwrite quiz results for any user
- Manipulate scoring and answers
- Invalidate legitimate quiz attempts
- Potentially expose or corrupt quiz data
While this isn’t a critical RCE or SQL injection, it’s a real vulnerability with real impact. Sites using this plugin for certifications, assessments, or user tracking are affected.
💼 Pentest Perspective:
In corporate environments, this IDOR would be flagged as critical. Quiz platforms are often used for compliance training (HIPAA, SOC 2, ISO 27001), and manipulated records can lead to failed audits, forged certifications, and regulatory penalties. According to the 2023 Verizon DBIR, integrity violations are increasingly common in environments where data accuracy is legally required. In pentesting, integrity matters just as much as confidentiality — a “simple” bug can have serious business impact depending on context.
CVE Reference:
https://www.cve.org/CVERecord?id=CVE-2025-10493
What I Learned
This bug wasn’t hidden behind layers of obfuscation or complex logic. It was sitting in plain sight, in a very basic piece of functionality.
Here’s what finding it taught me:
1. Simple bugs are everywhere
You don’t need to reverse-engineer binaries or find zero-days in kernel code. Most vulnerabilities exist in everyday features that developers assumed would “just work.”
2. Pay attention to sequential identifiers
Anytime you see id=1, user_id=123, or completion_id=2, ask yourself: “What happens if I change this?” Sequential IDs are often a red flag for missing access controls.
3. Client-controlled data should never be trusted
Cookies, hidden form fields, URL parameters — if the client can modify it, the server must validate it. Every time.
4. You don’t need to know everything
When I started this research, I didn’t have a methodology. I didn’t have a mental checklist of every OWASP vulnerability. I just observed, tested, and asked questions. That’s enough.
Final Thoughts
If you’re reading this and wondering when you’ll “get good enough” to find bugs — you already are. You just need to start looking.
The truth is, even people who find CVEs regularly don’t know everything. We forget things. We Google stuff. We test the same endpoint five times because we forgot what we were doing. We feel lost. We feel behind.
But we keep going.
This bug wasn’t complex. It didn’t require advanced skills or insider knowledge. It just required paying attention to a cookie that looked a little too simple — and being willing to ask, “What if?”
So go break something. Start small. Test a plugin. Watch the requests. Change a parameter. See what happens.
Then write about it.
Because the next CVE isn’t going to come from someone who knows everything.
It’s going to come from someone who was just curious enough to keep clicking.
Key Takeaways
- Simple bugs have real impact — don’t overlook basic functionality
- Sequential IDs are a red flag — always test for missing access controls
- Client data must be validated — cookies, parameters, headers — all of it
- You don’t need to be an expert — curiosity + persistence = results
- Write about your findings — sharing helps everyone, including you
Found this helpful? Feel like you learned something? Good. Now go find your own bug and tell the world about it.
