Year after year, developers bang their heads against the obstacles posed by complex frameworks when trying to test their applications offline. In order to bypass this, SDKs started offering “alternative” code paths for offline testing work. However, what do you do when this is not available out-of-the-box? You start hacking and monkey patch it, of course!
Monkey patching is a technique allowing a developer to alter the behavior of a piece of code at runtime, without changing the underlying code. For example, using this technique, a fraudster could make a back button in the app do something different when the program is run than what the original app developer intended. Monkey patching can be used on code libraries, methods, or even variables. While useful for developers to debug code, this technique can also be used to commit fraud.
In the code snippet below, Rafsan Jany (Monkey Patching in Python blog) shows how extremely easy this is to do in languages like Python, where you just need to assign a method its replacement.
Monkey patch in python
Source: Rafsan Jany - Monkey Patching in Python
This gets more and more complicated as we dive into “deeper” programming languages, as shown by this piece of Java code:
Method patched to always return false
Source: White Ops Threat Intelligence
The above code is a snippet from the latest finding of the White Ops Threat Intelligence team. It allows app developers to modify any SDK’s internals, in this case an advertising SDK, in order to twist its behaviour to their whim. More on that later, though.
Primal Needs
While investigating a batch of Android apps with high levels of user complaints for displaying ads on top of other apps and hiding the app’s icon, White Ops Threat Intelligence discovered a collection of classes and code which exhibited the monkey patching technique to perform ad fraud.
We dubbed this collection “DefPackage,” as the classes don’t have a specific package name, but are inserted into the defpackage package when first added to the code. The identified apps display out-of-context ads which stick around longer on your screen (thanks to the above mentioned technique) and have been installed over 2.6 million times. White Ops Threat Intelligence shared their findings with Google and all of the offending apps were removed from the Google Play Store as of October 2019.
The main app we analyzed to demonstrate the monkey patching technique was “Cut Paste Photo Editor S” (package name: com.hammergod.picture.photo) which had 100,000 downloads on the Google Play Store before being removed in mid-September 2019. It was marketed as a photo editor, but the app did not live up to the task, according to the comments.
App Name | Cut Paste Photo Editor S |
Package Name | com.hammergod.picture.photo |
SHA256 | 73ef822a22aba4011998741a7ce8c9d2b3a7cfedd58909 bc92bf645a090de22a |
Summary of the app information
Source: White Ops Threat Intelligence
Screenshot of the app information
Source: Google Play Store
A scan of many of the apps identified as having the DefPackage SDK found a plethora of negative reviews as shown below. Many of these apps did not function as advertised and continually generated ads, which may have contributed to their poor scores.
Screenshot of malicious apps and negative reviews
Source: Google Play Store
The Cut Paste Photo Editor S app included many negative reviews with complaints regarding the app’s monetization practices as shown below.
Comments from Cut Paste Photo Editor S Google’s Play Store entry
Source: Google Play Store
Monkeying Around
The app exhibited an interesting behavior with the use of the getDeclaredField method. Through the use of monkey patching, the author(s) of the DefPackage SDK removed certain checks, including timing for the back button functionality, from the associated advertising SDK. In our tests, the monkey patching changed the time when the back button became available to use, forcing the ads to stay on the screen for longer than we might otherwise expect.
Upon opening the AndroidManifest.xml, two entries stood out:
Suspicious service inside AndroidManifest.xml
Source: White Ops Threat Intelligence
The PixelService (used for residency) seemed to be present across all the apps (although sometimes renamed). The service had verbose output in logcat (default system log tracer in Android OS) and was visible in the process list.
Logcat log confirming the service’s activity
Source: White Ops Threat Intelligence
The service tried to restart itself every 5-15 minutes in case it was shut down (through Android’s job scheduler) after which it registered a broadcast receiver in order to hook any user activity (ie. unlocking the phone).
Persistence code inside the malicious service
Source: White Ops Threat Intelligence
Intents being caught by an obfuscated class inside the app
Source: White Ops Threat Intelligence
The obfuscated strings inside the receiver above were able to be decrypted and identified as:
- UNLOCK_AD_CLOSE_ACTION
- UNLOCK_DELAY_AD_CLOSE_ACTION
- CLEAN_ALL_TASK_ACTION
The authors of the DefPackage SDK used the same encryption techniques as in other parts of the code. With that knowledge and the help of the CyberChef tool, White Ops Threat Intelligence decrypted the obfuscated strings.
The (obfuscated) broadcast receiver listens for the screen on/off events in addition to other internal ad-related events. This gives the DefPackage SDK an entry point for pushing ads right after a user unlocks their phone.
The next step is a helper function called from the broadcast listener to check if all of the following conditions are met before the ad is launched:
- Computed random number between 1 and 100 is lower than 30
- Device is connected to the Internet
- Device screen is on
- Device is unlocked
- 8 to 24 hours have passed since the app was installed.
Checks put in place for randomly displaying an ad inside PixelActivity
Source: White Ops Threat Intelligence
If all the conditions are met, the ad activity is launched through an intent . The popup ad displays in full screen upon triggering the activity.
Cut Paste Photo Editor S app generating out-of-context ad
Source: White Ops Threat Intelligence
The activity is empty initially (just an overlay) and the content is spawned based on some internal logic that’s heavily obfuscated:
Activity shell inside the DefPackage SDK
Source: White Ops Threat Intelligence
Going Bananas
The DefPackage SDK tampers with various variables such as the value returned by the method zzhk which relays information about when the ad is finished and the user can press the back button.
Method patched to always return false
Source: White Ops Threat Intelligence
Method zzhk always returns false after being patched, which leads to incorrect processing of the back button press so the user cannot exit the ad display.
Ad SDK code that processes the patched method
Source: White Ops Threat Intelligence
White Ops Threat Intelligence detected the same behaviour on other SDKs as well.
Monkeys in Action
The following screenshots are an example of an ad displayed after unlocking the screen on our test phones during analysis.
DefPackage SDK generating ad after device unlock
Source: White Ops Threat Intelligence
Natural Selection
In order to combat attacks like these, more rigorous framework integrity checks are needed. One way to achieve this is to monitor for class name changes. This is possible in one of the above mentioned cases because the patched member belongs to a Dynamic Proxy. Dynamic proxies are JAVA’s syntactic sugar for performing advanced reflection which hooks every method call done to the object they’re proxying.
To demonstrate this, White Ops Threat Intelligence set up a simple proof-of-concept of a monkey patch scenario. The project is available here. We have a class that sets up a private value which can only be read. The value is used inside a simple toast notification.
Container for private variable
Source: White Ops Threat Intelligence
Tampering the “getter” using the same technique
Source: White Ops Threat Intelligence
In order to detect any tampering, we’ll implement a simple SDK to detect any changes in the class name of an object. This is done via locking the class name of a member of any publicly accessible parent object from the residing point in the SDK.
Locking down the class names
Source: White Ops Threat Intelligence
To detect tampering, we have to check our locked objects’ type against their original types. To do this, we just have to repeat the above procedure for each member and parent pair. If we detect a mismatch, we instantly break the loop and report the tampering.
Checking class names against the locked types
Source: White Ops Threat Intelligence
As seen in the below screenshot, before initiating the monkey patch, our initial Secret class matched the initially stored value.
Before tampering
Source: White Ops Threat Intelligence
After the tampering, we can see the change in the class name to that of the proxy object:
After tampering
Source: White Ops Threat Intelligence
One obvious flaw of a mechanism like this would the classic “who watches the watcher” scenario, as the anti-tamper-proof SDK can also be tampered with by a threat actor with enough time on their hands. This is why SDKs need to be heavily obfuscated and the “locks” hidden well inside the code, in order to prevent an adversary from easily modifying this anti-tamper layer.
While not a perfect solution, remember: You don't need unbreakable code, just a way to delay the bad guys until your next release cycle.
Indicators of Compromise (IOCs)
Below is a list of identified app packages using the DefPackage SDK and corresponding installation numbers, as of early September 2019. All of the apps have been removed from the Google Play Store.
Package Name | Installs |
com.acaleph.octopus | 500,000 |
com.butterfly.picture.photo | 50,000 |
com.chile.eritrea.sky.camera | 50,000 |
com.cyprus.ghana.blur.image.plus | 100,000 |
com.estonia.brunei.fashion.hairstyles.pic.editor2019 | 100,000 |
com.flatfish.soldiercrab.autoblur.photo | 100,000 |
com.gailun.effect | 100,000 |
com.game.panzerkiller | 100,000 |
com.game.rotarypaint | 100,000 |
com.hammergod.picture.photo | 100,000 |
com.hnnmsl.picture.fashion | 100,000 |
com.jordan.iraq.blur.image.pro | 100,000 |
com.kiribati.zowbat.image.blur.editor.free | 100,000 |
com.maxwell.photocutpro | 100,000 |
com.missing.collage.picture | 100,000 |
com.oman.mayotte.hairstyles.photo.editorplus | 50,000 |
com.palau.guam.fashion.hairstyles.pic.editor | 100,000 |
com.pop.color | 10,000 |
com.positive.photo.collage | 100,000 |
com.pottwal.bowhead | 100,000 |
com.qingjiao.collage.photo | 100,000 |
com.risemeup.protectball | 1,000 |
com.rwanda.seychelles.latest.hairstyles.free | 100,000 |
com.seisikou.photobackground | 1,000 |
com.sgame.cannonbricks | 1,000 |
com.sgame.connectionpipe | 10,000 |
com.sgame.drivesafely | 5,000 |
com.sweets.caincamera | 50,000 |
com.yasuo.art | 100,000 |
com.yongdegree.face.feature | 100,000 |
Table 1 - Apps using DefPackage SDK
Source: Google Play Store