Skip to content
Go back

Dissecting Unstop SmartHire: How smart is it really?

Published:  at  01:39 AM

Imagine enforcing rules client-side. Now try to imagine someone doing that in javascript. Add to it the fact the code isn’t even obfuscated. And we’re talking about a platform that boasts a userbase of 20+ million.

Table of Contents

Open Table of Contents

But what on Earth is Unstop “SmartHire”

Unstop has a handy feature which allows quizzes and coding assessments for events to be conducted on Unstop itself, so the event organizers don’t have to worry about putting together their own infrastrcture.

Such quizzes are typically taken in the end-user’s browser. Obviously, browser APIs can’t detect software an average Joe would run to share their screen to cheat: take OBS Studio as an example. Unstop’s solution to this was to come up with a Desktop app, called SmartHire, that claims to “make sure an honest evaluation happens by preventing candidates from opening any unauthorized websites or downloading unauthorized apps/software while attempting the assessment”.

I learnt about it when a friend who had registered for one such event sent me a pic like so:

download-smarthire-app-to-continue

No Linux build ??

This friend had their Windows partition wiped upon unboxing their laptop, like any sane Linux user would. They sent me this pic asking how they could get it to work, because there is no download option for linux.

Lipstick on a Pig

I went ahead and registered for the assessment myself, and downloaded the Windows build: unstopsmarthire-1.0.17.exe. It turned out to be a kernel-level anti cheat service a “Nullsoft Installer self-extracting archive” that could be unarchived using 7z as-is.

Inside the extracted output were a bunch of standard Windows DLLs and an app-64.7z. Upon extracting the app-64.7z, we get to the meat of it:

file-contents

app.asar

After npx asar unpacking app.asar, we find Unstop’s silly electron wrapper over its browser-based assessment software in all its glory.

unstop-smarthire-source-code

Btw, in the extracted app-64.7z there exists a directory named app.asar.unpacked. It contains nothing of interest and is probably either an artifact of packaging or an intentional red-herring 😂.

Getting the unpacked code to run

Copy over the preload.js we extracted from the resources directory inside app-64.7z, into the src directory, where main.js resides. Then change the getAppURL() function inside src/utils/windowManager.js to behave as if app.isPackaged is true:

adding-linux-support

We can now run the app using npx electron .. That should work everywhere electron runs, which includes (most) Linux. This fulfills our initial goal.

But can a dishonest party do more? Of course, they can.

How does SmartHire attempt to detect cheating?

The app registers IPC handlers on the electron browser window using preload.js, and loads the same url (https://assessment-v1.unstop.com/ at the time of writing this) as the one used in web-based assessment. The presence of these handlers is what the website loaded from the url checks to distinguish a regular browser from the SmartHire app.

smarthire-code

Obviously, each security check can be easily bypassed…

Carrot on a stick

For the specific case illustrated here, the quiz/coding-assessment round is a demo-round intended for users to gain familiarity, before the actual round is conducted. So playing around a little bit shouldn’t be a concern.

Let’s take the pig for a ride:

1. Starting with src/main.js, let's first get auto-update out of our way
diff for src/main.js
2. Supress detection of display plug/unplug events in src/utils/displayManager.js
diff for src/utils/displayManager.js
3. Make detectMirroring simply return false in src/utils/displayUtils.js
The new src/utils/displayUtils.js
4. Hardcode display count and disable auto-update in src/utils/ipcHandlers.js
diff for src/utils/ipcHandlers.js
5. Spoof running process list and system info in src/utils/systemUtils.js
The new src/utils/systemUtils.js

That pretty much obliterates any edge in violation detection that SmartHire (a “desktop app”) has over the traditional browser based assessment.

Taking it further

Let’s open the floodgates: add the line mainWindow.webContents.openDevTools() to the createMainWindow function inside src/utils/windowManager.js.

We now have the dev tools, and surprisingly, there is no debugger detection. It’d still look odd as the entire screen is being shared, so let’s fix that: Inside permissions.js, replace desktopCapturer.getSources({ types: ["screen"] }) with desktopCapturer.getSources({ types: ["window"] }). This won’t work just yet, because the browser-based part of the javascript has a check which requires the test-taker to share their entire screen. But now that we have dev-tools (undocked out into a separate window), let’s get this out of our way with local overrides:

Comment out the outlined part of the code to bypass the check that enforces the entire screen to be shared. This makes it possible to just share the unstop smarthire window.
local override to share a window

There are a ton of other checks that can be bypassed this way. For instance, a dishonest candidate can search for and edit the declaration of the checkLiveliness function to always return true, effectively stopping Unstop from stopping the assessment when a pre-recorded video is fed through a virtual camera. Even doing so little as this easily gives any dishonest party a ticket to cheat freely without getting caught.

~Post()

By the way, Unstop now (by the time I got around to writing this post) also provides a download option for Linux, which obviously doesn’t change the situation a single bit because on unpacking, it’s the same as its Windows/mac counterparts.

If automated online assessments absolutely need to be relied upon, I’d say they should be best left to professional platforms made for the purpose. Unstop isn’t one of them, even remotely. To me, it looks like a pile of garbage with a cringe UI and a questionable privacy-policy that just so happened to have attracted enough unsuspecting users in its early days, that its use has become a societal requirement — kinda like the state of Wh🤢tsApp in India.