CrossC2 Expanding Cobalt Strike Beacon to Cross-Platform Attacks

From September to December 2024, JPCERT/CC has confirmed incidents involving CrossC2, the extension tool to create Cobalt Strike Beacon for Linux OS. The attacker employed CrossC2 as well as other tools such as PsExec, Plink, and Cobalt Strike in attempts to penetrate AD. Further investigation revealed that the attacker used custom malware (hereafter referred to as "ReadNimeLoader") as a loader for Cobalt Strike. Information submitted to VirusTotal suggests that this attack campaign may have been observed across multiple countries, not only in Japan. This article explains CrossC2 and Cobalt Strike, the malware used in the campaign, as well as other tools employed by the attacker. A tool released by JPCERT/CC to support the analysis of CrossC2 is also introduced at the end.

CrossC2

CrossC2 is an unofficial Beacon and builder compatible with Cobalt Strike version 4.1 and above, developed in C language. It is designed to operate on Linux (x86, x64) and macOS (x86, x64, M1) architectures. While the builder is publicly available on GitHub[1], allowing users to create Beacons, the source code for both the builder and the Beacon are not released.

Upon execution, CrossC2 immediately forks itself, and the main processing is carried out in the child process. The C2 information is retrieved from the configuration, while the C2 server host and port number can also be obtained from the environment variables "CCHOST" and "CCPORT". Once executed, CrossC2 is capable of executing various Cobalt Strike commands after establishing communication with the Cobalt Strike TeamServer. However, the range of executable commands is limited compared to the full functionality of standard Cobalt Strike. The Beacon contains the following multiple anti-analysis features:

  • String encoding using single-byte XOR
  • Insertion of a large amount of junk code

Figure 1 shows a part of the inserted junk code. A significant amount of such code is embedded in key functions. However, the obfuscation can be easily removed by replacing the following byte sequence with NOP instruction.

8B 85 ?? ?? ?? ?? 2D ?? ?? ?? ?? 89 85 ?? ?? ?? ?? 0F 84 ?? ?? ?? ?? E9 00 00 00 00

Figure 1: A part of obfuscated code in CrossC2


The configuration data is stored at the end of the file. CrossC2 first retrieves its own file path using the readlink function and then fread its own code. It keeps searching for the string "HOOK" to locate the address of the configuration data. The structure of the configuration is as shown below. The encrypted configuration data can be decrypted using AES128-CBC(no padding). CrossC2 uses OpenSSL library functions to perform the decryption.

0x0: "HOOK" search tag
0x4: Size of the configuration data
0x8: Encrypted configuration data

CrossC2 can create Beacon using legitimate TeamServer extensions. By default, the generated Beacon is packed with UPX, but attempting to unpack it with UPX fails due to the configuration information at the end of the file. To unpack the Beacon successfully, the configuration block must first be removed. After unpacking using UPX, the configuration block needs to be reinserted at the end.

Cobalt Strike

Figure 2 illustrates the flow of Cobalt Strike execution. The process is initiated by a legitimate java.exe file, which is executed from a Task Scheduler job registered by the attacker. This java.exe loads jli.dll through DLL sideloading. The DLL file is ReadNimeLoader, which is written in Nim language. ReadNimeLoader reads a data file named readme.txt from the same directory, decrypts it, and executes its content in memory. This file contains OdinLdr[2], an open-source Shellcode-format loader, which decodes the embedded Cobalt Strike Beacon and executes it in memory. All related files, including ReadNimeLoader, were located under "C:\$recycle.bin\" on the affected system. In addition, some ReadNimeLoader samples were found to contain the following PDB path.

D:\BuildServer\bna-4\work-git\phoenix-repository\phoenix\Release\Battle.net Launcher.exe.pdb

Figure 2: Flow of Cobalt Strike execution


ReadNimeLoader

ReadNimeLoader incorporates the following four anti-analysis techniques:

  • Detect debugging by checking BeingDebugged value in PEB
  • Detect debugging by checking CONTEXT_DEBUG_REGISTER value
  • Measure the difference of elapsed time and proceed to debag checking when the value exceeds 0x512
  • Detect debugging by causing an exception and checking whether an exception handler is obtained

A part of the decryption key required to decode OdinLdr is embedded within the functions for the abovementioned anti-analysis techniques. As a result, unless these functions are executed, the correct decryption key is not generated, and OdinLdr cannot be decrypted. In addition to these anti-analysis mechanisms, a large amount of junk code is also inserted. Figure 3 shows a part of the code.

Figure 3: A part of junk code


Strings used by ReadNimeLoader are encoded, and they are decoded using two distinct XOR-based decoding functions. Figure 4 shows a portion of each code.

Figure 4: Each decoding function


The encoded strings can be decoded using the Python script shown below. In earlier versions of ReadNimeLoader do not contain the decode02 function, indicating that it was added in a later version.

def BYTE1(in_data):
    return (in_data >> 8) & 0xff


def BYTE2(in_data):
    return (in_data >> 16) & 0xff


def BYTE3(in_data):
    return (in_data >> 24) & 0xff


def decode02(enc_bytes, xor_key):
    result = []
    for enc_byte in enc_bytes:
        enc_byte ^= BYTE3(xor_key) & 0xEE ^ BYTE2(xor_key) & 0xEE ^ (xor_key ^ BYTE1(xor_key)) & 0xEE
        result.append(i)
        enc_byte += 1
    return result


def decode01(enc_bytes, xor_key):
    xor_table = [ 0, 8, 0x10, 0x18]
    result = []
    for enc_byte in enc_bytes:
        for j in range(4):
            enc_byte ^= ((xor_key >> xor_table[j]) & 0xEE)
        result.append(i)
    return result

ReadNimeLoader uses AES256-ECB mode to decrypt the malware payload. The key is created by combining specific strings decoded by the aforementioned decoding functions, which is then converted into hexadecimal format, transformed to uppercase, and zero-padded. This decryption can be performed using the following Python script:

from Crypto.Cipher import AES
import binascii

def ZeroPadding(hexstr, num):
    padding_num = num - len(hexstr)
    if padding_num < 0:
        return hexstr
    return  hexstr + b"\x00" * padding_num


def decrypt(readme_data, key_string):
    capitalized_key = binascii.hexlify(ascii_to_bytes(key_string)).upper()
    key = ZeroPadding(capitalized_key, 32)
    Cipher = AES.new(key, AES.MODE_ECB)
    return Cipher.decrypt(readme_data)

OdinLdr

After execution, OdinLdr decrypts the internally encoded Cobalt Strike Beacon and runs it in memory. To avoid detection, the Beacon is periodically re-encrypted using a randomly generated XOR key and stored in newly allocated heap memory. It is a distinctive characteristic that there is the string "OdinLdr1337" at the beginning of the heap memory. Additionally, it has been confirmed that there are two types of Shellcode deployed by ReadNimeLoader: some samples execute the Cobalt Strike Beacon through OdinLdr, while others run the Beacon directly. Appendix B outlines the relations between ReadNimeLoader versions and their corresponding decryption keys, the specific readme.txt files loaded, and the encoded malware payloads. A part of the Cobalt Strike configuration used is included in the appendix.

Tools used by the attacker

Among the tools used by the attacker, multiple ELF versions of SystemBC were identified. For more information on the differences from the Windows version of SystemBC, please refer to the report by ANY.RUN [3]. Other confirmed tools include PsExec, commonly used for lateral movement; GetNPUsers [4], often leveraged in AS-REP Roasting attacks; Plink, an SSH client tool; and privilege escalation tools for Windows systems.

Attribution

Despite architectural differences, confirmed identical characteristics suggest that this attacker and the attack campaign have a potential connection to BlackBasta. More specifically, the domain confirmed to be used for the C2 in this campaign matches the one listed in Rapid7’s report on BlackBasta [5]. Further similarities include the use of the files named jli.dll and readme.txt. Additionally, SystemBC was leveraged, and when attacking AD, AS-REP was also used.

Tools for analyzing CrossC2

As an analysis tools for CrossC2, a configuration parser is publicly available on JPCERT/CC’s GitHub.

GitHub: JPCERTCC/aa-tools/parse_crossc2beacon_config.py
https://github.com/JPCERTCC/aa-tools/blob/master/parse_crossc2beacon_config.py

CrossC2 can generate binaries for macOS, not only for Linux, and this parser is designed to support macOS binaries as well. Figure 5 shows a sample output from running the configuration parser.

Figure 5: Sample output of the configuration parser


In Closing

While there are numerous incidents involving Cobalt Strike, this article focused on the particular case in which CrossC2, a tool that extends Cobalt Strike Beacon functionality to multiple platforms, was used in attacks, compromising Linux servers within an internal network. Many Linux servers do not have EDR or similar systems installed, making them potential entry points for further compromise, and thus more attention is required. We hope the information provided in this article will be useful to you in incident response and malware analysis. For details on confirmed C2 servers and malware hash values, please refer to the Appendix.

Yuma Masubuchi (Translated by Takumi Nakano)

References

[1] CrossC2
https://github.com/gloxec/CrossC2

[2] OdinLdr
https://github.com/emdnaia/OdinLdr

[3] ANY.RUN
A new SystemBC RAT is targeting Linux-based platforms
https://x.com/anyrun_app/status/1884207667058463188

[4] GetNPUsers.py
https://github.com/fortra/impacket/blob/master/examples/GetNPUsers.py

[5] Rapid7
BlackSuit Continues Social Engineering Attacks in Wake of Black Basta’s Internal Conflict
https://www.rapid7.com/blog/post/2025/06/10/blacksuit-continues-social-engineering-attacks-in-wake-of-black-bastas-internal-conflict/

Appendix A: Example of CrossC2 configuration

C2: 162.33.179[.]247:8443
PUBLICKEY: 
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCaW
34Iv7znqVuomjiJn4Yr1ck9YSWylfAoiy20DnR0ab
CoHtdPK3L05CgOjnLGSfM5Vji0IRd8xtCGpU699Jt
FCa/Jg7zmuejilkKTFpMB36+49UQtaYp4KjFuImRC
z72NdzszsLzHDlVWAPmn5CSTfsTIzceomQfmCDY//
IygzQIDAQAB
-----END PUBLIC KEY-----

Appendix B: The relations between ReadNimeLoader and malware payloads

Table 1: The relations between ReadNimeLoader and malware payloads

ReadNimeLoader Hash(SHA256) Version Key readme.txt Hash(SHA256) Encoded Malware
56b941f6dcb769ae6d6995412559012abab830f05d5d8acf2648f7fa48c20833 New toupper(to_hex("mfzuyqroasv")) + zero padding 6246fb5c8b714707ac49ade53e6fe5017d96442db393b1c0ba964698ae24245d OdinLdr + CobaltStrike
dfe79b9c57cfb9fc10597b43af1c0a798991b6ceeec2af9b1e0ed46e6a8661c8 New toupper(to_hex("vbewtdsmmswfweoz")) acdf2a87ed03f2c6fe1d9899e8a74e8b56f7b77bb8aed5adf2cc374ee5465168 OdinLdr + CobaltStrike
3f96b6589996e57abc1c4d9b732528d2d11dea5c814f8241170c14ca2cd0281d New toupper(to_hex("lgehaoevolq")) + zero padding 6b80d602472c76b1d0f05bcce62e0a34de758232d9d570ba61b540784c663c01 CobaltStrike
0ab709728666f8759ad8db574d4009cf74ebce36ef2572ef52b058997a9b2a25 New toupper(to_hex("ffjazoinsmsiywwt")) 3079a29575a0adff91f04c5493a7f3e1c89795e3a90cf842650cd8bd45c4e1bc CobaltStrike
ecca3194613b0bab02059c3544fdc90f6d4af5a4c06518c853517eb1d81b9735 Old toupper(to_hex("bcstctskmngpjjax")) Unknown Unknown
ad90a4490d82c7bd300fdbbdca0336e5ad2219d63ea0f08cebc33050d65b7ef2 Old toupper(to_hex("lklzndaawijhd")) + zero padding 70b3b8e07752c1f3d4a462b2ab47ca3d9fb5094131971067230031b8b2cd84f2 CobaltStrike
99d6b73b1a9e66d7f6dcb3244ea0783b60776efd223d95c4f95e31fde434e258 Old toupper(to_hex("ifovxtgokm|yzjwz")) Unknown Unknown

Appendix C: Example of Cobalt Strike configuration

BeaconType                       - HTTPS
Port                             - 443
SleepTime                        - 30000
MaxGetSize                       - 2097328
Jitter                           - 40
MaxDNS                           - Not Found
PublicKey_MD5                    - d67a7903c6777d64b69845b6fcd5db65
C2Server                         - 64.95.10[.]209,/Collector/2.0/settings/,179.60.149[.]209,/Collector/2.0/settings/,64.52.80[.]62,/Collector/2.0/settings/
UserAgent                        - Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Teams/1.4.00.2879 Chrome/80.0.3987.165 Electron/8.5.1 Safari/537.36
HttpPostUri                      - /MkuiIJzM2IZs
Malleable_C2_Instructions        - Remove 46 bytes from the end
                                   Remove 130 bytes from the beginning
                                   NetBIOS decode 'a'
HttpGet_Metadata                 - ConstHeaders
                                        Accept: json
                                        Host: westeurope-teams.azureedge.net
                                        Referer: https://teams.microsoft.com/_
                                        x-ms-session-id: f73c3186-057a-d996-3b63-b6e5de6ef20c
                                        x-ms-client-type: desktop
                                        x-mx-client-version: 27/1.0.0.2021020410
                                        Accept-Encoding: gzip, deflate, br
                                        Origin: https://teams.microsoft.com
                                   ConstParams
                                        qsp=true
                                        client-id=NO_AUTH
                                        sdk-version=ACT-Web-JS-2.5.0&
                                   Metadata
                                        base64url
                                        parameter "events"
HttpPost_Metadata                - ConstHeaders
                                        Connection: Keep-Alive
                                        Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
                                   SessionId
                                        base64url
                                        parameter "id"
                                   Output
                                        base64url
                                        print
PipeName                         - Not Found
DNS_Idle                         - Not Found
DNS_Sleep                        - Not Found
SSH_Host                         - Not Found
SSH_Port                         - Not Found
SSH_Username                     - Not Found
SSH_Password_Plaintext           - Not Found
SSH_Password_Pubkey              - Not Found
SSH_Banner                       -
HttpGet_Verb                     - GET
HttpPost_Verb                    - POST
HttpPostChunk                    - 0
Spawnto_x86                      - %windir%\syswow64\powercfg.exe
Spawnto_x64                      - %windir%\sysnative\powercfg.exe
CryptoScheme                     - 0
Proxy_Config                     - Not Found
Proxy_User                       - Not Found
Proxy_Password                   - Not Found
Proxy_Behavior                   - Use IE settings
Watermark_Hash                   - NtZOV6JzDr9QkEnX6bobPg==
Watermark                        - 987654321
bStageCleanup                    - True
bCFGCaution                      - True
KillDate                         - 0
bProcInject_StartRWX             - True
bProcInject_UseRWX               - False
bProcInject_MinAllocSize         - 8096
ProcInject_PrependAppend_x86     - Empty
ProcInject_PrependAppend_x64     - Empty
ProcInject_Execute               - ntdll.dll:RtlUserThreadStart
                                   NtQueueApcThread-s
                                   SetThreadContext
                                   CreateRemoteThread
                                   kernel32.dll:LoadLibraryA
                                   RtlCreateUserThread
ProcInject_AllocationMethod      - VirtualAllocEx
bUsesCookies                     - False
HostHeader                       -
headersToRemove                  - Not Found
DNS_Beaconing                    - Not Found
DNS_get_TypeA                    - Not Found
DNS_get_TypeAAAA                 - Not Found
DNS_get_TypeTXT                  - Not Found
DNS_put_metadata                 - Not Found
DNS_put_output                   - Not Found
DNS_resolver                     - Not Found
DNS_strategy                     - round-robin
DNS_strategy_rotate_seconds      - -1
DNS_strategy_fail_x              - -1
DNS_strategy_fail_seconds        - -1
Retry_Max_Attempts               - 0
Retry_Increase_Attempts          - 0
Retry_Duration                   - 0

Appendix D: Network

  • 64.52.80[.]62:443
  • 64.95.10[.]209:443
  • 67.217.228[.]55:443
  • 137.184.155[.]92:443
  • 159.65.241[.]37:443
  • 162.33.179[.]247:8443
  • 165.227.113[.]183:443
  • 179.60.149[.]209:443
  • 192.241.190[.]181:443
  • api.glazeceramics[.]com:443
  • doc.docu-duplicator[.]com:53
  • doc2.docu-duplicator[.]com:53
  • comdoc1.docu-duplicator[.]com:53

Appendix E: Malware

Table 2: List of malware and tools

Malware Filename Hash(SHA256)
java(Legitimate) java.exe 16b1819186f0803b9408d9a448a176142f8271a4bc0b42cdb78eb4489bce16fe
ReadNimeLoader jli.dll 56b941f6dcb769ae6d6995412559012abab830f05d5d8acf2648f7fa48c20833
ReadNimeLoader jli.dll dfe79b9c57cfb9fc10597b43af1c0a798991b6ceeec2af9b1e0ed46e6a8661c8
ReadNimeLoader jli.dll 3f96b6589996e57abc1c4d9b732528d2d11dea5c814f8241170c14ca2cd0281d
ReadNimeLoader jli.dll 0ab709728666f8759ad8db574d4009cf74ebce36ef2572ef52b058997a9b2a25
ReadNimeLoader jli.dll ecca3194613b0bab02059c3544fdc90f6d4af5a4c06518c853517eb1d81b9735
ReadNimeLoader jli.dll ad90a4490d82c7bd300fdbbdca0336e5ad2219d63ea0f08cebc33050d65b7ef2
ReadNimeLoader jli.dll 99d6b73b1a9e66d7f6dcb3244ea0783b60776efd223d95c4f95e31fde434e258
Cobalt Strike readme.txt 6246fb5c8b714707ac49ade53e6fe5017d96442db393b1c0ba964698ae24245d
Cobalt Strike readme.txt acdf2a87ed03f2c6fe1d9899e8a74e8b56f7b77bb8aed5adf2cc374ee5465168
Cobalt Strike readme.txt 6b80d602472c76b1d0f05bcce62e0a34de758232d9d570ba61b540784c663c01
Cobalt Strike readme.txt 3079a29575a0adff91f04c5493a7f3e1c89795e3a90cf842650cd8bd45c4e1bc
Cobalt Strike readme.txt 70b3b8e07752c1f3d4a462b2ab47ca3d9fb5094131971067230031b8b2cd84f2
CrossC2 gds 28d668f3e1026a56d55bc5d6e36fad71622c1ab20ace52d3ab12738f9f8c6589
CrossC2 gss 9e8c550545aea5212c687e15399344df8a2c89f8359b90d8054f233a757346e7
ELF-SystemBC monitor 74a33138ce1e57564baa4ea4db4a882d6bf51081b79a167a6cb2bf9130ddad7f
ELF-SystemBC monitor 7ccff87db7b4e6bc8c5a7e570f83e26ccb6f3a8f72388210af466048d3793b00
GetNPUsers GetNPUsers_windows.exe e0e827198a70eef6c697559660106cfab7229483b0cd7f0c7abd384a3d2ee504
Tools related to privilege escalation wermgr.exe f79e047ae4834e6a9234ca1635f18b074a870b366fe4368c10c2ddc56dfbb1bc
Tools related to privilege escalation wermgr.exe ac02aee660d44a8bfbc69e9c46cf402fd41e99915e13d0de3977e662ef13b2ca
Plink v0.81 conhost.exe 2e338a447b4ceaa00b99d742194d174243ca82830a03149028f9713d71fe9aab
PsExec v2.43 PsExec.exe 078163d5c16f64caa5a14784323fd51451b8c831c73396b967b4e35e6879937b
cab-related tool hhupd.exe d74eac55eeaa3138bc1e723c56013bb1af7709f0a77308bfbf268d4e32b37243
Back
Top