Welcome back! This is the fourth and final 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 third attack scenario and what is required to effectively 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.

Enjoy! :-)

The Third Attack

If we MitM the API traffic again, we still see the "SF-HMAC" header.

Recall we require three things break this protection:

1 The HMAC algorithm

2 The HMAC secret

3 The HMAC message

Once again, we will decompile the app's APK using the tools
"apktool", "dex2jar" and "JD-GUI". Looking around, we find the
following:

Okay, it looks a little different this time round. There is a
lot more 'noise' between the static secret we discovered earlier
and the initialisation of the HMAC. We added this obfuscation
ourselves, but it should be noted that there are commercial
tools available which achieve the same goal automatically.

Things were going so well for ShipFast, but alas, it is typically
not possible to obfuscate public library methods and we still
notice a couple of interesting points:

1 The algorithm is still "HmacSHA256"

2 The message appears to be the same

3 The secret key is computed using a combination of static
and dynamic data.

Looking at the decompiled code, we see there are multiple
variables involved in computing the secret, but the result
still ends up being a single parameter to the "SecretKeySpec"
object constructor. In order to break this, we need to run the
app and find out what that first parameter is.

As an attacker, we have a number of options available to us. We
can repackage the original signed APK and enable debugging of
the app by adding the following string to the manifest file:

android:debuggable="true"

We can then resign the APK and run it on an emulator or device
and debug it in order to derive the HMAC key.

There is another option available which avoids the need to modify
the original APK to include a debug flag. We could use a dynamic
instrumentation framework such as Frida
and follow along the docs
to create a script which can dump the HMAC key when required.
Frida is capable of attaching to a running process, poking
around that process, then detaching from it and leaving it in
the original state. Ouch! To run this, however, we do need to
be on a rooted/jailbroken device or emulator.

There are other such frameworks to modify apps on rooted/jailbroken
devices such as the Xposed framework for Android. I recommend you
check out the video on YouTube
to see how Xposed is used to break TLS certificate pinning.

In this walkthrough though, we will choose the option to enable app
debugging, unzipping the APK, adding the debug flag to the
manifest, zipping the APK and resigning it before running it on
an emulator.

In Android Studio, we add a method breakpoint to the
"javax.crypto.spec.SecretKeySpec" constructor which accepts the
key and algorithm as parameters. When we run our app and trigger
an action to perform an authenticated API request, such as
fetching the nearest available shipment, we will break on the
constructor of SecretKeySpec and see the computed HMAC secret.

Using a debugger also makes it possible to step through the code
and learn about the algorithm so we can replicated it in the
rogue ShipRaider website.

If things are configured correctly, you should now be able to run
ShipRaider against the ShipFast server and grab those bonus shipments,
even although we have migrated to using a dynamic secret key for the
HMAC used to sign API requests.

The Final Defence?

It is possible to build on the previous defence by use of a more
sophisticated dynamic key for the HMAC.

One option would be to introduce code to compute a signature of the
app's APK at runtime, something similar to the V1 and V2 signatures
already included.

We could also verify the signing authority of the APK to ensure the
app is not repacked and resigned by someone else (I'm looking at you,
ShipRaider pirates!).

As pointed out in the third attack, it is possible for an attacker to
debug the running app if they modify the original APK or use an
instrumentation framework such as Frida to scrape data from a running
app. To mitigate these, we would required:

1 A way of detecting app repackaging

2 A way of detecting app debugging

3 A way of detecting running an app on a rooted or jailbroken device
or on an emulator

This information could all be tied to the API request HMAC to add further
protection.

We need to be careful with root/jailbreak detection as there are
methods of circumventing these on mobile platforms such as RootCloak and
Hide my Root
on Android, and tsProtector
on iOS. A quick online search for "detect android root from app" or
"detect ios jailbreak from app" yields results which developers typically
adopt to protect these environments. This knowledge has been used
recently by the online community to work around root/jailbreak
detection on popular app store apps and unlock protected features.

If we step back for a moment and look at the previous defenses objectively,
there are several problems we can identify:

1 The use of static secrets or sensitive data embedded in the app, running
in an untrusted environment (untrusted, as it is in control of a user
rather than the company who provides the server and API)

2 The use of a dynamically-obfuscated secret in the app only known at
runtime which, despite dispersion in code, still cumulates to a single
'secret key' variable

4 The lack of TLS certificate-pinning implemented in such a way
that it is not trivial for an attacker to simply replace the set of
trusted certificates or certificate fingerprint 'pins' and enable a
MitM proxy to steal sensitive data in-flight

5 The use of standard, out-of-the-box, platform-provided cryptographic
functions; the SHA256 HMAC in our case which is typically not obfuscated
as it is public library API

6 The implementation of 'secure' code in Kotlin (or indeed Java) rather
than native C/C++ or assembly which results in a reasonably high-level
bytecode representation free tools do a good job of reversing back into
their original source form, even with the lack of symbols

For the ShipFast company to ensure its shipment service is protected from
attacks such as those suggested in this walkthrough, the API server running
in a trusted environment must ensure it can authenticate code running in
untrusted environments such as mobile devices. It must authenticate the
mobile app and its environment at runtime, in addition to authenticating
the user and the network channel.

Although it is possible for ShipFast to develop a suitable solution
themselves, this requires sophisticated mobile device and cloud server
security knowledge and experience, incredible creative and persevering
penetration testing, the ability to analyse, identify and adapt
vulnerabilities yesterday, and a great deal of time
(I hear flux capacitors
are good for this too). If ShipFast fail to act quickly and effectively,
their profits will be hit badly and their reputation could be severely
crippled.

Another option for ShipFast would be to invest in an existing solution
which has solved these problems already and is prepared to protect their
API and mobile app business. Approov by CriticalBlue
is a product specifically designed to do just that in a unique way which:

2 Does not require a static secret to be embedded in the app or the
use of a system-provided cryptographic library, but instead performs
a dynamic integrity check using a patented low-level approach based on
many years of low-level software analysis experience

3 Is easy to integrate and quick to deploy via a cloud service and
mobile SDK for Android, iOS and hybrid mobile platforms without
impacting customer experience. Nimses, a multi-million user base customer
with API data-scraping problems went live with Approov in just over
a week.

4 Is a constantly-monitored, enterprise-grade, highly-available and
highly-performant proven cloud-based API and app protection service

For this defense, we will walk through the process of integrating
Approov into the ShipFast mobile app and backend Node.js server and
demonstrate its effectiveness in protecting the ShipFast web API
from those pesky ShipRaider pirates!

The steps to integrate Approov into a mobile app and backend API are
really simple. If you do not believe me, check out our recent
press release
and you will see a real case of this in action! The steps required are:

3 Add some code to your mobile app to trigger the app authentication
process which will communicate with the Approov Cloud Service and issue
you with an authentication JSON Web Token (JWT) similar to the one
used by Open ID Connect (OIDC) for authenticating users

4 Send that token along with any web server API calls which require
your app to be authenticated

5 Register your mobile app with the Approov Cloud Service so we can
verify it at runtime and ensure it is present and unaltered

6 Add a simple JWT check to your server code to verify the Approov
JWT in authenticated requests, using one of the many available
industry-standard JWT libraries

In this walkthrough we have already added a new Android module to our
Android Studio project so we can include the Approov aar SDK. The
steps to achieve this can be found in the Approov User Guide

This request interceptor can be included in our OkHttpClient as follows:

We also need a solution to mitigate stealing of the Approov JWT by
MitM attacks, so we provide a custom Hostname Verifier which ensures
the TLS connection used by the app to perform authenticated server
API requests is tracked by Approov. Essentially, if the connection to
the Approov Cloud Service or any protected API endpoints is proxied,
the Approov authentication process can track that and provide an
invalid token.

It is worth noting here that Approov achieves this
"dynamic TLS certificate pinning" without you having to embed any
certificate data in the app and worry about updating it when a
certificate expires or the certificate's private key is compromised.
Approov will keep up with changes to your API certificate.

This hostname verifier can be included in our OkHttpClient as follows:

If you are unfamiliar with the concept of hostname verification
this file may look a little daunting, but I will attempt to
explain it a little here. A hostname verifier verifies a
particular host used in a particular HTTPS connection. Before
the "HTTP" part, layer 7 of the OSI model,
the "S" part, layer 4 of the OSI model, must first be
established. That is, Transport Layer Security must be applied.

You can read many documents about how this uses asymmetric
cryptography and X.509 certificates in a handshake to establish
a symmetric shared key between the client and server used to
encrypt, decrypt, sign and verify network traffic; but prior to
this phase, the hostname verifier allows apps to customise the
validation process of the host server and we use that to synchronise
the current network session for an API request with Approov so
the Approov token's validity can reflect the validity of the host
server certificate. If we are talking to the real thing, we should
get a valid Approov token. If API traffic is intercepted by a
proxy decrypting and re-encrypting API traffic so it can spy as
a MitM, then we should get an invalid Approov token.

So why use a hostname verifier and not just set all this up front
as a 'pin set' in the trust manager and then use that for API
requests? That is why it is dynamic pinning, and how we avoid
the challenge of playing catch-up as many certificate pinning
solutions are faced with when a certificate expires or in the
unexpected case when the private key used to sign the certificate
is compromised and a rapid emergency fix is required.
Dynamic pinning allows you to adapt to any eventuality without
notice and without requiring a new release of the app.
In addition to this, dynamic pinning is more secure as it doesn't
suffer from the fact that static in-app certificate files can be
replaced and the app repackaged to use a rogue certificate pin set.

Second, when we are done with the app, we need to build the APK, sign it, and
register it with the Approov Cloud Service by following the Approov
documentation

Finally, we need to add the server side changes which will be performed
using our request authentication express middleware
server/shipfast-api/auth.js:

The Approov Token Secret is the base 64-encoded HS256 symmetric secret
you are given when signing up for the Approov service. It is what the
Approov Cloud Service uses to sign short-lived Approov JWTs, and what
your web service will use to verify these tokens for protected API
requests. The app does not and should not know whether these tokens are
valid (i.e. signed correctly and not expired). If your app is genuine and
untampered and passes the Approov authentication process, you will be
issued with a valid Approov JWT; otherwise it will be an invalid one.
The mobile app is simply a carrier of this token.

The Challenge (aka homework)

Since we have an Approov-authenticated ShipFast app now, there are a
number of tests that should be performed to test the app's
resilience to attack. You can perform these tests and try to request
the nearest shipment, update the current shipment, etc, all of which
should now be authenticated by Approov:

1 Try modifying ShipRaider to include a fake Approov token, or use
the Linux cURL command or
equivalent to perform fake API requests to the ShipFast server. In
other words, try using the ShipFast API without the genuine
Approov-registered app. Be as sneaky as you can - attackers will be!

2 Take the Approov-registered APK, unzip it, try changing something
such as adding the debug flag to the Android manifest as we did in a
previous attack, re-sign it, install it and run it on an unrooted
device. Even without debugging, the app modification of the APK will
be detected by Approov as the signature has changed.

3 Follow the previous step, and attach a debugger to the app. Approov
detects the presence of the debugger.

4 Try running the original Approov-registered app, unaltered, on a
rooted device.

5 Try running the original Approov-registered app, unaltered, on an
emulator.

6 Try proxying network traffic from a device to the ShipFast server.
Use an SSL proxy (MitM attack) such as Charles or mitmproxy to snoop
the API requests.

7 Try attaching an instrumentation framework such as Frida or
Xposed to the Approov-registered app. Install a TLS certificate
'unpinning' framework to try to circumvent MitM mitigation.

IMPORTANT: Approov's app and API protection features can be enabled
and disabled by contacting us. These features will not all necessarily
be running during your penetration tests, so please check the status
of your Approov Cloud Service first before attempting to elevate our
engineering team's heart rate considerably. In all seriousness though,
we are confident that Approov provides highly-competitive app and API
protection (and we have the customers to back that statement up) so if
you do have any concerns or queries, please get in touch using the form
at https://approov.io/contact-us.html
or, depending on the situation,
https://www.criticalblue.com/careers.html.

Wrap Up

I sincerely hope that you have enjoyed this walkthrough and found it
useful, and thank you for reading it!

I hope that it is clear that API protection needs not only to authenticate
the user with techniques such as OAuth 2.0 and Open ID Connect, and
authenticate network traffic using Transport Layer Security and
certificate pinning; but also authenticate the running mobile application.
The combination of these provide a crucial synergy of API protection techniques.

You know who is talking to you. You know how they are talking to you.
Do you know what is talking to you?