The Hard Way to Dynamic Analysis of Mobile Apps

META

Activist
SUPREME
MEMBER
Joined
Mar 1, 2026
Messages
118
Reaction score
380
Deposit
0$
I begin every article by mentioning that our team is developing a mobile app security analysis platform. Why? By posting information I find useful, along with various discoveries and tips, I want to share them with like-minded people, help those who are on the same path, and share interesting stories. I'm sure we're not the only ones facing challenges in developing our own products, and we can share experiences here. Today, I'd like to share a few things we've encountered in recent months while working on the new release of our product. Perhaps this will inspire someone, or perhaps someone will offer us some advice. We're open to discussion!

00575d07da305e0ebc93897ad34ec190.jpg

Table of contents​

Introduction​

First, a brief overview of our solution. The Stingray platform is a dynamic mobile app analyzer. Its main purpose is to monitor how an app behaves on the device, what system calls it makes, how it communicates with other apps, what it sends to the server, how it stores locally, and so on. Of course, we also use static analysis, app resource analysis, and many other techniques. Everything described in my previous articles is based on our experience using various app analysis techniques.

We strive to make our product easy and convenient to use. Anyone who works with scanners, analyzers, and other information security tools (even outside the context of mobile app analysis) knows they're not easy to use! Ideally, we want even people who've never analyzed mobile devices to be able to download the app, poke around the interface, and get a clear report with real vulnerabilities. We want everything to work as simply, quickly, and efficiently as possible out of the box. At the same time, we also want to keep in mind those who want to fine-tune the tool and tailor it to their needs and the needs of their applications. This user-friendliness, which we strive for, creates enormous challenges in system development and design. And some of you will surely be interested in "lifting the hood" and seeing what's really going on underneath the hood.

I'd like to write more, but we'll see how appropriate this narrative format is. If all goes well, I'll definitely write a brief history of our product's transformation and all the challenges we faced (and there were plenty). Early on, when asked to describe our system, a colleague of mine remarked, "You could say we're building Python on Linux, deployed inside an Android emulator in Docker." That was the way things used to be, and it was fun, but now things are much more interesting.

Dynamic iOS Analysis​

For quite a long time, we specialized exclusively in Android apps. This was logical and understandable, as Android devices are more accessible, it's easier to gain superuser rights, and the system is generally more open, with emulators and many other aspects. We'd long planned to add full-fledged dynamic functionality to iOS apps and had made several attempts, but we lacked either the time or the expertise. Finally, we implemented this challenging scenario, although, of course, we encountered a fair few bumps along the way.

Firstly, we're dealing with live devices, with all the attendant issues and peculiarities. Furthermore, we must keep in mind that iOS is a closed system, and when problems arise, simply looking at the source code implementation and understanding the cause is impossible. This means we have to either search for relevant materials or reverse engineer the necessary functionality ourselves.

Secondly, we need to maintain a fleet of devices to fully launch and test the application. This means we need to organize the right place for all these devices, think through their placement, power supply, a stable communication channel, cooling, and a bunch of other details we hadn't even considered until recently. As a result, we now have a dedicated stand in the server room, where all our devices and associated infrastructure are neatly laid out and connected to a UPS. A pair of fans on printed stands cools everything, allowing for quick and easy switching on/off and changing the airflow angle and direction. After expansion, we plan to install a dedicated cabinet for all devices with fireproof protection (just in case).

The process of preparing devices

Thirdly, a number of difficulties are associated with device preparation. You need to manually manage a large number of devices, install all the necessary packages, and verify proper operation without losing anything. Of course, doing all this manually is a huge amount of work, so we've created specially designed auxiliary software to handle all of this.

Fourth, another headache is device updates. With each new release, we change something in the configuration. To avoid having to do this manually, we developed an automatic device configuration update mechanism and implemented it directly into the product. When a new release is released, the system automatically updates everything.

Fifth, it's essential to organize monitoring of the entire infrastructure. Device status must be monitored, problems automatically corrected, and administrators notified if something can't be resolved remotely (for example, a phone has stopped charging). This ensures that devices are always ready to handle app analysis tasks and are always up-to-date and functional. This mechanism also runs before each scan to ensure the configuration is up-to-date and save the user time. If problems arise, it notifies that scanning cannot be performed on that device.

I won't go into the architecture of the solution we designed and implemented (I think that's a topic for a separate article), but I will note that we also had to puzzle over how to ensure constant functionality, ease of configuration and administration of the entire "zoo," and integrate monitoring into a unified system. I'll just say that iOS device profiles and pfSense, combined with Radius, were very helpful in this regard.

Full-fledged IAST for Android applications​

So, it's more or less clear what happened with iOS, but what about Android? We didn't leave it out either. Until now, we've been adept at finding any sensitive information processed, stored, or transmitted by an app, and we've had the ability to detect several more interesting vulnerabilities, such as potential code execution within the context of an app, but that wasn't enough.

As you already know, we keep our finger on the pulse and are constantly reviewing all reports, writeups, open bugs in bug bounty systems, and a significant portion of other materials, many of which I publish. We've accumulated quite a few vulnerabilities whose detection we wanted to automate. These are all real bugs that can be exploited on non-rooted devices simply by installing an app nearby, and sometimes just by clicking a link. These are the most complex and most "tasty" vulnerabilities, and we've always wanted to add detection. We've outlined our immediate automation plan and have already begun implementing them.

Classic IAST scheme

But after adding several such types, it became clear that the process was very labor-intensive and required extensive hacks, and overall, it wasn't very convenient. Ultimately, we rolled back to the previous state of the system, wasting a significant amount of time. This spurred us to try to find a solution that would simplify the further addition of new vulnerability types and allow us to abstract away specific security issues and apply a more general approach to finding them. Over the course of a month, we reviewed and tried many different options, covering an entire office wall with graffiti, and after many sleepless nights, tons of code, and experiments, we succeeded.

We prioritized the user and their input into the application, as this could lead to problems if it ends up in a potentially dangerous function. We realized that if we could accurately determine that externally received data was entering potentially dangerous functions unchanged, we would be able to detect virtually any vulnerabilities. And that's exactly what we needed! After all, we already had complete control over the application, all its data, and any calls to any API. All that remained was to implement it in a way that would allow us to describe vulnerabilities using signatures.

And then, with a little magic and a ton of time, we created a system that accepts specially crafted rules as input, defining what and how to intercept, how to label data, which functions we're interested in, and how to further integrate them into the application's call model. By mapping the data flow from entry to exit, we gain the ability to detect virtually any vulnerability. And at the same time, we minimize the number of false positives, since data communication either exists or it doesn't.

Previously, I'd only encountered such systems and analysis methods for web applications. It turns out that the first fully-fledged IAST engine has appeared for mobile applications. Thanks to a simple configuration system, adding new vulnerability types takes practically no time. The main focus is analyzing the problems, understanding their nature and the data flow within. Once that's done, it's a matter of writing a few lines of code, building connections, and—voila—the new vulnerability is added and can be tested on the hundreds of applications we periodically analyze.

Updated design​

For some reason, we have a very interesting relationship with the UI. If you think back to the beginning of our story, the very first version was simply HTML, which we generated server-side and served to the browser. The second version involved me purchasing an Angular theme and developing a new interface, which was separate from the server and was a full-fledged SPA. It was, to be honest, one of the most useless purchases of my life. Armed with a downloaded Angular course, a newly purchased theme (which almost immediately went into the trash), and a reserve of energy, I wrote my first SPA, which lasted for quite a while until we found a competent and dedicated developer for these tasks. After seeing the horror of the code, we decided to rewrite everything practically from scratch and implement the interface using modern technologies and the right approaches. This led us to designing each page (admittedly, due to resource constraints at the time, on a piece of graph paper, but that's not important) and implementing them consistently. As a result, we received a stable, well-functioning interface that lasted for quite a long time.

Three versions of one screen

Throughout this version's lifespan, we've been collecting user feedback, noting various inconvenient aspects, and, based on all this, we've compiled a solid list of improvements and feature requests for the new interface. After reviewing all the features we'd like to implement and taking a look at what we already have, we decided to take another leap of faith—rewriting and redesigning the front end of our system almost entirely. Essentially, the only thing that remained unchanged was the functionality for retrieving data and working with the API. Everything else (including interface elements, even buttons, switches, fonts, input fields, file and image processing) was rethought, modified, and hand-drawn. All screen transitions, the entire user experience, were redesigned and redesigned, and the interface is finally fully adaptive to various screen resolutions. Everything that was missing before is now in the new version. In fact, there were so many changes that at some point we stopped counting and recording them and simply immersed ourselves in the work.

At one point, I thought our front-end developer hated me, but now using Stingray is not only convenient, but also visually pleasing and, in some cases, much more obvious and simple.

Integrations​

Our team understands that without integration with related systems, when a tool is "separate" from the main development, it is impossible to build a full-fledged process.

That's why we decided to consider adding new ways to obtain apps for analysis. The result was the ability to download and submit systems for analysis from Firebase, Google Play, and the App Store, but it cost us a lot of gray hairs.

When developing these integrations, it was extremely challenging to ensure everything not only worked, but also worked smoothly and without any issues. After all, every distribution system and app store implements download functionality differently, with some deliberately hiding it or not providing it at all.

Take Firebase integration, for example. If you open the web app, you'll see a wonderful, large "Download Distribution" button. However, there's no external API for downloading, and there never has been one, nor are there any plans for one. Implementing the ability to download the app required considerable effort, including studying the structure of internal Firebase calls, determining how and what authentication and authorization in Google services are based on, understanding which internal calls need to be made, with which headers, and much, much more.

It would seem that we've sorted out Google authentication, and now we can seamlessly integrate the Play Store. No way! Apparently, they're being developed by completely different teams with different approaches and interfaces. And everything we'd done for Firebase turned out to be completely unusable for app store integration. So we had to start from scratch: learning the API, device registration methods, and the subsequent process for obtaining APK files. We eventually implemented this functionality, but here's the problem: just the other day, Google changed the app store login process, and now we're having to look for ways around the restrictions all over again. We need to re-examine our traffic and figure out what Google did.

Surprisingly, the Apple App Store presented far fewer challenges, as we managed to find a practically ready-made solution that we integrated into our overall script. Flush with success, we set our sights on the holy of holies: downloading files from the TestFlight distribution system, now known as Apple Connect. And here we encountered a major setback. Previously, before the renaming, the TestFlight console had a wonderful button for downloading test builds, and it was relatively easy to use. But now, after the reorganization of Apple's distribution system, this feature has been removed entirely. After spending several weeks searching for a solution, paying for a developer account, going through several rounds of data verification hell, and registering for several programs, we realized we couldn't find an option to download the app. Looking at the TestFlight app's traffic, we managed to catch an interesting request to download an IPA file, but when we tried to install the downloaded app, nothing happened. As a result, we haven't added this integration yet, but as soon as time arises, I think we'll try to return to this issue and try to make it work.

We're planning a separate article on integrations, where we'll discuss the targeted process of mobile app analysis in the context of development, and release a completed version of our script for downloading apps from various sources. It's still available in our Github repository , but it's not yet fully polished. However, it's still usable, and I hope you find it useful.

Conclusion​

In conclusion, I'd like to say that this year didn't start out quite as planned. We encountered a fair number of challenges and difficulties, most of which we managed to overcome and even wrote about. But we certainly don't want to and won't rest on our laurels; we intend to continue setting seemingly impossible goals and achieving them.

This article format is a bit out of place in the general series on mobile app security, but looking back on what we've been through, I just couldn't resist writing about it and sharing my thoughts.

And, of course, this article would never have been possible without the amazing team that brought everything I've written to life and much more to life. So, I'd like to thank everyone who contributed to the creation of our product. Thank you so much, guys, you're the best team!
 
Top Bottom