Wed 17 January 2018 By Simon Rigg
Welcome back! This is the third part of a mini series which uses a fictional product, “ShipFast”, to walk you through the process of defending against various exploits in a mobile application to gain access to data on a remote server allowing real users of the system to gain an unfair business advantage at the expense of the company.
In this post I'll dive into the second attack scenario and look at how we can defend against it.
Full source code for the demo is available on github if you want to play around and try things out for yourself, but it’s not necessary to gain an understanding of the exploits and defences I’ll be demonstrating.
The Second Attack
If we use a MitM proxy technique to view network activity between the app and the server, we can observer that a new "SF-HMAC" header is introduced:
In this case, the name of the request header is a bit of a giveaway, however, deeper analysis of the mobile app would also lead us to discover that API requests are now signed with an HMAC. Once that is discovered, we know there are three things to find to break this protection:
1 The HMAC algorithm
2 The HMAC secret
3 The HMAC message
Since we know the app must be computing this HMAC request header, we can attempt to decompile it using freely-available reverse engineering tools and perform static analysis. We know the result is added to an "SF-HMAC" header. We know an HMAC is used. We can hypothesise that the app must contain an embedded secret for the HMAC and probably uses whatever HMAC function comes as part of the standard libraries. We will now test our hypothesis.
We will decompile the app's APK using the tools "apktool", "dex2jar" and "JD-GUI". I will not cover the details here as other people have done that very well, but a good tutorial can be found on Bright Developers.
Looking through the decompiled code, we find something interesting:
Eureka! My bath is overflowing, brb... false alarm. Well, we have found quite a nugget here! This method, first of all, has been obfuscated. So +1 for the developer for at least doing that. In our case this used ProGuard but other solutions are available.
However, it is very important to note that you typically cannot obfuscate public methods as it is not certain at compile time what will be invoking them, so it is not safe to obfuscate them as it could lead to a nasty runtime exception. This is especially true for standard libraries, for example, javax.crypto which provides HMAC support.
What we see in our decompiled code snippet is:
1 The HMAC algorithm (the "HmacSHA256" string)
2 The HMAC secret (the long base 64 string "4ymoo..." etc)
3 The HMAC message. This is harder to find, but we can follow through the decompiled Java code or even the byte code pretty easily and look for calls to "Mac.update()" as those calls make up the HMAC message. We know when things are finished, because the "Mac.doFinal()" method will be called which produces the result.
HINT: It is easy to find standard library cryptographic functions in obfuscated and signed mobile apps.
In this stage of the demo, the secret is in code which is much better than in a static text file, but is still easily retrievable. Armed with our new knowledge, we can update ShipRaider to compute the new HMAC header and therefore continue to authenticate our rogue API requests.
To enable this stage of the demo, modify the "currentDemoStage" variable in the ShipRaider's "shipraider.js" file server/shipraider-rogue-web/web/js/shipraider.js:
If things are configured correctly, you should now be able to run ShipRaider against the ShipFast server and grab those bonus shipments.
The Second Defence
It turns out that our previous approach is a really good starting point: signing the API requests in the app proving both who and what is making those requests which can then be verified by the server. This binds the requests to the user and to the running app.
The problem is our implementation. To begin with, we use a static secret in code in the form of a single string. You may think this is obvious now that I mention it, but you would be surprised, even shoked, to discover the number of cloud-based services which offer access through an API or SDK which involves initialisation using an API key, an API secret and often a base URL in code. This makes it easy to adopt the service, but unfortunately also makes it easy to exploit the service.
We can obfuscate the HMAC secret by computing it at runtime which means regular static analysis will not yield the secret: the app must be run in order to generate the secret and store it in memory for use in the HMAC computation. The approach we use is kept simple for demonstration purposes, and the process is as follows:
1 Take our original HMAC secret embedded in the app code
2 Take the user's ID Token JWT fetched at runtime by the user logging in using Auth0
3 Perform an XOR operation on the two
4 Use the result as our new dynamic HMAC secret
In our Android app, the code looks like this:
And on the ShipFast server side, the code looks like this:
It is almost like some strange game of spot the difference, but as you can hopefully see, the app and server perform the same HMAC calculation using the same secret key and message, and the server ensures both components end up with the same answer before authenticating the API request.
To enable this stage of the demo, modify the "currentDemoStage" variable in the app's "DemoConfiguration.kt" file app/android/kotlin/ShipFast/app/src/main/java/com/criticalblue/shipfast/DemoConfiguration.kt: Also modify the "config.currentDemoStage" variable in the server's "demo-configuration.js" file server/shipfast-api/demo-configuration.js:
Restart everything again, the app should continue to work normally, but it looks like we have blocked those pesky ShipRaider pirates for now!
We need to get smarter now! In my next and final post in this series I will demonstrate a runtime attack on this second defence... but can we effectively defend against this?
Time to hack some kernels...
Thank you for reading and stay tuned!