cwpdoor: Trojanized Control Web Panel Server Runs Root Commands Without Leaving Logs

QiAnXin XLab has published an analysis of cwpdoor, a backdoor built into a recompiled copy of cwpsrv, the web server behind Control Web Panel (CWP). The modified server keeps serving the hosting panel as normal. When it receives a request carrying signed X-Ngx-Integrity headers, it runs the attacker’s command and leaves no access-log entry, and its installer puts the original file timestamps back afterwards. When XLab checked, the implant had zero detections on VirusTotal. A sample uploaded from Brazil was packaged together with the Ebury SSH backdoor, so CWP operators should look for both.
| Quoi | cwpdoor: a trojanized, recompiled cwpsrv binary that gives remote command execution |
| Chercheurs | QiAnXin XLab (Alex.Turing, Acey9), published 5 Nov 2026; reported by The Hacker News on 6 Nov 2026 |
| Affecté | CWP hosts running cwpsrv 1.15.10 ou 1.24.0 (the only versions the installer supports) |
| Prerequisite | Root on the server. The initial access vector is unknown, and no CVE is linked |
| Détection | 0/63 engines on VirusTotal for the backdoored binary at the time of XLab’s analysis |
| In the wild | Yes, according to XLab: the installer was captured on 29 Jun 2026 and a sample was uploaded from Brazil on 30 Sep. The number of victims is unknown |
| Action | Hunt on every CWP host, check for Ebury at the same time, rotate credentials, and rebuild where possible |
Ce qui s'est passé
On 29 June 2026, XLab’s threat-intelligence system captured a script called cwpsrv_update.sh. The script downloads a replacement cwpsrv from 136.0.141[.]71 and installs it in place of the legitimate binary. XLab stresses that cwpdoor is not a separate implant. In its words, “the attacker inserts malicious code into the cwpsrv source code and recompiles it.” The function names XLab recovered, such as ngx_http_process_request, show that cwpsrv is derived from nginx. That means the backdoor runs inside a process that defenders expect to be listening on the network.
On 30 September, someone uploaded an archive named evidencia.tgz (“evidence”) to VirusTotal from Brazil. It held two files: cwpsrv.suspeito, which is cwpdoor with zero detections, and libkeyutils.so.1.5.2, which most engines flagged as Ebury. Ebury is an OpenSSH backdoor and credential stealer that has been active since 2009. XLab says it is unclear whether the two implants are related, and that two different actors may have compromised the same server. XLab also counted more than 340,000 internet-exposed CWP instances in the past month. That figure measures how many servers could be targeted, not how many are infected.

Comment fonctionne l'attaque
Installer. The script runs as root and presents itself as a version update. It works as follows:
- It checks the local cwpsrv version and downloads the matching build.
- It backs up the original binary as
cwpsrv.prev-YYYYmmdd-HHMMSS, then stops the service and overwrites/usr/local/cwpsrv/bin/cwpsrv. - It restarts the service and checks that the master, worker and helper processes are running. If that check fails, it rolls back. If it succeeds, it deletes its temporary files and the backup.
- Whatever the result, it restores the access and modification times of the binary and of its parent directory, so
lsetstatshow no change. It then runshistory -dto remove the session from shell history and reports the outcome to 136.0.141[.]71.
Implant. XLab describes two functions added to the server:
ngx_http_integrity_initruns when the server parses its configuration. It finds and caches the index of the logging module, binds a Unix domain socket namedngx_http_integrity_helper, and starts a helper child process.ngx_http_integrity_checkruns at the start of request processing. It reads the three trigger headers listed below.
X-Ngx-Integrity-Time: a timestamp that must be within ±300 seconds of the server clock.X-Ngx-Integrity-Data: the command, Base64-encoded. It holds a 16-byte IV followed by AES-256-CBC ciphertext. The AES key is the SHA-256 hash of an RSA public key embedded in the binary and obfuscated by XOR with0x55.X-Ngx-Integrity-Sig: an RSA signature overTime:Data.
If verification fails, the request is handled as ordinary traffic. Because of the signature and the time window, only the holder of the private key can trigger the backdoor, and captured requests cannot be replayed. When verification succeeds, the master process copies the request’s module-context array and replaces the logging module’s pointer, so the request is never written to the access log. It then passes the decrypted command over the socket to the helper, which runs it. The output comes back AES-encrypted in an HTTP 200 response with the type application/octet-stream.
Indicateurs de compromis
cwpsrv_update.sh (installer), MD5 | e505d4ffa21cf494a5dc2d7c8f4f1236 |
| Backdoored cwpsrv 1.15.10, MD5 | 965aea67fb40de1bab5169ada993349f |
| Backdoored cwpsrv 1.24.0, MD5 | d3f4e95113d49ab3991b45e457bda75b |
| Download and reporting server | 136.0.141[.]71 (/static/cwpsrv_update.sh, /static/cwpsrv_1.15.10, /static/cwpsrv_1.24.0) |
| Unix socket | ngx_http_integrity_helper |
| Strings and headers | X-Ngx-Integrity-Time, -Data, -Sig |
| Brazil sample set | evidencia.tgz, cwpsrv.suspeito, libkeyutils.so.1.5.2 (Ebury) |
XLab published MD5 hashes only, with no SHA-256 values. Because cwpdoor is rebuilt from source, a new build would have a different hash, so the behavioural checks below matter more than the hashes.
Detection on CWP hosts
The first three checks come from XLab. The rest are MAG212 recommendations. Run all of them as root.
# XLab: helper socket, helper processes, header strings in the binary
ss -axp | grep integrity
ps aux | grep cwpsrv
strings /usr/local/cwpsrv/bin/cwpsrv | grep X-Ngx-Integrity
# MAG212: hash the binary on disk, any backups, and the running image
IOC='e505d4ffa21cf494a5dc2d7c8f4f1236|965aea67fb40de1bab5169ada993349f|d3f4e95113d49ab3991b45e457bda75b'
md5sum /usr/local/cwpsrv/bin/cwpsrv* | grep -Ei "$IOC"
for p in $(pgrep cwpsrv); do md5sum /proc/$p/exe; done | grep -Ei "$IOC"
find / -xdev -name 'cwpsrv_update.sh' 2>/dev/null
# MAG212: the installer resets atime/mtime, but it cannot reset ctime (inode change time)
stat -c '%n mtime=%y ctime=%z' /usr/local/cwpsrv/bin/cwpsrv /usr/local/cwpsrv/bin
ls -la --time-style=full-iso /usr/local/cwpsrv/bin/ | grep 'cwpsrv.prev-'
# MAG212: traces of contact with the download server
grep -rIl '136[.]0[.]141[.]71' /var/log/ /root/ /tmp/ 2>/dev/null
A ctime later than your last legitimate CWP update, while mtime looks old, means someone has tampered with the timestamps. Do not treat a clean cwpsrv access log as evidence that nothing happened. On the network side, the trigger headers travel inside TLS to the panel ports, so an IDS can only see them where you terminate TLS in front of the panel. Look instead for application/octet-stream responses from the panel, and for the server making outbound connections to 136.0.141[.]71.
Ebury check
ESET’s original checks were the ssh -G test and looking for an sshd-owned shared-memory segment of about 3 MB. Both date from 2014. ssh -G has been a legitimate OpenSSH option since version 6.8, so that test now gives false alarms. ESET also reported in 2024 that Ebury 1.8 adds a userland rootkit that hides processes, files, sockets and memory mappings. Treat the following as triage, and use ESET’s published detection script and IOCs (linked under Sources) for a definitive answer.
# MAG212 triage (RHEL-family CWP hosts); ideally run from trusted media against the mounted disk
ls -la /lib64/libkeyutils* /usr/lib64/libkeyutils*
rpm -qf /usr/lib64/libkeyutils.so.1* # every file should belong to keyutils-libs
rpm -V keyutils-libs openssh openssh-server openssh-clients
ipcs -m -p # legacy ESET indicator: large segment created by sshd
Incident response: what to do now
Steps 5 and 6 include XLab’s remediation advice. Everything else is a MAG212 recommendation.
- 1. Scope. List every CWP server you own or host, including customer VPS images, and run the detection block on each one.
- 2. Preserve evidence before you change anything. Copie
/usr/local/cwpsrv/bin/, the running image from/proc/<pid>/exe, and the output ofss -axpetps -ef --forest. Take a memory capture if you can. Hash everything you collect. - 3. Contain. Block 136.0.141[.]71 in both directions. Restrict the CWP admin and user panel ports to a VPN or an allowlist of admin IPs. If you find signs of infection, isolate the host.
- 4. Check for Ebury on the same host and on any servers it can reach over SSH or that share its network segment.
- 5. Eradicate (XLab). Replace the backdoored binary with the
cwpsrv.prev-*backup, upgrade CWP, and confirm thatss -axp | grep integrityreturns nothing. The installer deletes that backup after a successful swap, so it may not exist. Never reuse a binary you cannot verify. Because the backdoor allowed any command to run as root, MAG212 recommends rebuilding the host from clean media and restoring only data, especially if Ebury is present. - 6. Rotate credentials. XLab advises rotating all panel credentials. MAG212 recommends also rotating root and sudo passwords, SSH keys, MySQL and mail passwords, API tokens and the passwords for hosted accounts. If Ebury is found, assume that every SSH credential used on that host has been stolen.
- 7. Find the way in. Patch CWP fully. Unauthenticated RCE flaws such as CVE-2025-48703, which CISA added to KEV in November 2025, show that the panel has been a target before, although nothing currently ties cwpdoor to a specific CVE. Review SSH and authentication logs rather than the panel’s access logs.
- 8. Monitor. Add a watch rule (
auditctl -w /usr/local/cwpsrv/bin/ -p wa -k cwpsrv_bin), set up file-integrity monitoring and egress filtering, and alert on unexpected Unix sockets owned by cwpsrv. Hosting providers should notify affected customers.
What we still don’t know
- How attackers got root. No initial vector or CVE has been identified.
- How many servers are infected and who is behind it. There is no attribution.
- Whether cwpdoor and Ebury were planted by the same actor.
- Whether the CWP developers have responded. We found no vendor statement, and the technical details come from a single research team.
Related on MAG212 CYBER
Sources
- QiAnXin XLab: “CWPdoor: The Invisible Backdoor Living Inside Control Web Panel” (5 Nov 2026)
- The Hacker News: “Hackers Hide Linux Backdoor in Control Web Panel Server to Execute Commands Without Logs” (6 Nov 2026)
- ESET WeLiveSecurity: “Ebury is alive but unseen: 400k Linux servers compromised for cryptocurrency theft and financial gain” (14 May 2024). Detection script: eset/malware-research; IOCs: eset/malware-ioc
- ESET WeLiveSecurity: “Windigo not Windigone: Linux/Ebury updated” (10 Apr 2014)
- Help Net Security: “Critical Control Web Panel vulnerability is actively exploited (CVE-2025-48703)” (5 Nov 2025)
Written and edited by El Mostafa Ouchen for MAG212 CYBER. Facts verified against the sources above as of 8 November 2026.
