Saturday, March 11, 2023
HomePythonReverse Engineering Nike Run Membership Android App Utilizing Frida

Reverse Engineering Nike Run Membership Android App Utilizing Frida


Hello everybody! 👋 When you have been following my weblog then you definitely 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 acquired to find out about Frida. I used to be informed that Frida is far sooner and lots simpler for situations the place I wish to listen in on capabilities. Effectively, I’m glad to report that each one the recommendations have been completely right.

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 aim was to extract authentication tokens that Nike generates whenever you log-in. This was one other challenge on the backburner and what higher strategy 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. After I began this sport I acquired hooked to Nike Run Membership. I used to diligently report every run in order that I had a report for all my runs. This went on for two years till I discovered that the majority of my new working buddies have been utilizing Strava. I made a decision to maneuver over to Strava however was fairly bummed by the shortage 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 needed one thing a bit extra automated. Subsequently, I made a decision to do what every other insane individual 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 may reverse engineer the login course of and generate tokens in a very 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 have to know the way I ended up deciding to reverse engineer the app and never merely do a MITM assault utilizing mitmproxy.

Intercept NRC visitors

After I began the challenge, I made a decision to snoop the visitors utilizing mitmproxy. You’ll be able to obtain the NRC APK from APKMirror to comply with 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 have to run mitmproxy. In the event you battle with establishing an emulator or the proxy, check with this earlier article of mine the place I’m going in barely extra element about do these items. 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 have been earlier than I even signed in:

mitmproxy all requests

After 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 affordable 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 wherever. It needed to be generated throughout the APK itself. I attempted replaying the request a few occasions 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 right excuse to begin snooping contained in the NRC app.

By the best way, 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’ll be able to 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 take advantage of grep:

$ grep -r `client_id` ./

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

./sources/res/values/strings.xml:    <string title="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)));

Primarily based on my earlier expertise with encryption, this appears to be an AES encrypted string. However I want 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

In response to the official web site, Frida is a

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

It permits you to inject scripts in native functions and take a look at what they’re doing below the hood. We can 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 gadget. 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 known as frida and the opposite known as 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’ll 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 appropriately, 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 gadget with the given ID
  -U, --usb             connect with USB gadget
  -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    listing solely functions
  -i, --installed       embrace all put in functions
  -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 gadget 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’ll be able to verify the newest model at GitHub. Simply change the model quantity within the instructions under 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 working, 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 title of the NRC app by working 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.Sources");
    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 needed to get some extra follow with hooking. So let’s ask frida to put a hook on the decrypt technique and print out the enter to the tactic within the console at any time when it’s referred to as. That is attainable 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 it’s best to begin seeing the tactic calls within the terminal:

******* begin ******
enter str: FwWEAP2r615reIRlMz45fzIVs4OQqq+IVpyN7d1M1kQ3tYV5Fo6VlOjM435cvAfI0zq+
           4M8hWLnqCqpffQqiDbshGTro1zjvyB2D9ow2tGnjXboNcP1f84F0S9RuNZIEobgvmdb4
           1SGGuM1ZZxsyMVMjRagwaRTjDGAXb63dSEf2hQjd+40GCElD2qr4OBTZ1b1nY0mLUzHF
           7NuLwpxx0HwSN9nyjDaEBRm8NzDpuYOXYPYGkNMsB3nCveLToltwS8impqIEakdOlW3d
           Kt9yWH+IaThkwEiDoyv+QPiGJxepMA0jaJH3YxHF8e4exlGoJXYVP1+IKwoZny2W20qQ
           0Beg4+7ewL4kw3m+5ttGTd7jRZveDWENoSKnzjb5tUqbHukTcgB2q9o7K3rVFe2iqXxS
           rAwxd6VLmKVjB3dF92vm5/Wlxi5mOKFSaiRJMtZg
output str: Fundamental 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  ******

Up to now so good however I have to delve a bit of 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 have 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 similar to the title advised, the .so file was obfuscated. Fortunately Frida supplies us with a super-power to hook into native operate calls as nicely. For that, you need to use the Interceptor and have to know which operate calls you wish to hook into. If I bear in mind appropriately, I discovered that the .so file was obfuscated and likewise the precise capabilities contained in the libnike-obfuscator.so by loading it in Ghidra and letting Ghidra do its magic. You’ll be able to see the capabilities listing in the fitting column:

Ghidra

The title of the decryption operate was Java_com_nike_clientconfig_NativeObfuscator_decrypt and I hooked into it by utilizing 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("exterior decrypt");
    }
});

I additionally needed to alter 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"
gadget = frida.get_usb_device()
pid = gadget.spawn(app_name)
gadget.resume(pid)
time.sleep(1)
course of = gadget.connect(pid)
script = course of.create_script(jscode)
script.load()
sys.stdin.learn()

The arguments to capabilities 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("exterior 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
exterior decrypt

This was good, my concept concerning client_id being static was right 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 have been scrambled, the inputs weren’t on the precise enter param pointers and the entire thing was a large number. I spotted that there was not use for me to really undergo the trouble 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 accomplished for this challenge. I nonetheless had to determine how the login works in order that I can create a software for automated token and exercise extraction.

Notice: There’s additionally the jnitrace program based mostly 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 may truly need some problem, attempt reverse engineering the .so file. There was a name to android.content material.pm.Signature in it and I consider that the code may be utilizing one thing from there to get the important thing for AES decryption.

Reversing login endpoint

Bear in mind I informed you at first 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 fascinating. There was an _abck cookie and one other cookie which I’m forgetting now. I did some analysis on-line and discovered that they have been utilized by Akamai Bot Supervisor to filter out bots. That’s the reason the API endpoint replay was failing as nicely.

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 have been alleged to work however they have 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 exterior the scope of this text so I’ll simply offer you a abstract of what I did subsequent. The first subject was that I want entry to the Bearer tokens in an automatic trend in order that I may ultimately use it to make API calls to Nike. I already knew manually extract it however needed to offer customers with an automatic software. I searched round and discovered a special URL that might 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 permits you to export your run information from Nike and convert it to GPX format. It’s nonetheless in its infancy and there are fairly a couple of tough edges however it works for my functions. If you’re , please be happy to enhance it and submit pull requests. I’m all the time completely satisfied to obtain contributions.

Conclusion

I hope you realized one thing new on this article. I for positive realized various new stuff. It’s all the time thrilling to see what completely different APKs are doing below the hood. This text barely scratched the floor of what Frida is able to. If you wish to be taught extra about it, take a look at 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 most likely be blocked as quickly as I publish this text however now you understand how 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 fascinating 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