Audit #35 Suspicious
Show full summary
Verdict: SUSPICIOUS — vendor self-own, not a supply-chain attack.
muchat-ai v2.0.55 (released 2026-04-29 to wp.org, ~100 active installs) ships with API authentication explicitly disabled. The plugin's AuthMiddleware::verify_token() was modified to unconditionally return true; for all requests, with the comment // TEMP: Auth disabled (always allow). The previous validation flow remains in place as dead code below the early return. The change is openly disclosed in the v2.0.55 changelog: "Tweak: Temporarily disable API authentication (all requests allowed)."
The practical impact is unauthenticated PII enumeration. The plugin's /wp-json/muchat-api/v1/orders/track endpoint (WooCommerce sites only) accepts an order_id parameter and returns the order's full billing first/last name, email, phone, line items, totals, payment method, customer notes, and public order meta. With auth disabled, anonymous requests can iterate order_id 1..N and dump every order on the site. The 'sensitive' rate limit (20 req/min) only slows enumeration, it does not prevent it. The /products and /posts//pages//custom-post-types/{type} endpoints are similarly unauthenticated, though those return only publish-status content with a sensitive-CPT blocklist in place.
This is not a supply-chain attack. All 23 SVN revisions trace back to the legitimate muchatai committer (the only author, present since the plugin's first release on 2025-05-26), the plugin builds via a Update from GitHub autosync workflow, and the diff is a 3-line return true; insertion on top of dead-code dev-mode logic that has been present since v2.0.32. The vendor's own changelog announces the change. The most likely root cause is a development-mode shortcut accidentally promoted through the GitHub→SVN autosync pipeline. The hijack-indicator matrix is clean across the board — no new committer, no author drift, no malicious primitives, no unexpected outbound HTTP destinations.
We are publishing this audit as suspicious rather than malicious because intent appears unintentional, but the exposed surface is real and currently shipping. Site operators running v2.0.55 on a WooCommerce store should roll back to v2.0.54 immediately or wait for a vendor fix.
Not yet confirmed malicious. Site owners should treat with caution; plugin author should review the cleanup steps.
If you run muchat-ai on your site
Verify your install matches the wp.org canonical version:
wp plugin verify-checksums muchat-ai
A patched build isn't yet published for this audit. Check the security advisories index or remove the plugin until one is available.
Plugins under the same committer's SVN access
muchatai holds push access to 1 plugin totalling 200 active installs.
Plugin version history
Every release on wp.org for this plugin, color-coded by relationship to the incident. The compromise window shows where the wp.org Plugin Review Team deleted the malicious tags from SVN — those versions cannot be re-downloaded today.
-
Clean 21 earlier releases before the incident
-
2.0.32 -
2.0.33 -
2.0.34 -
2.0.35 -
2.0.36 -
2.0.37 -
2.0.38 -
2.0.39 -
2.0.40 -
2.0.41 -
2.0.42 -
2.0.43 -
2.0.44 -
2.0.45 -
2.0.46 -
2.0.47 -
2.0.48 -
2.0.49 -
2.0.50 -
2.0.51 -
2.0.52
-
-
2.0.54Last clean Last clean release before incident -
2.0.55PRT cleanup PRT cleanup release — incident closed
Plugin & vendor
- Slug:
muchat-ai("Muchat – AI Chatbot (with Autosync)") - Active installs: 100
- Vendor: Muchat Team, https://mu.chat
- Author slug:
muchatai - First release: 2025-05-26 (v2.0.32)
- Latest release: 2026-04-29 (v2.0.55)
- Status on wp.org: Open (not closed)
Timeline
| Date | Version | Event |
|---|---|---|
| 2025-05-25 | — | plugin-master initial wp.org listing entry |
| 2025-05-26 | 2.0.32–2.0.35 | Initial muchatai release wave (4 commits same day) |
| 2025-06–2025-12 | 2.0.36–2.0.52 | Steady feature development by muchatai |
| 2025-12-07 | 2.0.52 | Last release where // return true; is commented out (auth functional) |
| (between 2.0.52 and 2.0.54) | 2.0.53 | Internal release, never tagged on wp.org. Changelog claims "Fixed authentication bypass — verify_token() no longer unconditionally returns true" |
| 2026-02-25 | 2.0.54 | Auth flow refactored: MUCHAT_DEV_MODE constant gate added, dead-code // return true; removed entirely |
| 2026-04-29 | 2.0.55 | Three-line change reintroduces unconditional return true; at top of verify_token() |
Boundary diff (v2.0.54 → v2.0.55)
Total: 3 files, 10 insertions, 3 deletions.
includes/Api/Middleware/AuthMiddleware.php:
@@ -34,6 +34,9 @@ class AuthMiddleware
*/
public function verify_token($request)
{
+ // TEMP: Auth disabled (always allow)
+ return true;
+
if (defined('MUCHAT_DEV_MODE') && MUCHAT_DEV_MODE === true) {
return true;
}The remaining changes are a Stable tag: 2.0.55 bump in readme.txt and a Version: 2.0.55 constant in muchat-ai.php. There are no other code changes between v2.0.54 and v2.0.55.
Compare in Forgejo: https://plugin-repo.wpbeacon.io/wporg/muchat-ai/compare/v2.0.54...v2.0.55
Exposed endpoints
Routes::register_routes() registers all endpoints under namespace muchat-api/v1. The permission_callback is [$this, 'check_auth_and_rate_limit'], which calls $this->auth_middleware->verify_token($request) — now an unconditional true.
| Endpoint | Method | Returns | Risk in v2.0.55 |
|---|---|---|---|
/products | POST | All published products with full meta_data | Public catalog data, low |
/products/{id} | POST | Single product detail | Public catalog data, low |
/product-ids | POST | Product IDs + modified dates | Low |
/products/initialize-muchat-tracking | POST | Writes muchat_date_modified meta to all products | Allows anonymous attackers to bulk-write meta on every product |
/orders/track | POST | Full billing first/last name, email, phone, line items, totals, customer notes, public meta | HIGH — anonymous PII enumeration |
/posts | POST | Published posts | Public content, low |
/pages | POST | Published pages | Public content, low |
/custom-post-types/{type} | POST | Published items of a non-blocklisted CPT | Limited (blocklist covers shop_order, edd_*, wpforms_entry, gf_entry, etc.) |
The PII-leak surface is /orders/track. Looking at OrderController::format_order_data() (lines 470–546):
'customer' => [
'first_name' => $order->get_billing_first_name(),
'last_name' => $order->get_billing_last_name(),
'email' => $order->get_billing_email(),
'phone' => $this->normalize_phone_number($order->get_billing_phone())
],
'items' => array_values(array_map(function ($item) { ... })),
'totals' => [ 'subtotal' => ..., 'total' => ... ],
'customer_notes' => array_values(array_map(...)),
'meta_data' => $formatted_metaOrder ID-based enumeration is not blocked: validate_parameters() (lines 128–184) accepts a request that supplies only order_id, and get_order() skips email/phone matching when order_id is present (lines 367–400). So an attacker can issue:
POST /wp-json/muchat-api/v1/orders/track { "order_id": N }for N in 1..max and dump every WooCommerce order. The 'sensitive' rate limit caps this at 20 req/min per source, but this only slows enumeration, not prevents it.
Hijack-indicator matrix
| Indicator | Result | Evidence |
|---|---|---|
| Sole committer for ≥1 year? | Yes (clean) | muchatai is the only release-tagging committer across all 23 commits since 2025-05-26 |
| Sudden new committer before incident? | No | All v2.0.32 → v2.0.55 commits authored by muchatai. The only other slug in revision history is plugin-master (the wp.org listing-entry account, one commit on 2025-05-25) |
| Author profile drift? | No | muchatai slug aligns with plugin name, plugin author URI mu.chat, and the centralized constants MUCHAT_APP_URL/MUCHAT_CDN_URL |
| Code-level malware patterns? | No | No eval/base64_decode/file_put_contents/move_uploaded_file primitives. The only eval token is a regex defending against eval injection in Widget.php |
| Outbound C2 / unknown domains? | No | All HTTP egress targets app.mu.chat (/api/validate-token) and cdn.mu.chat (widget asset). No third-party endpoints, no IP literals, no encoded URLs |
| New SVN credentials before incident? | No | Same muchatai author, no rotation event in revision history |
| Update-channel hijack? | No | Plugin uses wp.org's standard update infrastructure; no PUC library bundled |
Matrix is clean. This is the shape of a vendor mistake, not a hijack.
Probable root cause
The // Development mode - return true shortcut has been a recurring fixture in this plugin's verify_token() since v2.0.32:
- v2.0.32 → v2.0.52:
// return true;is commented out above the production validation block - v2.0.53 → v2.0.54 (refactor): comment-shortcut removed entirely; replaced with
MUCHAT_DEV_MODEconstant gate - v2.0.55: fresh
return true;(uncommented) inserted at top of method, bypassing the constant gate
Each release commit message is Update to version X.Y.Z from GitHub, indicating an automated GitHub→SVN autosync pipeline. The most plausible root cause is a developer left a return true; line in their working branch (consistent with the // TEMP: comment) and the autosync pipeline tagged and shipped it without a security review gate.
Comparable cases
- WP Beacon Audit #15 (content-egg): Historical PHP Object Injection chain that was latent and gated. Both audits document a real exploitable issue with no malicious actor — the difference is muchat-ai's issue is currently shipping, not historical.
- WP Beacon Audit #34 (speedy-go): Single-author plugin shipped a license-bypass refactor with auth-related claims. Audit #34 was kept suspicious pending vendor confirmation; same posture here.
This audit does not match any active campaign in our seed (78 acquirers / 241 slugs as of 2026-05-04 convergence) and contains no IOC overlap with the EP, scroll-top/Benjamin, SiteGuarding, or codeandcore clusters.