Files
Serdtse-Sysoly/qualifiers/mileage-mdm-ios-reverse-hard/WRITEUP.md
T
2026-09-17 00:50:07 +03:00

50 lines
2.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<h1 align="center">Mileage MDM</h1>
<p align="center">
<img src="https://img.shields.io/badge/category-Reverse-blueviolet" alt="Reverse"/>
<img src="https://img.shields.io/badge/difficulty-hard-critical" alt="hard"/>
</p>
В раздатке есть `MileageMDM.ipa` и `NOTES.md` со схемой маскирования. MDM-агент автопарка хранит офлайн-доказательство подлинности профиля и карту API-селекторов. Нужно связать параметры профиля с блоком данных в Mach-O.
## Решение
Распаковываем IPA как ZIP и смотрим `Payload/MileageMDM.app/`:
| Файл | Что ищем |
|---|---|
| `Info.plist` | Значение `AttestSeed` |
| `embedded.mobileprovision` | Тот же seed для сверки |
| `MileageMDM` | Маркер `MDMPROOF` и зашифрованную карту |
Согласно `NOTES.md`, ключ состоит из первых 16 байт SHA-256 от `AttestSeed`, а данные скрыты повторяющимся XOR. Seed лежит в `Info.plist` и дублируется в профиле, так что секретным он не является.
В бинаре находим `MDMPROOF`. Следующие четыре байта — длина блока в little-endian, дальше идёт сам блок. Читаем `AttestSeed` из plist в переменную `seed`, а зашифрованные байты — в `blob`, затем расшифровываем:
```python
import hashlib
import json
import plistlib
import zipfile
with zipfile.ZipFile("public/MileageMDM.ipa") as ipa:
data = ipa.read("Payload/MileageMDM.app/MileageMDM")
profile = plistlib.loads(ipa.read("Payload/MileageMDM.app/Info.plist"))
seed = profile["AttestSeed"]
pos = data.index(b"MDMPROOF") + len(b"MDMPROOF")
size = int.from_bytes(data[pos:pos + 4], "little")
blob = data[pos + 4:pos + 4 + size]
key = hashlib.sha256(seed.encode()).digest()[:16]
plain = bytes(b ^ key[i % len(key)] for i, b in enumerate(blob))
proof = json.loads(plain)
print(proof["flag"])
```
Важно брать байты хеша через `digest()`, а не первые 16 символов hex-представления. С правильным ключом получается JSON-карта селекторов, в которой поле `flag` содержит искомое доказательство.
[Солвер](solve/solve.py).
## Флаг
`caplag{selector_hashes_opened_the_mdm_proof}`