← All audits

Audit #35 Suspicious

Muchat – AI Chatbot (with Autosync) · 200 installs · baseline 2.0.54 → head 2.0.55 · closed 1mo ago

Actor: Muchat Team (legitimate vendor — vendor self-own, not a hijack)
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.

⚠️
Pattern detected — pending vendor response or further evidence.

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.

If you're the plugin author

## If you are running muchat-ai v2.0.55 on a WooCommerce store

Roll back to v2.0.54 immediately, or remove the plugin until the vendor ships a fix.

Your customer PII (billing name, email, phone, order line items, totals, payment method, customer notes) is currently retrievable by anonymous attackers via /wp-json/muchat-api/v1/orders/track order-ID enumeration. The 20-req/min rate limit slows but does not prevent enumeration.

Quick rollback via WP-CLI:

``bash wp plugin install muchat-ai --version=2.0.54 --force ``

Or deactivate entirely if you don't actively use Muchat — the plugin's REST endpoints are only registered while it's active.

Optional verification that v2.0.55 is auth-bypassed on your site (returns 200 with order data if vulnerable):

``bash curl -X POST https://your-site.example/wp-json/muchat-api/v1/orders/track \ -H "Content-Type: application/json" \ -d '{"order_id": 1}' ``

If you get an order JSON back without supplying a Bearer token, you are exposed. (A 404 "order_not_found" with no token also confirms exposure — the validator accepted your unauthenticated request and just couldn't find order #1.)

## If you are running muchat-ai v2.0.54 or earlier

You are not exposed by this issue. v2.0.54's verify_token() enforces Bearer-token validation against app.mu.chat/api/validate-token. Stay on v2.0.54 until the vendor releases a v2.0.56 with the unconditional return true; removed.

## If you are running a non-WooCommerce site

The PII-enumeration path requires WooCommerce. Without WooCommerce, the unauthenticated endpoints expose only public published post/page/CPT content (the same data WordPress's own REST API exposes by default), so the practical impact is minimal. We still recommend rolling back to v2.0.54 on principle.

## Your site is not compromised

This is a vendor authorization failure, not a malware infection. No backdoor was installed, no data was exfiltrated to a malicious server, no persistent attacker access was created. The fix is a plugin-version rollback — no file cleanup, credential rotation, or core-integrity check is required.

## What we are recommending to the vendor

The Muchat team should:

  1. Ship a v2.0.56 that removes the // TEMP: Auth disabled early return.
  2. Add a release-pipeline check that fails the build if verify_token() returns true outside the MUCHAT_DEV_MODE gate.
  3. Consider notifying the ~100 active install operators directly (Muchat Agent ID is required for normal operation, so the vendor knows who runs the plugin).

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

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.

  1. 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. 2.0.54 Last clean Last clean release before incident
  3. 🛑 Compromise window 63 days · 2026-02-25 → 2026-04-29

    Malicious releases pushed during this window were deleted from SVN by the wp.org Plugin Review Team. Last malicious tag: 2.0.55.

  4. 2.0.55 PRT 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

DateVersionEvent
2025-05-25plugin-master initial wp.org listing entry
2025-05-262.0.32–2.0.35Initial muchatai release wave (4 commits same day)
2025-06–2025-122.0.36–2.0.52Steady feature development by muchatai
2025-12-072.0.52Last release where // return true; is commented out (auth functional)
(between 2.0.52 and 2.0.54)2.0.53Internal release, never tagged on wp.org. Changelog claims "Fixed authentication bypass — verify_token() no longer unconditionally returns true"
2026-02-252.0.54Auth flow refactored: MUCHAT_DEV_MODE constant gate added, dead-code // return true; removed entirely
2026-04-292.0.55Three-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.

EndpointMethodReturnsRisk in v2.0.55
/productsPOSTAll published products with full meta_dataPublic catalog data, low
/products/{id}POSTSingle product detailPublic catalog data, low
/product-idsPOSTProduct IDs + modified datesLow
/products/initialize-muchat-trackingPOSTWrites muchat_date_modified meta to all productsAllows anonymous attackers to bulk-write meta on every product
/orders/trackPOSTFull billing first/last name, email, phone, line items, totals, customer notes, public metaHIGH — anonymous PII enumeration
/postsPOSTPublished postsPublic content, low
/pagesPOSTPublished pagesPublic content, low
/custom-post-types/{type}POSTPublished items of a non-blocklisted CPTLimited (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_meta

Order 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

IndicatorResultEvidence
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?NoAll 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?Nomuchatai slug aligns with plugin name, plugin author URI mu.chat, and the centralized constants MUCHAT_APP_URL/MUCHAT_CDN_URL
Code-level malware patterns?NoNo 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?NoAll 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?NoSame muchatai author, no rotation event in revision history
Update-channel hijack?NoPlugin 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_MODE constant 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.