Init. Commit

This commit is contained in:
Caplag
2026-09-17 00:50:07 +03:00
commit 880d8698d6
115 changed files with 7611 additions and 0 deletions
@@ -0,0 +1,45 @@
<h1 align="center">Grant Residue</h1>
<p align="center">
<img src="https://img.shields.io/badge/category-Forensic-blueviolet" alt="Forensic"/>
<img src="https://img.shields.io/badge/difficulty-medium-orange" alt="medium"/>
</p>
В архиве `grant_residue_case.zip` находится выгрузка SOC. В материалах инцидента описаны отправка кода финансовому отделу под предлогом передачи встречи и последующий экспорт Microsoft Graph. Пароль не менялся, MFA прошла штатно. По журналам определяем, какой поток авторизации использовался для доступа.
## Решение
Начинаем с рабочей станции, затем идём по облачным журналам:
| Файл | Что ищем |
|---|---|
| `endpoint/powershell_transcript.txt` | Код, который видела жертва |
| `endpoint/defender_device_events.jsonl` | Подтверждение того же кода |
| `entra/signin_logs.jsonl` | Успешный вход и `correlationId` |
| `entra/audit_logs.jsonl` | Выданный OAuth-грант |
| `m365/unified_audit_log.jsonl` | Экспорт с этим грантом |
В транскрипте PowerShell находим `FJQ9-L2RM` — строку формата `[A-Z0-9]{4}-[A-Z0-9]{4}`. Тот же код есть в телеметрии Defender, в `AdditionalFields.UserCodeObserved`.
Теперь фильтруем входы Entra: `authenticationProtocol == "DeviceCode"`, `userCodeEvidence == "FJQ9-L2RM"`, `status.errorCode == 0`. Получаем успешную авторизацию через device code flow и её `correlationId`.
Пароль не менялся, поскольку жертва прошла штатный вход, но подтвердила код чужого запроса. Теперь выясняем, какие права получила эта авторизация.
В `entra/audit_logs.jsonl` находим `Consent to application` с тем же `correlationId` и `result == "success"`. Среди `targetResources[].modifiedProperties[]` читаем `newValue` свойства `OAuth2PermissionGrant.Id`. Сохраняем полный идентификатор для сопоставления событий, а его хвост после последнего дефиса, `grant72`, используем в ответе.
Проверяем последствия в M365. Нужна запись `GraphExportJobCreated`, где одновременно совпадают `CorrelationId` и `OAuth2PermissionGrantId`, а `ResultStatus == "Succeeded"`. Из `ExportId` берём хвост `export18`.
Формат ответа собирается так:
```python
parts = ["dc", "FJQ9-L2RM".replace("-", "").lower(), "grant72", "export18"]
flag = "caplag{" + "_".join(parts) + "}"
```
`dc` обозначает найденный поток. Остальные части связывают наблюдение кода на рабочей станции с успешным входом, выдачей согласия и последующим экспортом. Совпадение идентификаторов на каждом переходе подтверждает эту связь.
[Солвер](solve/solve.py).
## Флаг
`caplag{dc_fjq9l2rm_grant72_export18}`