Parse Security in iOS App



    Parse is the most beautiful BaaS, which allows 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 problems and emerging vulnerabilities.

    For those who are not familiar with the service, we will 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 much more. As part of this study, we are interested in exactly the data warehouse called Cloud Core.
    All data in Parse is stored in classes (essentially tables), between the records of which you can establish full-fledged relationships.



    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 client permissions.



    Having closely encountered Parse on one of the working projects and tinkering with ACL settings, I decided to play with other people's applications. I selected an object for research directly on parse.com/customers . It became Cubefree - a service for finding places for coworking.

    To connect to the Parse account in the iOS application, a bunch of two keys is used - Application ID and Client Key. To perform any action on data in Cloud Core, the first thing you need to do is find out that data. Using the smart idb utility that automates many routine actions during pentesting, we decrypt the executable file of the application. While the process is underway, we will check NSUserDefaults, which is a very likely place to store the keys of interest to us.

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



    As expected, the connection to the Parse account occurs here. Using these keys, we will analyze the data structure of the application and access rights to them.

    The next step is to search for the names of the Parse tables. In fact, where to look for them, it becomes clear from the same screenshot - immediately after connecting to the server, call the registerSubclass methods of several successor classes of the root PFObject . Each of them must necessarily implement the parseClassName method, which returns the name of the table on the server of interest to us.



    We 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 an application, you need to define access rights to all Parse classes. This is done quite simply - we just fulfill the requests to the server that correspond to different permissions and analyze their result. To simplify these routine actions, I wrote a simple utility called Parse Revealer , which automatically determines access levels to all known classes.

    Based on the data we received, we can build a table:
    Class nameData structureAccess rights
    ChatroomchatId (String)
    user1 (User)
    user2 (User)
    GET: False
    FIND: True
    UPDATE: True
    CREATE: True
    DELETE: False
    ADD FIELDS: True
    CheckinavailableToShareTable (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
    ChatmessagechatId (String)
    Message (String)
    sender (User)
    unread (Bool)
    GET: False
    FIND: True
    UPDATE: True
    CREATE: True
    DELETE: False
    ADD FIELDS: True
    Notificationdate (Date)
    sendUser (User)
    chekin (Cheking)
    status (Bool)
    type (Number)
    accepted (Bool)
    GET: True
    FIND: True
    UPDATE: True
    CREATE: True
    DELETE: False
    ADD FIELDS: True
    Reviewdate (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
    Workspaceaddress (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 you can see from the obtained access rights, the developers have implemented a certain security policy, but, nevertheless, insufficient. We show you 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 others' messages. After executing this code, a nice greeting turns into a habrasuicide:
    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, just 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 the incorrect mapping of data received from Parse. If the freshly created ChatMessage object does not have a sender field- the application crashes. Thus, nothing prevents us from running through all the watches that have ever been created, adding an invalid message to them - and the application will crash from all users. This is already fraught with low ratings in the App Store, an outflow of users and the failure of the project as a whole.
    The remaining classes have similar vulnerabilities - but they are already outside the scope of the current study.

    With regard to security - everything here is quite transparent. Only a few rules need to be followed:
    • Always configure 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) - it is worth thinking about selecting it in 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, theoretically, applicationId and clientKey can be accessed by any attacker, and think over 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 Cloud Code.

    If in this study you see features and your applications, do not scold Parse - as I said, this is an excellent service that minimizes the cost of creating the server side of the application. And all the considered vulnerabilities are solely the responsibility of the application developers.

    Useful links:

    Other security content for iOS apps:

    Also popular now: