Professor M. Eista Hax uses a digital tool to manage all his students. He is very happy with the system, but it does have one drawback: it does not support multiple users. This is a problem, because M. Eista Hax has employees who need access as well. To solve this he writes a super modern, highly encrypted web application to share the password with authorized users. Problem solved.
This so-called super modern web application (spoiler alert: don’t laugh, it’s written in perl) allows students to either register or login. Registration can be accomplished simply by submitting a valid public PGP key. Once the upload is completed, the website renders a message encrypted with the provided key. By decrypting the text it is now possible to recover the random password associated with the email address specified in the public key.
As an example, we create a PGP keypair for the user ID marco@c00kiesland.com
The message printed by the website, after decryption, is the following:
As this sentence suggests, it is not possible to authenticate to the system using these credentials. If we try to submit that email / password combination, the website kindly refuses to proceed by printing
Exploitation - Step 1: SQLi
After some attempts, we noticed that the email field of the public PGP key can be exploited to cause a SQL injection in the application backend. Since there are no activated accounts, our aim is to access the website source code to look for other vulnerabilities which may lead to file inclusion or remote command execution. Indeed, we were able to dump the sources by providing the following payload as email address within the uploaded key:
Some statements and brackets were repeated to bypass a simple SQL injection filter. The source code location has been recovered by dumping the lighttp webserver configuration file found in /etc/lighttp/lighttp.conf.
Exploitation - Step 2: Perl Insanity
The full source code of the application is depicted below:
In short, the application includes a configuration file ../private/config.ini containing the flag and prints it upon successful logins. The website also accepts a PGP public key via the gpg_file parameter and stores the file under /tmp/<md5(key)>. As previously stated, there are no active users in the database, hence abusing the login function does not sound like a good idea.
By carefully reading the index.pl file, we noticed a curious definition of the templates dictionary:
This data structure is used to quickly access the file names of the HTML template. Indeed, HTML files are rendered using the print_template() function which calls the get_template() function responsible of reading a file from the filesystem and returning its content. Let’s go back to the templates dictionary: what’s so weird about it? Well, the error key is assigned a value straight from the GET parameter error, but then print_template($templates{error}) is used nowhere in the code… It should be clear that if we were able to print arbitrary contents from the filesystem we could just dump the ../private/config.ini configuration file (which is not readable via the SQLi due to missing permissions) and obtain the flag.
But ehy, Perl is such a messy language :) Thanks to a brilliant talk given by Netanel Rubin at the 31th Chaos Communication Congress, we managed to discover some tricks needed to include arbitrary files and solve the challenge.
Abusing $cgi->param()
If we provide to the application multiple GET parameters with the same name, instead of saving only the first value as found in the HTTP request, Perl makes a list out of them. For instance, if we request
by printing $cgi->param('error') we obtain the list ('a', 'b', 'c'). Gross!!!
Abusing lists in hashes
Lists in hashes expand the hash. Thank you Perl! Believe it or not, but given this program
the actual output is the following
/o\
Mixing it all together
Now it should be clear that via an HTTP parameter pollution attack we can extend the templates dictionary and overwrite the value associated with one of the template components, e.g., footer, in order to get the contents of an arbitrary file. Our payload looks like
And, as expected, the application prints the flag instead or rendering the footer
Bonus: RCE!
Did you know that using the open() function, if the filename begins with a |, the filename is interpreted as a command to which output is to be piped, and if the filename ends with a |, the filename is interpreted as a command which pipes output to us link? By reading the get_template() function, we are free to put whatever we want as a filename
and easily achieve command execution by appending a pipe to our command, using the parameter pollution attack previously described
Clearly, one could have also printed the flag just by using cat
Thanks lama for this funny challenge, Perl is soooooo gross!!!