← Back

How a no-auth Redis box got turned into a cryptominer in one botnet's sweep

A cryptominer + Redis-scanner botnet took over a production VPS through an unauthenticated Redis container. Full kill-chain, IoCs, and the containment that stopped the reinjection loop.

BF
BountyForge Team
August 28, 2026
redisbotnetollamabountyforgeiocmalware
How a no-auth Redis box got turned into a cryptominer in one botnet's sweep

On August 26th an external net-scan alert landed on our radar: our IP address was sweeping a Hetzner block on :6379, in the classic sequential burst of a Redis-grab botnet. We did not run that scan. That alert was the botnet spreading from our own box and it meant we were already a node in a cryptomining operation.

This is the full reconstruction. It has a happy ending: the malware is gone, the vector is closed, and the confusion at the center of it all "wait, why is the Ollama user running a miner?" turned out to be a uid collision, not a breach of Ollama.

What we found

Three processes, all started on August 25th, all running as uid 999, all from random-named files in /tmp:

  • A cryptocurrency miner at ~150% CPU with ~2.4 GB of resident memory (an XMRig-family daemon)
  • A spreader/scanner whose config was a 400+ character URL-safe base64 blob passed as argv[1] — nothing written to disk
  • A third sibling that was already a zombie by the time we contained the box

Every binary had been unlink()ed immediately after exec, so ls /tmp showed nothing and /proc/<pid>/exe just said (deleted). No crons, no systemd units, no SSH keys, no ld.so.preload — a completely clean persistence audit. The malware had no on-disk presence at all.

The confusing part: "the Ollama user"

In ps, the processes resolved to the ollama account — home /usr/share/ollama, shell /bin/false. That looks like the malware was abusing Ollama. It wasn't.

Two things collided:

  1. The official redis container image runs as uid 999
  2. On Debian, the ollama system account is also uid 999

So processes launched inside the Redis container showed up in the host's process table with ollama as the resolved owner — a pure identity collision. The legitimate ollama.service unit (Main PID 716) was untouched, and the implants sat completely outside its cgroup. Ollama's API was bound to loopback for the entire window. We reported the campaign signature to the Ollama team as a defensive notice, explicitly not as a CVE claim.

The actual root cause

An unauthenticated Redis.

A redis:5.0.9 container (querybook_redis) was published to 0.0.0.0:6379 with no requirepass. From anywhere on the internet: redis-cli -h <ip> -p 6379 PINGPONG. Full, unauthenticated admin access on a public port.

Redis 5.x without a password admits the entire botnet toolkit: CONFIG SET dir/dbfilename, SLAVEOF — the classic rogue-master RDB trick — and MODULE LOAD. There was no zero-day here. No exploit of a vendor bug. We weren't pwned by anything clever. We were pwned by an open door.

The kill chain

  • Phase 0 — Targeting. The botnet continuously sweeps the IPv4 space on :6379 looking for unauthorized PONGs. Our container answered. Game over at the config stage.
  • Phase 1 — Initial access. Unauthenticated Redis admin over the public port.
  • Phase 2 — Execution. Code execution inside the container (rogue RDB / module load), plus — the one step forensic rigor leaves open — a pivot to host-level processes.
  • Phase 3 — Malware launch. Miner + spreader + a dead sibling, all launched from /tmp, all deleted post-exec.
  • Phase 4 — Defense evasion. Random 12-char basenames, unlink() after exec, everything else kept in argv, nothing on disk.
  • Phase 5 — Mining. ~150% CPU of someone else's electricity.
  • Phase 6 — Propagation. The spreader mass-scanned egress :6379 in sequential blocks. That's the traffic the external alert caught — and it's exactly how the botnet found us in the first place.
  • Phase 7 — Persistence. There is none. These families don't write files; they reinject — every boot, they re-exploit the still-open Redis and re-drop the binaries. Kill the processes alone and they're back. Close the door and they're gone.

Phases 1, 3, 4, 5, 6 are proven artifacts; Phase 2's container-to-host step is the one genuinely unresolved detail, and we flag it as such rather than guess.

Containment

  • Killed both live implants and captured timestamped baselines (ps, ss, docker ps, last, kernel log).
  • Closed the vector: published ports dropped in the DOCKER-USER chain (6379, SMTP, the lab ports, and later Postgres 5432 + MySQL 3306 — anything on the public interface) and stopped the Redis container.
  • Verified the persistence model: after the port was dropped and the container stopped, no process respawned. That confirmed the no-file-persistence / reinjection-by-design hypothesis.

Lesson one: when Docker-published ports are involved, INPUT alone is useless the traffic is DNAT'd into the bridge before INPUT ever sees it. The chain that actually gates published ports is DOCKER-USER.

Hardening we're applying

  • No database publicly bound, ever always, plus requirepass when Redis is genuinely needed.
  • Published-port inventory as part of deployment review, not an afterthought.
  • Rotating every secret the box ever saw, and regenerating the deployment SSH keypair.
  • A reimage file-persistence was clean, but "we found nothing" after a cryptominer has touched a host is not the same as "nothing is there." Fresh disk, known state.
  • The OLLAMA_HOST=127.0.0.1 loopback-default advice goes into our operator docs — not because Ollama was the vector, but because this botnet family also probes exposed Ollama APIs, and a stable low-priv uid like 999 is exactly the free identity it wants on any host that has it.

What this cost us

The obvious answer is a VPS and some stolen CPU-time and a few days of incident response. The honest answer is credibility and trust: a production box silently turned into an attacker's terminal is every security team's nightmare precisely because the data plane your customers trusted was sharing a host with malicious processes.

There's also a good-news story buried in here. The only reason this was caught at all was external observation — a third-party net-scan sensor saw anomalous egress and surfaced an alert. We did not detect the initial compromise. Defense in depth isn't a slogan; it's the alarm on the other side of the fence that saved us.

TL;DR

  • We were cryptomined by a commodity botnet through an unauthenticated Redis on a public port.
  • Not a zero-day, not an Ollama bug — the "Ollama" process owner was a uid-999 collision between the Redis image and the Ollama system account.
  • The malware had no file persistence; it survived by re-exploiting the open Redis every time.
  • Killing processes wasn't enough — closing the port was the cure. We also learned the hard way that DOCKER-USER gates published ports, INPUT does not.
  • Full cleanup requires secret rotation and a reimage. Detection that mattered came from an external sensor, not our own alerts.

If you're running a public-facing Redis today — even a tiny lab one — go check requirepass and your bind address right now. The sweep is already running.

— BountyForge operations

Full technical annex (IoCs, evidence table, proven-vs-hypothesis analysis): internal incident report.