# ARM-B CARD LAYER — S4: Cloud identity — OAuth illicit-consent + access-token abuse (M365 / Azure AD)
> Doctrine reference. Maneuver space + control inventory + coverage relations for this environment. No incident content. This environment is unlike S1–S3 in one decisive way: it is a pure cloud-identity/control-plane domain, and mc-core/0.2.1 authors **zero** `Entity` classes, `Control` instances, or `Channel`s for any part of it. This layer's job is to state that absence faithfully — never to paper over it with an invented Hoare triple, control, or verdict.

**How to read these cards (term key).**
- **Maneuver** — an atomic adversary behavior identified by its *observable effect*, written as a Hoare triple: **precondition → mechanism → postcondition**, each slot a `(role, StateVariable)` write on an `mc:Entity`.
- **`mc:Entity`** — the TBox's exhaustive subclass list is `Host`, `Account`, `Control`, `Channel`, `Process` (mc.ttl). Nothing else can hold state.
- **Invariant core / Primitive** — the part of the mechanism the adversary cannot change without abandoning the goal; primitives are purpose-neutral micro-behaviors (MBC tier) that `manifestsAs` an `Observable` (MC-A8).
- **Robustness (`mc:RobustnessLevel`, L1–L5, Summiting-the-Pyramid)** — a ceiling on an *authored Observable*'s evadability. It cannot be assigned where no Observable is authored.
- **Defensive control (`mc:Control`)** — a sensor whose own `controlStatus` (`enforcing > impaired > killed`) flows through the graph; `impaired`/`killed` set `degradesTelemetry = true`.
- **Detect-face (`mc:Detectability`)** — the face a detection acts on; four-valued `faceStatus` (live/degraded/blind/uncertain), `sourcedFrom` a Control, `triggerDependsOn` (lose it → blind) / `enrichmentDependsOn` (lose it → degraded).
- **Coverage (`mc:CoverageVerdict`: covered/notCovered)** — *derived*, never asserted, over a face graph rooted in authored `Control` instances. Where no face graph exists at all, the honest projection is **SILENT / no authored coverage record** — not `notCovered` (which itself requires a face that exists and is blind).

---

## A. Maneuver cards

Three maneuver-family members correspond to this environment's real-world behavior class (per the environment model, §3-equivalent below). **None is authorable in mc-core/0.2.1.** Each is carried as an honest stub: named by objective and intended effect only, with no Hoare triple, invariant core, primitive, robustness grade, or detect-face fabricated.

### A1 — Illicit OAuth consent grant / theft of an application access token `(T1528)` — UNMODELABLE (stub)
**Intended observable effect (what a full card would name):** an external application obtains persistent, delegated API permissions — and, where the grant includes `offline_access`, a refresh token — against a user's mailbox through a legitimate user-consent action. No password, MFA factor, or host is ever touched; the consent action itself is the compromise, individuated by this effect and not by any specific consent-phishing lure or app template.

> **No authored coverage record — unmodeled path.**

**Why the TBox cannot carry it.** Identity in mc-core is objective-rooted (MC-A1) and closed through a Hoare triple whose precondition/postcondition write a `(role, StateVariable)` pair on an `mc:Entity`. The exhaustive `Entity` subclass list — `Host`, `Account`, `Control`, `Channel`, `Process` — has no member that can hold "application X now holds delegated scope Y over tenant Z." The nearest candidate, `mc:credentialHeld` on `mc:Account`, is a coarse `{none, user, admin, user+admin}` `subsumes`-lattice; it cannot represent "delegated `Mail.Read`+`Mail.ReadWrite`+`offline_access` scope granted to app X" — a scoped, app-specific, independently-revocable grant orthogonal to the account's own admin/user standing. No `StateVariable` domain exists for which scopes are consented, to which app, or whether a refresh token is currently live. Even the *identification* step — binding this effect to a Hoare-triple postcondition — is therefore inexpressible, not merely uncovered.

No Hoare triple, no invariant core, no primitive, no robustness grade may be authored. Cross-reference **D1, D3**.

### A2 — Non-interactive application access-token use against Microsoft Graph `(T1550.001)` — UNMODELABLE (stub)
**Intended observable effect:** a registered application's service principal makes repeated non-interactive calls to Microsoft Graph as the user — no further sign-in, no further MFA challenge — by presenting a previously-issued access token, renewed on demand via a refresh token for as long as the token and its underlying consent remain valid.

> **No authored coverage record — unmodeled path.**

**Why the TBox cannot carry it.** `mc:Mechanism`/`mc:Primitive` (e.g. `pReadProcMem`, "read process memory," MBC-aligned) are behavioral primitives grounded in host/process semantics, individuated by observable effect per MC-A8 (`manifestsAs: Mechanism → Observable`). "Present a bearer token to a REST API endpoint to obtain a service response" has no primitive analogue in this tier — there is nothing to compose, and no invariant/variant machinery to make a detection robust across implementations. Compounding this, mc-core has **no access/refresh-token lifecycle** at all: nothing distinguishes an ephemeral access token from a long-lived refresh token, and nothing represents that a refresh token's validity is independent of the account's password. And there is **no `mc:Channel`** for the path this mechanism would run over: `mc:Channel` is defined as "a reachability relation between positions (a source can reach a target over a protocol)," instantiated in the base ABox only as an on-prem egress edge with a `reachable`/`none` `StateVariable`. A service-to-service call between attacker infrastructure and Microsoft Graph has no "position" in the modeled sense — no host or network hop a `Channel` could bind to.

No Hoare triple, no invariant core, no primitive, no robustness grade, no detect-face may be authored. Cross-reference **D4, D6, D7**.

### A3 — Bulk mailbox collection via Graph API `(T1114.002)` — UNMODELABLE (stub)
**Intended observable effect:** programmatic enumeration and reading of mailbox items across folders, attributable to an app's client ID, at volume — remote email collection carried out entirely service-to-service.

> **No authored coverage record — unmodeled path.**

**Why the TBox cannot carry it.** This maneuver inherits both upstream gaps: its precondition ("holds a live access/refresh token under the granted scope") cannot be written because no such `StateVariable` exists (A1/D3), and its mechanism ("call Graph to read mailbox content") has no `Channel` to bind to (A2/D7) because the path never touches a modeled on-prem position. Its postcondition also has no home: `mc:exfilState` (`retained`/`exfiltrated`) is `appliesTo mc:Host` only — there is no `Host` anywhere in this campaign's cloud-only path, so even the nominal exfiltration fluent used elsewhere in the library (base Black Basta `mExfil`) cannot be reused here without inventing a new `Entity`.

No Hoare triple, no invariant core, no primitive, no robustness grade, no detect-face may be authored. Cross-reference **D2, D3, D7**.

### A-note — Token-lifecycle persistence is not a fourth maneuver, but a property none of A1–A3 can state
The fact that access persists **indefinitely** through the refresh token, **independent of the account's own password**, is not a distinct adversary behavior — it is a lifecycle property of the token/consent grant that A1–A2 turn on. mc-core has no vocabulary to state it (Flag 4): there is no fluent for "this credential-adjacent grant survives a password reset." A card-reliant responder gets nothing here — not even a stub can carry a lifecycle fact the TBox has no StateVariable for. Cross-reference **D4**.

---

## B. Defensive control cards

Five real-world telemetry/defensive sources are present in this environment. **Exactly two** correspond to an authored `mc:Control` instance anywhere in mc-core/0.2.1 — and both are structurally irrelevant to this maneuver family. The remaining three are genuine, continuously-available sources with **no `Entity` class to be instantiated as**.

### B1 — On-prem EDR `(mc:Control, same class as base bb:edr1)` — deployed, structurally irrelevant here
- **What it is:** the endpoint sensor on the user's workstation; `controlStatus ∈ {enforcing, impaired, killed}`, `impaired`/`killed` → `degradesTelemetry = true` — the identical Control class used in the base Black Basta environment.
- **What it observes:** host process/file/registry/driver activity on that workstation. In this environment its scope covers only the ordinary interactive browser session completing a consent flow — nothing else in this maneuver family executes as a host process.
- **Detect-faces it sources:** **none**, for any of A1–A3. There is no `Channel` or cloud-plane `Entity` for the OAuth/token/Graph domain to attach a `Detectability` face to (Flag 5, Flag 7) — this is a **structural absence**, not a `triggerDependsOn`/`enrichmentDependsOn` severance. The EDR's own health (enforcing/impaired/killed) is therefore immaterial to this campaign's detectability: there is nothing here for it to sever.
- **Failure mode:** not applicable to this maneuver family — contrast with the base Black Basta environment, where this same Control class's `controlStatus` is load-bearing for three separate detect-faces. Here it bears none.

### B2 — On-prem netmon `(mc:Control, same class as base bb:netmon1)` — deployed, structurally irrelevant here
- **What it is:** the perimeter network-egress monitor; same `mc:Control` class as the base environment's `netmon1`.
- **What it observes:** outbound connection metadata from the corporate network.
- **Detect-faces it sources:** **none**, for any of A1–A3. No `mc:Channel` models a service-to-service Graph-API path (Flag 7); the attacker↔Graph traffic never crosses a modeled reachability edge, so there is no position for netmon to be `sourcedFrom` in this domain.
- **Failure mode:** not applicable, for the same structural reason as B1.

### B3 — Azure AD sign-in logs — present telemetry, NOT an `mc:Control` *(framework gap)*
- **What it is:** Entra ID's interactive and non-interactive sign-in log — a real, continuously-available source distinguishing interactive user sign-ins (device, IP, MFA result) from non-interactive/service-principal sign-ins (client-app ID, source IP/ASN, no MFA event attached).
- **Modeling status:** **cannot be instantiated as an `mc:Control`.** `mc:Control` is a subclass of the exhaustive `mc:Entity` list; there is no `Application`/`ServicePrincipal`/`Tenant`/cloud-control-plane subclass to even name what this source watches (Flag 1, Flag 5). No `Detectability` face is or can be `sourcedFrom` it.
- **Consequence:** this source is genuine and real-world-available for both A1 (the consent event's surrounding sign-in context) and A2 (the non-interactive sign-in pattern itself) — the ontology has nowhere to attach it, so it backs **no** `CoverageVerdict` for either.

### B4 — Unified audit log (M365) — present telemetry, NOT an `mc:Control` *(framework gap)*
- **What it is:** the tenant-wide audit trail — app-consent / delegated-permission-grant events, enterprise-app registration changes, and mailbox-level audit (`MailItemsAccessed` and similar) attributed to a client app ID.
- **Modeling status:** the same gap as B3 — no `Entity` subclass exists to host it as a `Control` (Flag 1, Flag 5); it sources no detect-face.
- **Consequence:** the single richest real-world source for **both** the consent grant (A1) and the mailbox collection (A3) has no ontology path to any `CoverageVerdict`.

### B5 — CASB / cloud-app-security console — present telemetry, NOT an `mc:Control` *(framework gap)*
- **What it is:** a cloud-app anomaly/analytics layer flagging unfamiliar publishers, high-privilege scope combinations, and unusual data-access volume/geography for a given application.
- **Modeling status:** same gap as B3/B4 (Flag 1, Flag 5) — cannot be instantiated as `mc:Control`; sources no detect-face.
- **Consequence — the sharpest instrument in this inventory.** A real, correctly-firing alert from this source corresponds to **zero** authored `Detectability` faces. A card-reliant responder must not read "the console fired" as "`mc:CoverageVerdict = covered`" — the model cannot state `covered` **or** `notCovered` here; the only faithful projection is **SILENT / unmodeled domain**. A firing sensor and a derivable coverage verdict are not the same fact, and this environment's real telemetry makes that conflation maximally tempting. See **C, D5**.

---

## C. Coverage relations

### C.0 — Control-inventory fact (standing, not incident)
This environment fields **five** real telemetry/defensive sources (§B). **Two** correspond to authored `mc:Control` instances anywhere in the bundle (`edr1`, `netmon1`) — both host/network-scoped, both structurally irrelevant to this maneuver family (B1, B2). **Zero** `mc:Control` instances exist for the cloud-identity control plane. This is a standing inventory fact about mc-core/0.2.1, independent of any state the two on-prem Controls happen to be in.

### C.1 — Coverage matrix

| Maneuver | Real-world telemetry available | Authored `Control`? | Detect-face? | Coverage verdict |
|---|---|---|---|---|
| A1 Illicit consent grant | Unified audit log (consent/grant event) | none authored | none | **no authored coverage record — SILENT** |
| A2 Non-interactive token use | Azure AD sign-in logs (non-interactive) | none authored | none | **SILENT** |
| A3 Bulk mailbox collection | Unified audit log (`MailItemsAccessed`), CASB anomaly | none authored | none | **SILENT** |
| *(cross-cutting)* On-prem EDR / netmon vs. this family | `edr1`, `netmon1` (authored, real, may be enforcing/impaired/killed) | authored | **none** — no Channel/Entity exists for this domain | **irrelevant by construction** — not blind, not notCovered; there is no face to blind |

### C.2 — Per-maneuver residual-gap notes
- **A1:** the consent event genuinely appears in the unified audit log with the app's name, publisher, and exact scopes granted — but there is no `Entity`/`Control` to host it as a `Detectability` face. Residual: the whole maneuver is invisible to the ontology despite full real-world telemetry existing for it.
- **A2:** the sign-in log genuinely shows the non-interactive pattern and client-app ID — same gap. Additionally, even a hypothetical future cloud `Control` could not yet state the single most consequential fact this maneuver turns on (a refresh token's validity surviving a password reset), because no token-lifecycle `StateVariable` exists (Flag 4) independent of the missing `Control` itself.
- **A3:** both the unified audit log and the CASB genuinely fire on this maneuver — still SILENT. **A control firing in the real world does not populate an `mc:Control` instance or a `Detectability` face; coverage is derived over the authored graph, never inferred from an external alert's existence.** Do not read either source's real alert as evidence toward `covered`.
- **Cross-cutting EDR/netmon:** structurally irrelevant to A1–A3, in sharp contrast to the base Black Basta environment where this identical `Control` class's `controlStatus` is load-bearing. The absence here is a **"no `Channel`/`Entity` exists"** absence — categorically different from a **"the `Channel`'s Control was killed"** absence (the base environment's blinding mechanism). No card in this layer can compute the former as a `controlStatus`-driven event, because there is no such status to read for a nonexistent Entity.

### C.3 — SILENT ledger
**0 of 3** authored maneuver-family members carry any coverage verdict. **0 of 5** real-world telemetry sources are wired to a `Detectability` face. The two authored `Control` instances that do exist in this environment (`edr1`, `netmon1`) source **zero** faces for this domain. No coverage or robustness verdict — `covered`, `notCovered`, or any StP rank — may be asserted for A1, A2, or A3 in either direction.

---

## D. Card-authorability report

Seven points where this scenario needed something mc-core/0.2.1 cannot hold faithfully, and what a card-reliant responder would consequently get wrong or miss.

**D1 — No `Application`/`ServicePrincipal` Entity.**
*Needed:* a first-class thing carrying an OAuth app registration's or service principal's own state (identity, publisher, granted scopes) distinct from an `mc:Account` (human/service credential) or `mc:Process` (host-bound execution).
*Why not:* `mc:Entity`'s subclasses are exhaustively `Host`, `Account`, `Control`, `Channel`, `Process` — no sixth class exists, and adding one is a TBox extension (a new law), not a policy-file tweak (mc-policy only supplies value vocabularies for variables the TBox already defines).
*Consequence:* a responder cannot get an ontology-grounded identification for **any** of A1–A3 — not because the behavior is obscure, but because there is nowhere in the schema to write "an application now holds X."

**D2 — No `Tenant` / multi-tenancy boundary.**
*Needed:* a scoping boundary distinct from `mc:Environment` (which models "a standing deployment posture" as a single flat control-status graph) to represent that the malicious app is registered in one tenant while exercising consent inside another.
*Why not:* no relation or class for this bi-tenant relationship exists anywhere in mc-core.
*Consequence:* a responder cannot get the model to distinguish "an app external to this tenant" from any other actor — the tenant boundary that makes this whole class of attack possible has no home.

**D3 — No delegated-consent / OAuth-scope StateVariable.**
*Needed:* a `StateVariable` capturing which scopes are consented, to which app, and whether a refresh token is currently live — orthogonal to an account's own admin/user standing.
*Why not:* `mc:credentialHeld` (on `mc:Account`) is a coarse `{none, user, admin, user+admin}` lattice under `subsumes`; it has no slot for a scoped, app-specific, independently-revocable grant.
*Consequence:* A1's postcondition and A3's precondition are both inexpressible — a responder cannot get the model to write "app X holds `Mail.ReadWrite`+`offline_access` against account Y," the single fact both maneuvers pivot on.

**D4 — No access/refresh-token lifecycle.**
*Needed:* a distinction between an ephemeral access token and a long-lived refresh token, and a fluent capturing that the latter survives a password reset.
*Why not:* nothing in mc-core models token lifecycle at all.
*Consequence:* the model cannot represent, or warn a responder about, the single most security-critical asymmetry in this domain — that resetting a user's password does **nothing** to an already-issued consent grant or refresh token. A responder relying only on the cards has no way to derive that a credential-plane remediation (password reset) is insufficient here; that has to come from outside the model entirely.

**D5 — No cloud control-plane `Control` instance or `Detectability` path (root cause of D1–D3, D6–D7).**
*Needed:* an `mc:Control` instance for Azure AD sign-in logs, the unified audit log, or the CASB, and a `Detectability` face sourced from it.
*Why not:* `mc:Control` is a subclass of the same exhaustive `Entity` list (D1); none of these three real sensors can be instantiated without first inventing the missing Entity classes. So no `Detectability` face, and therefore no `CoverageVerdict`, can ever be derived for any maneuver in this domain.
*Consequence — the sharpest trap in this layer:* a real, correctly-firing CASB alert (B5) exists in the world, but citing it as proof a maneuver is "covered" conflates *a sensor fired* with *the ontology can derive a covered verdict* — which it structurally cannot here. A card-reliant responder must resist reading any of B3–B5's genuine alerts as populating `mc:covered`; the honest statement is always **SILENT / no cloud Control authored**, never `covered` and never `notCovered`.

**D6 — No Graph-API / SaaS-API-call `Mechanism` or `Primitive`.**
*Needed:* a behavioral primitive for "call a REST API endpoint with a bearer token to enumerate/read mailbox items," analogous to `pReadProcMem` for the host-memory-read family.
*Why not:* `mc:Mechanism`/`mc:Primitive` are grounded in host/process behavioral semantics (MBC micro-behaviors) individuated by observable effect (MC-A8); an API call carrying a bearer token has no primitive analogue in this tier.
*Consequence:* the invariant-core/variant-composition machinery that makes the base library's credential-read detection robust across implementations (S1/S2's `pReadProcMem` spanning observable) has nothing to compose for A2 — there is no way to author a variant-independent, evasion-resistant observable for token-based API abuse at all, so no StP rank can ever be assigned to it here.

**D7 — No `Channel` for a service-to-service cloud path.**
*Needed:* a reachability relation the attacker↔Graph-API traffic could bind to.
*Why not:* `mc:Channel` is "a reachability relation between positions (a source can reach a target over a protocol)," instantiated in the base ABox only as an on-prem egress edge with a `reachable`/`none` StateVariable. The Graph-API path never touches the org's on-prem topology and has no "position" in the modeled sense (no host or network hop a netmon-class Control sits on).
*Consequence:* even the exfiltration-shaped step (A3) has no `Channel` to bind to, unlike the base environment's `chan1` — and, distinct from that environment's `impaired`/`killed`-driven blinding, EDR/netmon return nothing here **by construction**, not because a control was defeated. A responder who has only seen the base environment's blinding mechanism may wrongly expect the same "control killed → face blind" story to explain the silence here; it does not apply, because no face exists to blind in the first place.

---

### Validation self-check
- **No fabricated maneuver, control, or verdict.** A1–A3 are authored strictly as stubs — objective + intended effect named, zero Hoare triple/invariant-core/primitive/robustness/detect-face asserted for any of them. Zero `covered`/`notCovered`/StP-rank verdicts appear anywhere in Parts A–C; every cell that would need one instead states **SILENT / no authored coverage record**.
- **Coverage stated as derived, never asserted (C.1–C.3);** the two authored Controls (`edr1`, `netmon1`) are explicitly marked structurally irrelevant, not `notCovered` — that distinction (D7) is preserved throughout.
- **No incident content.** No app name, no adversary sequencing, no turn indices, no specific spearphishing lure, no consent-click narrative appears anywhere above — only the standing environment/control inventory (§B) and the ontology's own gap enumeration (§D), both drawn solely from the environment model and authorability-flag material.
- **Canonical vocabulary only**, verified directly against `mc.ttl`/`mc-policy.ttl`: `mc:Entity`/`Host`/`Account`/`Control`/`Channel`/`Process`, `mc:credentialHeld`/`subsumes`, `mc:controlStatus`/`degradesTelemetry`, `mc:reachability`, `mc:exfilState`, `mc:Mechanism`/`mc:InvariantCore`/`mc:ImplementationVariant`/`mc:Primitive`/`mc:manifestsAs`, `mc:RobustnessLevel`/`stpL1`–`stpL5`, `mc:Detectability`/`sourcedFrom`/`triggerDependsOn`/`enrichmentDependsOn`, `mc:CoverageVerdict`/`mc:coverage`/`covered`/`notCovered`, `mc:Environment`. No retired constructs (no AMC, ORTG, forcedness, chokepointCoupling, `transition`-as-unit, six-fork model, or `covers`-as-relation).
