- Malware Analysis
- Threat Intelligence
- Reverse Engineering
Along Came a Spider: One URL, Two Attack Surfaces
Rejected Open WebUI requests and emulated Redis sessions shared a loader URL. Separate static analysis recovered a MIPS sample’s configuration; we observed no infection.
Zachary Gardner
Researcher at Beelzebub Labs
Giovanni Braccini
Researcher at Beelzebub Labs
Four requests reached our Open WebUI honeypot on September 22, 2026. Each carried Python code designed to download and run a payload. All four received the same answer: 401 Not authenticated.
Beelzebub, our open-source deception framework, preserved the request bodies. Inside was a download URL: hxxp://hand.genddos[.]st:48180/bug. The same IP address had sent it to a Redis honeypot the day before. Two different services, two ways to run code, and one place to fetch it.
The Redis replies were emulated. Our payload analysis was static; we observed no infection, live controller session, or working installation. Saved September 23 VirusTotal lookups returned no file records for the checked loader and MIPS hashes, and no independent infected-host count was established: the population is unknown.
The recovered code supports describing the MIPS sample as an IoT DDoS and shell bot. We did not observe those capabilities in use.
Two routes to execution
The Open WebUI requests came from 94.154.43[.]7 in two rounds, about 23 minutes apart. Each round began with a signup request; the honeypot returned HTTP 200 and a user object. The source then submitted a Pipe class to /api/v1/functions/create and a Tools class to /api/v1/tools/create.
These APIs let authorized users install Python code that runs on the server. Function creation is restricted to administrators; Tool creation requires permission.
The submitted classes put the download command before the ordinary plugin methods. The download-and-run command sat in __init__, so creating an instance would launch it; pipe() and run() simply returned empty strings. With imports omitted and the command redacted, one submission looked like this:
class Pipe: # The Tools submission used the same constructor pattern.
def __init__(self):
subprocess.Popen("<download-and-run command referencing /bug>", shell=True)
def pipe(self, *args, **kwargs):
return ""
The Redis attempts took a different route. Two sessions from the same address contained three values formatted as cron jobs and referencing /bug. The source tried to change Redis’s dir and dbfilename settings, then call SAVE to write a snapshot where cron could run the embedded command. It also tried to delete the key and restore the original settings.
These requests show attempts to use known execution paths, with no confirmed authentication bypass or cron execution. We followed /bug to examine the associated artifacts.
Following the URL
Caronte let us follow the URL beyond the requests recorded by the honeypot. We retrieved and statically analyzed /bug, a shell loader, and its associated payload, /spider.mips. The payload was a stripped, statically linked, big-endian MIPS ELF. We verified its SHA-256 and used Caronte’s decompilations to examine what it could do.
These files were collected separately from the honeypot exchanges; we cannot establish which versions were served during those requests.
We use Spider as a working name, drawn from the SPIDER_* variables in the loader and recovered strings. Its malware family remains unresolved.
The recovered loader script is written to try sixteen binary names until one runs. The suffixes cover MIPS, ARM, x86, PowerPC, SuperH, SPARC, M68k, and ARC:
hxxp://hand.genddos[.]st:48180/spider.<architecture>
The loader also allows for missing download tools: it restores execute permissions and falls back from curl to wget to BusyBox wget. Each candidate runs as .spid in a writable directory, with output suppressed. The loader removes the file after the attempt and stops when a candidate returns success. Where available, timeout limits each attempt to fifteen seconds.
Reading Spider’s instructions
Much of the bot’s behavior is hidden from a plain string scan. Functions ask for strings by number, and a decoder retrieves them from a 197-entry table using a repeating 24-byte XOR key. We recovered the full table statically. Commands, SPIDER_* names, and attack methods such as http2, fivem, and voip became readable, giving us a way to recognize the handlers in Caronte’s decompilations.
We recovered the MIPS sample’s configured controller by decrypting its ChaCha20-protected hostname and port. The result uses the distribution hostname on TCP port 48101; it does not establish reachability or a completed handshake:
Distribution (from loader): hand.genddos[.]st:48180
Recovered MIPS controller: hand.genddos[.]st:48101/TCP
Recovered commands
The recovered command table and handlers describe floods, shell execution, and bandwidth tests.
| Command | Recovered handler or format |
|---|---|
!ATK | Attack methods cover UDP/TCP, HTTP/HTTPS/HTTP2, SIP, FiveM, and Source-engine traffic. |
!EXEC | Runs shell commands; returns output, errors, and completion markers as EXECR lines. |
!STOP | Stops attack tasks; replies with STOPR %d. |
!KILL | Performs socket and process checks, then exits the bot. |
!SPD | Requests bandwidth measurements; replies with SPD %llu. |
The bandwidth tests could help an operator assess a device’s contribution to a flood. Spider measures downloads against public test files and uploads against a supplied host; both routines can feed the SPD %llu reply. Its HELLO registration also includes speed=, though we could not trace that value’s source.
Keeping a foothold
Recovered MIPS code includes detachment from its parent and copying through /proc/self/exe into locations such as /usr/local/bin/ and /var/lib/, using names including kworker. That could preserve a copy after .spid is deleted; we did not confirm an installation.
The recovered installer templates offer several ways to start it again: systemd, init, cron, shell, and Android hooks. The cron templates run every five minutes; a launcher checks /proc/[0-9]*/exe before starting another copy.
The recovered process-management routine searches /proc for names including mirai, gafgyt, mozi, and xmrig, then signals matching processes. Security tools such as clamd and rkhunter are targets too, with systemctl stop and disable commands for matched services. These are code targets, not observed terminations or evidence of ancestry.
A separate duplicate-instance check in the recovered code scans /proc/net/tcp for a listener on port 39123. We have not identified code that opens that listener.
Static inspection also confirms debugger checks: the MIPS code reads TracerPid from /proc/self/status, calls ptrace(PTRACE_TRACEME), and times a two-million-iteration loop against a threshold of roughly 2,500 ms. The code includes kernel-thread process names such as [kworker/u2:1].
A recovered routine across three builds
Comparing Caronte’s MIPSel, x86_64, and PowerPC decompilations, we recovered a shared !KILL sequence. This suggests a common implementation or copied module, not a validated family signature.
Before the exit path, the recovered code parses TCP socket-table rows, retains up to 256 socket inodes, matches them to socket:[inode] links in process file descriptors, and walks up to eight levels of process ancestry.
The x86_64 and PowerPC decompilations also expose the same row-parsing format string. We verified that string in the big-endian MIPS reference sample, though we have not confirmed the full sequence in that build.
The sequence and limits are a comparison lead; they do not identify the original source or author. The shared code does not establish who operated these samples.
The socket checks decide whether an auxiliary routine runs before the bot exits. Its purpose remains unresolved; the rival-process scan is separate.
What defenders can hunt
These clues come from submitted commands and recovered code. They need correlation and validation against real telemetry:
The candidate YARA rule combines recovered file markers. It compiles with YARA 4.5.4 and matches the big-endian MIPS reference plus the MIPSel, x86_64, and PowerPC siblings; coverage beyond these four samples and false positives remain untested.
- Delivery: a downloader that tries multiple architectures, restores execute permissions on download utilities, and runs a file named
.spid. Because the loader removes it, historical file telemetry may be more useful than a disk search. - Persistence:
SPIDER_*environment variables, a process masquerading as a kernel thread, and associated startup entries. Inspect the executable and its connections alongside the five-minute cron schedule or launcher’s/proc/*/execheck. - Protocol: the recovered
HELLOtemplate ends withspeed=,disk=, andboot=in that order. This grammar is unconfirmed in observed traffic;HELLOorPINGalone is too broad.
The recovered controller, hashes, and startup paths are listed below. TCP 39123 is a code-search clue; its presence as a listening port remains unconfirmed.
What the honeypots made visible
One source, one loader URL, two doors: an AI application’s plugin API and a Redis cron write.
Open WebUI returned 401; Redis returned an emulated +OK. Usually, that is where the story ends: a response in a log. Here, the honeypot kept the request bodies, including the loader URL. Caronte followed it to the loader and payload; static analysis showed what the MIPS sample was built to do.
If you run Open WebUI, treat plugin creation as privileged access and review rejected submissions, not only accepted ones. If your Redis is reachable from the internet, unexpected CONFIG SET dir and dbfilename changes are the tell. The controller, hashes, and startup paths are below.
Beelzebub is open source. Everything above started with a request it refused and remembered. If /bug or spider.<architecture> has shown up on your own sensors, we would like to compare notes.
Reference material
Network indicators
An October 1, 2026 query of VirusTotal’s passive-DNS records returned 176.65.139[.]155 at 08:12:42 UTC on September 11 and 176.65.139[.]170 at 09:06:01 UTC on September 28, 2026. These historical resolutions provide addresses to investigate, but do not identify which IP served /bug during the September 21–22 attempts.
| Role | Indicator | Context |
|---|---|---|
| Request source | 94.154.43[.]7 | Redis submissions on September 21 and Open WebUI submissions on September 22, 2026. |
| Loader distribution | hxxp://hand.genddos[.]st:48180/bug | Submitted URL and separate loader acquisition records. The URL has been associated with two distinct loader hashes. |
| Payload distribution | hxxp://hand.genddos[.]st:48180/spider.<architecture> | Sixteen-candidate URL pattern in the loader. |
| Configured controller | hand.genddos[.]st:48101/TCP | Hostname and port recovered from the encrypted MIPS configuration. |
| Historical address | 176.65.139[.]155 | VirusTotal resolution dated September 11, 2026. |
| Later resolution | 176.65.139[.]170 | VirusTotal resolution dated September 28, 2026. |
Artifact hashes and provenance
| Artifact | Size | SHA-256 |
|---|---|---|
/bug, SPIDER_NAME='Mentalist' | 1,529 bytes | 732394c84732f6cc68ba6f6e65f3ef8f005ef3db5988c6befd050048f5ecfd6b |
/bug, SPIDER_NAME='webrtc' | 1,526 bytes | a632b7a71cdb1d8d7f5fab841eb0d335d6a1f806ed32f56a1eeba879a8eda5e5 |
spider.mips, big-endian MIPS ELF | 582,460 bytes | db75a8ddcdbf6eaf388c1753af4baaddc2e9c32fe28523a8c57f331c93206b2f |
spider.mipsel | 582,492 bytes | 84301bca0070ab51048f3f910162e722bc43aeca0b4c118ad7e470069d69b6cb |
spider.x86_64 | 352,328 bytes | 5aa66c3051cb3eacde36e7a743c901aaafe8f01b6a8dd12b3b3213654414d964 |
spider.powerpc | 397,164 bytes | b258c6af3acf43b1bd56f1b8c559b9a7df0c39629904a2da87f731bd8cb6420a |
The hashes and sizes identify the files in our Caronte analyses. We independently rehashed the MIPS reference sample used for the static configuration recovery.
Our analysis covered two /bug loaders. The September 22 loader report identifies the 1,529-byte Mentalist copy, 732394c8…fd6b; the September 23 URL report identifies the 1,526-byte webrtc copy, a632b7a7…a5e5. Both reports link their loader to the same MIPS payload. These are report dates; the separately fetched files may not be the builds offered during the September 21–22 honeypot requests. The meaning of the two SPIDER_NAME values remains unknown.
Recovered installation details
These paths and templates come from static analysis, with no working installation confirmed.
The loader searches $TMPDIR, /data/local/tmp, /tmp, /var/tmp, $HOME, and the current directory for staging. The installer’s candidate directories include /usr/local/bin/, /var/lib/, /usr/bin/, /tmp/, and /var/tmp/; names include kworker and kworker-boot.
- Systemd:
/run/systemd/system,/etc/systemd/system, andmulti-user.target.wants. The unit template usesType=oneshot,RemainAfterExit=yes, anExecStartentry, andWantedBy=multi-user.target. - Init and cron:
/etc/init.d,/etc/rc.d,/etc/rc2.dthrough/etc/rc5.d,/etc/cron.d,/etc/crontabs/root, and/var/spool/cron/crontabs/root. Separate system and per-user cron templates contain a five-minute schedule. - Shell and Android startup:
/etc/rc.local,/etc/profile.d,.profile,.mkshrc,/data/adb/service.d, and/data/adb/post-fs-data.dappear in the decoded configuration.
The installer exports SPIDER_NAME in the generated launcher. The main routine reads SPIDER_INSTALL to select an installation-management path and SPIDER_PS to configure the process name.
The command handler’s shell fallbacks include /system/bin/sh, /vendor/bin/sh, /usr/bin/sh, and /data/local/tmp/sh.
Other detection results
Caronte’s MIPS analysis recorded ClamAV’s Unix.Trojan.Mirai-8041698-0 detection. That label does not establish Mirai ancestry; the family remains unresolved. We did not rerun ClamAV with current signatures.
ATT&CK mapping
The Python and Redis entries describe submitted commands; payload entries describe statically recovered code and templates. None establishes successful execution on a victim.
| Technique | Name | Evidence |
|---|---|---|
| T1059.006 | Python | Shell-launching constructors in the Open WebUI submissions. |
| T1059.004 | Unix Shell | Shell loader and !EXEC handler. |
| T1105 | Ingress Tool Transfer | Loader’s retrieval of architecture-specific payloads. |
| T1053.003 | Cron | Redis write-to-cron attempt and installer templates. |
| T1543.002 | Systemd Service | Installer service-unit templates and startup paths. |
| T1037.004 | RC Scripts | Installer init/rc scripts and rc.local hooks. |
| T1546.004 | Unix Shell Configuration Modification | Installer shell startup hooks, including /etc/profile.d. |
| T1057 | Process Discovery | /proc enumeration and process file-descriptor inspection. |
| T1070.004 | File Deletion | Loader cleanup of .spid after each launch attempt. |
| T1027 | Obfuscated Files or Information | XOR string table and encrypted controller configuration. |
| T1140 | Deobfuscate/Decode Files or Information | Runtime string-table and controller-field decoders. |
| T1498 | Network Denial of Service | UDP/TCP and application-protocol traffic methods in the payload. |