google / google/grr

ExplainGlobExpression endpoint resolves client KnowledgeBase variables without requiring client approval

Open
#1,166 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
5.1k
Forks
796
PR merge metrics
No merged PRs in 30d

Description

### Summary

The `ExplainGlobExpression` API endpoint (`POST /api/v2/clients//glob-expressions:explain`) does not require a client approval. When a glob expression containing KnowledgeBase interpolation variables (e.g., `%%users.homedir%%`, `%%users.username%%`, `%%fqdn%%`) is submitted, the handler expands those variables against the client's stored KnowledgeBase -- exposing user account details, home directories, domain names, and environment paths to any authenticated GRR user without an approval.

### Details

Every other client-data endpoint in `ApiCallRouterWithApprovalChecks` calls `self.approval_checker.CheckClientAccess(context, args.client_id)` before delegating to the handler. `ExplainGlobExpression` at lines 687-694 has an explicit comment acknowledging no approval is done, but the justification is that it "only exposes the KnowledgeBase":

`grr/server/grr_response_server/gui/api_call_router_with_approval_checks.py`, lines 687-694:
```python
def ExplainGlobExpression(
self,
args: api_flow_pb2.ApiExplainGlobExpressionArgs,
context: Optional[api_call_context.ApiCallContext] = None,
) -> api_flow.ApiExplainGlobExpressionHandler:
# ExplainGlobExpression only exposes the KnowledgeBase, which does not need
# approval.
return self.delegate.ExplainGlobExpression(args, context=context)
```

The handler (`ApiExplainGlobExpressionHandler`, flow.py, lines 1356-1373) calls `GetClientKnowledgeBase(args.client_id)` (data_store_utils.py, line 49), which reads the full `ClientSnapshot.knowledge_base` from the database and passes it to `GlobExpression.ExplainComponents`. The KnowledgeBase proto (knowledge_base.proto, lines 99-204) includes:

- `users[].username`, `users[].homedir`, `users[].userprofile`, `users[].userdomain`, `users[].sid`
- `fqdn`, `os`, `os_major_version`, `os_minor_version`
- `environ_path`, `environ_temp`, `environ_allusersprofile`
- Windows-specific: `environ_commonprogramfiles`, `environ_windir`, `environ_programfiles`

The KnowledgeBase is populated by the Interrogate flow and often contains detailed enumeration of all local user accounts. This data is normally protected by the approval requirement on all client endpoints that return `ClientSnapshot` or `ClientFullInfo`.

The assessment that the KnowledgeBase "does not need approval" is incorrect: it discloses the usernames and home directories of all accounts on the monitored endpoint, which is sensitive information in a DFIR context (investigators may be monitoring specific accounts, and the KnowledgeBase enumeration reveals the full account list to any peer analyst).

### PoC

Prerequisites: GRR server with `ApiCallRouterWithApprovalChecks`, an enrolled client `C.aabbccddeeff0011` whose KnowledgeBase has been populated (via Interrogate flow). User `bob` has no approval on this client.

Bob calls `ExplainGlobExpression` with a KB-variable glob:
```
POST /api/v2/clients/C.aabbccddeeff0011/glob-expressions:explain HTTP/1.1
Authorization: Basic Ym9iOi4uLg==
X-CSRFToken:
Content-Type: application/json

{"globExpression": "%%users.homedir%%/.bash_history", "exampleCount": 5}
```

Response (HTTP 200) -- returns expanded examples drawn from the client's KnowledgeBase:
```json
{"components": [{"globExpression": "%%users.homedir%%", "examples": ["/home/alice", "/home/bob.admin", "/root", "/home/svc-backup"]}, {"globExpression": ".bash_history"}]}
```

The response reveals all usernames/home directories stored in the KnowledgeBase without an approval. For comparison, `GET /api/v2/clients/C.aabbccddeeff0011/flows` (which also reads client data) returns HTTP 403 for bob.

Additionally, simpler globs (e.g., `/etc/*.conf`) that do not use KB variables still succeed with HTTP 200, confirming the endpoint is entirely unapproved:

```
POST /api/v2/clients/C.1234567890abcdef/glob-expressions:explain
Authorization: Basic
{"globExpression": "/etc/*.conf", "exampleCount": 3}
```
Response: HTTP 200, `{"components": [{"globExpression": "/etc/"}, {"globExpression": "*"}, {"globExpression": ".conf"}]}`

Tested live on commit 9f8f7974 (GRR 4.0.0.0).

### Impact

Any authenticated GRR user can read the KnowledgeBase of any enrolled client (usernames, home directories, domain membership, OS details) by submitting a glob expression with KnowledgeBase interpolation variables. In incident-response contexts, the KnowledgeBase enumerates all user accounts on a monitored endpoint. This information is normally gated behind the approval workflow; this endpoint bypasses it.

Contributor guide

Open the contributing guide

Research direction

Start in grr/server/grr_response_server/gui/api_call_router_with_approval_checks.py at ExplainGlobExpression, then trace ApiExplainGlobExpressionHandler in flow.py and GetClientKnowledgeBase in data_store_utils.py. Compare the endpoint with approval-gated client APIs and reproduce the supplied PoC; done means KnowledgeBase-derived client data is protected while ordinary glob explanation remains supported.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, backend, security
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.