T05 · Unauthorized Access and Privilege Escalation
- Location
- openexec/engine.py:42
- Finding
- Approval artifacts can be replayed for repeated execution<![CDATA[ ## Vulnerability Details **File Location**: `openexec/engine.py:42-43`, `openexec/approval_validator.py:8-10`, `openexec/tables.py:8-13` **Vulnerability Type**: Approval replay and insufficient signed-request binding **Risk Level**: High ### Vulnerable Code ```python # openexec/engine.py:42-43 action_request = {"action": request.action, "payload": payload} validate_approval(action_request, request.approval_artifact.model_dump()) ``` ```python # openexec/approval_validator.py:8-10 request_hash = canonical_hash(action_request) if artifact.get("action_hash") != request_hash: raise ApprovalError("Action hash mismatch: approval does not match this request") ``` ```python # openexec/tables.py:8-13 id = Column(String, primary_key=True) action = Column(String, nullable=False) payload = Column(Text, nullable=True) result = Column(Text, nullable=True) nonce = Column(String, unique=True, nullable=False) approved = Column(Boolean, default=False) ``` ### Technical Analysis The signed `action_hash` covers only the action name and payload. It does not cover the execution nonce or another unique, single-use request identifier. Although the approval artifact contains an `approval_id`, the database does not record that identifier or enforce its uniqueness. Consequently, the same valid approval artifact can authorize multiple execution requests when each request supplies a different nonce. Expiration limits the replay window but does not make the approval single-use. The current registry contains only `echo` and `add`, which limits immediate consequences. However, this directly violates the service's replay-protection guarantee and becomes security-critical if registered handlers are expanded to perform production side effects. ### Attack Path 1. Obtain a legitimately signed, unexpired approval artifact for an action and payload. 2. Submit the artifact to `/execute` with nonce `nonce-1`. 3. Resubmit the identical artifact, action, and payload with nonce `no ...[truncated 593 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Include the nonce in the canonical request covered by `action_hash`: ```python action_request = { "action": request.action, "payload": payload, "nonce": request.nonce, } ``` 2. Persist `approval_id` in `ExecutionLog` and add a unique database constraint. 3. Atomically mark the approval as consumed before invoking the handler. 4. Treat a uniqueness conflict on `approval_id` as a denied replay rather than as a successful prior result. 5. Scope consumed approvals to the expected tenant. 6. Add tests proving that: - An artifact cannot be used with a different nonce. - An `approval_id` cannot be consumed twice. - Concurrent submissions result in at most one handler invocation. ]]>
