Closed Bug 1294567 Opened 8 years ago Closed 8 years ago

SQL Injection at arewefastyet.community.scl3.mozilla.com

Categories

(Websites :: Other, defect)

defect
Not set
normal

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: awile, Unassigned)

References

()

Details

(Keywords: reporter-external, sec-low, wsec-errorhandling, Whiteboard: [reporter-external] [web-bounty-form] [verif?])

I was enumerating mozilla's community subdomain, and found 'arewefastyet' connected to it, so I proceeded to look around and see if I could find anything. It was an older platform (from what I saw) that had not been closed off from the public yet. It had a php file: /regressions/data-search.php

That took JSON data as a post request, and returned what looked like machine IDs or something like that. I simply intercepted the POST request in Burp Suite, and threw in an apostrophe and it terminated the SQL query, and gave an error message.

The error message is as follows:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ORDER BY awfy_run.sort_order DESC' at line 6

So there is clearly at minimum an information disclosure vulnerability that tells us the database type, and an SQL injection vulnerability, albeit challenging to exploit, it should be fixed. I have included a sample post request from burp suite if you should choose to examine it further. I am unsure if it is exploitable, but if anything it is a serious information disclosure vuln, it tells me the database type. 

I hope to hear from you soon, please consider for bounty. :)

I hope to hear from you all soon, thank you.
Flags: sec-bounty?
"I have included a sample post request from burp suite if you should choose to examine it further."

I don't see an attachment? Can you re-attach? 

I'd be interested to know if you can retrieve more than just the database type?
Jeff, I was unable to pull any more information other than the database type. It's a tricky vulnerability because the way it works (at least how I think), is it's taking the JSON from the post request and concatenating it into an SQL query, rather than using PDO/prepared statements. I was unable to create a payload that pulled anything, just was able to get the database type. bugzilla doesn't like my upload, so here's the paste request text:

POST /regressions/data-search.php HTTP/1.1
Host: arewefastyet.community.scl3.mozilla.com
Content-Length: 204
Accept: application/json, text/plain, */*
Origin: http://arewefastyet.community.scl3.mozilla.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
Content-Type: application/json;charset=UTF-8
Referer: http://arewefastyet.community.scl3.mozilla.com/regressions/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: PHPSESSID=p6nhi3t488i1qfi44h59imqiv4; _gat=1; _ga=GA1.2.912236108.1470945181
Connection: close

{"machines":["11","12","14","16","17","26","27","28","29","30","31","32","33"],"modes":["14","16","20","21","22","23","25","26","27","28","29","31","32","33","35"],"states":["confirmed"],"bug":"1167677"}'

Note the semicolon at the end to trigger the bug. You can import that into burp for testing. It is more than likely exploitable, I was just unable to do it myself.
I'm looking into this now, I should have some details to share shortly.
awile: first, thank you for submitting this bug.  We certainly appreciate your submission and alerting us of this.

When performing a simple GET request for this page, I get the same result.

REQUEST

GET /regressions/data-search.php HTTP/1.1
Host: arewefastyet.community.scl3.mozilla.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: PHPSESSID=SNIP
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0

RESPONSE

HTTP/1.1 200 OK
Date: Fri, 12 Aug 2016 01:56:42 GMT
Server: Apache/2.2.22 (Ubuntu)
X-Powered-By: PHP/5.3.10-1ubuntu3.21
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Vary: Accept-Encoding
Content-Length: 179
Connection: close
Content-Type: text/html

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ORDER BY awfy_run.sort_order DESC' at line 6

I also went and replayed your POST action and although a trailing ' causes a MySQL error, so does any arbitrary string.  Like so...

REQUEST

POST /regressions/data-search.php HTTP/1.1
Host: arewefastyet.community.scl3.mozilla.com
Content-Length: 207
Accept: application/json, text/plain, */*
Origin: http://arewefastyet.community.scl3.mozilla.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
Content-Type: application/json;charset=UTF-8
Referer: http://arewefastyet.community.scl3.mozilla.com/regressions/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: PHPSESSID=9obk3815njo1kbnekaqshsge06; _gat=1; _ga=GA1.2.912236108.1470945181
Connection: close

{"machines":["11","12","14","16","17","26","27","28","29","30","31","32","33"],"modes":["14","16","20","21","22","23","25","26","27","28","29","31","32","33","35"],"states":["confirmed"],"bug":"1167677"}anything

RESPONSE

HTTP/1.1 200 OK
Date: Fri, 12 Aug 2016 02:00:32 GMT
Server: Apache/2.2.22 (Ubuntu)
X-Powered-By: PHP/5.3.10-1ubuntu3.21
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Vary: Accept-Encoding
Content-Length: 179
Connection: close
Content-Type: text/html

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ORDER BY awfy_run.sort_order DESC' at line 6

With that said, I'm convinced we're seeing an actual SQL injection as much as we're seeing a SQL error, the same error in fact that doesn't require SQL-like injections to trigger.  To the other point above about this being a "serious information disclosure vuln" I would disagree.  This project is an open-source project and the full DB schema can be found here (https://github.com/h4writer/arewefastyet/blob/master/database/schema.sql).

I'm assigning this a sec-low severity rating until an actual SQL injection vulnerability can be proven with a viable PoC.  Until then, I think this should be treated as a non-security bug.
> With that said, I'm convinced we're seeing an actual SQL injection as much
> as we're seeing a SQL error, the same error in fact that doesn't require
> SQL-like injections to trigger.  To the other point above about this being a
> "serious information disclosure vuln" I would disagree.  This project is an
> open-source project and the full DB schema can be found here
> (https://github.com/h4writer/arewefastyet/blob/master/database/schema.sql).

Correction: "With that said, I'm NOT convinced we're seeing an actual SQL injection..."
Hans: I see you're active developer on https://github.com/h4writer/arewefastyet.  Would you be able to comment on why we're seeing the above SQL error messages when performing the above described GET and POST requests to arewefastyet.community.scl3.mozilla.com?
Flags: needinfo?(hv1989)
The reason the SQL error is being shown, is because php://input is not being validated before it is being sent into json_decode. If a malformed json request gets sent to json_decode, it returns nothing, and causes the SQL query to be messed up. Regardless, I don't know exactly how to do it, but the only type of user input sanitation that is going on is mysql_escape_real_string, which is totally bypass-able. The author of this code should be using prepared statements rather than building the statement himself. It helps take care of problems like these. On top of that it should also confirm that the POST data is properly parsed before building an SQL query on top of it. I am unsure of how to exploit this myself, but I am certain it is possible given a skilled attacker.
So, just to flesh out the possible attack scenario, an attack would need to do the following...

1.) Provide a valid JSON string in the post data that would viably parse as JSON into results
2.) The parsed data elements for machines, modes, states, bug would also need to include a bypass string to defeat either the int type casting strategy ((int)$request->machines[$i];) or the mysql_real_escape_string strategy("'".mysql_real_escape_string($request->states[$i])."'";)
3.) The resulting output would also need to produce a valid/viable SQL statement that an attacker could inject viable queries into.

Based on the above hypothesis, I believe the concept is plausible, but as you mentioned this would be a relatively high-level of sophisticated attack to pull this off.  However, the source code is publicly accessible and an attacker could continually perfect the payload offline and with some luck they might be able to achieve a successful SQL injection.

To address the viability of #2 above, I've included the following stackoverflow article (http://stackoverflow.com/questions/5741187/sql-injection-that-gets-around-mysql-real-escape-string) which states that the bypass scenario is only viable in "very obscure edge cases" which I don't believe apply in this context.

At this time, I think the viability of such an attack, although plausible, has yet to be demonstrated in this bug report.  If you'd like to continue working and provide a more clear example of successful SQL injection,  I think this would go a long way to helping us understand whether this is a higher severity issue.
I did a little follow up testing with sqlmap (Level=1, Risk=1) and was unable to demonstrate any viable SQL injection beyond the heuristics test, which is likely FP'ing because of how easy it is to trigger the SQL error.  I'm including the examples I ran here just so it's clear what I've already covered...

# Testing bug parameter
sqlmap.py -u "http://arewefastyet.community.scl3.mozilla.com/regressions/data-search.php" --method POST --data="{"machines":["11","12","14","16","17","26","27","28","29","30","31","32","33"],"modes":["14","16","20","21","22","23","25","26","27","28","29","31","32","33","35"],"states":["confirmed"],"bug":"*"}" --dbms=mysql -v 2

sqlmap.py -u "http://arewefastyet.community.scl3.mozilla.com/regressions/data-search.php" --method POST --data="{"machines":["11","12","14","16","17","26","27","28","29","30","31","32","33"],"modes":["14","16","20","21","22","23","25","26","27","28","29","31","32","33","35"],"states":["confirmed"],"bug":"*"}" --dbms=mysql -v 2 --tamper=space2comment

# Testing states parameter
sqlmap.py -u "http://arewefastyet.community.scl3.mozilla.com/regressions/data-search.php" --method POST --data="{"machines":["11","12","14","16","17","26","27","28","29","30","31","32","33"],"modes":["14","16","20","21","22","23","25","26","27","28","29","31","32","33","35"],"states":["*"],"bug":"1167677"}" --dbms=mysql -v 2 --tamper=space2comment

# Testing machines parameter
sqlmap.py -u "http://arewefastyet.community.scl3.mozilla.com/regressions/data-search.php" --method POST --data="{"machines":["*","12","14","16","17","26","27","28","29","30","31","32","33"],"modes":["14","16","20","21","22","23","25","26","27","28","29","31","32","33","35"],"states":["confirmed"],"bug":"1167677"}" --dbms=mysql -v 2 --tamper=space2comment
I believe #2 above is probably the biggest hurdle here, if you can find a way around that, this seems like it could be possible.
Yeah, I tried and tried again with sqlmap, unable to do anything with it, but then again, sqlmap is not capable of pulling off an attack of this nature. So, I think a better demonstration of the severity of the attack and the viability of the situation is explained very well in this article here:

http://www.iodigitalsec.com/mysql_real_escape_string-wont-magically-solve-your-sql-injection-problems/

That is a better article for use as a reference point. As for myself, I may call it quits, web appsec is not my specialty and manual sql injections most certainly not. I can assure you though that someone with more skills than myself is more than likely capable of this attack.
awile: thanks for the reference, hopefully hans will have some mitigations added soon, but if not I might spend some time trying to proof it out.  Thanks!
As for mitigations, using prepared statements will most certainly prevent the injection without a doubt. He does typecast the integers as for proper practice which is good. To prevent the triggering of the error message in the first place, a simple validation of whether or not the POST data was actually parsed should suffice. Insure that the json_decode function actually detects JSON, before continuing to build the statement by putting the SQL query in an if block. 

Those should mitigate the problem. I do have a question though. I'm new to the bug bounty program, so I am kind of unsure as to how it works, I don't know if this bug qualifies or not, and if not, why?

Thanks.
I would also like to note, after running the source code through RIPS static code analysis tool, it did find a lot of problems, mostly false positives, but I suggest hans do the same, just to insure that the code is totally secure. There was one sink of user input that could possibly lead to code execution.

If you would like the report I could upload it here perhaps?
awile: If you have a specific PoC for a separate issue, please submit a separate bug, just so it's more clear what issue is what.  I also dug into this issue further and I believe the likelihood of a viable bypass for mysql_real_escape_string is unlikely for data-search.php.  One of the things you'll notice about the call here (https://github.com/h4writer/arewefastyet/blob/master/website/regressions/data-search.php#L20) is that the call is wrapped in single quotes upon insertion into the array.  This is in fact is part of the guidance provided in the article you referenced here (http://www.iodigitalsec.com/mysql_real_escape_string-wont-magically-solve-your-sql-injection-problems/).

Including here for clarity...

"As pointed out by reader Koval, this issue could have been solved by amending the code to single quote the $gids. This would require a single quote to break out of, which would be escaped by mysql_real_escape_string:"

Hans: the only feedback on how to improve this code would be to move toward prepared queries, but from a security perspective, I don't any issue with the current implementation from a SQLi vulnerability standpoint.
awile: I missed your comment earlier about the bug bounty program, but I'll answer it now.  This particular site is not part of the Mozilla Web Sec Bug Bounty program.  If you're curious about getting bounties for bugs, this site is a good resource to check out (https://www.mozilla.org/en-US/security/bug-bounty/faq-webapp/#eligible-bugs) as it will increase your changes of getting a bounty.  With that said, we always reserve the right to reward bounty participants for non-eligible sites if we believe they've helped us sufficiently mitigate a high-risk issue (Example Bug 1293111).
Flags: sec-bounty? → sec-bounty-
Thanks for the concern and looking into this!
As far as I can see there is no SQL injection possible (but just being a bit sloppy). All inputs are casted to int and for the uses of mysql_real_escape_string it is used correctly. I did read the given article hoping to learn something new about mysql_real_escape_string, but to me it just seems misuse of mysql_real_escape_string.

The mysql error is only produced when $where is empty. I.e. no where clause statements are created (due to bad input or no given input). In that case the code still creates the WHERE statement, but being empty the query is invalid. Giving this sql error

I've added the following to resolve this:
https://github.com/h4writer/arewefastyet/blob/master/website/regressions/data-search.php#L40
Flags: needinfo?(hv1989)
Hannes: if you can let me know once it's deployed, I can do a quick follow up to verify the live behavior.
Flags: needinfo?(hv1989)
(In reply to Jonathan Claudius [:claudijd] (use NEEDINFO) from comment #19)
> Hannes: if you can let me know once it's deployed, I can do a quick follow
> up to verify the live behavior.

It is already deployed
Flags: needinfo?(hv1989)
Confirmed no other SQL error were present via GET/POST payload mentioned above.

Thanks Hannes!
Status: UNCONFIRMED → RESOLVED
Closed: 8 years ago
Resolution: --- → FIXED
Group: websites-security
You need to log in before you can comment on or make changes to this bug.