CVE-2025-48703 + CVE-2026-57517 — the gsocket systemd backdoor CWP's cleanup never removes
A field writeup for anyone whose CWP (Control Web Panel) box got hit in the
June–July wave: what actually happened, copy-paste IOCs, a generic detector, and
remediation — including the persistence layer CWP's own cleanup never touches.
If your CWP server has been behaving strangely since mid-June — your root SSH
key vanishing overnight, MySQL users disappearing, "cleanups" that don't
hold, or lines in /etc/hosts pointing gsocket.io / cloudsyndication.org at
127.0.0.1 — it's all one incident, and there are public CVEs behind it
that CWP still hasn't openly documented.
There's a theory going around that "a CWP dev went rogue." That is not what
happened. Below are the dots, connected.
Related threads (and a direct answer)
This ties together the two open threads in the CWP Bugs board:
- Critical: multiple CWP servers infected — arbitrary PHP code execution via filemanager (July 2025)
- CWP: new security issue and no communication (current)
To answer, directly, the question a few people keep asking in the second one:
- The "unexplained script hunting for a mysterious SSH key" is CWP's OWN
/scripts/temp_hacker_check, and the key it hunts is the attacker's:
IPCsi58xDKuXuq8CMnlIFQHoqiGkyziMQpAks2t0EBa0. - The "bug that wipes all your SSH keys" is that same script (details in
section 2). - What nobody has posted yet is the actual persistence that makes cleanups
keep failing — that's section 3.
TL;DR
- Root cause: two unauthenticated panel bugs that chain to RCE —
CVE-2025-48703 (command injection, fixed0.9.8.1205) and the newer
CVE-2026-57517 (blind SQLi → RCE via the MySQLFILEprivilege, fixed
0.9.8.1225). If your panel (ports 2030–2087) was internet-exposed before you
patched past both, you were rootable pre-auth — your SSH firewall didn't
matter. - The script wiping your
/root/.sshand dropping your MySQL users is CWP's
own cleanup (/scripts/temp_hacker_check), not the attacker. Early
builds were destructive;0.9.8.1244quietly made it surgical. Stop-gap:
usermod -d /var/empty operator. - Why your cleanups don't hold: a gsocket reverse C2 installed as a
systemd service, disguised as a kernel thread (e.g.defunct.service).
CWP's cleanup never removes it, so the attacker keeps reconnecting. Detector
below. - CWP has published no changelog for versions 1236–1244 and no advisory —
even though their own cleanup script hardcodes the attacker's SSH key.
1. Root cause: two unauthenticated panel bugs
The entry point is the CWP panel itself, not SSH. In the space of a year there
have been two unauthenticated holes that chain to remote code execution:
- CVE-2025-48703 — OS command injection in the file manager (shell
metacharacters via thet_totalparameter of afilemanager&acc=changePerm
request). Fixed in0.9.8.1205(June 2025). On
CISA's KEV list,
mass-exploited, 200k+ internet-facing servers, public PoC + Metasploit. This
is the vector behind the July-2025 "arbitrary PHP code execution via
filemanager" thread. - CVE-2026-57517 — unauthenticated blind SQL injection via the
userRes
POST parameter. It executes as the MySQL root user, which holds theFILE
privilege, so it escalates to RCE by writing a PHP payload (INTO DUMPFILE)
into a web-reachable path (e.g..../roundcube/logs/). Fixed in0.9.8.1225
(disclosed July 2026, KIS-2026-12).
This is the one the current wave lines up with.
Both need only a valid non-root username — no password. If your panel ports
(2030–2087) were reachable from the internet before you patched past both
fixes (anything below 0.9.8.1225), you were exploitable pre-auth; your SSH
firewall was irrelevant. Two unauth RCEs in twelve months is also the strongest
argument for the firewall step in section 5: stop exposing the panel at all.
2. temp_hacker_check is CWP's own cleanup — not the attacker
This is the part most people get backwards. /scripts/temp_hacker_check ships
with CWP (added in the late-June 0.9.8.1239 build). It's CWP's attempt to
auto-remediate: it null-routes the C2 in /etc/hosts (that's your
127.0.0.1 gsocket.io / cloudsyndication.org lines) and strips the attacker's
SSH key.
The early version was buggy, and it's what was locking admins out — not the
intruder:
- It ran cleanup through the
operatorsystem account, whose home is
/rooton RHEL-family boxes → it effectively didrm -rf /root/.ssh. Your
own root key vanished. The stop-gap that stops it:
usermod -d /var/empty operator. - It dropped MySQL users with
Host='%'→ apps lost DB access. (See the
"remove all users database" / "remove extra DB users" threads on 1239.)
So "someone keeps erasing my keys and my DB access" = CWP's own cleanup
misfiring, not the hacker. The hacker does the opposite — he plants keys.
Important update (as of 0.9.8.1244, script dated July 5): CWP has quietly
made this cleanup non-destructive. It no longer wipes /root/.ssh — it now
backs the file up, removes the immutable flag, and surgically deletes only the
malicious key line with:
and it explicitly locks the operator account (usermod -L + nologin). Read
that again: their own script hardcodes the attacker's key to remove it. So
CWP knows the exact IOC. They just publish nothing about it. (More at the end.)
3. The attacker's foothold — and why your cleanups don't stick
The IPCsi58x... key is the attacker's, planted via the RCE. On every box I've
seen: 30+ copies of it, NOPASSWD sudoers for every system account, a fake
login user (uid ~992, sudo), and authorized_keys set immutable
(chattr +i) so removal "doesn't stick."
But keys and users are just re-planted symptoms. The reason it keeps coming back
after you clean is a persistence layer that CWP's script never touches: a
gsocket reverse C2 installed as a systemd service, disguised as a kernel
thread.
- Unit:
defunct.service, withDescription=spoofed to look like a D-Bus
unit, andRestart=always. - ExecStart launches gsocket via
exec -a '[watchdogd]'(or[kaluad], etc.)
so inps/topit shows up as a bracketed kernel thread. The on-disk
binary is deleted-while-running. - It's an outbound connection (gsocket relay) → your firewall is irrelevant.
The attacker dials back in whenever he wants and re-plants
keys/users/immutable-flags by hand. That is your "we clean, it returns."
CWP's cleanup kills the /tmp/.gs-* gsocket but never removes this systemd
unit, so it survives the update.
Generic detector (name-independent — they rename it per box, so don't just
grep "defunct"). A real kernel thread has an empty /proc/PID/cmdline; an
imposter has a bracketed cmdline but a resolvable /proc/PID/exe:
Any hit = a process pretending to be a kernel thread. That's your persistence.
4. IOCs to check
- systemd units impersonating kernel/system services (
Restart=always,
ExecStart withexec -a '[...]'); e.g.defunct.service. /usr/bin/defunct,*/systemd/system/defunct.{service,dat},/tmp/.gs-0/,
/dev/shm/.gs-0/etc/hostslines null-routinggsocket.io/cloudsyndication.org(added by
CWP's cleanup = you were hit).- The
IPCsi58x...authorized_key; NOPASSWD/etc/sudoers.d/*; aloginuser;
immutableauthorized_keys(check withlsattr /root/.ssh/authorized_keys). - Dropper / lateral-move scripts reported in the wild:
/tmp/.auto_monitorand
/tmp/.tmp_baf(they iterate over every account and re-drop payloads), plus
random-named webshells inpublic_html(e.g.defauit.php, or a
<random>.jpgthat is actually PHP).
5. Remediating the persistence
Then: usermod -d /var/empty operator, remove the rogue login user + the
NOPASSWD sudoers, and firewall the CWP panel ports (2030–2087) to your own
IPs only, so the next panel 0-day isn't reachable from outside.
6. If you run WordPress / a CMS on the box — check the app layer too
The end goal here is malvertising (that cloudsyndication.org is an ad
network). On the static side we also found the panel's default index.html
files modified to load `<script src="//ak.akam60800.net/">` (a typosquat
of "akamai") — so check any stray index.html CWP left lying in your docroots.
Separately, on our main WordPress site the attacker planted a rogue user
admin. Two things worth sharing precisely, because they cut both ways:
- Wordfence Premium is what surfaced it — alert: "An admin user with the
username admin was created outside of WordPress." An OS-level sweep never
sees app-layer accounts, so without Wordfence we'd have missed it entirely. - But the account never actually held admin power. Its raw usermeta was
wp_capabilities = a:1:{s:10:"subscriber";b:1;}andwp_user_level = 0—
i.e. only ever a subscriber (level 0), with no rights over plugins,
themes, content or settings. Whatever the attacker intended, the administrator
capability never landed in the database. Its single login came from another
host that was itself part of the same compromise (attacker using a rooted box
as a jump point).
So: detection = Wordfence; actual privilege = never granted. Don't conflate the
two, and don't trust the display role — the ground truth for "is this user
really an admin?" is the raw {prefix}capabilities / {prefix}user_level in
usermeta. Check every WP site's DB users after an incident; the OS sweep won't.
7. What we did about it
For the two worst-hit boxes we did not clean in place — we rebuilt them on a
fresh OS and dropped CWP entirely. One is now a plain nginx static host (no
panel, no PHP handler at all); the other runs its app in Docker. Removing the
panel removes the pre-auth RCE surface completely: nothing listening on
2030–2087 means CVE-2025-48703 (and the next panel 0-day) simply isn't
reachable. On the servers we kept on CWP we've patched to the latest build,
firewalled the panel ports to our own IPs only, and are rotating every secret
that lived on the boxes.
I'm not saying everyone should ditch CWP. But if a box was rooted for weeks,
"fresh OS + restore data only + no internet-exposed panel" is the only
option that actually lets you sleep — in-place cleanup leaves too much room for
one missed persistence unit (see section 3) to undo all of it.
Bottom line — and CWP should come clean
For a box that was root-compromised for weeks, the honest gold standard is a
rebuild (fresh OS, patched CWP, restore data only from a pre-mid-June
backup — never /root, cron, sudoers, or system accounts) + rotate every
secret that lived on it (root / MySQL / CWP-admin / FTP / API tokens / SSH keys).
In-place cleanup only holds if you remove that systemd C2 and rotate secrets.
And on transparency, the evidence speaks for itself:
- The official changelog skips every version from
0.9.8.1236through the
current0.9.8.1244— not one note published for any of the "security"
releases. - Yet their own
/scripts/temp_hacker_checkhardcodes the attacker's key
(IPCsi58x...) and null-routesgsocket.io. They know exactly what happened. - The only public word from staff so far is "only new hackers with AI are now
attacking... but we solve them."
A lot of admins out there think they're still being actively hacked when part of
what they're seeing is CWP's own cleanup wiping their keys — and the real
persistence (the systemd gsocket C2) is left untouched, so "we solve them"
simply isn't true for anyone who hasn't removed that unit by hand.
Please, CWP: publish the CVE, the affected versions, the IOCs, and clear
manual remediation steps. We're not trying to bury you — we want to keep using
the panel. But silence on something this severe isn't acceptable.
If this helped, reply on the forum thread with what you found on your box (unit
name, argv0 disguise, first-seen date) — the more IOC variants we collect, the
faster everyone can check.
References
- CVE-2025-48703 — op-c.net analysis ·
BleepingComputer (CISA KEV) ·
SentinelOne DB - CVE-2026-57517 — Full Disclosure: KIS-2026-12 (userRes SQLi)