Client Trust Gone Wrong
While poking around the api and source code of the site we were doing classwork on at school instead of doing my coursework, I noticed something odd: the platform ships a chunky JavaScript utility that looked obfuscated. but the purpose was still obvious. Encoding and Decoding of test scores... That code handles compression on the client before grades are fired over the wire. My curiosity meter pegged immediately.
Peeking at the Helper
Nothing wild there, but the important bit is that it lives entirely in the browser. No signatures, no HMAC, no guard rails. Here is the trimmed version I found:
function mapChar(alphabet, char) {
if (!cache[alphabet]) {
cache[alphabet] = {};
for (let i = 0; i < alphabet.length; i++) { cache[alphabet][alphabet.charAt(i)]=i; } } return
cache[alphabet][char]; } const
alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" ; const lzHelper={
compressToBase64(value) { if (value==null) return "" ; return lzHelper._compress(value, 6, (code)=>
alphabet.charAt(code));
},
decompressFromBase64(payload) {
if (!payload) return "";
return lzHelper._decompress(payload.length, 32, (index) => mapChar(alphabet, payload.charAt(index)));
}
};
sorry i dont want to fix formatting i'm too lazy lol
Anything the client wants to say about a quiz result gets packed with this helper and shipped directly to the server. The server just trusts it.
Forging the Grade
Once you realize grades are self-reported, the exploit path is straightforward:
- Open a quiz, answer a few questions (or none at all).
- Intercept the submission request with Burp Suite or your favorite proxy.
- Paste the compressed payload into the console and run
lzHelper.decompressFromBase64to see the JSON. - Flip the fields: set
passed = true, max outpointsScored, tweak the timer to look realistic. - Run
lzHelper.compressToBase64on the edited JSON and drop it back into the request before forwarding it.
That is it. The server never checks the answers or recomputes the score. If the client says you aced it, you aced it.
Why It Matters
Education platforms rely on honest scoring to decide who passes a module or earns a certification. When the back end takes the client's word for it, anyone can inflate their transcript. That cheapens the credential for everyone else and gives organizations a false picture of student readiness.
Patching the Hole
- Recompute scores on the server from the submitted answers instead of accepting raw scores.
- Attach a server-issued signature or MAC to any payload you expect to trust later.
- Limit client helpers like this to internal bundles so casual inspection is harder.
Reporting It
I sent the details to TestOut so they can close the gap. They never did and never wrote back. If you stumble across similar issues, please report them responsibly. Students deserve tools that are fair, and vendors deserve a heads-up before things get noisy.