Saturday, May 18, 2024
HomePythonReverse Engineering Nike Run Membership Android App Utilizing Frida

Reverse Engineering Nike Run Membership Android App Utilizing Frida


Hello everybody! 👋 When you’ve got been following my weblog you then might need already learn the article on reverse engineering an Android app by writing customized smali code. I’m nonetheless very a lot a reverse engineering newbie so after that article, I received to study Frida. I used to be informed that Frida is way sooner and loads simpler for eventualities the place I wish to eavesdrop on features. Nicely, I’m glad to report that each one the strategies had been completely appropriate.

On this article, I’ll share some particulars about how I used to be ready to make use of it to snoop across the Nike Run Membership Android app. The ultimate purpose was to extract authentication tokens that Nike generates while you log-in. This was one other challenge on the backburner and what higher technique to get a challenge off the again burner by studying one thing new?

Excited

Backstory

I’m an avid runner. Most of my family and friends know that. Once I began this sport I received hooked to Nike Run Membership. I used to diligently file every run in order that I had a file for all my runs. This went on for two years till I discovered that the majority of my new operating mates had been utilizing Strava. I made a decision to maneuver over to Strava however was fairly bummed by the dearth of information export choices in NRC.

There have been paperwork on-line concerning the Nike API they usually allowed you to additionally export your information within the JSON format however I wished one thing a bit extra automated. Due to this fact, I made a decision to do what another insane particular person would do and began my journey of reverse-engineering the Nike Run Membership APK. I made a decision to go to the supply and determine if I might reverse engineer the login course of and generate tokens in a totally automated trend.

This text will educate you the essential utilization of Frida and the way you can also go forward and snoop round completely different APKs. However earlier than we go to that half, you’ll want to know the way I ended up deciding to reverse engineer the app and never merely do a MITM assault utilizing mitmproxy.

Intercept NRC site visitors

Once I began the challenge, I made a decision to snoop the site visitors utilizing mitmproxy. You may obtain the NRC APK from APKMirror to observe alongside. After downloading it, rename it to nike.apk in order that the remainder of the instructions on this tutorial are model agnostic.

Subsequent, you should utilize adb to put in this APK on the emulator:

$ adb set up nike.apk

Now, you’ll want to run mitmproxy. For those who wrestle with organising an emulator or the proxy, consult with this earlier article of mine the place I am going in barely extra element about do this stuff. I used the next command to run mitmproxy:

$ mitmproxy --set block_global=false --showhost

The very last thing to do is to put in mitmproxy system certificates on the Android emulator by following the official docs and level the emulator to your native mitmproxy occasion.

Emulator proxy settings

After this setup, I opened up NRC and began trying out the requests in mitmproxy. I used to be kinda shocked and fairly spooked by the variety of analytics requests NRC was sending. There was no SSL pinning in place so I didn’t need to do something particular earlier than all requests began exhibiting up. All these 68 requests had been earlier than I even signed in:

mitmproxy all requests

Once I tried signing in, I noticed this login request:

Login request

It appears like a legit login request. However what’s that client_id? The whole lot else appears cheap and is one thing we are able to produce on our personal however the client_id appears fairly distinctive. The place is it coming from?

I checked out a few requests earlier than and after this specific API name and couldn’t discover the client_id anyplace. It needed to be generated inside the APK itself. I attempted replaying the request a few instances in mitmproxy and it began failing after 2-3 profitable replays. I began receiving this response:

<HTML>
<HEAD>
  <TITLE>Entry Denied</TITLE>
</HEAD>
<BODY>
  <H1>Entry Denied</H1>
    You do not have permission to entry "http://unite.nike.com/login?" on this server.
  <P>
    Reference #18.2eaf3817.1593493058.26b69156
</BODY>
</HTML>

There was one thing dynamic within the request and I simply had to determine what. This was the proper excuse to begin snooping contained in the NRC app.

By the way in which, the NRC app makes heavy use of HTML of their app. The login web page is definitely an HTML doc and is loaded from the server. You may entry it at this url. Extra about this later.

Decompile NRC APK

I assume you already downloaded the NRC APK from APKMirror and renamed it to nike.apk. I uploaded the APK to this on-line model of jadx and decompiled it. We’re on the hunt for the client_id and the very first thing I do at any time when I’m looking for a string, I exploit grep:

$ grep -r `client_id` ./

Ooo it did output fairly a bunch of stuff. This appears to be probably the most attention-grabbing string:

./sources/res/values/strings.xml:    <string identify="unite_client_id">IFc97q8fSoR84EHfevnzBNivAiT6H+i8vmVZDnCAax/ZjSGxw5ejdekfXtCrzrtJrQfJnj30JeK+MsyruZi8sW6iUBfe//NGZlpQJXUbz8LuPEXnLMAlxKdLV6BrBgKHqNI94nfSHCCr0xW3HOZk/XyFdevndG52zmZR0XXym0yW5d8n/XvLGDCtVyryFLYoYwHYrDC9JZ+GfAacPKE5S437fT9Af+Z/AeZgqPplm9mCaPBoOc0Co4+h3nT8TvXMsU4Vy8pRTuWv0skMU0uwUkq7R/UN06daQ8AkAaYt7KWG0S36tSbHuR03ji7om8ebOJqOzgFyYOp/KfkHkvX5+PVk2lG7lk1hBltitrBND8njmHHIPisC6+W7Ul1an0mRiNTQVFfSJpyNUVvE1D17NQ==</string>

grep additionally informed me the place it was being decrypted:

./sources/com/nike/plusgps/login/UniteConfigFactory.java:        UniteConfig uniteConfig = new UniteConfig(this.userAgentHeader.getUserAgent(), this.appResources.getString(C5369R.string.unite_experience_id), this.obfuscator.decrypt(this.appResources.getString(C5369R.string.unite_client_id)));

Based mostly on my earlier expertise with encryption, this appears to be an AES encrypted string. However I would like to verify that is the precise client_id we noticed within the request. Time to make use of Frida and hook into the APK and determine what the decrypted worth of this string is.

Intro to Frida

Based on the official web site, Frida is a

Dynamic instrumentation toolkit for builders, reverse-engineers, and safety researchers.

It lets you inject scripts in native purposes and take a look at what they’re doing beneath the hood. We might be utilizing it to maintain a tab on technique inputs within the NRC APK and likewise make customized calls to completely different strategies. Earlier than we transfer any additional, let’s set up Frida first.

Frida set up

There are two elements of Frida (that I’m conscious of). Frida shopper and Frida server. The shopper runs on the host working system and the server runs of the Android/iOS machine. To make testing simpler, it’s significantly better to make use of an Android emulator with Frida.

Putting in the shopper Python packages

For the client-side, there are once more two separate packages/libraries we are able to set up utilizing pip. One is named frida and the opposite is named frida-tools. frida permits us to import and use frida in our code whereas frida-tools present us with some actually helpful command-line instruments that can assist us in the entire reverse engineering course of.

Let’s create a brand new listing and a digital setting and set up each of those packages. We are going to work on the server half after this.

$ mkdir nike_project
$ cd nike_project
$ python -m venv env
$ supply env/bin/activate
$ pip set up frida frida-tools

If every thing has been put in and arrange accurately, frida-ps -h ought to output one thing like this:

$ frida-ps -h
Utilization: frida-ps [options]

Choices:
  --version             present program's model quantity and exit
  -h, --help            present this assist message and exit
  -D ID, --device=ID    connect with machine with the given ID
  -U, --usb             connect with USB machine
  -R, --remote          connect with distant frida-server
  -H HOST, --host=HOST  connect with distant frida-server on HOST
  -O FILE, --options-file=FILE
                        textual content file containing extra command line choices
  -a, --applications    record solely purposes
  -i, --installed       embody all put in purposes
  -j, --json            output outcomes as JSON

Putting in the frida-server on Android

Now we’ve got to put in the frida-server on the Android emulator (you may as well use your Android machine however I choose the emulator for testing). On the time of writing this text, the newest launch of the frida-server is 12.10.4. You may verify the newest model at GitHub. Simply change the model quantity within the instructions beneath and they need to work:

wget https://github.com/frida/frida/releases/obtain/12.10.4/frida-server-12.10.4-android-x86_64.xz
unxz frida-server-12.10.4-android-x86_64.xz
adb push frida-server-12.10.4-android-x86_64 /information/native/tmp/frida-server
adb shell chmod 755 /information/native/tmp/frida-server

The above instructions downloaded and copied the frida-server to the emulator, now we have to run the server. Open up adb shell:

adb shell

Now run the following command inside adb shell:

/information/native/tmp/frida-server &

Frida hooking

As soon as we’ve got the frida-server operating, we are able to begin prepping our JavaScript code for injection.

Prepping preliminary Frida hook

I checked out the NRC supply, adopted the breadcrumbs, and located that the decryption magic was taking place within the NativeObfuscator file. I discovered the method identify of the NRC app by operating frida-utils -Ua after which wrote the next Python code for testing my speculation:

import frida, sys

jscode = """
Java.carry out(operate (){
    var MainActivity = Java.use('com.nike.plusgps.onboarding.login.WelcomeActivity');
    var ConfFactory = Java.use('com.nike.plusgps.login.UniteConfigFactory');
    var String = Java.use("java.lang.String");
    var obfuscator = Java.use("com.nike.clientconfig.NativeObfuscator");
    var sources = Java.use("android.content material.res.Assets");
    var logger = Java.use("com.nike.logger.NopLoggerFactory");
    var strRes = "IFc97q8fSoR84EHfevnzBNivAiT6H+i8vmVZDnCAax/ZjSGxw5ejdekfXtCrzrtJrQfJnj30JeK+MsyruZi8sW6iUBfe//NGZlpQJXUbz8LuPEXnLMAlxKdLV6BrBgKHqNI94nfSHCCr0xW3HOZk/XyFdevndG52zmZR0XXym0yW5d8n/XvLGDCtVyryFLYoYwHYrDC9JZ+GfAacPKE5S437fT9Af+Z/AeZgqPplm9mCaPBoOc0Co4+h3nT8TvXMsU4Vy8pRTuWv0skMU0uwUkq7R/UN06daQ8AkAaYt7KWG0S36tSbHuR03ji7om8ebOJqOzgFyYOp/KfkHkvX5+PVk2lG7lk1hBltitrBND8njmHHIPisC6+W7Ul1an0mRiNTQVFfSJpyNUVvE1D17NQ==";
    var context = Java.use('android.app.ActivityThread').currentApplication().getApplicationContext();    
    var log = logger.$new();
    var obs = obfuscator.$new(context, log);
    console.log(obs.decrypt(strRes));
});
"""

course of = frida.get_usb_device().connect('com.nike.plusgps')
script = course of.create_script(jscode)
script.load()

I saved this as nike.py and ran it utilizing Python:

$ python nike.py
WLr1eIG5JSNNcBJM3npVa6L76MK8OBTt

The output was precisely what was being despatched as a part of the request. So it looks as if client_id wasn’t being dynamically generated.

This was my first time utilizing Frida for an precise app so I wished to get some extra observe with hooking. So let’s ask frida to position a hook on the decrypt technique and print out the enter to the strategy within the console at any time when it’s referred to as. That is potential by overloading strategies utilizing Frida:

jscode = """
  setTimeout(operate() {
      Java.carry out(operate (){
          var obfuscator = Java.use("com.nike.clientconfig.NativeObfuscator");
          obfuscator.decrypt.overload('java.lang.String').implementation = operate (str){
              console.log("******* begin ******");
              console.log('enter str: ' + str);
              var output = this.decrypt(str);
              console.log('output str: ' + output);
              console.log("*******  finish  ******n");
              return output;
          }
      });
  },0);
"""

Now restart the NRC app and you need to begin seeing the strategy calls within the terminal:

******* begin ******
enter str: FwWEAP2r615reIRlMz45fzIVs4OQqq+IVpyN7d1M1kQ3tYV5Fo6VlOjM435cvAfI0zq+
           4M8hWLnqCqpffQqiDbshGTro1zjvyB2D9ow2tGnjXboNcP1f84F0S9RuNZIEobgvmdb4
           1SGGuM1ZZxsyMVMjRagwaRTjDGAXb63dSEf2hQjd+40GCElD2qr4OBTZ1b1nY0mLUzHF
           7NuLwpxx0HwSN9nyjDaEBRm8NzDpuYOXYPYGkNMsB3nCveLToltwS8impqIEakdOlW3d
           Kt9yWH+IaThkwEiDoyv+QPiGJxepMA0jaJH3YxHF8e4exlGoJXYVP1+IKwoZny2W20qQ
           0Beg4+7ewL4kw3m+5ttGTd7jRZveDWENoSKnzjb5tUqbHukTcgB2q9o7K3rVFe2iqXxS
           rAwxd6VLmKVjB3dF92vm5/Wlxi5mOKFSaiRJMtZg
output str: Primary ZmE2NzJlOTUtYmQwNi00YWZhLWExZjYtYTczNGUzYjhkNmI5OnFidDJ5clJzc
					 UIzbVFTWQ==
*******  finish  ******

******* begin ******
enter str: 4sk+vdohXtRY2UkXX2piy+oON0fs/0DjGNfyitJBXc+lIw57oSGIEJLreAPfzf/9Kbdk
Lz2tKJM5wEbDy0LnrAba/lJNTv4semzO9HkudZD4sF/X8O/qwGQEBlQpyjntD11fOMGzIl83ZSC4w8R
vDFCe2DQRMcVB/OCIKFwtcqUJyVLw1W9wJU4AQ733Uabd8KxWOeo/5Hi1B5/mdXlDJD6JnAZJDPhqcl
ECRYW0bPHucvhNtrju/FT2kxlr/x5BhErDSuz2CbWCsLni3zM3Hx+XBvfNHhmINCxBpLdJhF976uHPU
nlaVa0l7y6pV69ir/U8ikMD3Zqis0oBUyZA1wIESnJc+UsS63aNulB+agpMeIGtSVhKiZf8ctjv/lxD
5dvWb4Dp54K5ZAfZ0Zxzrw==
output str: AQzIBimI3XFvsMKXXjFREYpjfS43McGw
*******  finish  ******

To this point so good however I have to delve a bit bit deeper.

Hooking into JNI calls

Within the Android code, I noticed that Nike had put the entire encryption and decryption code in a .so file and had been utilizing JNI to entry that. I discovered this out as a result of the NativeObfuscator class contained this code:

System.loadLibrary("nike-obfuscator")

What I additionally discovered was that identical to the identify urged, the .so file was obfuscated. Fortunately Frida gives us with a super-power to hook into native operate calls as effectively. For that, you must use the Interceptor and have to know which operate calls you wish to hook into. If I bear in mind accurately, I discovered that the .so file was obfuscated and likewise the precise features contained in the libnike-obfuscator.so by loading it in Ghidra and letting Ghidra do its magic. You may see the features record in the correct column:

Ghidra

The identify of the decryption operate was Java_com_nike_clientconfig_NativeObfuscator_decrypt and I hooked into it through the use of this code:

Interceptor.connect(Module.findExportByName("libnike-obfuscator.so", "Java_com_nike_clientconfig_NativeObfuscator_decrypt"), {
    onEnter: operate (args) 
    {
				console.log("inside decrypt");
    },
    onLeave: operate(args)
    {
        console.log("outdoors decrypt");
    }
});

I additionally needed to modify my nike.py code to guarantee that frida relaunches the NRC app on every script execution as a result of the decryption stuff occurs proper when the APK masses up for the primary time. I did that by changed the code on the backside with this:

app_name="com.nike.plusgps"
machine = frida.get_usb_device()
pid = machine.spawn(app_name)
machine.resume(pid)
time.sleep(1)
course of = machine.connect(pid)
script = course of.create_script(jscode)
script.load()
sys.stdin.learn()

The arguments to features in .so are literally reminiscence pointers and we are able to print out the worth at that pointer by modifying our Interceptor code:

Interceptor.connect(Module.findExportByName("libnike-obfuscator.so", "Java_com_nike_clientconfig_NativeObfuscator_decrypt"), {
    onEnter: operate (args) 
    {
        console.log("inside decrypt");
        var information = Reminiscence.readByteArray(args[0], 10);
        console.log("Reminiscence information: ");
        console.log(information);
    },
    onLeave: operate(args)
    {
        console.log("outdoors decrypt");
    }
});

I don’t know the dimensions of the enter so I manually put in 10 on this case. The output was related for all operate calls:

inside decrypt
Reminiscence information:
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
00000000  10 43 5f c6 4a 72 00 00 00 4c                    .C_.Jr...L
outdoors decrypt

This was good, my principle relating to client_id being static was appropriate and I had proved that by hooking onto the precise Java technique calls. I attempted to undergo the obfuscated .so code utilizing Ghidra however quickly gave up. The pointer addresses had been scrambled, the inputs weren’t on the precise enter param pointers and the entire thing was a large number. I noticed that there was not use for me to truly undergo the hassle as a result of there wasn’t something dynamic that was being generated by that file. I solely cared concerning the encrypted and decrypted strings and I had all of these by simply hooking to Java code.

That is the place my APK reverse engineering journey ended and I made a decision to place “reverse engineering the .so file” on the backburner. However, I wasn’t achieved for this challenge. I nonetheless had to determine how the login works in order that I can create a instrument for computerized token and exercise extraction.

Observe: There’s additionally the jnitrace program primarily based on Frida that’s alleged to print all JNI calls however NRC was crashing at any time when I attempted utilizing jnitrace. And for these of you who would possibly really need some problem, strive reverse engineering the .so file. There was a name to android.content material.pm.Signature in it and I imagine that the code is perhaps utilizing one thing from there to get the important thing for AES decryption.

Reversing login endpoint

Bear in mind I informed you to start with that the NRC app makes use of HTML and masses the login web page from their server? I continued the remainder of my testing on that endpoint. I noticed the precise request in additional element and noticed sure cookies which appeared attention-grabbing. There was an _abck cookie and one other cookie which I’m forgetting now. I did some analysis on-line and discovered that they had been utilized by Akamai Bot Supervisor to filter out bots. That’s the reason the API endpoint replay was failing as effectively.

I appeared across the web hoping that somebody would have managed to reverse engineer the bot supervisor however it’s a cat and mouse sport. Those that have reverse-engineered it don’t put all their analysis on-line as a result of then Akamai will merely patch it. There have been sure repos on GitHub that had been alleged to work however they had been up to date a very long time in the past. I wasn’t capable of finding any repo that both labored or had precise particulars on use it.

Now we’re getting outdoors the scope of this text so I’ll simply provide you with a abstract of what I did subsequent. The first difficulty was that I would like entry to the Bearer tokens in an automatic trend in order that I might finally use it to make API calls to Nike. I already knew manually extract it however wished to offer customers with an automatic instrument. I searched round and discovered a unique URL that may very well be automated utilizing Selenium, seleniumwire, and geckodriver.

And after some sleepless nights, I whipped up a script referred to as nrc-exporter.

NRC Exporter

It lets you export your run information from Nike and convert it to GPX format. It’s nonetheless in its infancy and there are fairly a number of tough edges nevertheless it works for my functions. In case you are , please be happy to enhance it and submit pull requests. I’m at all times completely satisfied to obtain contributions.

Conclusion

I hope you realized one thing new on this article. I for certain realized numerous new stuff. It’s at all times thrilling to see what completely different APKs are doing beneath the hood. This text barely scratched the floor of what Frida is able to. If you wish to study extra about it, try the sources linked on the finish of this text. As for me and my curiosity in APK reversing?

Hooked

The token automation utilized by nrc-exporter will likely be blocked as quickly as I publish this text however now you know the way to generate it manually so it’s all good.

Please present the nrc-exporter some star love on GitHub and I’ll see you with an much more attention-grabbing article sooner or later. Take care and keep protected! 👋 ❤️

Additional sources

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments