How to rig an election

Primaries huh

2-min read

Photo by Morning Brew

With elections around the corner, I thought it would be a good idea to write about a hypothetical story that occurred two years ago, and explain how easy it is to bump up voter count on online survey platforms.

Burning the midnight oil, I was on a Discord call with a few friends. Student elections were around the corner and votes were lagging for the friend who was contesting. The SurveyMonkey was linked on the channel and some non-technical friends started spamming the votes for our friend’s favor. With absolutely no verification (whether this was in the form of student email verification or CAPTCHA’s), a programmer mentioned off-hand it was trivial to write a script to turn the tables.

Selenium simulates and automates user interactions with websites. Used for unit tests, my experience with this software is to write one-off scripts to either scrape information from websites for local analysis or run regression test suites on some of my hosted applications whenever I commit or make any new changes.

In a few minutes, a script was posted in chat which leveraged Selenium to automate the voting. One interesting takeaway was that SurveyMonkey rotated options around to prevent bots from targeting specific values. One could potentially circumvent this by targeting text on the screen (instead of className, id, or name):

driver.find_element_by_xpath('//label/span[contains(text(), 'TARGET_TEXT')]/ancestor::label')

You can find the complete python code here

The script was hitting 100+ votes a minute. But the programmer realized the admin dashboard on the survey website might track and log IP addresses and they would have to rotate connections after every successful vote to hide their identity. With their VPN client, they wrote another script with a configuration file to handle this restriction, checking for server availability and performance on the fly to increase reliability and not leave their actual address exposed.

Wit this roadblock, one successful vote meant closing your previous connection, waiting for it to resolve the next IP address, and then execute, which slowed down the script to a vote every 10 seconds. Completely unreasonable cycle times. Spinning up multiple instances of the script and hosting it on multiple servers was the next best alternative option.

In a few hours, the final log showed more than 1000 votes submitted in the primary runner’s favor. Only a few hundred people were allowed to vote. The student election locked voting and launched an investigation. Nothing ever surfaced, because, in random intervals, the script also voted for other candidates. The only egregious mistake the programmer made was they ran it for too long and didn’t impose a hard limit.