matomo-org / matomo-org/plugin-TreemapVisualization

TreemapVisualization Plugin Authorization Bypass via Unrestricted API Proxy in Matomo

Open
#88 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
PHP
Stars
15
Forks
17
Avg merge
18h 53m
Merged PRs (30d)
11

Description

## Summary:

The `TreemapVisualization.getTreemapData` API method in the Matomo TreemapVisualization plugin acts as an **unrestricted API proxy** that allows any authenticated user to invoke arbitrary `get*` API methods from any installed Matomo plugin, with **no module-level authorization check** and **no scope validation**.

The only security gate is a case-insensitive `"get"` prefix check on the method name (`stripos($method, 'get') !== 0` in `API.php:71`). This allows any of the 62+ `get*` methods available in a default Matomo installation to be invoked through the proxy without the proxy itself verifying whether the caller is authorized to access the target module or method.

**Impact:**
- **No module restriction**: Administrative modules (UsersManager, SitesManager, CoreAdminHome, etc.) are reachable through the proxy — not just reporting/visualization modules
- **No authorization gate at the proxy level**: All authorization relies entirely on the inner methods' own checks, violating defense-in-depth. Any `get*` method that lacks its own auth check is fully executable without restriction
- **Information disclosure via error messages**: Error responses differ between non-existent plugins (`"The plugin X was not found"`) and existing admin-only methods (`"The method 'Y' does not exist or is not available in the module '\Piwik\Plugins\X\API'"`), enabling systematic enumeration of all installed plugins and their internal PHP namespace paths
- **Unauthenticated method execution**: Methods without internal auth checks (e.g., `API.getSettings`) are executed through the proxy without restriction
- **Fragile name-based boundary**: The `"get"` prefix is the only gate — any method named `get*` bypasses it entirely

## Steps To Reproduce:

A complete Docker-based PoC environment is provided in the attached `VUL-001-poc.tar.gz`. The following steps can be used to reproduce with any Matomo 5.x installation that has the TreemapVisualization plugin activated:

**Prerequisites:** An authenticated Matomo user with `view` access to at least one site, and the user's `token_auth`.

1. Confirm that the viewer user CANNOT directly access admin-only methods (baseline — Matomo's API framework properly blocks this):
```bash
curl -s 'http://MATOMO_HOST/index.php?module=API&method=UsersManager.getUsers&format=JSON&token_auth=VIEWER_TOKEN'
# Response: {"result":"error","message":"You can't access this resource as it requires admin access for at least one website."}
```

1. Use the TreemapVisualization proxy to invoke the same admin-only method — the proxy **invokes the method** (the rejection comes from the inner method, not the proxy):
```bash
curl -s 'http://MATOMO_HOST/index.php?module=API&method=TreemapVisualization.getTreemapData&format=JSON&apiMethod=UsersManager.getUsers&idSite=1&period=day&date=today&column=nb_visits&token_auth=VIEWER_TOKEN'
# Response: {"result":"error","message":"You can't access this resource as it requires admin access for at least one website."}
# The error proves the method WAS INVOKED through the proxy — the proxy provided no authorization gate
```

1. Enumerate installed plugins by comparing error messages (information disclosure):
```bash
# Non-existent plugin:
curl -s '...&apiMethod=FakePlugin.getData&...'
# Response: {"result":"error","message":"The plugin FakePlugin was not found."}

# Existing admin-only plugin — reveals internal PHP namespace:
curl -s '...&apiMethod=CoreAdminHome.getGeneralSettings&...'
# Response: {"result":"error","message":"The method 'getGeneralSettings' does not exist or is not available in the module '\Piwik\Plugins\CoreAdminHome\API'."}
# ^^ Confirms plugin exists AND reveals internal namespace path
```

1. Execute a method that lacks internal auth checks through the proxy:
```bash
# Direct call (no auth required):
curl -s 'http://MATOMO_HOST/index.php?module=API&method=API.getSettings&format=JSON&token_auth=VIEWER_TOKEN'
# Response: {"SDK_batch_size":10,"SDK_interval_value":30}

# Via proxy — method executes (returns [] because result is not DataTable, but method DID run):
curl -s '...&apiMethod=API.getSettings&idSite=1&period=day&date=today&column=nb_visits&...'
# Response: []
```

## Impact

**1. Defense-in-depth violation — proxy provides zero authorization boundary**

The `getTreemapData` method acts as an API gateway but implements no authorization of its own. It creates a new entry point into Matomo's API surface where only a method-name prefix check (`stripos($method, 'get') !== 0`) stands between a low-privilege user and 62+ internal API methods. The proxy's entire security model depends on every invoked method having its own correct auth check — a single missing check in any `get*` method anywhere in the Matomo ecosystem results in a full bypass.

**2. Plugin and method enumeration (information disclosure)**

Error messages forwarded by the proxy allow systematic fingerprinting of the Matomo installation:
- Non-existent plugin: `"The plugin X was not found"` → confirms absence
- Existing admin-only method: `"The method 'Y' does not exist or is not available in the module '\Piwik\Plugins\X\API'"` → confirms presence **and** leaks the internal PHP namespace path

An attacker can iterate through all known Matomo plugin names to build a complete map of installed plugins, their versions (by checking which methods exist), and their internal class paths — information not normally available to view-level users.

**3. Unauthenticated method execution through proxy**

Methods that lack internal auth checks are fully executable through the proxy. `API.getSettings` was confirmed to execute (returns `{"SDK_batch_size":10,"SDK_interval_value":30}` directly, and returns `[]` via proxy only because the result is not a DataTable — but the method **did execute** server-side). Any future or third-party `get*` method that omits auth checks becomes silently exploitable through this proxy.

**4. Attack surface expansion**

Without this proxy, a view-level user must call each API method directly through Matomo's standard API framework, which enforces centralized authorization checks. The proxy creates an **alternative execution path** that bypasses this framework-level auth — the inner `Request::processRequest()` call inherits the caller's token but skips the framework's access control layer, relying solely on each individual method's self-check.

**5. Fragile name-based security boundary**

The only gate — checking that the method name starts with `"get"` — is a convention-based check, not a security control. It was presumably intended to limit the proxy to read-only reporting methods, but in practice it exposes all `get*` methods across all installed modules including administrative ones (UsersManager, SitesManager, CoreAdminHome, DBStats, etc.).

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with TreemapVisualization.getTreemapData in API.php, especially the method-name check at line 71, and reproduce the supplied curl requests or Docker PoC. Trace how the proxy invokes the requested API method and verify that unauthorized or out-of-scope methods are no longer reachable through it, including the reported error-message behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
api, backend, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.