After Singapore

I would walk 500 miles and I would walk 500 more

The system cheated me, so I cheated the system


People invent technologies to help other people. However, it sometimes happen that it puts people into a rat race. Computer programmers create automations, others invented CAPTCHA to prevent automation. Photoshop can manipulate photos, while there are technologies invented to detect manipulated images. OpenAI created ChatGPT, while some people created a program to detect ChatGPT-made articles to prevent cheating.

The IT guys in my office created a website to track attendance of their employees. We need to login using our SSO (Single Sign On) account and click something like “I am here” button for our attendance to be recorded. This is a replacement for the previous system, fingerprint scanner.

I honestly hate this new system. Previously we only had to put our finger to a scanner to be recorded. Now we have to wait our own computer to boot up, join wi-fi, open web browser, type user name and password, click some buttons, et cetera. When something goes wrong in one of the steps, we may have to wait a few minutes then try again.

In this new system there are two actions to perform: check in and check out. Check in is to tell the system that you’re ready to work. Check in itself is divided into WFO (Work from Office) and WFH (Home). The system is clever enough to prevent people from home claiming WFO: you need to join the office’s wifi to do that (VPN excluded). Check out is when you report what you have done today, and can be performed anywhere.

If you do the procedure correctly, you will be rewarded ±Rp17.800 for transportation cost (depends on seniority). However, it turned out that it’s challenging to perform it correctly according to the algorithm actually implemented in the code. In the beginning of January, I diligently performed the check in and check out procedure almost every working day, only to find out that only 7 out of ±20 working days of my WFOs were rewarded. There were rumours that I should gave ample time between check in and check out to be actually rewarded, but nobody could confirmed the exact algorithm.

So I planned for revenge: having someone (something, actually) do the WFO check in for me while at home.


The Devices

I didn’t have a desktop computer in the office anymore, so I used my own Raspberry Pi (a small ARM-based computer), paired with a timed power plug.

Image of a timed power plug

Timer for the power is set to on every weekday, 7 to 8 AM. The good thing about Raspberry Pi is that it boots up automatically when it receives power. After configured properly, Raspberry can join to trusted WiFi and opens GUI when it boots up.

Next step is to have a script to do the check in. Using the built in Chromium browser, Chromium web driver, Selenium WebDriver, and a simple scripting framework me and my former student made, I automated the following steps:

  1. Open web browser, trigger Captive Portal
  2. Enter username and password
  3. Open attendance system website
  4. Enter username and password
  5. Click “WFO Check in”
  6. Done and close the browser
1 = open https://{redacted}
2 = sendkeys #username {redacted}
3 = sendkeys #password {redacted}
4 = click #{redacted}
5 = open https://{redacted}
6 = sendkeys #username {redacted}
7 = click #{redacted}
8 = sendkeys #password {redacted}
9 = click button[name=submit]
10 = click a[href='{redacted}']
11 = click a[onclick='{redacted}()']
12 = quit

Actually, it took only a few minutes to perform everything. Then Raspberry would wait an hour before powered off. To prevent filesystem damage, I also set a cronjob to perform graceful shutdown at 7.45.

45 7 * * 1-5 /sbin/shutdown -h now

There were some challenges, though:

  1. The office WiFi is heavily firewalled. Therefore I could not connect remotely using SSH nor VNC. When I am not in office, I just have to trust that the machine works.
  2. The browser needs GUI, and the GUI seems to be needing a monitor connected (because it has to decide what is the proper screen resolution), so I can’t be sure it can run headless. When the monitor is turned on and showing activity, it may attract attention from my roommates.
  3. The script engine is very simple, that it can only run without branches and crash if what it sees in the browser is not as expected. Meanwhile, captive portal does not ask for username/password anymore if a successful login has happened that day. Hence, the script can only run successfully once a day.

The Actions

I set it up on site on Monday, and left it to work on Tuesday. I intentionally didn’t go to office on Tuesday. Well, it worked!

I just needed to perform check out that day, since it’s more difficult to automate both check in and check out.

On Wednesday, I still didn’t come to office. It was fortunately working well again.


On Thursday, I went to office to check if everything was okay. It did already attracted attention of my roommate because of animations on my monitor. I plan to keep this device automation running to perform 13 successful WFO check in while I don’t come to office as payback for January; or until I get caught. I also reduce the lifetime of Raspberry to half an hour only, 7.00 to 7.30 (shutdown at 7.15).

This post was written with enough details to give idea for someone to replicate their own, but of course many other details were omitted. If you wish to build your own, you need to understand some technical and programming stuffs.

Also, I should have spend my time working like a proper lecturers instead of tinkering like this.


After successfully running for two days, somebody found out that I was cheating, I guess. It was not working for a few days, and when I troubleshooted on one fine Tuesday, I found out that there was a webdriver detector because my script threw some error messages:

selenium.common.exceptions.UnexpectedAlertPresentException: Alert Text: Caught in the second case: bot is banned!!!

Upon checking on the website source code, I found the following detector:

        $(document).ready(function () {
            try {
                if (window.document.documentElement.getAttribute("webdriver"))
                    alert("Caught in the first case: bot is banned!!!");
            } catch (Exception) {

            try {
                if (navigator.webdriver)
                    alert("Caught in the second case: bot is banned!!!");
            } catch (Exception) {

So, that’s the end of it, I guess.