Hacking News Opinions Security

Barking up the wrong tree

Investigating what I thought could be an SQL Injection in WHMCS

For those of you who haven’t heard of WHMCS before, you may have heard of cPanel/WHM? cPanel is a very popular control panel installed to many web servers worldwide (WHM is the administration panel that sits behind it).

WHMCS is a very popular billing system which integrates seamlessly with cPanel and WHM for automatic account creation. In my opinion, it hasn’t had a great security history. It’s probably better these days, but I recall reporting an XSS vulnerability to them a few years ago and I wasn’t particularly happy with the way they prioritised it.

Recently, I was logged onto a WHMCS instance used by one of the hosting companies I use. For reasons that aren’t relevant, I tried to disable two factor authentication on my account. When you try to do this on WHMCS, it asks you for your password. I input my password, but the system said my password was wrong? It certainly wasn’t – I put it in straight from my password manager.

I observed my password contained a quote, and wondered whether this could be triggering some sort of SQL issue.

To test this, I changed my password to the same value, but removed the single quote. This time, disabling Two Factor Authentication worked fine, without error.

Have I just discovered an SQL Injection in WHMCS? This seemed serious enough for me to investigate – it was certainly behaving in a way it shouldn’t.

I purchased a monthly license, and installed WHMCS to my local machine. Further testing needed to be done.

The source code of WHMCS is closed source and obfuscated, so it’s not as easy as looking at the source code to see what it’s doing.

I registered a new client account, ensuring to put a quote in my password, and I was able to replicate the issue locally. The version I installed was 8.0.4 – I haven’t tested other versions.

I decided to load Burpsuite to try and capture the registration request before it was passed onto the server. From here, I exported the request and loaded it into SQLMap – a useful tool for automatically testing for SQL vulnerabilities. To my surprise, SQLMap didn’t find any SQL vulnerabilities.

I wasn’t going to leave it there though – this code was misbehaving and I wanted to find out why. When quotation marks cause errors in user inputs, it is often indicative of an SQL error and potential SQL Injection risk. If SQLMap couldn’t identify any SQL injection risks, why was this code misbehaving?

After much discussion and help from atthacks, we identified the issue we were dealing with wasn’t an SQL Injection risk – disappointing, I thought I was onto something here!

As part of our analysis, we registered an account with the following password: password’

We then took the hash from the database and cracked it using John.

The output from John the Ripper

As we can see, the quote has stored in the database as '. Therefore, as far as the system is concerned, my password was actually password', not password’. The quote seems to have been represented as the ASCII code for a quote, rather than the quote itself.

The question is, why would they do this? Technically, you can limit SQL Injection risks by removing quotes, but it certainly shouldn’t be relied upon. There are potential ways around this. They should be using proper parameterised queries to fully mitigate SQL injection vulnerabilities. I can only assume they’re doing both, as I found no way to leverage this into an SQL Injection.

This is completely unnecessary – parameterised queries will eliminate the SQLi risk. Encoding the quotation marks offers no further protection and breaks functionality.

The thing that bothers me most about this though is how this bug hasn’t been picked up on a penetration test? This bug isn’t a security bug, but a penetration test would have easily identified this. Do they not pentest their software before they make the software available? Was this tested but missed? Or, was it identified during testing, but added to the pile of bugs not worth fixing? Who knows – it does highlight serious concerns though at the level of security testing they do prior to releasing their software though. I’ve used WHMCS myself in previous jobs – I can’t say I have enough trust in its security these days, especially when you consider the type of data it should be securing.

Hacking Security

How NOT to hack Troy Hunt

I often try to remind myself that it’s really important to share knowledge. Everyone starts off in a similar position – unfortunately, some people quickly forget they were once starting out, so they retain as much knowledge as possible without sharing this amongst the community.

Fortunately, for the most part, I find the Infosec community is really good at sharing lessons learnt.

Troy Hunt shares a very good resource where you can test your hacking skills – I decided to give it a go. Unfortunately, I broke the website unintentionally. Sorry Troy! 😳

What I found

The website appears to be a directory of super cars. You can visit any of the manufacturer pages which list different cars. There is also the ability to register for an account and vote for your favourite car.

I decided to register for an account. I input a random e-mail address, name, and password. The website warned me I could not register as the password I had chosen was in use by another user. Worse still, the website told me which user had that same password! This isn’t good security practice at all.

Instead of trying a different password, I used the username and password it gave me to log in.

Stored XSS Vulnerability

I proceeded to go and vote on a car.

It looks as if all comments are made public, so I decided to put custom JavaScript in.

<script>window.location.href = "";</script>

I added this code as my comment, and it then appeared in the comments section of the web page. Consequently, this meant the JavaScript was then executed whenever someone visited this page. As a result, users were redirected to my website.

Stored XSS is extremely dangerous. There’s a number of things you can do, but here are a few examples:

  • Redirect users to another website. You could potentially redirect users to a fake login page hosted elsewhere that is styled like the initial website. They may then be tricked into submitting their login details on your website.
  • Cookie theft – a lot of websites will store a session cookie on your computer to remember you when you return to the website. Ultimately, if someone else obtained this session cookie, they could log in without your password. You can steal cookies very easily using XSS.

Now I had identified the stored XSS vulnerability, I browsed the website further to see what I could find.

SQL Injection

The next vulnerability I found was an SQL Injection. At the bottom of the main page are some links where you can display cars based on their cylinder count. Take a look at the URL:

As we can see, there is a parameter in the URL which specifies the number of cylinders.

At a guess, the query in the code that sits behind this will look something like this:

Speculative query: SELECT carID, carName FROM tableWithCars WHERE cylinders = $_GET['Cylinders']

I find it’s always helpful to try and predict the query you are trying to inject. If the code isn’t correctly sanitising the ‘Cylinders’ parameter, then perhaps we can add additional SQL code here and manipulate the query. If successful, we can bring back data we are not supposed to see.

Here are the injections I tried:

  • ?Cylinders=V12' OR 1=1--
  • ?Cylinders=V12' OR 1=1 UNION ALL SELECT '1' As t, table_name FROM information_schema.tables--
  • ?Cylinders=V12' OR 1=1 UNION ALL SELECT '1' As t, column_name FROM information_schema.columns WHERE table_name = 'UserProfile'--
  • ?Cylinders=V12' OR 1=1 UNION ALL SELECT '1' As t, Concat(Email, ' ', Password) COLLATE database_default FROM UserProfile--

These are just a few of the injection methods I found. Using these SQL Injections, you can:

  • See what databases are on the server
  • See the column and table names on the server
  • See the contents of other tables (such as the users table)

There are loads of things you can do. One of the things I attempted was to delete the contents of the users table. I presumed this would just flush the contents of the table meaning people would need to re-register. I won’t explain how I did this, as it seemed to break the pages where you can vote for a car. The pages were returning a server error – division by zero.

I tried to understand the reason for this. My assumption (which could be completely wrong) is as follows:

When truncating the users table, I believe it also dropped all rows in the Votes table. This is probably because the tables relate to each other and it will delete all related rows in other tables to avoid breaking referential integrity.

I then believe the pages where you vote for the car need at least one vote in order to function, potentially because it tries to calculate the rank based on the number of votes. If the car has zero votes, I guess it is trying to divide something by zero, which is a mathematical impossibility.

I tried finding other injection points on the website so I could attempt to fix this by inserting some vote rows for each car. Fortunately, I found a way! I inserted a test comment for each super car using another SQL Injection, and the website became functional again.

I recommend testing your skills on this website – Troy Hunt encourages users to do so, cordially. I certainly do not recommend deleting table contents though as I did – I didn’t think it would break the site quite like this. Sorry!