Vulnerability Inheritance across Forks - The Sugar/Vtiger/SuiteCRM Story

Posted by: Darren Martyn | Posted on: 2016-03-01 00:00:00 +0000

This story starts, as do many others, with our narrator having a bit too much free time on his hands, and some terrible code. Lets take it from the top, shall we?

Our head of sales wanted a CRM. Being a security company, and not wanting to outsource our CRM containing client data to a third party such as SalesForce (because we have not audited SalesForce, and therefore cannot be certain of their security), we decided it was best if whatever solution we implemented to make his life easier was an internally hosted solution.

Being fans of open source software, we decided to have a look at SuiteCRM, as not only was it simple to set up and use, it was also open source and easily extensible. Unfortunately for both SuiteCRM and our Sales department, I happened to have the notion to give the solution a quick and dirty assessment.

For those who are unaware, SuiteCRM is one of a couple of forks from the “SugarCRM” project, and is mantained by SalesAgility. Another fork from SugarCRM is the somewhat lesser known “VtigerCRM”.

Lets start with SuiteCRM.


Last May, I disclosed a moderately serious vulnerability to the SuiteCRM project. Now, before we get too much into it, I didn’t consider this a super serious issue originally - as the bug is post-authentication and requires a privileged account - however it clearly demonstrates a bypass of a poorly thought out security control.

In the “Upload Logo” functionality, when you upload a file, it is placed into the “upload/tmp_logo_company_upload/” directory, with whatever filename you set it to. If the file extension matches a “bad” extension on their blacklist, the filename has “.txt” appended to it, in an attempt to prevent shell uploads.

As any application security person will tell you, blacklisting is not the correct way to filter input. Whitelisting is a superior approach, as if your blacklist is incomplete (and protip: your blacklist is always incomplete), it can be bypassed.

Due to the developers not blocking “.phtml” extensions, which allow PHP code to be executed (there are loads of valid PHP extensions… Go ahead, try block them all!), it was possible to evade the blacklist and upload a PHP backdoor to this directory, where it was also executable. Had the directory been made non executable in the .htaccess configuration files, it is possible that remote code execution could have been mitigated against.

From this shell upload, it is trivial to spawn a reverse connecting shell as in my PoC and steal information, elevate privileges to root, etc.

In the patch/fix in this pull request/commit, they do a check to see if the file uploaded is a valid image, and if not, they delete the file by calling unlink(). The problem here is, they are still writing out the file, with the valid PHP extension, to the web accessible, executable directory. This introduces a race condition, wherin the attacker has to try access the shell and execute code (to, say, drop a second backdoor), before the verify_uploaded_image() call fails and unlink() is called. While this leaves open a window of only a split second or so, it is long enough for us to reliably execute code on the system.

All we have to do to make this exploit bypass the patch, is start sending commands to where the shell will be constantly and in a multithreaded fashion while uploading it. With more threads and repeated attempts at uploading the payload, it is highly likely that we can win the race within a couple of seconds and beat the unlink() call, spawning ourselves a reverse shell.

So I wrote a PoC. How the PoC worked is not much different to the prior exploit, and the PoC is simply a modified version of it. The file uploaded (this time using a .pht file extension, because blacklisting sucks), exists on disc for a mere split second before it fails the image validity check and unlink() is called on it. So we must “beat the unlink call” and request our backdoor file before it gets deleted. The most effective way of doing this is to repeatedly upload the file in one thread, while requesting it in another. The PoC I have written here does this in a rather crude manner, landing us with a shell within about seven to ten seconds.

To optimize the exploit, instead of logging in every single time we want to pwn the box, we simply log in once, and reuse the session we get from the server. This means we can do the upload step in a far “tighter loop”, and generally speeds things up (without this optimization, we were getting a shell in about a minute or so). A video is embedded below to demonstrate how fast this process works.

The vulnerabilities found in SuiteCRM (incomplete blacklisting, broken access restrictions, and the race condition) were issued the following CVE’s: CVE-2015-5946, CVE-2015-5947, CVE-2015-5948. As far as I am aware, the current version of SuiteCRM is patched against these, but I have not put a great deal of time into exploring it - I was heavily discouraged from doing so by some of the reactions from the SuiteCRM community.

Now, lets take a look at the “Parent” that SuiteCRM was forked from - SugarCRM.


I won’t spend a whole lot of time on SugarCRM, as the vulnerability it suffers is the exact same as the original SuiteCRM issue. The blacklist used to prevent upload of executable code via the “Company Logo” uploader is broken, and you can upload a .phtml webshell to a directory in the webroot, allowing remote code execution. One caveat, however - by default, the .htaccess supplied with SugarCRM blocks web access to this directory. Thankfully, most administrators seem to cripple this protection somehow (I have not seen an instance outside of my testing where it worked right) rendering the issue exploitable.

Looking over the history of the projects, it is fairly clear that the SuiteCRM issue actually originated deep within the bowels of SugarCRM, and was inherited as part of the fork. This gave me the notion to take a quick look at the other SugarCRM fork - VtigerCRM.


VtigerCRM made heroic efforts to try prevent me from getting remote code execution, with slightly better extension blacklisting (well, a bigger blacklist), some extension whitelisting, some attempts to filter out PHP code, and suchlike. It proved to be a very interesting exercise in getting around layered “protections” in PHP applications. Take a look at the following piece of code.

It is a nice effort, but whoever put it in had clearly never heard of PHP short tags, where I simply use “<?”… So thats one security “protection” avoided.

To prove this, I ended up cobbling it together into a quick script before proceeding. I hope the output below clearly shows how trivial it was to avoid this “check”.

So, we can sneak PHP code past the “Code Injection Checker”, which is a start. However, we don’t get off that lightly.

Next, we have to get our valiant PHP code past a “getimagesize()” check. So, our PHP code has to be part of an image. Luckily for us, image file formats contain a lovely feature called “EXIF Metadata”. So, we do the following to add an EXIF comment containing a basic webshell to a legitimate PNG image file.

Now, when we upload “hacked.png”, it gets past the getimagesize() check, gets past the code injection test, and lands in the webroot. We just have the little problem of its extension not being executable. So it is time to look at that whitelist of extensions, and get creative…

While looking at extensions that are accepted, I noted that “x-png” is an allowed extension. Furthermore, Apache has no default handler for this extension in my LAMP instance. So, we can name our file something like “hacked.pht.x-png”, and Apache will just execute it as PHP (because of the .pht).

In order to upload our file, we name our file “hacked.pht.png”, trap the request using Burp Proxy, and change it to “hacked.pht.x-png” before allowing it to go on to the webserver. I do this, because it worked better than uploading it as “hacked.pht.x-png” and changing the Content-Type in the upload on the fly, which just seemed to fail every time (incidentally, yes, it does a MIME type check). The file lands in the webroot and we are able to call it like so, executing whatever we want.

Altering the request in Burp Proxy

Arbitrary Code Execution

All in all, despite the extra (useless, and hilarious) checks imposed on us, we effectively have the exact same vulnerability at the core of it - functionality inherited from the SugarCRM project.

So to wrap all this up, it is often worthwhile, when auditing open source projects, to have a look at forks of the project to find out if the bugs originated elsewhere, and if the bugs were copied across forks. Often you get to watch bugs evolve and get more complex as they slowly mutate through inheritance.

PS: MD5…. MD5 everywhere

About the Author

Darren Martyn is an Irish ex-pat, presently employed by Xiphos Research to break things and research how to break things, and has yet to learn the refined skill of referring to himself in the third person (like the queen).