Malware analysis at machine speed. From suspicious sample to actionable intelligence in minutes. Discover Caronte
  • 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

Zachary Gardner

Researcher at Beelzebub Labs

Giovanni Braccini

Giovanni Braccini

Researcher at Beelzebub Labs

Along Came a Spider: One URL, Two Attack Surfaces

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.

Honeypot requests share the /bug URL; separate artifact analysis links a loader to a MIPS payload and its configured controller
Beelzebub recorded the URL; Caronte linked the separately collected loader and payload. Open full-size diagram

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.

CommandRecovered handler or format
!ATKAttack methods cover UDP/TCP, HTTP/HTTPS/HTTP2, SIP, FiveM, and Source-engine traffic.
!EXECRuns shell commands; returns output, errors, and completion markers as EXECR lines.
!STOPStops attack tasks; replies with STOPR %d.
!KILLPerforms socket and process checks, then exits the bot.
!SPDRequests 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/*/exe check.
  • Protocol: the recovered HELLO template ends with speed=, disk=, and boot= in that order. This grammar is unconfirmed in observed traffic; HELLO or PING alone 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.

RoleIndicatorContext
Request source94.154.43[.]7Redis submissions on September 21 and Open WebUI submissions on September 22, 2026.
Loader distributionhxxp://hand.genddos[.]st:48180/bugSubmitted URL and separate loader acquisition records. The URL has been associated with two distinct loader hashes.
Payload distributionhxxp://hand.genddos[.]st:48180/spider.<architecture>Sixteen-candidate URL pattern in the loader.
Configured controllerhand.genddos[.]st:48101/TCPHostname and port recovered from the encrypted MIPS configuration.
Historical address176.65.139[.]155VirusTotal resolution dated September 11, 2026.
Later resolution176.65.139[.]170VirusTotal resolution dated September 28, 2026.

Artifact hashes and provenance

ArtifactSizeSHA-256
/bug, SPIDER_NAME='Mentalist'1,529 bytes732394c84732f6cc68ba6f6e65f3ef8f005ef3db5988c6befd050048f5ecfd6b
/bug, SPIDER_NAME='webrtc'1,526 bytesa632b7a71cdb1d8d7f5fab841eb0d335d6a1f806ed32f56a1eeba879a8eda5e5
spider.mips, big-endian MIPS ELF582,460 bytesdb75a8ddcdbf6eaf388c1753af4baaddc2e9c32fe28523a8c57f331c93206b2f
spider.mipsel582,492 bytes84301bca0070ab51048f3f910162e722bc43aeca0b4c118ad7e470069d69b6cb
spider.x86_64352,328 bytes5aa66c3051cb3eacde36e7a743c901aaafe8f01b6a8dd12b3b3213654414d964
spider.powerpc397,164 bytesb258c6af3acf43b1bd56f1b8c559b9a7df0c39629904a2da87f731bd8cb6420a

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, and multi-user.target.wants. The unit template uses Type=oneshot, RemainAfterExit=yes, an ExecStart entry, and WantedBy=multi-user.target.
  • Init and cron: /etc/init.d, /etc/rc.d, /etc/rc2.d through /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.d appear 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.

TechniqueNameEvidence
T1059.006PythonShell-launching constructors in the Open WebUI submissions.
T1059.004Unix ShellShell loader and !EXEC handler.
T1105Ingress Tool TransferLoader’s retrieval of architecture-specific payloads.
T1053.003CronRedis write-to-cron attempt and installer templates.
T1543.002Systemd ServiceInstaller service-unit templates and startup paths.
T1037.004RC ScriptsInstaller init/rc scripts and rc.local hooks.
T1546.004Unix Shell Configuration ModificationInstaller shell startup hooks, including /etc/profile.d.
T1057Process Discovery/proc enumeration and process file-descriptor inspection.
T1070.004File DeletionLoader cleanup of .spid after each launch attempt.
T1027Obfuscated Files or InformationXOR string table and encrypted controller configuration.
T1140Deobfuscate/Decode Files or InformationRuntime string-table and controller-field decoders.
T1498Network Denial of ServiceUDP/TCP and application-protocol traffic methods in the payload.

Bring one high-friction workflow. Leave with a scoped proof of value.

Choose the smallest useful deployment
Define scope, approvals, and evidence requirements
Connect the output to your existing security stack