{"id":4090,"date":"2026-11-08T09:47:45","date_gmt":"2026-11-08T17:47:45","guid":{"rendered":"https:\/\/cyber.mag212.com\/cwpdoor-control-web-panel-backdoor-hides-from-logs\/"},"modified":"2026-11-08T09:47:45","modified_gmt":"2026-11-08T17:47:45","slug":"cwpdoor-control-web-panel-backdoor-hides-from-logs","status":"publish","type":"post","link":"https:\/\/cyber.mag212.com\/fr\/cwpdoor-control-web-panel-backdoor-hides-from-logs\/","title":{"rendered":"cwpdoor: Trojanized Control Web Panel Server Runs Root Commands Without Leaving Logs"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">QiAnXin XLab has published an analysis of <strong>cwpdoor<\/strong>, a backdoor built into a recompiled copy of <code>cwpsrv<\/code>, 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 <code>X-Ngx-Integrity<\/code> headers, it runs the attacker&#8217;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.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><strong>What<\/strong><\/td><td>cwpdoor: a trojanized, recompiled <code>cwpsrv<\/code> binary that gives remote command execution<\/td><\/tr><tr><td><strong>Researchers<\/strong><\/td><td>QiAnXin XLab (Alex.Turing, Acey9), published 5 Nov 2026; reported by The Hacker News on 6 Nov 2026<\/td><\/tr><tr><td><strong>Affected<\/strong><\/td><td>CWP hosts running cwpsrv <strong>1.15.10<\/strong> or <strong>1.24.0<\/strong> (the only versions the installer supports)<\/td><\/tr><tr><td><strong>Prerequisite<\/strong><\/td><td>Root on the server. The initial access vector is unknown, and no CVE is linked<\/td><\/tr><tr><td><strong>Detection<\/strong><\/td><td>0\/63 engines on VirusTotal for the backdoored binary at the time of XLab&#8217;s analysis<\/td><\/tr><tr><td><strong>In the wild<\/strong><\/td><td>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<\/td><\/tr><tr><td><strong>Action<\/strong><\/td><td>Hunt on every CWP host, check for Ebury at the same time, rotate credentials, and rebuild where possible<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">What happened<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">On 29 June 2026, XLab&#8217;s threat-intelligence system captured a script called <code>cwpsrv_update.sh<\/code>. The script downloads a replacement <code>cwpsrv<\/code> 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, <a href=\"https:\/\/blog.xlab.qianxin.com\/cwpdoor-the-invisible-backdoor-living-inside-control-web-panel\/\" target=\"_blank\" rel=\"noopener\">&#8220;the attacker inserts malicious code into the cwpsrv source code and recompiles it.&#8221;<\/a> The function names XLab recovered, such as <code>ngx_http_process_request<\/code>, show that cwpsrv is derived from nginx. That means the backdoor runs inside a process that defenders expect to be listening on the network.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On 30 September, someone uploaded an archive named <code>evidencia.tgz<\/code> (&#8220;evidence&#8221;) to VirusTotal from Brazil. It held two files: <code>cwpsrv.suspeito<\/code>, which is cwpdoor with zero detections, and <code>libkeyutils.so.1.5.2<\/code>, which most engines flagged as <strong>Ebury<\/strong>. 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.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1800\" height=\"1013\" src=\"https:\/\/cyber.mag212.com\/wp-content\/uploads\/2026\/11\/cwpdoor-control-web-panel-backdoor-hides-from-logs-diagram.jpg\" alt=\"Diagram of the cwpdoor install chain via cwpsrv_update.sh and the runtime trigger path from signed headers to log suppression and helper-process execution\" class=\"wp-image-4089\" srcset=\"https:\/\/cyber.mag212.com\/wp-content\/uploads\/2026\/11\/cwpdoor-control-web-panel-backdoor-hides-from-logs-diagram.jpg 1800w, https:\/\/cyber.mag212.com\/wp-content\/uploads\/2026\/11\/cwpdoor-control-web-panel-backdoor-hides-from-logs-diagram-300x169.jpg 300w, https:\/\/cyber.mag212.com\/wp-content\/uploads\/2026\/11\/cwpdoor-control-web-panel-backdoor-hides-from-logs-diagram-1024x576.jpg 1024w, https:\/\/cyber.mag212.com\/wp-content\/uploads\/2026\/11\/cwpdoor-control-web-panel-backdoor-hides-from-logs-diagram-768x432.jpg 768w, https:\/\/cyber.mag212.com\/wp-content\/uploads\/2026\/11\/cwpdoor-control-web-panel-backdoor-hides-from-logs-diagram-1536x864.jpg 1536w, https:\/\/cyber.mag212.com\/wp-content\/uploads\/2026\/11\/cwpdoor-control-web-panel-backdoor-hides-from-logs-diagram-18x10.jpg 18w\" sizes=\"auto, (max-width: 1800px) 100vw, 1800px\" \/><figcaption class=\"wp-element-caption\">Diagram: MAG212 CYBER<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">How the attack works<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Installer.<\/strong> The script runs as root and presents itself as a version update. It works as follows:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>It checks the local cwpsrv version and downloads the matching build.<\/li>\n\n<li>It backs up the original binary as <code>cwpsrv.prev-YYYYmmdd-HHMMSS<\/code>, then stops the service and overwrites <code>\/usr\/local\/cwpsrv\/bin\/cwpsrv<\/code>.<\/li>\n\n<li>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.<\/li>\n\n<li>Whatever the result, it restores the access and modification times of the binary and of its parent directory, so <code>ls<\/code> and <code>stat<\/code> show no change. It then runs <code>history -d<\/code> to remove the session from shell history and reports the outcome to 136.0.141[.]71.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Implant.<\/strong> XLab describes two functions added to the server:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>ngx_http_integrity_init<\/code> runs when the server parses its configuration. It finds and caches the index of the logging module, binds a Unix domain socket named <code>ngx_http_integrity_helper<\/code>, and starts a helper child process.<\/li>\n\n<li><code>ngx_http_integrity_check<\/code> runs at the start of request processing. It reads the three trigger headers listed below.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>X-Ngx-Integrity-Time<\/code>: a timestamp that must be within \u00b1300 seconds of the server clock.<\/li>\n\n<li><code>X-Ngx-Integrity-Data<\/code>: 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 with <code>0x55<\/code>.<\/li>\n\n<li><code>X-Ngx-Integrity-Sig<\/code>: an RSA signature over <code>Time:Data<\/code>.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">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&#8217;s module-context array and replaces the logging module&#8217;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 <code>application\/octet-stream<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Indicators of compromise<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><code>cwpsrv_update.sh<\/code> (installer), MD5<\/td><td><code>e505d4ffa21cf494a5dc2d7c8f4f1236<\/code><\/td><\/tr><tr><td>Backdoored cwpsrv 1.15.10, MD5<\/td><td><code>965aea67fb40de1bab5169ada993349f<\/code><\/td><\/tr><tr><td>Backdoored cwpsrv 1.24.0, MD5<\/td><td><code>d3f4e95113d49ab3991b45e457bda75b<\/code><\/td><\/tr><tr><td>Download and reporting server<\/td><td>136.0.141[.]71 (<code>\/static\/cwpsrv_update.sh<\/code>, <code>\/static\/cwpsrv_1.15.10<\/code>, <code>\/static\/cwpsrv_1.24.0<\/code>)<\/td><\/tr><tr><td>Unix socket<\/td><td><code>ngx_http_integrity_helper<\/code><\/td><\/tr><tr><td>Strings and headers<\/td><td><code>X-Ngx-Integrity-Time<\/code>, <code>-Data<\/code>, <code>-Sig<\/code><\/td><\/tr><tr><td>Brazil sample set<\/td><td><code>evidencia.tgz<\/code>, <code>cwpsrv.suspeito<\/code>, <code>libkeyutils.so.1.5.2<\/code> (Ebury)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Detection on CWP hosts<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The first three checks come from XLab. The rest are MAG212 recommendations. Run all of them as root.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># XLab: helper socket, helper processes, header strings in the binary\nss -axp | grep integrity\nps aux | grep cwpsrv\nstrings \/usr\/local\/cwpsrv\/bin\/cwpsrv | grep X-Ngx-Integrity\n\n# MAG212: hash the binary on disk, any backups, and the running image\nIOC='e505d4ffa21cf494a5dc2d7c8f4f1236|965aea67fb40de1bab5169ada993349f|d3f4e95113d49ab3991b45e457bda75b'\nmd5sum \/usr\/local\/cwpsrv\/bin\/cwpsrv* | grep -Ei \"$IOC\"\nfor p in $(pgrep cwpsrv); do md5sum \/proc\/$p\/exe; done | grep -Ei \"$IOC\"\nfind \/ -xdev -name 'cwpsrv_update.sh' 2&gt;\/dev\/null\n\n# MAG212: the installer resets atime\/mtime, but it cannot reset ctime (inode change time)\nstat -c '%n  mtime=%y  ctime=%z' \/usr\/local\/cwpsrv\/bin\/cwpsrv \/usr\/local\/cwpsrv\/bin\nls -la --time-style=full-iso \/usr\/local\/cwpsrv\/bin\/ | grep 'cwpsrv.prev-'\n\n# MAG212: traces of contact with the download server\ngrep -rIl '136[.]0[.]141[.]71' \/var\/log\/ \/root\/ \/tmp\/ 2&gt;\/dev\/null<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A <code>ctime<\/code> later than your last legitimate CWP update, while <code>mtime<\/code> looks old, means someone has tampered with the timestamps. Do not treat a clean <code>cwpsrv<\/code> 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 <code>application\/octet-stream<\/code> responses from the panel, and for the server making outbound connections to 136.0.141[.]71.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Ebury check<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ESET&#8217;s original checks were the <code>ssh -G<\/code> test and looking for an <code>sshd<\/code>-owned shared-memory segment of about 3 MB. Both date from 2014. <code>ssh -G<\/code> 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&#8217;s published detection script and IOCs (linked under Sources) for a definitive answer.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># MAG212 triage (RHEL-family CWP hosts); ideally run from trusted media against the mounted disk\nls -la \/lib64\/libkeyutils* \/usr\/lib64\/libkeyutils*\nrpm -qf \/usr\/lib64\/libkeyutils.so.1*        # every file should belong to keyutils-libs\nrpm -V keyutils-libs openssh openssh-server openssh-clients\nipcs -m -p                                   # legacy ESET indicator: large segment created by sshd<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Incident response: what to do now<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Steps 5 and 6 include XLab&#8217;s remediation advice. Everything else is a MAG212 recommendation.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>1. Scope.<\/strong> List every CWP server you own or host, including customer VPS images, and run the detection block on each one.<\/li>\n\n<li><strong>2. Preserve evidence before you change anything.<\/strong> Copy <code>\/usr\/local\/cwpsrv\/bin\/<\/code>, the running image from <code>\/proc\/&lt;pid&gt;\/exe<\/code>, and the output of <code>ss -axp<\/code> and <code>ps -ef --forest<\/code>. Take a memory capture if you can. Hash everything you collect.<\/li>\n\n<li><strong>3. Contain.<\/strong> 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.<\/li>\n\n<li><strong>4. Check for Ebury<\/strong> on the same host and on any servers it can reach over SSH or that share its network segment.<\/li>\n\n<li><strong>5. Eradicate (XLab).<\/strong> Replace the backdoored binary with the <code>cwpsrv.prev-*<\/code> backup, upgrade CWP, and confirm that <code>ss -axp | grep integrity<\/code> returns 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.<\/li>\n\n<li><strong>6. Rotate credentials.<\/strong> 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.<\/li>\n\n<li><strong>7. Find the way in.<\/strong> 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&#8217;s access logs.<\/li>\n\n<li><strong>8. Monitor.<\/strong> Add a watch rule (<code>auditctl -w \/usr\/local\/cwpsrv\/bin\/ -p wa -k cwpsrv_bin<\/code>), set up file-integrity monitoring and egress filtering, and alert on unexpected Unix sockets owned by cwpsrv. Hosting providers should notify affected customers.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">What we still don&#8217;t know<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>How attackers got root. No initial vector or CVE has been identified.<\/li>\n\n<li>How many servers are infected and who is behind it. There is no attribution.<\/li>\n\n<li>Whether cwpdoor and Ebury were planted by the same actor.<\/li>\n\n<li>Whether the CWP developers have responded. We found no vendor statement, and the technical details come from a single research team.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Related on MAG212 CYBER<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/cyber.mag212.com\/the-trusted-path-mag212-cyber-daily-november-8-2026\/\">The Trusted Path: MAG212 CYBER Daily, November 8, 2026<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Sources<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/blog.xlab.qianxin.com\/cwpdoor-the-invisible-backdoor-living-inside-control-web-panel\/\" target=\"_blank\" rel=\"noopener\">QiAnXin XLab: &#8220;CWPdoor: The Invisible Backdoor Living Inside Control Web Panel&#8221;<\/a> (5 Nov 2026)<\/li>\n\n<li><a href=\"https:\/\/thehackernews.com\/2026\/11\/hackers-hide-linux-backdoor-in-control.html\" target=\"_blank\" rel=\"noopener\">The Hacker News: &#8220;Hackers Hide Linux Backdoor in Control Web Panel Server to Execute Commands Without Logs&#8221;<\/a> (6 Nov 2026)<\/li>\n\n<li><a href=\"https:\/\/www.welivesecurity.com\/en\/eset-research\/ebury-alive-unseen-400k-linux-servers-compromised-cryptotheft-financial-gain\/\" target=\"_blank\" rel=\"noopener\">ESET WeLiveSecurity: &#8220;Ebury is alive but unseen: 400k Linux servers compromised for cryptocurrency theft and financial gain&#8221;<\/a> (14 May 2024). Detection script: <a href=\"https:\/\/github.com\/eset\/malware-research\/tree\/master\/ebury\" target=\"_blank\" rel=\"noopener\">eset\/malware-research<\/a>; IOCs: <a href=\"https:\/\/github.com\/eset\/malware-ioc\/tree\/master\/windigo\" target=\"_blank\" rel=\"noopener\">eset\/malware-ioc<\/a><\/li>\n\n<li><a href=\"https:\/\/welivesecurity.com\/2014\/04\/10\/windigo-not-windigone-linux-ebury-updated\" target=\"_blank\" rel=\"noopener\">ESET WeLiveSecurity: &#8220;Windigo not Windigone: Linux\/Ebury updated&#8221;<\/a> (10 Apr 2014)<\/li>\n\n<li><a href=\"https:\/\/www.helpnetsecurity.com\/2025\/11\/05\/control-web-panel-cve-2025-48703-exploited\/\" target=\"_blank\" rel=\"noopener\">Help Net Security: &#8220;Critical Control Web Panel vulnerability is actively exploited (CVE-2025-48703)&#8221;<\/a> (5 Nov 2025)<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Written and edited by El Mostafa Ouchen for MAG212 CYBER. Facts verified against the sources above as of 8 November 2026.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>QiAnXin XLab&#8217;s cwpdoor is a recompiled CWP cwpsrv that runs signed, encrypted commands sent in X-Ngx-Integrity headers, skips access logging and hides its file changes. Here are the IOCs, detection commands, an Ebury check and full incident-response steps.<\/p>","protected":false},"author":1,"featured_media":4088,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[13,15],"tags":[258,259,257,260,261,31,263,238,262,264],"class_list":["post-4090","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cyber-incidents","category-threat-intelligence","tag-control-web-panel","tag-cwp","tag-cwpdoor","tag-cwpsrv","tag-ebury","tag-incident-response","tag-iocs","tag-linux-backdoor","tag-qianxin-xlab","tag-web-hosting-security"],"_links":{"self":[{"href":"https:\/\/cyber.mag212.com\/fr\/wp-json\/wp\/v2\/posts\/4090","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cyber.mag212.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cyber.mag212.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cyber.mag212.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/cyber.mag212.com\/fr\/wp-json\/wp\/v2\/comments?post=4090"}],"version-history":[{"count":0,"href":"https:\/\/cyber.mag212.com\/fr\/wp-json\/wp\/v2\/posts\/4090\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cyber.mag212.com\/fr\/wp-json\/wp\/v2\/media\/4088"}],"wp:attachment":[{"href":"https:\/\/cyber.mag212.com\/fr\/wp-json\/wp\/v2\/media?parent=4090"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cyber.mag212.com\/fr\/wp-json\/wp\/v2\/categories?post=4090"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cyber.mag212.com\/fr\/wp-json\/wp\/v2\/tags?post=4090"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}