Background

Before we get into this analysis, I want to briefly cover the static analysis pipeline behind it. It’s based on semantic analysis — detecting patterns, extracting strings, and inspecting binary headers for signs of obfuscation or packing. I’m not going to go deep into the implementation here, but what matters is that it doesn’t need dynamic execution. No sandbox, no detonation. Just the file.

This sample came up during routine analysis. What happened next was either ironic or predictable, depending on how you look at it.


The Sample

SHA256 3bfc43943390c0faef501ec62465fd85f48cf059d06a1993e5176cedb7c7cd01
Size 18,579,456 bytes (17.7 MB)
Type ELF 64-bit LSB, x86-64
MalwareBazaar Threat: Unknown — 7 vendor detections
Tags wraith, elf
YARA hits Warp, WarpStrings (Seth Hardy)
Reporter c2hunter

A second related sample also exists:

SHA256 e097c85216c39c34a37ce0e7cc626c8a43889bda3dec4d28e9001a5081997031
Size 19,333,120 bytes (18.4 MB)
Delta e097 = 3bfc + 736KB — identical up to 3bfc’s EOF

Both are Go binaries, dynamically linked, with the same ELF entry point (0x403470) and identical program header layout.


The Script Output

Before anything else — before disassembly, before ML scoring, before YARA — the pipeline produces this:

====================================================
  Binary       : 3bfc43943390c0faef501ec62465fd85f48cf059d06a1993e5176cedb7c7cd01.elf
  SHA256       : 3bfc43943390c0faef501ec62465fd85f48cf059d06a1993e5176cedb7c7cd01
  Size         : 18,579,456 bytes (17.7 MB)
  Verdict      : MALWARE_OBFUSCATED
  Malicious    : 99.0%  ###################
  Evasive      : 99.0%  ###################
====================================================

  [ELF STRUCTURE ANOMALIES]
    ✗ section_header_past_eof(shoff=30302232,size=18579456)
    ✗ load_filesz_past_eof(27158456>18579456)
    ✗ go_binary_dynamic_linked
  → No legit binary triggers these checks

  [Go Symbols]
    main.credential

────────────────────────────────────────────────────

Three anomalies, one symbol, verdict in under a second. No sandbox, no dynamic execution.


What the Anomalies Actually Mean

1. Section Header Past EOF

e_shoff = 30,302,232
file size = 18,579,456

The ELF section header table is supposed to live at offset e_shoff. Here, that offset is ~12MB past the end of the file. elftools, objdump -h, Ghidra — they all either crash or return “no sections.”

The thing is, the Linux kernel doesn’t use the section header table to load a binary. It uses program headers (segments). So the binary runs fine. The section table is only there for analysis tools.

Deliberately breaking it is a classic anti-analysis move. It’s also an instant flag.

2. LOAD Segment Larger Than the File

PT_LOAD filesz = 27,158,456
file size      = 18,579,456

One of the LOAD segments claims to be 27MB but the file is only 18MB. The kernel handles this by mapping what’s on disk and zero-filling the rest. The binary executes correctly. Any tool that tries to read the full segment from disk either gets garbage or crashes.

3. Go Binary, Dynamically Linked

Go is almost always statically linked — that’s the whole point of its deployment model. A Go binary with /lib64/ld-linux-x86-64.so.2 as interpreter is unusual enough to flag on its own.

Why would they do this? My guess is they were trying to bypass Go binary detection — some signatures specifically look for Go’s static linking patterns, so switching to dynamic linking throws them off. What they didn’t account for is that in doing so, the binary is now loudly announcing itself. No legitimate Go application mangles its section headers and ships an oversized LOAD segment. If you’re going to develop malware, you can’t obfuscate everything — at some point it becomes more obvious, not less.


What the Binary Actually Does

The ELF structure tells us how it hides. The strings tell us what it does.

SSH/SFTP Capability

*sftp.fxp
*ssh.Conn
*ssh.ecdh
SSH_FXP_OPENDIR
SSH_FXP_READDIR
SSH_FXP_SETSTAT
sk-ssh-ed25519-cert-v01@openssh.com

This binary contains a full SSH and SFTP client implementation. Not just connection handling — directory listing, file operations, key exchange. The Go crypto/ssh and pkg/sftp packages are both present.

Credential Harvesting

main.credential    ← only symbol left after stripping
pw_passwd          ← /etc/passwd field
parsing password
admin
guest
super
sysadmin
sudg

main.credential is the one function the stripper missed — or left intentionally. Combined with pw_passwd, a default credential wordlist, and a full SSH client stack, this is almost certainly an SSH credential harvester or brute-forcer.

Process Masquerading

/bin/systemd-worker
nohup ./

/bin/systemd-worker doesn’t exist on any standard Linux system. This is the persistence name — the binary copies itself there and runs as a fake systemd process. nohup ./ suggests it daemonizes on execution.

Multi-Protocol Support

imap2  imap3  imaps
pop3   smtp   ftp   sftp

Beyond SSH, the binary handles IMAP, POP3, SMTP, and FTP. This points to multi-service credential testing — not just SSH, but email accounts and FTP servers as well.


The Two Versions

e097 is 736KB larger than 3bfc, byte-for-byte identical up to 3bfc’s EOF. The extra data starts exactly where 3bfc ends.

ClamAV detects e097 as Unix.Trojan.Coinminer and Unix.Dropper.Mirai — neither of which it applies to 3bfc. That 736KB is almost certainly an embedded coinminer or Mirai dropper payload appended to the base binary. Same tool, different deployment stage. 3bfc is the credential harvester. e097 adds the next-stage payload after it’s done harvesting.


The Opsec Paradox

Good malware hides inside legitimate processes and behaviors. The goal is to blend in, not to fight every analysis tool individually. When you go too far with obfuscation, you end up doing the opposite — every anti-analysis trick becomes its own detection signature.

The point is to hide inside the system, not to be evasive on every action.

This binary tried to break static analysis tools by corrupting its own structure. Instead, it handed the pipeline a 99% verdict before a single disassembly pass ran. Three techniques, three flags, one verdict.


IOCs

Type Value
SHA256 3bfc43943390c0faef501ec62465fd85f48cf059d06a1993e5176cedb7c7cd01
SHA256 e097c85216c39c34a37ce0e7cc626c8a43889bda3dec4d28e9001a5081997031
MD5 971d731d72311740260682c1ae3f5148
MD5 ee88097b09fdf07bb5d89e63d914663d
Persistence path /bin/systemd-worker
Family Warp / Wraith (likely)
Protocols SSH, SFTP, IMAP, POP3, SMTP, FTP

Detection

The three ELF checks that caught this binary:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 1. Section header table outside file bounds
if e_shoff > file_size:
    flag("section_header_past_eof")

# 2. LOAD segment claims more bytes than exist on disk
if any(seg.filesz > file_size for seg in PT_LOAD_segments):
    flag("load_filesz_past_eof")

# 3. Go binary with dynamic linker
if is_go_binary and has_PT_INTERP:
    flag("go_binary_dynamic_linked")

Any one of these alone is worth investigating. All three on the same binary means someone deliberately modified the ELF structure. No build system does this by accident.


Analysis performed with a custom static ELF analysis pipeline. No dynamic execution, no sandbox, no network access to the sample.

MITRE ATT&CK

Technique ID Notes
Brute Force: Password Guessing T1110.001 SSH wordlist: admin, guest, super, sysadmin
Brute Force: Password Spraying T1110.003 Multi-target credential spray
Remote Services: SSH T1021.004 Lateral movement via harvested credentials
Credentials in Files T1552.001 /etc/passwd parsing via pw_passwd
Masquerading: Match Legitimate Name T1036.005 /bin/systemd-worker
File and Directory Discovery T1083 SSH_FXP_OPENDIR, SSH_FXP_READDIR
Application Layer Protocol: File Transfer T1071.002 SSH, SFTP, FTP, IMAP, POP3, SMTP
Obfuscated Files or Information T1027 ELF section header mangling, oversized LOAD segment
Create or Modify System Process T1543.002 Persistence as fake systemd service
Boot or Logon Autostart Execution T1547 nohup ./ daemonization