Post

Mirai-like ELF Reversing, Part I: Stage1 Trust Gate, Command Dispatch, and Killer Loop

Static analysis of a Mirai-lineage ELF bot covering C2 trust verification, seven-method command dispatch, anti-competition killer logic, and reproducible extraction scripts.

Mirai-like ELF Reversing, Part I: Stage1 Trust Gate, Command Dispatch, and Killer Loop

Mirai’s source leak in 2016 turned a single botnet into an entire lineage of Linux threats, and new variants keep surfacing on commodity IoT infrastructure. This post tears apart one such sample – a statically linked x86-64 ELF that showed up on an open download server – to document exactly how it authenticates to its controller, selects an attack method, and locks out competing malware. Every claim maps to a script, a virtual address, or a disassembly slice you can reproduce yourself.

For less technical readers: this malware is a bot. It connects to a control server, checks that the server is the one it expects, receives commands, and launches network flood routines. At the same time it runs a cleanup loop to remove or kill other tools/processes on the same device.

For technical readers: all findings below are tied to reproducible scripts, offsets, and disassembly slices in the local workspace.

Sample and Scope

Artifact SHA-256
d40cf9c95dcedf4f19e4a5f5bb744c8e98af87eb5703c850e6fda3b613668c28.elf (Stage1 sample) d40cf9c95dcedf4f19e4a5f5bb744c8e98af87eb5703c850e6fda3b613668c28
094e9d6ee057d38f40c35f018488e35ab6ccd006ed261b17322e78fd5ea2c0cb.elf (Variant validation sample) 094e9d6ee057d38f40c35f018488e35ab6ccd006ed261b17322e78fd5ea2c0cb

Acquisition source (defanged): http://144[.]172[.]108[.]230/bins/mynode.x86_64

Working assessment: Mirai-like Stage1 loader/bot, high confidence for lineage overlap.

Download Artifacts

Cross-Variant Validation (094e.. sample)

I reran the same scripts, notebook flow, and YARA checks against:

  • 094e9d6ee057d38f40c35f018488e35ab6ccd006ed261b17322e78fd5ea2c0cb.elf

What changed:

  • This variant does not expose the same named method_* symbol set as the original sample
  • It still exposes resolver and flood-family markers (__dns_lookup, udpfl00d, tcpFl00d, ovhudpflood, watchdog_maintain)
  • The pipeline now emits variant-scoped reports under reports/variant_<sha12>/... so runs do not overwrite each other
  • Dispatch mapping falls back to heuristic mode when callsite-level symbols are missing

YARA validation now covers both files:

  • MIRAI_LIKE_D40CF9_STAGE1_HighFidelity + MIRAI_LIKE_D40CF9_STAGE1_VariantHeuristic hit on d40...
  • MIRAI_LIKE_094E9_STAGE1_HighFidelity hits on 094e...
  • MIRAI_LIKE_STAGE1_Family_Heuristic hits on both

Executive Workflow

Mirai Stage1 Workflow

Stage map (sample-specific)

  • main at 0x4002a0: command/control loop and method dispatch.
  • verify_server_ip at 0x4001c0: trust gate for connected peer.
  • killer_thread_func at 0x400730: endless anti-competition loop.
  • disable_infection_tools at 0x400a10: downloader/tool disruption.
  • scan_and_kill at 0x400d60: /proc scan and process termination.

What the Malware Does

1) Connects and verifies who it is talking to

The sample contains a hardcoded server IP string at 0x41498a: 144[.]172[.]108[.]230.

main establishes a connection, then calls verify_server_ip. If the connected peer IP does not match the hardcoded value, the command path is rejected.

Plain language: this is a trust check so the bot listens only to its expected controller.

Hardcoded authorized C2 IP in `.rodata`

verify_server_ip in assembly:

`verify_server_ip` assembly (`0x4001c0`)

verify_server_ip in pseudocode:

`verify_server_ip` pseudocode (`0x4001c0`)

2) Parses command lines and dispatches attack methods

The parser in main routes method tokens to dedicated handlers.

Command Callsite in main Handler
udp 0x4004f1 method_udp (0x401380)
syn 0x40052e method_syn (0x4027b0)
ack 0x4005b3 method_ack (0x4026d0)
udpslam 0x400667 method_udpslam (0x401280)
junk 0x4006c6 method_junk (0x401190)
raknet 0x40063e method_raknet (0x4010a0)
udpburst 0x400703 method_udpburst (0x400f60)

Observed control tokens include !SIGKILL and !hello.

Plain language: the bot has a menu of traffic attacks, and the server chooses which one to run.

Method tokens in .rodata:

Method tokens (`udp/syn/ack/raknet/udpslam/junk/udpburst`) at `0x4149e7`

Main dispatch assembly excerpt from main (0x400412 to 0x400703):

400412: cld
400413: mov ecx, 0x9
400418: mov rsi, r15
40041b: mov edi, 0x4149c6
400420: rep cmpsb
400422: je  0x4005bd <force_sigkill path>
...
4004ce: rep cmpsb                    ; "udp"
4004f1: call 0x401380 <method_udp>
...
40050b: rep cmpsb                    ; "syn"
40052e: call 0x4027b0 <method_syn>
...
400590: rep cmpsb                    ; "ack"
4005b3: call 0x4026d0 <method_ack>
...
400617: rep cmpsb                    ; "raknet"
40063e: call 0x4010a0 <method_raknet>
...
400667: call 0x401280 <method_udpslam>
...
4006c6: call 0x401190 <method_junk>
...
4006e0: rep cmpsb                    ; "udpburst"
400703: call 0x400f60 <method_udpburst>

This excerpt is from reports/disasm/main.asm in the published analysis bundle and matches the dispatch table above.

3) Runs anti-competition logic in parallel

killer_thread_func repeatedly calls:

  1. disable_infection_tools
  2. scan_and_kill
  3. sleep
  4. repeat

Referenced binary/tool paths include:

  • /usr/bin/wget
  • /usr/bin/curl
  • /usr/bin/tftp
  • /usr/bin/ftp
  • /usr/bin/scp
  • /usr/bin/nc
  • /usr/bin/netcat
  • /usr/bin/ncat
  • /bin/busybox

Plain language: once installed, it tries to keep control by removing competitors and useful admin tooling.

killer_thread_func loop:

`killer_thread_func` assembly (`0x400730`)

disable_infection_tools routine:

`disable_infection_tools` assembly (`0x400a10`)

scan_and_kill routine:

`scan_and_kill` assembly (`0x400d60`)

4) Carries multiple flood payload templates

.rodata contains traffic templates and markers including:

  • M-SEARCH * HTTP/1.1
  • Via: SIP/2.0/UDP 192.168.1.1:5060

This aligns with multi-method DDoS behavior and protocol-specific packet crafting.

Subroutines of Interest (What each one does)

Function VA (sample-specific) Role
main 0x4002a0 Core bot loop: connect, verify peer, parse command, dispatch method handler.
verify_server_ip 0x4001c0 Compares connected peer IP against hardcoded trusted server IP.
killer_thread_func 0x400730 Long-running worker that repeatedly applies anti-competition logic.
disable_infection_tools 0x400a10 Targets downloader/admin tools to reduce competing access paths.
scan_and_kill 0x400d60 Walks /proc and kills processes matching internal checks.
__dns_lookup 0x41312c Builds DNS query, sends/receives UDP DNS data, validates response flow.
__decode_header 0x414034 Parses DNS header bytes into decoded fields (id, flags, section counts).
method_udpburst 0x400f60 One concrete flood method implementation reachable from command parser.

For less technical readers: these are the key “jobs” inside the bot. Together they explain command intake, trust control, attack execution, and persistence behavior.

Why the DNS decode subroutine matters

The routine identified as __decode_header parses a DNS wire header into decoded fields (id, flags, qdcount, ancount, etc.).

That routine itself is not a unique “family signature” in isolation. It is significant because:

  • it sits in the resolver path used by __dns_lookup,
  • it shapes response validation behavior,
  • and its byte sequence overlaps an Elastic rule motif (Linux_Trojan_Gafgyt_d0c57a2e), which supports lineage overlap.

Practical analyst takeaway: treat this as shared protocol/lineage evidence, not sole attribution evidence.

__dns_lookup and resolver path:

`__dns_lookup` assembly (`0x41312c`)

__decode_header assembly:

`__decode_header` assembly (`0x414034`)

__decode_header pseudocode:

`__decode_header` pseudocode (`0x414034`)

Comparison with Fortinet Gayfemboy Campaign

I compared this sample against Fortinet’s campaign write-up and figure-level indicators.

Reference: Fortinet IoT malware Gayfemboy Mirai campaign

What overlaps

  • Mirai-style resolver/decoder behavior.
  • Process-killer/anti-tooling patterns.
  • Shared DDoS template style.

What does not overlap

  • No campaign-specific domain strings shown in that report.
  • No reported process-killer keywords (twinks :3, meowmeow, whattheflip, ^kill^).
  • No watchdog control marker 47272 in extracted strings.

Conservative conclusion: Mirai-lineage overlap, but not enough to claim same campaign cluster.

Reproducible Data-Extraction Workflow

All outputs below are generated from scripts in malware/Mirai.

Public script bundle: analysis_data/mirai_mar_2026/scripts

End-to-end run

1
python3 scripts/run_full_analysis.py

Step-by-step scripts

1
2
3
4
5
6
7
8
python3 scripts/run_full_analysis.py --sample input/d40cf9c95dcedf4f19e4a5f5bb744c8e98af87eb5703c850e6fda3b613668c28.elf --outdir reports/variant_d40cf9c95dce
python3 scripts/run_full_analysis.py --sample input/094e9d6ee057d38f40c35f018488e35ab6ccd006ed261b17322e78fd5ea2c0cb.elf --outdir reports/variant_094e9d6ee057

python3 scripts/triage_mirai_elf.py --sample input/<sha>.elf --outdir reports/variant_<sha12>
python3 scripts/extract_mirai_rodata_artifacts.py --sample input/<sha>.elf --outdir reports/variant_<sha12>
python3 scripts/extract_command_dispatch.py --sample input/<sha>.elf --triage-json reports/variant_<sha12>/json/triage_report.json --outdir reports/variant_<sha12>
python3 scripts/export_disasm_slices.py --sample input/<sha>.elf --outdir reports/variant_<sha12>/disasm
python3 scripts/compare_fortinet_gayfemboy.py --sample input/<sha>.elf --out reports/variant_<sha12>/json/fortinet_gayfemboy_overlap.json

Artifacts produced

  • reports/variant_<sha12>/json/triage_report.json
  • reports/variant_<sha12>/json/rodata_artifacts.json
  • reports/variant_<sha12>/json/command_dispatch_map.json
  • reports/variant_<sha12>/json/fortinet_gayfemboy_overlap.json
  • reports/variant_<sha12>/disasm/*.asm
  • reports/static/*.txt

Notebook and IDA Scripts

Notebook

What it does:

  • runs the pipeline,
  • loads JSON artifacts,
  • summarizes key IOCs/offsets for reporting.

IDA scripts

YARA Rules

Rules files:

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
rule MIRAI_LIKE_D40CF9_STAGE1_HighFidelity
{
  meta:
    author = "taogoldi"
    date = "2026-02-26"
    version = "2"
    sha256 = "d40cf9c95dcedf4f19e4a5f5bb744c8e98af87eb5703c850e6fda3b613668c28"
    description = "High-fidelity rule for the analyzed Mirai-like ELF sample"

  strings:
    $s1 = "[*] Connected to authorized server (%s)" ascii
    $s2 = "[!!!] SECURITY ALERT: Command from unauthorized IP: %s (expected: %s)" ascii
    $s3 = "144.172.108.230" ascii
    $s4 = "!SIGKILL" ascii
    $s5 = "1337SoraLOADER" ascii
    $s6 = "method_udpburst" ascii
    $s7 = "[*] Killer thread started." ascii

  condition:
    uint32(0) == 0x464c457f and 5 of ($s*)
}

rule MIRAI_LIKE_D40CF9_STAGE1_VariantHeuristic
{
  meta:
    author = "taogoldi"
    date = "2026-02-26"
    version = "2"
    sha256 = "d40cf9c95dcedf4f19e4a5f5bb744c8e98af87eb5703c850e6fda3b613668c28"
    description = "Heuristic Mirai-like detector for this cluster family"

  strings:
    $m1 = "udpslam" ascii
    $m2 = "udpburst" ascii
    $m3 = "raknet" ascii
    $m4 = "M-SEARCH * HTTP/1.1" ascii
    $m5 = "Via: SIP/2.0/UDP 192.168.1.1:5060" ascii
    $m6 = "/proc/%s/cmdline" ascii
    $m7 = "/proc/%s/maps" ascii
    $m8 = "/bin/busybox" ascii
    $m9 = "disable_infection_tools" ascii
    $m10 = "scan_and_kill" ascii

  condition:
    uint32(0) == 0x464c457f and
    7 of ($m*)
}

rule MIRAI_LIKE_094E9_STAGE1_HighFidelity
{
  meta:
    author = "taogoldi"
    date = "2026-03-07"
    version = "2"
    sha256 = "094e9d6ee057d38f40c35f018488e35ab6ccd006ed261b17322e78fd5ea2c0cb"
    description = "High-fidelity rule for the validated Mirai-like variant (094e...)"

  strings:
    $s1 = "watchdog_maintain" ascii
    $s2 = "watchdog_pid" ascii
    $s3 = "udpfl00d" ascii
    $s4 = "tcpFl00d" ascii
    $s5 = "ovhudpflood" ascii
    $s6 = "TSource Engine Query" ascii
    $s7 = "KHserverHACKER" ascii
    $s8 = "/etc/config/resolv.conf" ascii
    $s9 = "__open_nameservers" ascii
    $s10 = "dnslookup.c" ascii

  condition:
    uint32(0) == 0x464c457f and 7 of ($s*)
}

rule MIRAI_LIKE_STAGE1_Family_Heuristic
{
  meta:
    author = "taogoldi"
    date = "2026-03-07"
    version = "2"
    description = "Family-level heuristic intended to match both d40... and 094e... Mirai-like variants"

  strings:
    $core1 = "/etc/config/resolv.conf" ascii
    $core2 = "__open_nameservers" ascii
    $core3 = "dnslookup.c" ascii
    $core4 = "opennameservers.c" ascii
    $core5 = "__dns_lookup" ascii

    $old1 = "!SIGKILL" ascii
    $old2 = "M-SEARCH * HTTP/1.1" ascii
    $old3 = "Via: SIP/2.0/UDP 192.168.1.1:5060" ascii
    $old4 = "udpburst" ascii
    $old5 = "udpslam" ascii

    $new1 = "watchdog_maintain" ascii
    $new2 = "udpfl00d" ascii
    $new3 = "tcpFl00d" ascii
    $new4 = "ovhudpflood" ascii
    $new5 = "TSource Engine Query" ascii

  condition:
    uint32(0) == 0x464c457f and
    3 of ($core*) and
    (2 of ($old*) or 2 of ($new*))
}

MITRE ATT&CK Mapping

Technique ID Name Evidence in Sample
T1071.001 Application Layer Protocol: Web Protocols C2 communication over TCP to hardcoded IP; SIP and SSDP protocol templates in .rodata
T1573 Encrypted Channel Trust-gate verification of C2 peer IP before accepting commands (verify_server_ip)
T1059.004 Command and Scripting Interpreter: Unix Shell Command dispatch loop in main parses server-sent tokens and routes to handlers
T1489 Service Stop scan_and_kill walks /proc and terminates competing processes
T1562.001 Impair Defenses: Disable or Modify Tools disable_infection_tools removes wget, curl, tftp, ftp, scp, nc, netcat, ncat, busybox
T1498.001 Network Denial of Service: Direct Network Flood Seven DDoS methods: UDP, SYN, ACK, UDP-slam, junk, RakNet, UDP-burst
T1018 Remote System Discovery __dns_lookup builds and sends UDP DNS queries to resolve target hosts
T1106 Native API Direct use of Linux syscalls for socket, connect, process enumeration via /proc
T1036 Masquerading Loader string 1337SoraLOADER references the Sora variant family name

IOC Appendix

File Hashes (SHA-256)

Sample SHA-256
Stage1 primary d40cf9c95dcedf4f19e4a5f5bb744c8e98af87eb5703c850e6fda3b613668c28
Stage1 variant 094e9d6ee057d38f40c35f018488e35ab6ccd006ed261b17322e78fd5ea2c0cb

C2 Infrastructure

Indicator Type Context
144[.]172[.]108[.]230 IPv4 Hardcoded C2 server IP and download host

Network Indicators

Indicator Type Context
http://144[.]172[.]108[.]230/bins/mynode.x86_64 URL Sample acquisition/distribution path
M-SEARCH * HTTP/1.1 Payload template SSDP reflection flood template in .rodata
Via: SIP/2.0/UDP 192.168.1.1:5060 Payload template SIP flood template in .rodata

Notable Strings

String Context
!SIGKILL Control token parsed by command dispatcher
!hello Control token parsed by command dispatcher
1337SoraLOADER Loader/variant identity string
KHserverHACKER Present in variant 094e...

Conclusion

This Stage1 bot follows a clear Mirai-lineage architecture: it connects to a single hardcoded C2 IP, verifies the peer before accepting any instructions, and then enters a command-dispatch loop that routes server-sent tokens to seven distinct DDoS flood methods (UDP, SYN, ACK, UDP-slam, junk, RakNet, and UDP-burst). In parallel, a dedicated killer thread continuously removes common transfer utilities and terminates competing processes through /proc enumeration, ensuring the bot maintains exclusive control of the infected device.

Cross-variant validation against a second sample (094e...) confirmed that the core resolver, flood-family markers, and anti-competition patterns persist across builds, even when named symbols shift. The family-level YARA rule catches both variants while the high-fidelity rules pin each individually. Comparison with the Fortinet Gayfemboy campaign showed shared Mirai-lineage traits but not enough overlap to assert same-cluster attribution.

All findings are reproducible through the published scripts, IDA annotators, and notebook. Part II will extend this work with runtime behavior profiling and broader variant clustering.

This post is licensed under CC BY 4.0 by the author.