Parse is the most beautiful BaaS, allowing you to quickly raise a full-fledged server infrastructure for a mobile application. Perhaps it is because of this simplicity that many developers forget about emerging security issues and emerging vulnerabilities.
For those who are not familiar with the service, take a short excursion into what it is. Parse provides the developer with services such as cloud storage, push notifications, writing your own API, collecting statistics, crash logs, and more. In this study, we are interested in exactly the data warehouse, called Cloud Core.
All data in Parse is stored in classes (in fact - tables), between the records of which you can make full connections.

')
For each of the classes, client access rights are configured that affect the ability to search, add new entries, modify existing ones, and so on. By default, all actions are allowed. Of course, as usual, most developers, once setting up the tables they need, forget about setting up client permissions.

Closely confronted with Parse on one of the work projects and fiddling with the ACL setting, I decided to play around with other people's applications. I chose the object for research right at
parse.com/customers . They became
Cubefree - a search service for coworking sites.
To connect to the Parse account in the iOS application, a combination of two keys is used -
Application ID and
Client Key . To perform any actions on data in Cloud Core, the first thing you need to know this data. With the help of the smart
idb utility, which automates many routine actions during pentesting, we will decipher the executable file of the application. While the process is underway, we
’ll check
NSUserDefaults , the likely place to store the keys of interest.

In this case, everything is quite harmless - no confidential data. Let's go back to the decoded binary and feed it to
Hopper disassembler , which specializes in reverse engineering of applications written in Objective-C. We’ll start searching for keys with the
application: didFinishLaunchingWithOptions: method in
AppDelegate . One of the great features of Hopper is the pseudo-code method representation, which significantly lowers the threshold for understanding decrypted code.

As expected, the connection to the Parse account is happening here. Using these keys, we will analyze the data structure of the application and access rights to them.
The next step is to find the names of the Parse tables. In fact, where to find them, it becomes clear from the same screenshot - immediately after connecting to the server, you should call the
registerSubclass methods of several classes that inherit from the root
PFObject . Each of them must necessarily implement the
parseClassName method, which gives us the name of the table on the server.

Let us study the structure of each of the classes thus obtained:
PFQuery *query = [PFQuery queryWithClassName:@"ParseClassName"]; [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) { NSLog(@"%@", objects); }];
However, knowledge of structure alone is not enough. To understand how we can affect the operation of the application, you need to determine the access rights to all Parse classes. This is done quite simply - we just execute requests to the server corresponding to various permissions and analyze their results. To simplify these routine actions, I wrote a simple
Parse Revealer utility that automatically determines access levels to all known classes.

Based on our data, we can build a table:
Class name | Data structure | Access rights |
---|
ChatRoom | chatId (string) user1 (User) user2 (User) | GET: False FIND: True UPDATE: True CREATE: True DELETE: False ADD FIELDS: True |
Checkin | availableToShareTable (Bool) date (Date) invisible (Bool) statusCheckin (String) statusUser (String) user (User) workspace (Workspace) | GET: True FIND: True UPDATE: True CREATE: True DELETE: True ADD FIELDS: True |
Chatmessage | chatId (string) Message (String) sender (User) unread (Bool) | GET: False FIND: True UPDATE: True CREATE: True DELETE: False ADD FIELDS: True |
Notification | date (Date) sendUser (User) chekin (cheking) status (Bool) type (Number) accepted (Bool) | GET: True FIND: True UPDATE: True CREATE: True DELETE: False ADD FIELDS: True |
Review | date (Date) parkingStatus (Number) powerStatus (Number) soundStatus (Number) user (PFUser) wifiStatus (Number) workspace (Workspace) | GET: True FIND: True UPDATE: False CREATE: True DELETE: False ADD FIELDS: True |
Workspace | address (String) cc (string) city (String) country (String) foursquareId (String) lat (string) lng (string) location (PFGeoPoint) name (String) postalCode (String) state (String) | GET: True FIND: True UPDATE: True CREATE: True DELETE: False ADD FIELDS: True |
As can be seen from the obtained access rights, the developers have implemented a certain security policy, but, all the same, insufficient. We show what results can be achieved by playing with the
ChatMessage class.
The most obvious vulnerability - in any of the open chats, you can change both your own and other people's messages. After executing this code, a nice greeting turns into a habrasuicid:
PFQuery *query = [PFQuery queryWithClassName:@"ChatMessage"]; [query whereKey:@"message" equalTo:@", !"]; [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) { PFObject *object = [objects firstObject]; object[@"message"] = @", !"; [object saveInBackground]; }];

Similarly, you can add new messages, you just need to provide the new
PFObject with the correct
chatId . But It is worth noting that
Delete , set to
false , will not allow us to delete any of the created objects.
A much more serious vulnerability is incorrect mapping of data obtained from Parse. If the newly created
ChatMessage object
has no
sender field, the application crashes. Thus, nothing prevents us from running through all the watches ever created, adding an invalid message to them - and the application will crash for all users. This is already fraught with low ratings in the App Store, outflow of users and the failure of the project as a whole.
Other classes have similar vulnerabilities - but they are already beyond the scope of the current study.
As for security, everything is quite transparent here. Only a few rules need to be followed:
- Always adjust access levels for all created classes.
- For user-created data, use ACLs, allowing you to change them only to a specific circle of people.
- If the client needs to change only one of the properties (for example, the unread flag), then it is worth thinking about separating it into a separate table. Thus, it will be possible to bypass the possibility of changing other parameters of the object.
- Do not rely on the fact that Parse will always give valid data - do not forget to embed the appropriate checks.
- Do not forget that, in theory, the applicationId and clientKey can be accessed by any attacker, and consider a security policy based on this knowledge.
- The previous rule does not mean that you need to completely forget about obfuscation of lines in the code :)
- In especially difficult cases, feel free to use the Cloud Code.
If you see the features of your applications in this study, you should not blame Parse - as I said before, this is an excellent service that minimizes the costs of creating the server part of the application. And all considered vulnerabilities are solely the responsibility of the developers of the application.
Useful links:Other materials dedicated to the security of iOS applications: