{"id":2244,"date":"2026-02-28T15:05:36","date_gmt":"2026-02-28T15:05:36","guid":{"rendered":"https:\/\/nicktailor.com\/tech-blog\/?p=2244"},"modified":"2026-03-02T19:38:47","modified_gmt":"2026-03-02T19:38:47","slug":"wp-tool-kit-deeper-look","status":"publish","type":"post","link":"https:\/\/nicktailor.com\/tech-blog\/wp-tool-kit-deeper-look\/","title":{"rendered":"Security Hole Cpanel &#8211; Wp-tool-kit: Deeper Look&#8230;\ud83e\udd26\u200d\u2642\ufe0f"},"content":{"rendered":"<p><!-- Title: Security Hole Cpanel \u2013 Wp-tool-kit: Deeper Look\u2026\ud83e\udd26\u200d\u2642\ufe0f --><\/p>\n<p style=\"font-size: 1.15em; font-weight: 500;\">I run security audits regularly. I&#8217;ve seen misconfigurations, oversights, and the occasional lazy shortcut. What I found in cPanel&#8217;s WordPress Toolkit is unbelievable\u2026<\/p>\n<p style=\"font-size: 1.15em; font-weight: 500;\">This doesn&#8217;t appear to be a bug. This is a deliberate architectural decision that gives unauditable code unrestricted root access to your server. By default. Without your consent. \ud83d\ude2e\ud83e\udd26\u200d\u2642\ufe0f<\/p>\n<p style=\"font-size: 1.15em; font-weight: 500;\"><strong>Millions of production servers are running this right now.<\/strong><\/p>\n<hr style=\"margin: 2em 0; border: none; border-top: 3px solid #ea580c;\" \/>\n<h2 style=\"color: #1e40af;\">Finding #1: Passwordless Root Access \u2014 Deployed Automatically<\/h2>\n<p>Open this file on any cPanel server running WordPress Toolkit:<\/p>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code>cat \/etc\/sudoers.d\/48-wp-toolkit<\/code><\/pre>\n<p>Here&#8217;s what you&#8217;ll find:<\/p>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code>wp-toolkit ALL=(ALL) NOPASSWD:ALL\nDefaults:wp-toolkit secure_path = \/sbin:\/bin:\/usr\/sbin:\/usr\/bin\nDefaults:wp-toolkit !requiretty<\/code><\/pre>\n<p><code style=\"background: #fef2f2; color: #dc2626; padding: 3px 8px; border-radius: 4px; font-weight: bold;\">NOPASSWD:ALL<\/code><\/p>\n<p>The wp-toolkit user can execute <strong>any command as root<\/strong> without a password. No restrictions. No whitelisting. Complete access to everything.<\/p>\n<p>You didn&#8217;t enable this. You weren&#8217;t asked. It&#8217;s baked into the RPM install script:<\/p>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code>rpm -q --scripts wp-toolkit-cpanel 2>\/dev\/null | grep -A 20 \"preinstall scriptlet\"<\/code><\/pre>\n<p>Every time WP Toolkit is installed or updated, this sudoers file gets created. Automatically. Silently.<\/p>\n<hr style=\"margin: 2em 0; border: none; border-top: 1px solid #e2e8f0;\" \/>\n<h2 style=\"color: #1e40af;\">Finding #2: It&#8217;s Actively Executing Root Commands<\/h2>\n<p>This isn&#8217;t sitting dormant. It&#8217;s running. Right now. On your server.<\/p>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code>grep wp-toolkit \/var\/log\/secure | tail -20<\/code><\/pre>\n<p>Here&#8217;s what I found in logs that made me dig deeper\u2026.<\/p>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code>Feb 28 12:11:17 sudo[1911429]: wp-toolkit : USER=root ; COMMAND=\/bin\/cat \/usr\/local\/cpanel\/version\nFeb 28 12:11:17 sudo[1911433]: wp-toolkit : USER=root ; COMMAND=\/bin\/sh -c 'whmapi1 get_domain_info --output=json'\nFeb 28 12:11:18 sudo[1911442]: wp-toolkit : USER=root ; COMMAND=\/bin\/sh -c 'whmapi1 listaccts --output=json'<\/code><\/pre>\n<p>Look at that pattern: <code style=\"background: #f1f5f9; padding: 2px 6px; border-radius: 4px;\">\/bin\/sh -c '...'<\/code><\/p>\n<p>Arbitrary shell commands. As root. Constant execution.<\/p>\n<hr style=\"margin: 2em 0; border: none; border-top: 1px solid #e2e8f0;\" \/>\n<h2 style=\"color: #1e40af;\">Finding #3: You Cannot Audit What It&#8217;s Doing<\/h2>\n<p>I wanted to see what these scripts actually do. Here they are:<\/p>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code>ls \/usr\/local\/cpanel\/3rdparty\/wp-toolkit\/scripts\/\n\ncli-runner.php\nexecute-background-task.php\nread-files.php\nwrite-files.php\ntransfer-files.php<\/code><\/pre>\n<p>Read those filenames again:<\/p>\n<ul style=\"font-size: 1.05em; line-height: 2;\">\n<li><code style=\"background: #f1f5f9; padding: 2px 6px; border-radius: 4px;\">read-files.php<\/code> \u2014 reads files as root<\/li>\n<li><code style=\"background: #f1f5f9; padding: 2px 6px; border-radius: 4px;\">write-files.php<\/code> \u2014 writes files as root<\/li>\n<li><code style=\"background: #f1f5f9; padding: 2px 6px; border-radius: 4px;\">transfer-files.php<\/code> \u2014 moves files as root<\/li>\n<li><code style=\"background: #f1f5f9; padding: 2px 6px; border-radius: 4px;\">execute-background-task.php<\/code> \u2014 executes tasks as root<\/li>\n<\/ul>\n<p>So let&#8217;s look at the source code:<\/p>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code>file \/usr\/local\/cpanel\/3rdparty\/wp-toolkit\/scripts\/*.php\n\ncli-runner.php: data\nexecute-background-task.php: data\nread-files.php: data\nwrite-files.php: data\ntransfer-files.php: data<\/code><\/pre>\n<p>They&#8217;re not identified as PHP files. They&#8217;re <code style=\"background: #f1f5f9; padding: 2px 6px; border-radius: 4px;\">data<\/code>.<\/p>\n<p>Because they&#8217;re <strong>ionCube encoded<\/strong>:<\/p>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code>head -5 \/usr\/local\/cpanel\/3rdparty\/wp-toolkit\/scripts\/cli-runner.php\n\n&lt;?php\n\/\/ Copyright 1999-2025. Plesk International GmbH. All rights reserved.\n\/\/ PLESK:\/\/PP.2500101\/C4OLIU+C...\n@__sw_loader_pragma__('PLESK_18');<\/code><\/pre>\n<p style=\"font-size: 1.1em; background: #fef2f2; border-left: 4px solid #dc2626; padding: 1em 1.5em; margin: 1.5em 0;\"><strong>Binary encoded. Obfuscated. The source code is hidden.<\/strong><\/p>\n<p>You cannot read what these scripts do. You cannot audit them for vulnerabilities. You cannot verify they&#8217;re secure.<\/p>\n<p>But they have root access to your entire server.<\/p>\n<hr style=\"margin: 2em 0; border: none; border-top: 1px solid #e2e8f0;\" \/>\n<h2 style=\"color: #1e40af;\">Finding #4: This Is Official Code \u2014 Verified and Signed<\/h2>\n<p>I wanted to be absolutely sure this wasn&#8217;t some compromise or modification. So I verified it:<\/p>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code>rpm -qi wp-toolkit-cpanel | grep -E \"Signature|Vendor\"\n\nSignature   : RSA\/SHA512, Wed 14 Jan 2026 05:56:56 PM UTC, Key ID ba338aa6d9170f80<\/code><\/pre>\n<p>Digitally signed by cPanel. Official package.<\/p>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code>rpm -V wp-toolkit-cpanel 2>&1 | head -10<\/code><\/pre>\n<p>All scripts match the official package. No modifications. No tampering.<\/p>\n<p>The script headers explicitly state:<\/p>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code>\/\/ Copyright 1999-2025. Plesk International GmbH. All rights reserved.\n\/\/ This is part of Plesk distribution.\n@__sw_loader_pragma__('PLESK_18');<\/code><\/pre>\n<p style=\"font-size: 1.1em; background: #fef2f2; border-left: 4px solid #dc2626; padding: 1em 1.5em; margin: 1.5em 0;\"><strong>This is Plesk&#8217;s WordPress Toolkit, distributed through cPanel&#8217;s official repository, digitally signed, running on millions of servers worldwide.<\/strong><\/p>\n<hr style=\"margin: 2em 0; border: none; border-top: 1px solid #e2e8f0;\" \/>\n<h2 style=\"color: #1e40af;\">Finding #5: It Restores Itself\u2026 Every Night \ud83e\udd26\u200d\u2642\ufe0f<\/h2>\n<p>So I removed the sudoers file. Problem solved, right?<\/p>\n<p>Nope.<\/p>\n<p>There&#8217;s a cron job:<\/p>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code>cat \/etc\/cron.d\/wp-toolkit-update<\/code><\/pre>\n<p>This runs daily at 1 AM (with random delay) and executes:<\/p>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code>yum -y update wp-toolkit-cpanel<\/code><\/pre>\n<p>When the package updates, the preinstall script runs. The preinstall script recreates <code style=\"background: #f1f5f9; padding: 2px 6px; border-radius: 4px;\">\/etc\/sudoers.d\/48-wp-toolkit<\/code>.<\/p>\n<p style=\"font-size: 1.1em; background: #fef2f2; border-left: 4px solid #dc2626; padding: 1em 1.5em; margin: 1.5em 0;\"><strong>Your fix gets silently undone. Every night. Automatically.<\/strong><\/p>\n<p>So removing the sudoers file alone doesn&#8217;t work. You have to disable the cron too, or you&#8217;ll wake up tomorrow with the same problem.<\/p>\n<hr style=\"margin: 2em 0; border: none; border-top: 3px solid #ea580c;\" \/>\n<h2 style=\"color: #ea580c;\">So\u2026.<\/h2>\n<p>cPanel ships WordPress Toolkit with:<\/p>\n<table style=\"width: 100%; border-collapse: collapse; margin: 1.5em 0;\">\n<tr style=\"background: #1e40af; color: white;\">\n<th style=\"padding: 14px; text-align: left; border: 1px solid #e2e8f0;\">What They Ship<\/th>\n<th style=\"padding: 14px; text-align: left; border: 1px solid #e2e8f0;\">What It Means<\/th>\n<\/tr>\n<tr style=\"background: #f8f9fa;\">\n<td style=\"padding: 14px; border: 1px solid #e2e8f0;\"><code>NOPASSWD:ALL<\/code> sudo access<\/td>\n<td style=\"padding: 14px; border: 1px solid #e2e8f0;\">Unrestricted root access, no authentication<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 14px; border: 1px solid #e2e8f0;\">Deployed automatically<\/td>\n<td style=\"padding: 14px; border: 1px solid #e2e8f0;\">No consent, no warning, no opt-in<\/td>\n<\/tr>\n<tr style=\"background: #f8f9fa;\">\n<td style=\"padding: 14px; border: 1px solid #e2e8f0;\">ionCube-encoded scripts<\/td>\n<td style=\"padding: 14px; border: 1px solid #e2e8f0;\">Source code hidden, cannot be audited<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 14px; border: 1px solid #e2e8f0;\">Scripts that read\/write\/execute<\/td>\n<td style=\"padding: 14px; border: 1px solid #e2e8f0;\">Complete filesystem and command access<\/td>\n<\/tr>\n<tr style=\"background: #f8f9fa;\">\n<td style=\"padding: 14px; border: 1px solid #e2e8f0;\">Digitally signed official package<\/td>\n<td style=\"padding: 14px; border: 1px solid #e2e8f0;\">This is intentional, not a compromise?<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 14px; border: 1px solid #e2e8f0;\">Nightly auto-update cron<\/td>\n<td style=\"padding: 14px; border: 1px solid #e2e8f0;\">Restores sudo access if you remove it<\/td>\n<\/tr>\n<tr style=\"background: #f8f9fa;\">\n<td style=\"padding: 14px; border: 1px solid #e2e8f0;\">No security scanner detection<\/td>\n<td style=\"padding: 14px; border: 1px solid #e2e8f0;\">Flying under the radar on millions of servers<\/td>\n<\/tr>\n<\/table>\n<p style=\"font-size: 1.15em;\">This is a <strong>&#8220;trust us&#8221;<\/strong> security model:<\/p>\n<ul style=\"font-size: 1.05em; line-height: 2;\">\n<li>&#8220;Trust us with passwordless root access&#8221;<\/li>\n<li>&#8220;Trust us with code you can&#8217;t read&#8221;<\/li>\n<li>&#8220;Trust us that we got it right&#8221;<\/li>\n<li>&#8220;Trust us that attackers won&#8217;t find a way in&#8221;<\/li>\n<\/ul>\n<p>On production servers. Hosting customer data. Running businesses.<\/p>\n<hr style=\"margin: 2em 0; border: none; border-top: 1px solid #e2e8f0;\" \/>\n<h2 style=\"color: #1e40af;\">The Attack Path<\/h2>\n<p>This is straightforward:<\/p>\n<ol style=\"font-size: 1.05em; line-height: 2;\">\n<li>Any vulnerability in WP Toolkit that allows command injection<\/li>\n<li>Payload reaches one of the encoded PHP scripts<\/li>\n<li>Script executes as <code style=\"background: #f1f5f9; padding: 2px 6px; border-radius: 4px;\">wp-toolkit<\/code> user<\/li>\n<li>User runs <code style=\"background: #f1f5f9; padding: 2px 6px; border-radius: 4px;\">sudo<\/code> \u2014 no password needed<\/li>\n<li><strong>Complete server compromise<\/strong><\/li>\n<\/ol>\n<p>And because the scripts are encoded, you will never see the vulnerability coming. You cannot audit code you cannot read.<\/p>\n<hr style=\"margin: 2em 0; border: none; border-top: 1px solid #e2e8f0;\" \/>\n<h2 style=\"color: #1e40af;\">Check Your Server Right Now<\/h2>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code># Check if the sudoers file exists\ncat \/etc\/sudoers.d\/48-wp-toolkit\n\n# Check if auto-update cron is enabled\ncat \/etc\/cron.d\/wp-toolkit-update\n\n# Verify scripts are encoded\nfile \/usr\/local\/cpanel\/3rdparty\/wp-toolkit\/scripts\/*.php\n\n# See what root commands are being executed\ngrep wp-toolkit \/var\/log\/secure | grep COMMAND | tail -20\n\n# Verify this is the official signed package (not tampered)\nrpm -qi wp-toolkit-cpanel | grep -E \"Signature|Vendor\"\n\n# Confirm scripts match official package\nrpm -V wp-toolkit-cpanel 2>&1 | head -10<\/code><\/pre>\n<hr style=\"margin: 2em 0; border: none; border-top: 1px solid #e2e8f0;\" \/>\n<h2 style=\"color: #1e40af;\">How to Fix It<\/h2>\n<p style=\"font-size: 1.1em; background: #fef2f2; border-left: 4px solid #dc2626; padding: 1em 1.5em; margin: 1.5em 0;\"><strong>Important:<\/strong> You need to do BOTH steps. Removing the sudoers file alone doesn&#8217;t work \u2014 the nightly cron will recreate it.<\/p>\n<h3 style=\"color: #1e40af;\">Step 1: Disable the Auto-Update Cron (Do This First)<\/h3>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code># Disable the nightly auto-update cron\nmv \/etc\/cron.d\/wp-toolkit-update \/etc\/cron.d\/wp-toolkit-update.disabled\n\n# Verify it's disabled\nls -la \/etc\/cron.d\/wp-toolkit-update 2>\/dev\/null || echo \"\u2713 Auto-update disabled\"<\/code><\/pre>\n<h3 style=\"color: #1e40af;\">Step 2: Remove or Harden the Sudoers File<\/h3>\n<p><strong>Option A: Remove it completely (Recommended)<\/strong><\/p>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code>rm \/etc\/sudoers.d\/48-wp-toolkit<\/code><\/pre>\n<p>Most WordPress management doesn&#8217;t require root. If something specific breaks, address it then with a scoped solution. The risk is not worth the convenience.<\/p>\n<p><strong>Option B: Whitelist specific commands (Advanced)<\/strong><\/p>\n<p>If you need WP Toolkit automation, replace blanket access with specific commands:<\/p>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code>cat &lt;&lt; EOF &gt; \/etc\/sudoers.d\/48-wp-toolkit\n# WP Toolkit - hardened configuration\nwp-toolkit ALL=(ALL) NOPASSWD: \/usr\/local\/cpanel\/3rdparty\/bin\/wp\nwp-toolkit ALL=(ALL) NOPASSWD: \/bin\/chown\nwp-toolkit ALL=(ALL) NOPASSWD: \/bin\/chmod\nDefaults:wp-toolkit secure_path = \/sbin:\/bin:\/usr\/sbin:\/usr\/bin\nDefaults:wp-toolkit !requiretty\nEOF<\/code><\/pre>\n<p>Always validate:<\/p>\n<pre style=\"background: #1e293b; color: #e2e8f0; padding: 1.5em; border-radius: 8px; overflow-x: auto; font-size: 0.95em;\"><code>visudo -c -f \/etc\/sudoers.d\/48-wp-toolkit<\/code><\/pre>\n<hr style=\"margin: 2em 0; border: none; border-top: 3px solid #ea580c;\" \/>\n<h2 style=\"color: #ea580c;\">The Bottom Line<\/h2>\n<p style=\"font-size: 1.15em; background: #fef2f2; border-left: 4px solid #dc2626; padding: 1.25em 1.5em; margin: 1.5em 0;\">Plesk and cPanel are officially shipping ionCube-encoded PHP scripts that execute as root with <code>NOPASSWD:ALL<\/code> sudo access. The package is digitally signed. The scripts are verified. This is intentional. You cannot audit what these scripts do. You cannot review the source code. You cannot verify their security. Yet they have root over your server. They could covertly do anything\u2026.<\/p>\n<p style=\"font-size: 1.15em;\">It would seem this is deployed by default. On every cPanel server running WordPress Toolkit. No security scanner flags it. Not even a &#8220;oh hey, this could be a problem for you but this is how we did it&#8221;\u2026<\/p>\n<p style=\"font-size: 1.2em; font-weight: bold;\">Check yours today.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I run security audits regularly. I&#8217;ve seen misconfigurations, oversights, and the occasional lazy shortcut. What I found in cPanel&#8217;s WordPress Toolkit is unbelievable\u2026 This doesn&#8217;t appear to be a bug. This is a deliberate architectural decision that gives unauditable code unrestricted root access to your server. By default. Without your consent. \ud83d\ude2e\ud83e\udd26\u200d\u2642\ufe0f Millions of production servers are running this right<a href=\"https:\/\/nicktailor.com\/tech-blog\/wp-tool-kit-deeper-look\/\" class=\"read-more\">Read More &#8230;<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,138,131],"tags":[],"class_list":["post-2244","post","type-post","status-publish","format-standard","hentry","category-cpanel","category-linux","category-security"],"_links":{"self":[{"href":"https:\/\/nicktailor.com\/tech-blog\/wp-json\/wp\/v2\/posts\/2244","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nicktailor.com\/tech-blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nicktailor.com\/tech-blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nicktailor.com\/tech-blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nicktailor.com\/tech-blog\/wp-json\/wp\/v2\/comments?post=2244"}],"version-history":[{"count":3,"href":"https:\/\/nicktailor.com\/tech-blog\/wp-json\/wp\/v2\/posts\/2244\/revisions"}],"predecessor-version":[{"id":2249,"href":"https:\/\/nicktailor.com\/tech-blog\/wp-json\/wp\/v2\/posts\/2244\/revisions\/2249"}],"wp:attachment":[{"href":"https:\/\/nicktailor.com\/tech-blog\/wp-json\/wp\/v2\/media?parent=2244"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nicktailor.com\/tech-blog\/wp-json\/wp\/v2\/categories?post=2244"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nicktailor.com\/tech-blog\/wp-json\/wp\/v2\/tags?post=2244"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}