I first posted a rough write-up of these vulnerabilities to r/CBSE using a throwaway reddit account, but I figured a proper write-up on my own blog would be a better home for it. The tweet (X post) where this is being discussed can be found here.
These vulnerabilities were initially discovered on 25 February 2026 and were promptly reported to CERT-In.
What is CBSE and On-Screen Marking?
The Central Board of Secondary Education (CBSE) is one of the largest national education boards in India. It operates under the Government of India and runs major examinations like the Class 10 and Class 12 board exams for millions of students every year.
CBSE is affiliated with over 28,000 schools in India and several hundred more abroad, which makes it one of the most influential educational bodies in the country. Every year, millions of answer sheets are evaluated by thousands of teachers and examiners as part of the board exam process.
To streamline all of that, CBSE has started moving to a digital On-Screen Marking (OSM) system for the Class 12 board exams (circular). Instead of checking physical answer sheets, examiners log into an online portal where scanned copies of answer scripts are assigned to them for evaluation.
Because this platform is used by huge numbers of evaluators and handles sensitive academic data, its security really matters. It seems like this platform is developed by Coempt EduTeck Pvt Ltd and this same OnMark platform is used by multiple boards & other institutions.
While poking around, I found several critical vulnerabilities in the OSM portal that could lead to full account takeover of examiner accounts. Anyone exploiting these could also tamper with or disrupt the grading process, which directly threatens the integrity of the exam evaluations.
I reported all of this to CERT-In before publishing this blog.

Finding the Vulnerabilities
Some background on me first. I'm a hobbyist cybersecurity researcher and I just finished my Class 12 exams this year. I've done bug bounty and security work for fun before, so when CBSE rolled out OSM and I noticed the portal link was completely public, my curiosity got the better of me.
I opened the On-Screen Marking portal and started playing around with the HTTP requests and everything else I could see.
The login page asks for three things: a user ID, a school code, and a password, followed by an OTP step. Nothing about that screen looks unusual. The problems only showed up once I stopped looking at the page and started looking at the code behind it.
Reading the JavaScript Bundle
Like most modern single-page apps, the portal is an Angular application that ships its entire frontend logic in one bundled, minified JavaScript file. The browser downloads this file and runs it locally to render every screen of the app.
That bundle is served publicly at:
https://cbse.onmark.co.in/cbseevalweb/main.dc17c24606b3b008.js
Anyone can request it, logged in or not. So I pretty-printed it and started reading. What I found inside was horrible.
Vulnerability 1: A Hardcoded Master Password
Sitting in plain text inside the frontend bundle was a hardcoded master password. Not a hash, not a token reference, but the literal password string, baked directly into the client-side JavaScript that gets shipped to every visitor's browser.
The logic around it was worse than the leak itself. When this master password was entered into the login form, the app automatically filled the OTP field and bypassed the normal authentication flow entirely. There was no second factor to clear and no server-side check to satisfy. Entering the magic string was enough.
To log in as a specific examiner, all an attacker needs is:
- A target's user ID and school code, both of which are publicly obtainable.
- The master password, sitting in a JS file anyone can download.
With those, I was able to log in as an examiner (bypassing the OTP/2FA flow totally) and reach the evaluation dashboard, where I could view and edit marks.
Vulnerability 2: OTP Validation Done Entirely Client-Side
The OTP step turned out to be pure theatre. When you trigger authentication, the server sends the OTP back inside the auth response, and the JavaScript running in your browser compares what you typed against that value locally before letting you through.
Think about what that means. The secret you're supposed to prove you received is handed straight to your browser, and the browser grades its own test. Anyone watching the network tab can just read the OTP out of the response. And because the comparison happens in client-side code, you can skip the form altogether and simply tell the app the check passed.
A security control that runs on the attacker's machine isn't a control at all.

Vulnerability 3: No Route Guards, So the Whole App Is Walk-In
Even setting the login flow aside, the app's routing offered no protection. The entire Angular route configuration had zero canActivate guards. Routes like /dashboard, /profile, /evalscriptsview, /heallscripts, /evaluatordetails, and /verificationdashboard were all directly navigable.
The only thing standing between an anonymous visitor and an internal page was a default redirect to /login, and that's trivial to defeat. By seeding a few values into browser storage and navigating straight to a route, you land on any page you like:
localStorage.setItem('jwtToken', 'dev-token-12345');
sessionStorage.setItem('role_id', '23');
sessionStorage.setItem('ValType', 'Regular');
sessionStorage.setItem('eval', JSON.stringify({
user_id: 'DEV001',
role_id: '23',
mobile_no: '9999999999',
email: '[email protected]',
jwtToken: 'dev-token-12345'
}));
// Then navigate
window.location.href = '/cbseevalweb/#/dashboard';
Paste that into the browser console and you're dropped onto the dashboard, having never authenticated against anything. The token is fake, the user is invented, and the app doesn't care.
Vulnerability 4: Changing Any Password Without Knowing the Old One
This is where the individual bugs start combining into a full account takeover.
The "change password" feature collects an old password from the user, like you'd expect. But when I inspected the actual request it sends, the ChangePassword API payload only contained:
{ "ValuatorID": "...", "pin_NewPassword": "..." }
The oldpassword variable exists in the component, it's just never included in the request that goes to the server. The current password is never verified. Whatever ValuatorID you put in the body gets its password reset to whatever you choose.
On its own that's bad. Combined with the next issue, it's catastrophic.
Vulnerability 5: Systemic IDOR Across the Entire API
Almost every API call in the app identifies the acting user by reading ValuatorID / user_id straight out of sessionStorage["eval"], the same browser-storage object I showed editing above. The server trusts whatever ID the client sends instead of deriving it from the authenticated session.
That makes this an Insecure Direct Object Reference (IDOR) vulnerability at the architectural level. It's not one broken endpoint. Practically every POST request in the service is affected. Change the ID in storage and the app acts as that user for any operation it offers.
Stitching it all together:
- IDOR lets you act as any examiner by editing one value in your browser.
- The
ChangePasswordAPI resets a password without checking the old one. - So you can set
ValuatorIDto any victim and reset their password to one you control. That's a complete account takeover, with no credentials and no insider access.
From there, an attacker can log into the victim's account legitimately, view assigned answer scripts, and alter marks. At the scale of a national board exam, the integrity implications speak for themselves.
Putting It Together
To summarize what these flaws allowed:
- Log in as any examiner using a master password leaked in the frontend.
- Bypass OTP entirely, because validation happens in the browser.
- Reach any internal page without authenticating at all.
- Reset any examiner's password without knowing their current one.
- Act as any user across the API thanks to systemic IDOR, and in doing so edit marks, change examiner details, and tamper with the evaluation process.
None of this required sophisticated exploitation. The hardest part was reading a JavaScript file and editing a couple of values in DevTools.
Responsible Disclosure
I reported all of this to CERT-In (the Indian Computer Emergency Response Team) before writing anything publicly.
My first email laid out the master-password leak and the client-side OTP validation. They replied asking for more detail and a screen recording, so I sent a full walkthrough: the master-password auth bypass on video, the browser-console login bypass, and then the extra findings I'd uncovered since, namely the missing route guards, the password-change flaw, and the systemic IDOR.
Their response was a boilerplate acknowledgement:
Dear Sir,
Thank you for reporting this incident to CERT-In. We have registered your complaint/incident under Ref: CERTIn-XXXXX. We are in process of taking appropriate action with the concerned authority.

After that, I followed up several times and never heard back. It's honestly funny that most of the vulnerabilities I reported went unpatched for a long time, when I'd have fixed them in an hour or two if they were mine to fix. The sheer incompetency of our authorities baffles me.
I held off on publishing for a while, mostly to give them a fair window to fix things. But these issues sat in a system handling the exam evaluations of millions of students, and that's exactly the kind of thing that deserves daylight once it's been responsibly reported.
Takeaways
If there's one lesson here for anyone building software like this, it's that the client cannot be trusted, ever. Every one of these vulnerabilities traces back to the same root mistake: putting secrets and security decisions in code that runs on the user's machine.
- Secrets (passwords, OTPs, anything sensitive) belong on the server, never in a JavaScript bundle.
- Authentication and authorization must be enforced server-side, on every request.
- A user's identity should come from their authenticated session, not from a value they can edit in DevTools.
- Sensitive operations like password changes must verify the requester's authority, and their current password.
These aren't advanced defenses. They're the basics. For a platform entrusted with the integrity of national board examinations, the basics are the least we should expect.
Aftermath
This section was written on May 27, about five days after this blog first went up. In that time, CBSE has publicly denied that any of these vulnerabilities ever existed. I want to lay out, in good faith, what's actually happened from my side, because the public record matters here.
To recap the timeline: every issue documented above was reported to CERT-In back in February, and they verified and acknowledged the findings. My complaint sits on file under Ref: CERTIn-16590126. I followed up multiple times after that and never got a substantive response. Three months passed, the Class 12 results were released, and the portal and its marking workflows were still in the same vulnerable state. That silence is what eventually pushed me to publish.
Once the story picked up in the press, CBSE issued a statement claiming the findings were false. There were two immediate problems with that statement. First, their initial release pointed to a domain that didn't actually exist; someone in my circle quietly registered it and pointed it at this very blog before they retracted the tweet. Second, even taken at face value, their position doesn't hold up: if what I accessed was a test environment, how was I able to pull what was unmistakably production data out of it?
The receipts for that are public:
- A proof screenshot of the access itself.
- CBSE's own official mails referencing the same URL they later tried to distance themselves from.
- A screen recording of the hardcoded master password being used to reach production data.
- An archive of the site and its source code as of 03/03/2026, preserved before anything was changed.
- Evidence that the vulnerabilities were still present in the March build of the site, well after my reports.
- The same master password sitting in the JS bundles of other
onmarkdomains, unpatched at the time of posting. - Findings from other researchers showing that every CBSE-related subdomain under
onmarkresolves to the same load balancer. Test environments don't typically need a load balancer, and they certainly shouldn't share infrastructure with the "prod" they're supposedly separated from.
On top of all that, three days ago, shortly before the site was taken down, I discovered an SQL injection vulnerability in the OSM portal and reported it to CERT-In. The response so far has been a one-line "thank you" mail.
I want to be clear about my motive: this entire body of work was done in good faith, to flag a serious problem in a system that grades the futures of millions of students. The right response from an institution of CBSE's scale is to acknowledge, investigate, and fix, not to deny and deflect. I hope this pushes things in that direction.
What Came Next
This section was written on June 7. The story moved a lot in the two weeks after CBSE's denial, and most of what follows escalated the picture rather than walking it back.
RCE and Shell on the Production Server
The SQL injection bug I mentioned in the previous section turned out to be the foot in the door for something much bigger. Using that SQLi as a primitive, we were able to enable xp_cmdshell on the underlying SQL Server, which gave us OS-level command execution on the host. From there it was a straightforward escalation to full Create, Read, Update, Delete (CRUD) access and a shell on CBSE's production servers, the same production environment CBSE describes in their own circular about the OSM rollout.
An archived proof of the access lives here, and the disclosure thread is on X. For an organisation that had spent the prior week insisting nothing was wrong, going from "denied" to "remote code execution on prod via SQLi → xp_cmdshell" inside a few days is a fairly damning data point.
To make the demonstration unmistakable, I played Bad Apple on the CBSE portal from the same access. It's a silly stunt on the surface, but the point is the substance underneath it: if I can render a video on their infrastructure, I can do anything else that machine can do.
Misconfigured S3: Public Listing of 2026 Answer Sheets and Question Papers
Around the same time, an unrelated but equally serious problem surfaced on the storage side. The S3 bucket backing the OSM media was misconfigured such that ListObjectsV2 worked without any authentication and the bucket root itself was listable. In practice that means anyone on the open internet could paginate through and enumerate every object, then download any of them directly. The contents included scanned 2026 answer booklets and question papers.
Worse, the same bucket was being used as a multi-tenant store across multiple institutions, so the exposure wasn't scoped to a single school's data. Full thread and screenshots are here.
Re-Evaluation Portal: A Separate PII Leak
Shortly after, another live CBSE production portal, the re-evaluation system, was found to be exposing a substantial amount of personally identifiable information of students and examiners. This is a different system from the OSM portal and a fresh class of bug, but the pattern is by now familiar: production data reachable without proper authorisation. CERT-In and CBSE were notified, and the details are documented in this thread.
CBSE Finally Acknowledges It
On May 31, CBSE's official handle put out a statement that, for the first time, acknowledged the vulnerabilities rather than denying them:
We have been closely monitoring the vulnerabilities in the OnMark portal of our service provider that are being flagged in the public domain. An expert team of cybersecurity professionals has been deployed over the last few days from across various arms of the government as well as the IITs to fortify these systems, including taking them over to a more secure set up. The identified vulnerabilities have been contained, and other exploitable weaknesses are being ruled out.
We are grateful to all alert citizens and ethical hackers pointing out such weaknesses, and have gotten in touch with some of them directly.
We request any others to reach out to our security teams at [email protected] for any further inputs.
This was followed by reporting in The Hindu, which captured the shift in posture:
After initially denying any breach, CBSE invited 19-year-old ethical hacker Nisarga Adhikary to help identify security gaps in its IT systems. An IIT-led team later fixed multiple critical vulnerabilities and strengthened the board's portals against cyberattacks.
I'll leave the reader to compare that to the "these vulnerabilities never existed" line from a week earlier. The system is being hardened now, which is the outcome that should have happened back in February when the original report landed at CERT-In. Better late than never, but the gap between "we got the report" and "we acted on the report" is exactly where the risk to students' results lived for those three months.
A lot of famous personalities and organizations like Deedy Das, Satish Acharya, Internet Freedom Foundation tweeted about it & this blog has been featured in news reports by multiple media outlets:
- India Today
- BBC News
- Bloomberg
- NDTV
- Times of India
- The Hindu
- The Hindu BusinessLine
- ThePrint
- News18
- Scroll
- Hindustan Times
- Financial Express
- Times Now
- CNBC TV18
- Moneycontrol
- IFF Blog
- Medianama
- Free Press Journal
- Careers360
and many more...
Thanks for reading.