August 6, 2025

SCENE 1: SoupDealer - Technical Analysis of a Stealth Java Loader Used in Phishing Campaigns Targeting Türkiye

Threat.Zone

Key takeaway: the malware bypassed every public sandbox and AV aside from Threat.Zone, and also evaded EDR/XDR in real-world incidents. We noted impact across many banks, ISPs, and mid-level organizations.
This case again shows why on-premises sandboxes are essential for critical infrastructure and why real dynamic analysis is crucial for SOC teams.

In recent weeks, our research team’ve identified a sophisticated phishing campaign targeting Türkiye. Threat actors targeted computers running Windows operating systems located in Türkiye and using the Turkish language. 

These emails distribute an advanced botnet that comes with a 3-stage loader under files such as “TEKLIFALINACAKURUNLER.jar”. Spreading itself depends on C2 owner via infected clients email accounts.

When this malware is executed, it uses advanced persistence mechanisms—including downloading TOR to establish communication with the command-and-control panel and scheduling tasks for automatic execution—to ensure the device is located in Türkiye and being used in Turkish. It then sends various information based on signals from the command-and-control server and gains full control over the device.

https://www.virustotal.com/gui/file/d286acf63f5846e775ba23599e2b5be88d0564d24f29e0646f6cff207249c130

For the Threat.Zone analysis, we used an Enterprise Tenant and routed traffic through a Turkish proxy as the dirty-line egress.

https://app.threat.zone/submission/5488b11f-43fb-46c0-a0e5-6e27c9138195/overview

Advanced Analysis

First Stage

According to File Info, the sample is malware written in Java, so we will continue the analysis with JByteMode (to easily switch between decompilers) and JADX.

After opening the sample on JADX, the Main Class is visible in the META-INF/MANIFEST.MF file.

Main-Class: B

The sample contains 11 classes and a stage2 payload (encrypted).

We can see that class B, including Sample's entry point, works like Custom ClassLoader.

Due to the obfuscation applied to the sample, we see decompilation errors in almost every function of the sample we opened on JADX.

After opening Sample with JByteMod (decompiler: CFR), some obfuscation operations are seen at the main point.

To simplify reading, after running java-deobfuscator on a sample with a specific config, it can be seen that only one function is executed at the main point, excluding junk operations.

Deobfuscator Config:

input: sample.jar
output: deobfuscated-sample.jar
transformers:
    - allatori.StringEncryptionTransformer
    - general.peephole.PeepholeOptimizer
    - normalizer.SourceFileClassNormalizer

After string decryption and peephole optimization, it was observed that most of the operations at the main point were junk codes added to complicate the reverse engineering process.

Among the junk operations, it performs the Loader4.w(string[]) operation.

In fact, the entire unpacking process is executed sequentially in the <init> function (Loader Constructor). After manually deleting the junk code in the Loader Constructor code on the sample we deobfuscated, the following code appears:

public Loader() {
    super(ClassLoader.getSystemClassLoader());
    this.w = new ConcurrentHashMap<String, byte[]>();
    this.u = new ConcurrentHashMap<String, byte[]>();
    final JarFile d = new Loader.Loader5((Loader3)null).d((Void)null);
    final String n5 = new Loader.Loader2((Loader3)null).n(d);
    final byte[] h = new Loader.Loader6((Loader3)null).h(n5);
    final byte[] x = new Loader.Loader7("875758066416").x(h);
    new Loader.Loader1(this, (Loader3)null).x(x);
}

Let's focus on the last two lines to get straight to the point.

private static final class Loader7 implements Loader.Loader8<byte[], byte[]> {
    private final String g; // from constructor

    public byte[] x(final byte[] a) {
        this = (Loader7) MessageDigest.getInstance("SHA-512");
        this = (Loader7) (Object) Arrays.copyOf(((MessageDigest) this).digest(this.g.getBytes("UTF-8")), 16);
        final SecretKeySpec key = new SecretKeySpec((byte[]) (Object) this, "AES");
        final Cipher instance = Cipher.getInstance("AES");
        instance.init(2, key);
        return instance.doFinal(a);
    }

    public Loader7(String a) {
        this.g = a;
    }
}

A decryption process is performed within the Loader7 class. The file named “d6RuwzOkGZM12DXi” in the resources is subjected to decryption using the decryption key (variable named g) sent to Loader7, and the second stage of Loader executes.

import hashlib
from Crypto.Cipher import AES

input_file = "d6RuwzOkGZM12DXi"
output_file = "stage2.jar"
KEY = "875758066416"

key = hashlib.sha512(KEY.encode("utf-8")).digest()[:16]

with open(input_file, "rb") as f:
    ciphertext = f.read()

cipher = AES.new(key, AES.MODE_ECB)
plaintext = cipher.decrypt(ciphertext)

with open(output_file, "wb") as f:
    f.write(plaintext)

Second Stage

When the decrypted stage2 payload is opened on JADX, it contains only one class and one file named "stub". We observed that this file is part of the Loader and exhibits a matryoshka-type behavior.

It can be seen that the k function is executed at the <init> point of the file (the function that loads the stub into memory), and then the main function within the class loaded into memory is executed at the main point.

The following codes are junk codes that have been manually cleaned up:

public static void main(String[] a) throws Exception {
    final Method method = new h().loadClass("Principal").getMethod("main", String[].class);
    final int modifiers = method.getModifiers();
    if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)) {
        method.invoke(null, new String[0]);
    }
}

public h() throws MalformedURLException, IOException {
    super(h.class.getClassLoader());
    this.k();
}

@Override
protected Class findClass(final String a) {
    final byte[] array = kf.get(a);
    final byte[] b = array;
    final Class<?> defineClass = this.defineClass(a, b, off, b.length);
    h.ea.put(a, defineClass);
    return defineClass;
}

static {
    f = "V7h7j8HppdPqMZnUmYcCqniAwTpOTQcftPQakfIYqLeZNtDDCGrkrVjiCFwVkdNvySuNYsGt";
    ea = new HashMap();
    kf = new HashMap();
}

Within the k function, it can be seen that the getResourceAsStream function loads the resource named stub into memory and performs certain operations on it using the v function. Then, within the findClass function, defineClass is used to define this file loaded into memory as a Java class at runtime.

Loads the file named stub and stores it as a byte array.

ldc String "stub"
invokevirtual InputStream Class.getResourceAsStream(String)
// ....
invokevirtual byte[] ByteArrayOutputStream.toByteArray()
astore 27
ldc String "V7h7j8HppdPqMZnUmYcCqniAwTpOTQcftPQakfIYqLeZNtDDCGrkrVjiCFwVkdNvySuNYsGt"
astore 5
aload 27
aload 5
invokespecial byte[] h.v(byte[], String)

There is an RC4 operation in the v function. This means that the file loaded into memory cannot be used directly as a dynamic class. In addition, RC4 decryption is required. When the stub file is decrypted using the provided key value with RC4, the resulting file is the stage 3 file.

Third Stage

In the file structure, TOR and Config files are clearly visible. Stage3 is identified as the malware itself.

Since the file is protected with String Encryption, the deobfuscator is run with the previously used config.

After checking whether the machine is running Windows at the sample entry point, it configures the https.protocols property to enable the use of older TLS versions (TLSv1, TLSv1.1, TLSv1.2).

Information it specifically checks:

  1. RAM Check
  2. CPU Core Check
  3. Windows Server Check
  4. Location Check (Only Türkiye) (with ip-api[.]com)

When the control steps are complete, it executes the if() function that starts the main process. After attempting to run the sample as an administrator within this function, the ri() function is executed.

An antivirus check is performed within the function named ri. If no antivirus is found running on the device, the download process is initiated.

Before starting the TOR download process, the sample adds itself to scheduled tasks.

If Sample cannot find the TOR, it goes to https://dist.torproject.org/torbrowser/ to start the download process and creates the download link using regex.

After downloading TOR, it sends a request to [check.torproject.org] via the proxy address 127.0.0.1:9050 and checks whether the connection is provided by TOR.

After completing TOR-related operations, it calls a class named “Adwind” that will execute malicious operations. In the constructor function of this class, it sets the connection address, the password to be sent with the connection, and the ports.

The ConfigSet class also sets this malware config.

PORT_1 = 49152
INSTALL = true
PORT_2 = 49153
TOR = true
STARTUP_DELAY = 30
SOURCE_VERSION = "5.3.0.2.F.0"
CONNECT_DNS = "4ufbghkgmeb7nevhki3vf5rfwmmrb4bb52xcw2qwbh3qo4x773ttnhqd.onion"
TOR_UPLOAD_DNS = "42dtw6kxl5zxfpo25yknm2hmqndafoqynk3m6j2luvhi4epeex6arvyd.onion"
PASSWORD = "04d3978c339e5601d4eb7411946b691c746a6438"
PREFIX = "SPAM"
STARTUP_TYPE = "REGEDIT"

The run function initiates a connection using the appropriate port and password values with the C&C address. After establishing this connection, it will continue its operations by waiting for a signal from the command control panel.

A listener that can process 11 signals specifically:

1: Signal that displays the message written by the actor on the screen. 

2: Signal that takes a screenshot and sends it to the server. 

3: Signal that closes the malware. 

4: Signal that restarts the malware. 

5: Signal that opens a URL on the victim's device. 

7: Signal that displays an image on the screen. 

8: A signal that initiates a DDOS attack 

9: File Manager Signal 

10: A signal that deletes malware from the device 

11: A handler signal that can execute several commands 

12: A signal that executes the Download or Execute command

There are many different commands within the 12th signal that runs dynamic commands:

File Download Phase (if no command is sent)

Command execution part

spread: Network Worm/Spreader

This function copies randomly named files (malware itself) to shared folders accessed from IP addresses on the local network.

killdefender: Disable defender or Malware Run as Admin with some Powershell commands 

killanti: Kill Defender 

killvss: Add JAR Path Defender Exclusion Path

cmd: Execute Command

kill: Uninstall and Kill Malware

fuck: Uninstall malware and shutdown device

jar, ajar, ax, x: Download file as JAR

An article written by Jonathan Silva(@silvasec) analyzes a similar type of malware. It appears that all loader stages are the same. The difference is that the malware described in this article establishes a C&C connection via TOR.

IOCs

SHA256 Hash

d286acf63f5846e775ba23599e2b5be88d0564d24f29e0646f6cff207249c130 (TEKLIFALINACAKURUNLER.jar)

d286acf63f5846e775ba23599e2b5be88d0564d24f29e0646f6cff207249c130 (FIYATTEKLIFI.JAR)

URL

4ufbghkgmeb7nevhki3vf5rfwmmrb4bb52xcw2qwbh3qo4x773ttnhqd[.]onion

42dtw6kxl5zxfpo25yknm2hmqndafoqynk3m6j2luvhi4epeex6arvyd[.]onion

Closing Note

This is the opening piece of the technical analysis. The malware presents different capabilities depending on what the C2 owner does. In Part 2, we’ll show how we validated this and how we prepared the honeypot environment.

Utku Çorbacı