SourMint Malicious SDK

About Mintegral SDK

The Mintegral SDK is a popular mobile app advertising SDK used by over 1,200 apps in the AppStore with over 300 Million downloads per month based on industry expert estimates.

The SDK is used by application developers to monetise their apps with third-party ads.

  • Mintegral has SDKs for various platforms, including Android, iOS.
  • Mintegral SDK for iOS is distributed via cocoapods as MintegralAdSDK.
  • Mintegral SDK is closed source. The following research was conducted on version 6.3.5.0 of the SDK (for x86 arch) available for download from github.

We have identified that mintegral iOS SDK versions 5.5.1 and above contain malicious functionality which leads to information leakage. In simple terms the SDK is spying on user link clicking, and network activity within the affected apps. The spying occurs even if the SDK was not enabled by the developer or the ad mediation platform.

At the moment malicious behavior appears to be limited to the iOS version of the SDK. We haven’t found evidence of malicious code in Android versions of the SDK.

Test your app for the malicious SDK

By submitting this form you consent to us emailing you occasionally about our products and services.
You can unsubscribe from emails at any time, and we will never pass your email onto third parties. Privacy Policy

High-level overview of the malicious behaviour

Mintegral SDK uses a technique called method swizzling to replace implementations of the UIApplication openURL and SKStoreProductViewController loadProductWithParameters methods at runtime, as well it registers a custom NSURLProtocol class.

These hooks are used to spy on application users by sending all the information about HTTP requests, opened URLs and App Store links they click on from within the application.

The HTTP request headers and URLs themself could contain sensitive data, but together with IDFA (Identifier for Advertisers), this data allows Mintegral to perform advertisement attribution fraud.

To monetise their applications, developers often install advertising platforms. Advertising platforms receive revenue from advertisers for each installation happening after a user clicks on their advertisement. It is not uncommon for developers to use multiple advertising platforms in their aps. Therefore, to determine which ad platform should receive given attribution for the installation, each click gets registered to an attribution provider, a mobile measurement platform (MMP).

The figure below shows how the malicious functionality in Mintegral works. In this example, the user clicked on an advertisement from “Another Platform”. But since Mintegral has an ability to intercept all URLs opened by the application it could perform additional requests to an attribution provider pretending that click actually happened by their advertisement. The full process works like this:

  1. The user clicks a link from an in-app advertisement, served by a non-Mintegral ad network, to install a new application from the App Store. 
  2. The ad network’s SDK sends the click information to their back-end platform.
  3. Having intercepted the click event via code injected into iOS event handlers through method swizzling, Mintegral logs the click data to their server.
  4. The ad network registers a click notification with the attribution provider.
  5. Mintegral registers a click notification with the attribution provider as well.

When the attribution provider attempts to match the install event to registered click notifications, it finds two that match. Using a last-touch attribution model, the Mintegral click notification is given the attribution and the click notification from the other ad network is rejected.

Snyk worked with a major attribution provider to confirm that Mintegral is using the click data to generate false click notifications. Through their investigation, the provider was able to show that false click notifications were being generated and resulting in mis-attribution of ad clicks to Mintegral.

Demo application

To demonstrate the attack, we setup a demo application.This will show the malicious openURL hook in action. We use a debugging proxy to intercept all network traffic.

 To initialize the application we use the following code snippet from the Mintegral documentation:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[MTGSDK sharedInstance] setAppID:@"xxxxxx" ApiKey:@"yyyyyyyyyyyyyyyyyyyyyyyyy"];
    return YES;
}

With the SDK initialized, we open example.com from the application using a button click:

- (IBAction)openURLWithOptions:(id)sender {
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://example.com?foo=bar&x=y"] options:@{} completionHandler:nil];
}

After launching the app, we immediately see a couple of requests from the Mintegral SDK in our proxy:

  • GET https://setting.rayjump.com/sdk/customid
  • GET https://setting.rayjump.com/setting
  • POST https://analytics.rayjump.com

Settings

The response from https://setting.rayjump.com/setting is a JSON object with many different options. Following table describes the most interesting fields for our research.

JSON FieldDescription
cswEnable or disable anti-debugging functionality.
couEnable or disable openURL method hook. 
cdaiURL to send the leaked information (*).
cspnEnable or disable hook on StoreKit methods.
cudEnable or disable hook on NSURLProtocol.
cudlURLs array to track via NSURLProtocol (**).

* In our case it was LdxThdi1WBK/WgfPhbxQYkeXHBPwHZKsYFh= which is https://n.systemlog.me/log after decoding.

** In our case it was kBzuJd5/H+i/D+SMY7V/DFKwR0M0D+SMhBPthdSsHZPUYFT0+N== which is ["itunes.apple.com","apps.apple.com"] after decoding.

Payload decoding

We can see two more requests after clicking the button which opens example.com. The first one is a GET request to https://example.com, as we would expect. The second one is a POST request to https://n.systemlog.me/log.

The body of the request looks like it is base64 encoded. But in reality, it is not a base64 encoded string. The Mintergral SDK implements their own encoding and decoding logic which can be found in +[MTGBase base64DecodeString:] and +[MTGBase base64CleverDecodeString:]. Note that MTGBase is not exposed in public headers of the SDK and not meant to be accessible for developers.

We use next code snippet to decode the payload:

Class cls = NSClassFromString(@"MTGBase");
NSObject *obj = [cls performSelector:NSSelectorFromString(@"base64CleverDecodeString:") withObject:@"THE REQUEST BODY HERE"];
NSLog(@"%@", obj);

The payload contains a lot of data including IDFA, IDFV, OS version, user agent and so on. However, it also contains a field called “clever”, which is, again, encoded. We can decode it using base64DecodeString:

[{'cn': 'ViewController', 'u': 'https://example.com?foo=bar&x=y', 'nid': 0, 'type': '3', 'mn': 'openURLWithOptions:', 'trc': '["2|awesomegame|0x0000000104102f18 -[ViewController openURLWithOptions:] + 152","3|UIKitCore|0x00007fff49326c1d -[UIApplication sendAction:to:from:forEvent:] + 83","4|UIKitCore|0x00007fff48cd5baa -[UIControl sendAction:to:forEvent:] + 223","5|UIKitCore|0x00007fff48cd5ef2 -[UIControl _sendActionsForEvents:withEvent:] + 396","6|UIKitCore|0x00007fff48cd4e63 -[UIControl touchesEnded:withEvent:] + 497"]'}]

As you can see it contains the URL, stack trace and some additional information like the controller and the method name.

Going deeper

As was mentioned before – the Mintegral SDK is closed source. We are going to look at version 6.3.5.0 for x86 architecture.

The binary is a Macho executable that contains multiple other binaries. The code we’re interested in resides in _CXX_CXX_OperationPKTask.o.

Looking at the class, we see +[_CXX_CXX_OperationPKTask load] which executes automatically when the class is added at runtime. That means if the SDK is installed via CocoaPods, malicious logic is going to be initialized regardless of whether developers actually use the SDK or not.

The load method performs a series of calls which lead us to ___cxxwebk_init_vw. This method checks whether the anti-debug protection flag is enabled and initialises hooks if the flag is set to false or if debugging is disabled.

Initialization of the hooks depends on the settings request mentioned above. In the following table we see the relationships between response JSON fields and MTGSetting class:

JSON FieldMTGSetting PropertyDescription
cswcSrtWEnable or disable anti-debugging functionality.
coucOpenURLEnable or disable openURL method hook. 
cspncPackageNameEnable or disable hook on StoreKit methods.

Anti-debug logic

The __cxx_cxx_op_isInSuperViewFrame function returns true in the following cases:

  • Device platform is a “simulator”.
  • Debugger is attached (implemented in ____mvpmvvm_isDebuggerAttached_block_invoke).
  • One of the files present on the device (indication of jailbreak)
    • /Applications/Cydia.app
    • /Library/MobileSubstrate/MobileSubstrate.dylib
    • /bin/bash
    • /usr/sbin/sshd
    • /etc/apt
    • /usr/bin/ssh
  • Proxy is enabled (using CFNetworkCopySystemProxySettings).

openURL method swizzling

The logic in the following screenshot is implemented in the ___cxxwebkmcouitninapo method which is called from ___cxxwebk_init_vw if the relevant flag is enabled.

The above screenshot shows the method swizzling implementation. It performs the following calls:

  1. NSSelectorFromString to get a selector for the “openURL:” method.
  2. NSClassFromString to get a class descriptor for UIApplication.
  3. class_getInstanceMethod to get the method descriptor.
  4. method_getImplementation to get actual implementation of the method.
  5. Then they define a code block which calls _____cxxwebkmcouitninapo_block_invoke_2 and then calls the original openURL.
  6. method_setImplementation to replace the original openURL with the code block from the previous step.

Almost the same happens for openURL:options:completionHandler: method except that they check the system version before doing so (the handler was first introduced in iOS 10).

At this point we had seen how the hook was applied. Next we will look at the implementation of the hook itself (_____cxxwebkmcouitninapo_block_invoke_2).

The openURL hook implementation

Effectively, the implementation is located in the ___cxxwebkmcoulsz method. There we can see one more anti-debug check call, then it checks the __mc_notifyInHouse flag and does nothing if this flag is 1.

This is done to ignore all marketing URLs clicked by a user on a Mintegral served ad. We can see relevant logic in +[MTGBase mtgOpenURL:options:completionHandler:] in the screenshot below:

Next piece of code shows that they also serialize the backtrace data and leak it in the payload they send to https://n.systemlog.me/log.

We are able to set a breakpoint at the +[_CXX_CXX_OperationPKTask _cxx_cm_log_warnings:to:ins:] call. You can see the call arguments on the next screenshot:

  • The URL that was opened (https://example.com?foo=bar&x=y).
  • The class where the click happened (ViewController).
  • The method from within where the click happened (openURLWithOptions:).
  • Backtrace info.

Another interesting function is _nsh_id_by_cc, which seems to be responsible for identifying competitor SDKs. Relevant logic shown in the next screenshot:

Going back to +[_CXX_CXX_OperationPKTask _cxx_cm_log_warnings:to:ins:], after the payload has been encoded via +[MTGBase base64EncodeString:] it creates a new code block and uses _dispatch_async to invoke it.

It leads to a series of calls with additional payload transformations and ends up in -[_MC_ApiManager AFRequestWithUrl:paras:success:failure:], which makes a post request to collectDomainUrl from MTGSetting which is https://n.systemlog.me/log by default.

SKStoreProductViewController hook

SKStoreProductViewController is a view controller that provides a page where the user can purchase media from the App Store.

The loadProductWithParameters:completionBlock: method loads a new product screen to display.

Instead of going into technical details of the hook implementation we added next code snippet to the demo application:

- (IBAction)openSKStoreProductViewController:(id)sender {
    SKStoreProductViewController *storeViewController = [[SKStoreProductViewController alloc] init];
    [storeViewController setDelegate:self];
    NSDictionary *productParams = @{SKStoreProductParameterITunesItemIdentifier: [NSNumber numberWithInt:1234567890]};
    [storeViewController loadProductWithParameters:productParams completionBlock:nil];
}

As a result we see a POST request to https://n.systemlog.me/log with the next payload in the “clever” field:

[{'cn': 'UIApplication', 'mn': 'sendAction:to:from:forEvent:', 'nid': 0, 'aid': '{"id":1234567890}', 'type': '2', 'trc': '["2|UIKitCore|0x00007fff49326c1d -[UIApplication sendAction:to:from:forEvent:] + 83","3|UIKitCore|0x00007fff48cd5baa -[UIControl sendAction:to:forEvent:] + 223","4|UIKitCore|0x00007fff48cd5ef2 -[UIControl _sendActionsForEvents:withEvent:] + 396","5|UIKitCore|0x00007fff48cd4e63 -[UIControl touchesEnded:withEvent:] + 497","6|UIKitCore|0x00007fff49362508 -[UIWindow _sendTouchesForEvent:] + 1359"]', 'dur': 24}]

As we can see the product ID is in the request Q.E.D.

NSURLProtocol hook

NSURLProtocol class allows a developer to redefine how Apple’s URL loading system operates. The Mintegral SDK registers malicious implementation of NSURLProtocol, which can be configured remotely to intercept any outgoing requests made by an application and track URLs and HTTP headers including the Authorization header.

For application developers it means that API tokens, cookies and basic authentication headers could potentially be collected by Mintegral.

To understand how the SDK activates that part of the malicious code we have to look back to ___cxxwebk_init_vw.

The above screenshot shows that the cud flag should be enabled and the cudl array shouldn’t be empty to activate the hook.

The following screenshot shows part of the ___cxxwebkterisiuuxx function called from the above method. 

The code in the ___cxxwebkterisiuuxx function performs the following actions:

  1. Creates a class inherited from NSURLProtocol (objc_registerClassPair, objc_allocateClassPair).
  2. Adds an implementation for canInitWithRequest: which is effectively an interceptor (class_addMethod).
  3. Calls +[NSURLProtocol registerClass:] to register the class with the URL loading system.

In our research, we added the following code snippet to verify what data is collected by the malicious code:

- (IBAction)httpRequestWithURLSession:(id)sender {
    NSURLSessionConfiguration *sConf = [NSURLSessionConfiguration defaultSessionConfiguration];
    sConf.HTTPAdditionalHeaders = @{@"Authorization": @"Basic YWRtaW46YWRtaW4K"};
    NSURLSession *session = [NSURLSession sessionWithConfiguration:sConf];
    NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://example.com/get-secret-data"]];
    req.HTTPBody = [@"foo=bar" dataUsingEncoding:NSUTF8StringEncoding];
    req.HTTPMethod = @"POST";
    NSURLSessionDataTask *task = [session dataTaskWithRequest:req];
    [task resume];
}

As a result we see a POST request to https://n.systemlog.me/log with the following payload in the “clever” field:

[{'cn': '', 'u': 'https://example.com/get-secret-data', 'mn': '', 'nid': 0, 'hhf': '{"Authorization":"Basic YWRtaW46YWRtaW4K","Content-Length":"7"}', 'type': '1', 'hm': 'POST'}]

In the request, we can see the URL and the authorization header. Although the request body is not included, headers often contain sensitive data. That data could even include personally identifiable information. For example, if an API uses JWT tokens, email or user name could be stored inside the token.

Conclusion

During the research we observed many interesting techniques Mintegral developers use to hide the malicious behaviour of their SDK. As we can see, they activate hooks only on specific applications in specific regions which helped the malicious code stay there for more than one year without any attention.

Talking about timeline, the first version (5.5.1) of the malicious SDK was published on Jul 17, 2019. We found that all subsequent versions housed the same malicious functionality.

Many popular applications were affected by the malicious activities of this SDK. We hope this research shedding light on the situation will drive greater scrutiny and privacy controls for advertiser networks moving forward.