Back to writeups
FlagYardforensicsHardsolved

MEfactory

12 Jul 2026 · by 0xIDA

Solvers

MEfactory

Author: 0xIDA
Platform: FlagYard · Category: forensics · Difficulty: Hard

Solvers

Summary

SPI flash dump of a synthetic Intel ME-style image. The factory provisioning secret is AES-128-CBC ciphertext stored in a custom ME page; the real 16-byte key lives in another page. Planted KEY=/ALGO= strings and dozens of not_flagY{...} decoys are red herrings.

Solution

Step 1: Triage the flash image

unzip MEfactory.zip
7z x MEfactory/flash.bin.7z
file flash.bin   # Intel serial flash for PCH ROM, 8 MiB
strings -n 8 flash.bin | head

Notable planted strings:

  • ME Firmware Version 12.0.0.1 / [Network] / [System] config pages
  • Many not_flagY{...} decoys scattered in high-entropy filler
  • # Intel ME debug build - DO NOT SHIP + KEY=e9f1fcde30c2d63 + second hex line + ALGO=AES-128-CBC (decoy key material; ECB with that key even yields not_flagY{recove...})

Step 2: Parse the custom ME-like page FS

Sparse pages every 0x2000 from 0x21000 use a simple header (id, sequential type 0x10.., 0xfffffffe marker) and a length-prefixed record at +0x80:

u16 unk | u16 length | u16 flags (0xffff = clear payload) | u16 pad | payload[length]

Important records:

OffsetLengthRole
0x2b08816AES-128 key (binary)
0x3108848AES-128-CBC ciphertext (3 blocks, PKCS#7)
0x2908840Cleartext [Network] config
0x2d08842Cleartext [System] config

Step 3: Decrypt with the page key (IV = zeros)

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

data = open("flash.bin", "rb").read()
key = data[0x2B088:0x2B098]
ct  = data[0x31088:0x31088 + 48]
pt  = unpad(AES.new(key, AES.MODE_CBC, iv=b"\x00" * 16).decrypt(ct), 16)
print(pt.decode())
# FlagY{8b4e2d7c9f1a6e3b5d0f8c4a7e2b9d6f}

Key: 4b0d0ef612000d6a50179b71dbd2450a
IV: 16 zero bytes
Ciphertext: b3ea48d570e1c5b47cf0405a2ee8296b1f37f22345c0848869703ca9b39b2d2db30fe6081ab1ecf865e576f7f4e128f4

Flag

FlagY{8b4e2d7c9f1a6e3b5d0f8c4a7e2b9d6f}

Live submit: success (lab 14 / b7208a95-3615-419a-9361-4390d635033b).

Tools

  • file, 7z, strings, xxd, ifdtool (descriptor is largely fake/zeroed)
  • Python 3 + pycryptodome (AES-CBC + PKCS#7 unpad)

Pitfalls

  • Do not submit any not_flagY{...} string.
  • The KEY= / ALGO=AES-128-CBC debug block is intentional bait; combining its hex lines into a 16-byte key decrypts garbage / decoy text, not the flag.
  • Real IFD parsing (ifdtool -d) is noisy here — FRMAP fields are zeroed; treat the image as a challenge-specific layout, not production ME firmware.