Threat intelligence

Ladvix: Inside a Self-Propagating ELF Malware with IoT Botnet Traits

by Security News

Overview

This week, the SonicWall Capture Labs Threat Research team analyzed a sample of a malicious ELF file infector that shares characteristics of IoT botnet malware. The sample demonstrates self-propagation capabilities, file system scanning, and selective infection mechanisms targeting other ELF binaries. 

Infection Cycle

figure1.png
Figure 1. Initial detection

The file is detected as a 64-bit ELF binary with no packer, protector, or encryption.

figure2.png
Figure 2. Entry point

The entry point immediately jumps to function FUN_00100ef0 to begin code injection and infection of the system.

figure3.png
Figure 3. Decode Function

The malware uses a custom string encoding/decoding mechanism to obfuscate embedded strings. This function (FUN_001015d0) performs character substitution using two lookup tables:

  • **Encoded characters:** Stored at `0x00302080`
  • **Decoded characters:** Stored at `0x00302020`

Encoding tables:

Decode table: "0123456789abcdefghijklmnopqrstuvzywxABCDEFGHIJKLMNOPQRSTUVZYWX|:. !#-/;&*'\"\n\r"
Encode table: ">@o$:,.l+*^?=)(|AB&%;D{!wkUxzvutsrqp_nm-ihgfFCcba~K23456789eyd1XSNQWTZMIRHGVOYLjPJE/]["

This encoding makes static string analysis difficult and hides C2 addresses, file paths, and other identifying information. When scanning the system for potential targets, the malware performs the following steps:

  1. Gets current working directory using getcwd()
  2. Opens directory for reading with opendir()
  3. Iterates through files using readdir()
  4. Checks each file:

   - Opens file for reading

   - Reads first 5 bytes

   -Validates ELF header: 0x7F 'E' 'L' 'F' 0x02 (64-bit ELF)

   - Checks if file is already infected using signature check


if ((((local_2855 == '\x7f') && (local_2854 == 'E')) && (local_2853 == 'L')) &&
    ((local_2852 == 'F' && (local_2851 == '\x02')))) {
    // This is a 64-bit ELF file
    iVar4 = FUN_00100e80(&local_2448,3);  // Check access permissions
    if (iVar4 == 0) {
        iVar4 = FUN_001016a0(&local_2448,"Ym9uZ3JpcHo0amV6dXoK");  // Check if already infected
        if (iVar4 == 0) {
            FUN_00101460(&local_2448,iVar2);  // INFECT THE FILE
   }
    }
}

 

figure4.png
Figure 4. Main Infection Loop

This large function (approximately 400+ lines of decompiled code) performs the following actions:

  • String decoding for obfuscated configuration
  • File descriptor management for self-reading
  • Directory traversal and file scanning
  • ELF validation and infection triggering
  • Process forking for continued propagation

This is the core infection function that:

  1. Opens the target file with write permissions
  2. Reads target file metadata using fstat()
  3. Allocates memory for file content manipulation
  4. Reads target file into memory buffer
  5. Positions file pointer to beginning with lseek()
  6. Determines injection point in target
  7. Copies self into target - Reads from own file descriptor and writes to target
  8. Appends infection marker to identify infected files
  9. Cleans up and closes file handles

Identified code:


undefined8 FUN_00101460(char *param_1,uint param_2)
{
// Open target file for modification
__fd = FUN_00100e70();  // open() system call

if (-1 < (int)__fd) {
    // Get file stats
    FUN_00100e30(1,pcVar11,local_20d8);  // fstat()

    // Allocate memory for file size
    unaff_R12 = (char *)FUN_00100e20((long)iVar9);  // malloc()

    // Read entire target file
    if (0 < iVar9) {
      // Copy original file content
      FUN_00100db0(unaff_RBP,pcVar11,1);  // read()
    }

    // Reset to beginning
    FUN_00100d50(unaff_RBP,0,0);  // lseek()

    // Infect: copy self from source descriptor into target
    FUN_00100d50((ulong)param_2,0,0);  // lseek() on self
    do {
      unaff_R15 = (char *)FUN_00100db0(param_2,local_20e0,1);  // Read from self
      write(__fd,local_20e0,(long)(int)unaff_R15);  // Write to target
    } while ((int)uVar2 < 0x20d7);  // Copy 8407 bytes

    // Write original content back
    write(__fd,pcVar10,1);

    free(unaff_R12);
}
return 0;
}

Decoded Strings

Using the character translation tables, several encoded strings can be decoded:

Encoded StringDecoded ValuePurpose
`Ym9uZ3JpcHo0amV6dXoK``bongripz4jezuz` (Base64)Infection marker
`TwU!?Tx);(T)q)`Requires full decodingPossible file path/C2
`T)z?T?U!{XA!vU;sT<`Requires full decodingPossible device/path

Identified code:


undefined8 FUN_001016a0(undefined8 param_1,char *param_2)
{
// Decode path and open file
__stream = (FILE *)FUN_00100e90(param_1,uVar4);

if (__stream == (FILE *)0x0) {
return 0xffffffff; // Error opening
}

// Search file for signature marker
do {
lVar3 = FUN_00100db0(uVar1,uVar2,uVar9); // read()
if (lVar3 == 0) {
fclose(__stream);
return 0; // Not found - safe to infect
}

sVar5 = strlen(param_2);
lVar3 = FUN_00100dd0(uVar2,local_298,param_2,sVar5); // memmem()
} while (lVar3 == 0);

return 1; // Found - already infected

Approximately 8407 bytes of malicious code are injected into each file.  The string "Ym9uZ3JpcHo0amV6dXoK" (which decodes to bongripz4jezuz) acts as a mutex file marker. The function opens the file and returns 0 if marker not found (safe to infect), or 1 if found (already infected). This prevents:

  • Infinite infection loops
  • File bloat from repeated infections
  • Detection from abnormal file sizes

The loop wrapper function drives the recursive infection process:


void FUN_00101820(undefined8 param_1,undefined8 param_2,undefined4 param_3)
{
int iVar1;

// Check file access permissions
iVar1 = FUN_00100e80(param_2,3); // access() syscall
if (iVar1 == 0) {
// Check if already infected
iVar1 = FUN_001016a0(param_2,"Ym9uZ3JpcHo0amV6dXoK");
if (iVar1 == 0) {
// Infect the file
FUN_00101460(param_2,param_3);
return;
}
}
return;
}

To maintain infection rates and prevent any single process failure from halting the attack, the main function (FUN_00100ef0) contains calls to fork() functionality (via FUN_00100ed0()) and waitpid() (via FUN_00100e60()), indicating to the malware:

  • Forks child processes to continue infection while main process exits
  • Waits on child processes to manage execution
  • Loops infinitely with self-spawning behavior

// Fork process
uVar1 = FUN_00100ed0(); // fork()

if (uVar1 == 0) {
// Child process - execute malicious payload
FUN_00100dc0(unaff_RBP,local_2878,param_3); // execve()

// Re-spawn
(*DAT_00301fe0)(FUN_00100ef0,uVar5,auStack_2880,...);

// Infinite loop
do {
/* WARNING: Do nothing block with infinite loop */
} while( true );
}

Complete Function Call Graph


entry (0x00101350)
└─> FUN_00100ef0 (0x00100ef0) - Main infection logic
       ├─> FUN_001015d0 (0x001015d0) - String decoder
       ├─> FUN_00100e70 - open()
       ├─> FUN_00100d50 - lseek()
       ├─> FUN_00100eb0 - getcwd()
       ├─> FUN_00100d80 - opendir()
       ├─> FUN_00100e10 - readdir()
       ├─> FUN_00100db0 - read()
     ├─> FUN_00101820 (0x00101820) - Infection driver
       │    └─> FUN_001016a0 (0x001016a0) - Signature check
       │         └─> FUN_00100dd0 - memmem()
       │    └─> FUN_00101460 (0x00101460) - Infection routine
       ├─> FUN_00100ed0 - fork()
     └─> FUN_00100e60 - waitpid()

MITRE ATT&CK Mapping

TacticTechniqueEvidence
**Initial Access**T1195 - Supply Chain CompromiseInfects legitimate binaries
**Execution**T1059 - Command and Scripting InterpreterExecutes as native ELF
**Persistence**T1554 - Compromise Client Software BinaryOverwrites executables
**Defense Evasion**T1027 - Obfuscated Files or InformationString encoding
**Defense Evasion**T1036 - MasqueradingInfected files retain original names
**Discovery**T1083 - File and Directory DiscoveryRecursive directory traversal
**Lateral Movement**T1080 - Taint Shared ContentInfects shared binaries
**Impact**T1485 - Data DestructionCorrupts executable files

Conclusion

This ELF malware sample represents a file infector botnet designed to propagate through Linux systems by compromising executable binaries. While the technical sophistication is moderate (basic string obfuscation, simple infection mechanism), the potential impact is significant due to:

  • Rapid local spreading through file system infection
  • Corruption of legitimate executables leading to system instability
  • Difficulty in remediation once widespread infection occurs
  • Potential for payload expansion (the infection mechanism could deliver additional malware)

Sonicwall Protection

SonicWall Capture Labs provides protection against this threat via the following signature:

  • GAV: Ladvix.Infector (Trojan)

This threat is also detected by SonicWall Capture ATP with RTDMI™ and the Capture Client endpoint solution.

IOCs

MD5: 45c7b3b9991719c02f13b2eb2bdd9608

SHA-1: 60b4de5d9ff6fe9095342670d79e3acd61cecbe0

SHA-256: 9ab3a648fba46ffb7ecae2eb66e319af7a6206b551ea1d265f609c8419fb5c5b

 

Share This Article

An Article By

Security News

The SonicWall Capture Labs Threat Research Team gathers, analyzes and vets cross-vector threat information from the SonicWall Capture Threat network, consisting of global devices and resources, including more than 1 million security sensors in nearly 200 countries and territories. The research team identifies, analyzes, and mitigates critical vulnerabilities and malware daily through in-depth research, which drives protection for all SonicWall customers. In addition to safeguarding networks globally, the research team supports the larger threat intelligence community by releasing weekly deep technical analyses of the most critical threats to small businesses, providing critical knowledge that defenders need to protect their networks.