matomo-org / matomo-org/plugin-TreemapVisualization
TreemapVisualization Plugin Authorization Bypass via Unrestricted API Proxy in Matomo
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
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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