2.5 KiB
Mileage MDM
В раздатке есть 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, затем расшифровываем:
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 содержит искомое доказательство.
Флаг
caplag{selector_hashes_opened_the_mdm_proof}