Exposing Critical Vulnerabilities in CBSE’s On-Screen Marking Portal: From Authentication Bypass to Full Account Takeover — ni5arga
ni5arga
← back to blog Exposing Critical Vulnerabilities in CBSE’s On-Screen Marking Portal: From Authentication Bypass to Full Account Takeover May 22, 2026
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 ChangePassword API resets a password without checking the old one. So you can set ValuatorID to 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. Media Coverage A lot of famous personalities like Deedy Das, Satish Acharya tweeted about it & this blog has been featured in news reports by multiple media outlets:
India Today NDTV The Hindu BusinessLine Medianama Free Press Journal Careers360
Thanks for reading.
ni5arga |
Critical vulnerabilities were discovered in the Central Board of Secondary Education's (CBSE) On-Screen Marking (OSM) portal, a digital system used for evaluating millions of student answer sheets, which exposed serious risks to the integrity of national board examinations. The security flaws were identified through analysis of the publicly accessible application components.
One major vulnerability involved a hardcoded master password found in the client-side JavaScript bundle of the application. This secret, unhashed, was directly embedded in the frontend code. Exploitation of this password allowed an attacker, armed with a target’s user ID and school code, to bypass the entire authentication flow, including the two-factor authentication process, and gain access to the evaluation dashboard, enabling the viewing and editing of marks by an examiner.
Furthermore, the system displayed a flaw where the One-Time Password (OTP) validation was performed entirely on the client side. The server’s response included the OTP, which the browser validated itself locally, allowing an observer to intercept this secret and bypass the security check entirely, fundamentally undermining the integrity of the two-factor authentication mechanism.
The application architecture also lacked proper route guards, permitting direct navigation to sensitive internal pages such as dashboards and evaluation script views. Attackers could exploit this by manipulating browser storage to inject fake session and role data, enabling them to navigate freely across the application without proper authentication.
The vulnerabilities culminated in a complete account takeover through systemic flaws. The password change mechanism failed to verify the current password when resetting an examiner's credentials, meaning any user could reset another examiner's password without knowing the original one. This flaw was exacerbated by a critical vulnerability where the application trusted user identification values provided by the client-side storage rather than enforcing identity through a secure, server-side session. This resulted in a systemic Insecure Direct Object Reference (IDOR), allowing an attacker to act as any examiner by simply modifying stored IDs and subsequently manipulate sensitive data, including altering marks and changing examiner details across the entire API.
In summary, these combined flaws allowed unauthorized access by bypassing login, accessing internal routes, resetting passwords, and achieving full control over data integrity by exploiting the lack of server-side validation and secure session management principles. The core lesson derived from this incident is that client-side code cannot be trusted, and all authentication, authorization, and sensitive operations must be strictly enforced on the server side. |