Author: Rory Mackie
Date: 4th March 2019
It started on a normal Friday. Another day, another dodgy server to investigate. Who does work on Fridays, anyway?
Being a good patrician of the Internet I take it upon myself to investigate the source of spam emails and have a poke around at the origin servers, because God knows emailing the abuse contacts is useless.
In this case, my digging turned out somewhat interesting: I found an installation of one ‘Vesta CP’ control panel installed on the origin server from one of my spam messages. Seemingly akin to ‘CPanel’ and other projects, it’s a tool used to manage servers and to configure server programs.
In essence it provides a nice web interface for configuring programs such as nginx, mail servers, adding FTP accounts and editing databases for people who are not acquainted with the command line.
After a little Google sleuthing, I found out it’s an open source project. Let’s have a quick browse of the source, shall we?
There’s a lot. So I’ll spare you, dear reader, the tiresome details! I’ll skip to the interesting bits. One in particular. Over here in the password reset code, what’s that I spy?
A timing attack, in MY password reset code? It’s more likely than you think.
For the uninitiated; this code is vulnerable to what’s known as a ‘timing attack’ because of the way PHP handles string comparisons. Not to pick on PHP specifically – it’s just the way it is.
In PHP, the equality operator ‘==’ (when being used for two strings) will check firstly if they are of the same length. It will then iterate over each of the passed strings, checking if the current character in the first string is equal to the current character in the second string. Following so far? Excellent.
Now. As soon as PHP finds a character that is NOT equal in both strings, it will bail out of the comparison. In most cases, this is a good thing as it saves computation time. Why bother comparing the rest of the string if you already know already that it’s not equal? A sensible optimisation.
However, herein lies the issue. This early bailout, however miniscule, is measurable. This means that if we compare two sets of strings; ‘AAA’ with ‘AAA’, and ‘AAA’ to ‘BAA’, we will notice a tiny difference in the amount of time taken to do so. This time difference can be easier noticed by performing the comparison repeatedly and collating the amount of time taken. The theory is that the more characters in the string that are equal, the longer the comparison will take – due to the program iterating just that single character further. This is not a novel attack and has been exploited several times in the past across multiple software stacks.
Back to our attack. So, we can perform a time attack against this ‘reset code’. What’s the reset code? Reading through the source, we can see that password reset codes are generated when a user account is created using the following function:
This shows us that default reset codes are ten characters long and mixed-case alphanumeric.
Another problem to note with the reset function is that it’s possible to brute-force this code, as there are no CAPCHAs, rate-limits or timeouts used. That said, brute-forcing every possible combination would result in a lot of requests. How many? Well, for 62 possible characters in a ten-character long password, that’s 62^10.
Not that bad, right? Wrong.
That little ^ is the power symbol. That means that 62^10 gives just about 839 quadrillion combinations. Even if you guessed a beefy 1000 combinations a second (we’re talking over the internet, here) you’d be waiting a solid 26 and a half million years. Yeah. Sod that for a laugh.
Instead, how about I show you a method where we only have to perform about 50,000 guesses for each character – 50000*62*10 — for a total of 31 million requests? If you were able to guess at a similar rate of 1000/second, it would take you about eight and a half hours. I’m liking these numbers a lot more already.
“So how would I only have to guess 50,000 for each character?”, I hear you wail. “Are you insane? Do you require assistance?”
Yes. I don’t talk about it. Not since last time. But I digress.
In our method, we will make 50,000 requests for each potential character of the password. Then we will take the most likely candidate (the character whose average request time took the longest) and repeat that process for all characters in the sequence. 50,000 is a random guess of how many requests we will need to make to be confident of the character in a given position.
To further explain the theory, you’d make 50,000 requests with the code 0000000000. Then 1000000000, 2000000000 and so on until you reach Z000000000. Assuming the reset code is ‘AB12345678‘, you’d realise that A000000000 took ever so slightly longer than the rest to complete, so you assume A is the first character. You then take a crack at guessing the second character by moving on to guessing the second position, sending A000000000, A100000000 and so on.
Wonderful. So how about doing it in practice?
Well to save you time and effort (I know, I’m so kind) here’s a link to a proof of concept that does exactly that. It took me a long time to achieve this on a VM, running locally, so YMMV getting this to run over the internet. It’s definitely possible, however. In Theory™. (Probably with a better thought out codebase, see ‘future ideas’ section in README.md)
If you happen to be running Vesta CP, they’ve now patched the issue in the source tree. Please update your installation to at least version 0.9.8-23.
For further details and advice for users, please see the Vesta CP website.
This vulnerability has been assigned CVE-2018-1000884 – additional details can be found on the CVE and NIST National Vulnerability Databases.
Cyberfort Colocation Services
Cyberfort has invested heavily in secure infrastructure, making us the perfect colocation service provider to host your mission-critical, sensitive and regulated data.
Find out more >
Cyberfort Deep Dives
Cyberfort’s cybersecurity consultants explore issues in cyber threat intelligence, incident planning and data security. Read our whitepapers to help make decisions that benefit your business.
Find out more >