题目的名字是justrom,给我求出flag,flag包裹形式是LilacCTF{}

Créé le : 25 janvier 2026

Répondu en utilisant GPT-5.2 Thinking par Chat01

Question

rom.bin

题目的名字是justrom,给我求出flag,flag包裹形式是LilacCTF{}

Réflexion

Handling partial completeness within limits

The time limit is up, but I can still give the user a progress update. I can explain that I haven’t finished, but I’m continuing. I’ve identified key/nonce candidates and linked it to SPARC code with ChaCha cipher. I can finish up the analysis while ensuring it fits the system’s guidelines.

Providing partial progress and next steps

Since I can't use tools right now, I'll provide the steps as far as I've gotten.

  • The ROM is a SPARC big-endian image with a trap table at 0x0, SPARC code at 0x4000, and encrypted blobs. Key/nonce might be at 0xC000.

  • The code includes the ChaCha/Salsa constant "expand 32-byte k."

Next, the user can complete the disassembly or emulate SPARC to trace the key and decrypt.

I'll summarize the steps without offering the flag, acknowledging that more work is needed.

Providing actionable script plan for brute force decryption

I could suggest a Python script to help the user brute-force decryption. The plan would be:

  • Read rom.bin.
  • Find ciphertext regions at 0x1000 and 0x13000.
  • Scan for possible key/nonce pairs and try decrypting with ChaCha20 until we spot "LilacCTF{".

This search would be resource-intensive, but more efficient if restricted to lower entropy regions like around 0xC000. Alternatively, emulating code might be a solution.

Suggesting script optimizations and key exploration

Since running tools isn't possible, I could provide code for the user to compute the keystream and search for the flag. The key might be correct at 0xC000, but the nonce or counter could be elsewhere. Also, if the first 8KB blob is decrypted, it might reveal the key needed for the second. Checking for SPARC instruction patterns in the decrypted blob could help.

Summarizing analysis and providing next steps

It’s possible the encryption isn’t ChaCha but AES in CTR mode, with some confusing clues pointing to ChaCha. We've examined branch conditions in SPARC and key segments in memory. While we haven’t yet obtained the flag, the user can follow the provided script to scan for the key/nonce to decrypt and find the flag.

Thought for 26m 43s

Réponse

我把你给的 rom.bin 先做了结构分析(还没完全跑到解出 flag 的最后一步,但已经定位到“怎么解”的核心点了):

  • 这不是常见主机 ROM(GB/NES 那类),而是 SPARC 大端(big-endian)指令的内存镜像
    • 0x0000 附近看起来像 trap table(大量 ba + nop)。
    • 0x4000 开始是一段明显的 SPARC 代码:开头就有典型函数序言 0x9de3bfa0save %sp, ...)。
  • 0x4018 附近能看到 4 个 32-bit 常量:
    0x61707865 0x3320646e 0x79622d32 0x6b206574
    这正是 ChaCha/Salsa 系列的常量 “expand 32-byte k”(只是以 32-bit word 形式出现)。
  • ROM 里有两段“高熵/像密文”的大块数据:
    • 0x1000 .. 0x2FFF(8KB)
    • 0x13000 .. 0x13FFF(4KB)
  • 0xC000 处有一段非常“像 key/nonce”的东西:
    • 32 字节:11 22 33 44 ... dd ee ff 00 de ad be ef ... 13 37 13 37
    • 12 字节:AAAABBBBCCCC
      但我用标准 ChaCha20(IETF) / Salsa20 直接解这两段密文 没有命中 LilacCTF{,所以这段可能是诱饵,或者实现里对 key/nonce 的端序/装载方式做了变化,或者是“先解一段得到真正 key,再解另一段”。

你可以直接跑的“自动爆破 key/nonce 位置 + 端序变体”的脚本

这个脚本会:

  1. 0x10000x13000 两段作为候选密文
  2. 在 ROM 里按 4 字节对齐扫描候选 key(32) + nonce(12)
  3. 试几种常见变体(key/nonce 按小端或大端拆成 word;输出按小端/大端序列化;counter 试 0..8)
  4. 一旦在明文中搜到 LilacCTF{ 就打印 flag

把下面保存成 solve.py,和 rom.bin 放同目录运行:python3 solve.py

python
#!/usr/bin/env python3 import struct from pathlib import Path ROM_PATH = "rom.bin" CIPHERS = [ ("cipherA_8k", 0x1000, 0x2000), ("cipherB_4k", 0x13000, 0x1000), ] FLAG_PREFIX = b"LilacCTF{" def rotl32(x, n): return ((x << n) & 0xffffffff) | (x >> (32 - n)) def quarter_round(a, b, c, d): a = (a + b) & 0xffffffff d ^= a; d = rotl32(d, 16) c = (c + d) & 0xffffffff b ^= c; b = rotl32(b, 12) a = (a + b) & 0xffffffff d ^= a; d = rotl32(d, 8) c = (c + d) & 0xffffffff b ^= c; b = rotl32(b, 7) return a, b, c, d CONST_WORDS = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574] # "expand 32-byte k" words def chacha20_block_generic(key32, nonce12, counter, key_word_endian="little", nonce_word_endian="little", out_endian="little"): if key_word_endian == "little": key_words = list(struct.unpack("<8I", key32)) else: key_words = list(struct.unpack(">8I", key32)) if nonce_word_endian == "little": nonce_words = list(struct.unpack("<3I", nonce12)) else: nonce_words = list(struct.unpack(">3I", nonce12)) state = CONST_WORDS + key_words + [counter & 0xffffffff] + nonce_words x = state.copy() for _ in range(10): # 20 rounds # column x[0], x[4], x[8], x[12] = quarter_round(x[0], x[4], x[8], x[12]) x[1], x[5], x[9], x[13] = quarter_round(x[1], x[5], x[9], x[13]) x[2], x[6], x[10], x[14] = quarter_round(x[2], x[6], x[10], x[14]) x[3], x[7], x[11], x[15] = quarter_round(x[3], x[7], x[11], x[15]) # diagonal x[0], x[5], x[10], x[15] = quarter_round(x[0], x[5], x[10], x[15]) x[1], x[6], x[11], x[12] = quarter_round(x[1], x[6], x[11], x[12]) x[2], x[7], x[8], x[13] = quarter_round(x[2], x[7], x[8], x[13]) x[3], x[4], x[9], x[14] = quarter_round(x[3], x[4], x[9], x[14]) out = [(x[i] + state[i]) & 0xffffffff for i in range(16)] if out_endian == "little": return struct.pack("<16I", *out) else: return struct.pack(">16I", *out) def chacha20_crypt_generic(key32, nonce12, counter, data, key_word_endian="little", nonce_word_endian="little", out_endian="little"): out = bytearray() blocks = (len(data) + 63) // 64 for bi in range(blocks): ks = chacha20_block_generic( key32, nonce12, (counter + bi) & 0xffffffff, key_word_endian, nonce_word_endian, out_endian ) chunk = data[bi*64:(bi+1)*64] out.extend(bytes([b ^ ks[i] for i, b in enumerate(chunk)])) return bytes(out) def find_flag(buf: bytes): i = buf.find(FLAG_PREFIX) if i == -1: return None j = buf.find(b"}", i) if j == -1: return None return buf[i:j+1] def main(): rom = Path(ROM_PATH).read_bytes() variants = [] for key_end in ("little", "big"): for nonce_end in ("little", "big"): for out_end in ("little", "big"): variants.append((key_end, nonce_end, out_end)) # 扫描 key(32)+nonce(12),4字节对齐 for off in range(0, len(rom) - (32 + 12), 4): key = rom[off:off+32] nonce = rom[off+32:off+32+12] # 粗略跳过全0/太像空洞的区域 if key.count(0) > 24: continue for (name, c_off, c_len) in CIPHERS: cipher = rom[c_off:c_off+c_len] # 只先解前 512 字节做快速匹配 sample = cipher[:512] for counter in range(0, 9): for (key_end, nonce_end, out_end) in variants: plain = chacha20_crypt_generic( key, nonce, counter, sample, key_end, nonce_end, out_end ) fl = find_flag(plain) if fl: # 命中后解全段 full_plain = chacha20_crypt_generic( key, nonce, counter, cipher, key_end, nonce_end, out_end ) fl2 = find_flag(full_plain) if fl2: print("[+] HIT!") print(f" key/nonce offset: 0x{off:05x}") print(f" cipher: {name} @0x{c_off:x} len=0x{c_len:x}") print(f" counter={counter}, key_end={key_end}, nonce_end={nonce_end}, out_end={out_end}") print(fl2.decode(errors="replace")) return print("[-] No flag found with this scan. 说明:key/nonce 可能不是平铺在 ROM 里,或者不是标准 ChaCha20(IETF) 输出字节序。") if __name__ == "__main__": main()

我现在卡在哪一步(直说)

我已经能确定这是 SPARC ROM +(很大概率)ChaCha/Salsa 系列加密/PRNG,也找到了明显的常量与疑似 key/nonce 区域,但还没把 SPARC 代码完整跑起来(需要一个小 SPARC 解释器或能反汇编 SPARC 的工具环境),所以我这条消息里还没直接给出最终 LilacCTF{...}

你先跑一下上面脚本:

  • 如果直接命中 flag,你就能马上拿到答案。
  • 如果没命中,把脚本最后输出的 “No flag found” 给我(或者你把运行输出贴出来),我就可以据此把搜索范围/算法变体再收紧(例如改成“先解 A 得到 key,再解 B”的链式逻辑,或者改用 Salsa20/原始 ChaCha 计数器布局等)。

Partager cette Q&R