The real kicker is that the app, on-load, will update the server with progress you’ve made since last signing it. It doesn’t do these updates as event based, either (as in, if you played 3 songs it submits 3 events with the details of the songs, and tallies up your XP) — instead, it just accepts a large JSON profile object.

The server blindly accepts anything that is sent through. Changing values here reflect accurately server-side!

Therefore, intercepting a single request and modifying the values, then clearing the app and re-signing in through Facebook will allow you to set your gems, level, and XP to anything you’d like. You can also set yourself to premium, as an alpha user, and your high score for each song.

Through this method, one is easy able to become the #1 player in the world, and give themselves an arbitrary amount of in-game currency.

That amount of gems is equivalent to $187,000. Now of course it’s not actually worth anywhere near that amount, but it’s easy to imagine how, on a more critical or SaaS product you could easily rack up charges due to non-verification of payment or purchase.

Possible Solutions

How should one fix this problem? We will explore a few common ways that are used to mitigate this attack vector.

Hash the contents and verify

Difficulty to break: Trivial

Difficulty w/ embedded salt: Medium

One of the first solutions to this problem was to include a hash along with the data, and would look somewhat like this:

This is great for testing the validity of your information and that there was not corruption, but it falls short of being a valid security method — all you would need is to rehash the information and replace the hash before forwarding the packet to the device.

This becomes somewhat more viable when you have a have a salt that is embedded in your binary. This is more formally known as “shared key” types of encryption. For instance, if you appended “My$ecure$@lt” to the end of your data before you verified it against the hash, it would prevent any pure man-in-the-middle attack. The attacker would have to decompile and inspect the binary, which is feasible but requires more effort and knowledge than a simple MitM attack.

What the hashing/verification would look like internally to your iOS application — it would append the salt, then hash it and verify against the provided hash. This is a “shared secret” method of encryption.

Through the use of an obfuscator, like Obfuscator-iOS, this method can become even more secure. It still would not stop a dedicated attacker, however.

Custom Encryption

Difficulty to break: Medium/Hard

This is more along the lines of obfuscation than clear and open design, but it will provide a non-trivial roadblock to most attackers. This pattern would involve encrypting the contents of your data somehow. This could range from a simple encoding (base-64) all the way to PGP, or even rolling your own encryption (which I do not recommend — a system is not secure until it has been exhaustively attacked). This would again require some form of key hidden in the binary, which is not impossible to reverse engineer and extract.

This will thwart all but the most dedicated attackers. A pure MitM attack will not suffice, and will require significant knowledge to figure out how to decrypt.

Cons to this approach are that it’ll require significantly more computing resources, and that it adds another layer of abstraction to your system.

Certificate Pinning

Difficulty to break: Difficult

Certificate pinning involves verifying that the certificate in which your information is encrypted/returns matches a known set of certificates — that is, it has not been “swapped out” in between. This is arguably one of the most secure methods to prevent MitM attacks — the device will not accept any connection that is not signed by the embedded certificates.

The only way to bypass this would be to jailbreak the device, and manually disable SSL verification or replace the embedded certificates with Burp Suite’s. This would require a much more sophisticated level of attack, and if your device is jailbroken/rooted then there are multiple other attack vectors to take into consideration.

Better Architectural Design

The best methodology for a secure system is to design it from the beginning with security in mind. This means securing your endpoints, and making conscious decisions for what information should be renewed each session, as well as how many levels of verification should be required at each stage. It involves having a more abstract, security-conscious view of your application, and what attack vectors it may have. In the example above, minimizing these vectors would mean requiring as little information as possible be refreshed each session, and not pull certain critical components from live-config.

Applicability and Further Research

Man-in-the-middle attacks are not anything new —this is more of an application of a security paradigm than a ground-breaking revelation. However, as a developer you are often more focused on preventing an outside attacker from compromising your users data integrity than from a MitM attack performed by your users themselves.

Snapchat and Facebook already implement certificate pinning, as it is one of the most reliable and secure methods of preventing any 3rd party from modifying the data in the middle of the connection (as of the writing of this article). It’s not impossible to circumvent (see iOS Kill Switch for jailbroken devices), but at that point an attacker would have access to a lot more than a single HTTP connection.

This write-up was meant to illustrate a common bad design pattern, and on how relying solely on SSL/TLS for your iOS app can lead to consequences, ranging from loss of revenue to full compromise of your application. This is especially true on iOS, where it is easy to simply rely on Swift’s HTTP requests without considering a user replacing the certificate with one of their own.