MISC-长虹守卫

ISCC2026 WriteUp 提交模板

MISC-长虹守卫

解题思路

1.解压

解压后发现有这几个文件
image.png

2.分析txt文档

image.png

发现后面有一段base64
解码:

image.png

KEYMAT:AURORA-ER|flight=LX-517|syn=<EVT_A_UTC>|idl=<EVT_B_UTC>|a2=<EVT_C_UTC>
ALGO:SHA256|XOR|CTR
REASM:SEQ_ORDER|frag=3
IDRULE:idx26_applied_to_idx>=27_only
UTC:local+date_corr-timezone

NOTE:EVT_A=TCP_SYN_SENT,EVT_B=IDL_CROSS,EVT_C=ANOMALY_MARKER_A2

可以看到KEYMAT后面,有:

  • syn=<EVT_A_UTC>SYN 握手时间取自事件 A 的 UTC 标准时间戳
  • idl=<EVT_B_UTC>空闲交叉时间取自事件 B 的 UTC 时间戳
  • a2=<EVT_C_UTC>A2 异常标记时间取自事件 C 的 UTC 时间戳
EVT_A: TCP_SYN_SENT
EVT_B: IDL_CROSS
EVT_C: marker=A2

3.分析流量包

image.png

也没几个包,发现这个后面与串hex

CIPH:6bcc4445589479de64236b4761bca7d66107ef6fb8cf966f9046a68caa36906c2201d29d53c6

根据提示:

DATE_CORR=-1DAY;bug=FDR-227(date_not_applied)
must_apply_DATE_CORR=-1DAY from idx>=0026
IDRULE:idx26_applied_to_idx>=27_only
UTC:local+date_corr-timezone

这几句合起来说明:

  1. 飞机东向跨越 +180 -> -180 时,本地日期应该回退一天
  2. 但飞参记录器有 bug,这个修正没有正常应用
  3. 更麻烦的是,提示又说这个规则可能只对 idx>=27 生效,也就是 idx26 这条 EVT_B 本身怎么解释存在歧义

所以为了稳妥,脚本对每个关键事件都同时尝试两种解释:

  • date_corr = 0:假设这条日志的本地日期没修
  • date_corr = -1:假设这条日志应该减一天再转 UTC

然后按提示里的公式去算候选 epoch:

UTC = local + date_corr - timezone

写脚本计算,得到:

ISCC{b8c4acc9fcfd685af25d69106dc9b59d}

Exp

import base64
import hashlib
import itertools
import re
import struct
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
from pathlib import Path


BASE_DIR = Path(__file__).resolve().parent
LOG_PATH = BASE_DIR / "LX517.txt"
TRACE_PATH = BASE_DIR / "LX517.pcap"
FLAG_RE = re.compile(rb"ISCC\{[0-9a-fA-F]{32}\}")


@dataclass(frozen=True)
class FlightMarker:
    row_id: int
    local_stamp: datetime
    latitude_deg: float
    longitude_deg: float
    code: str
    memo: str


def scan_manifest(raw_log: str) -> str:
    for fragment in re.findall(r"[A-Za-z0-9+/=]{80,}", raw_log):
        try:
            decoded_text = base64.b64decode(fragment).decode("utf-8")
        except Exception:
            continue
        if "KEYMAT:" in decoded_text and "ALGO:" in decoded_text:
            return decoded_text
    raise RuntimeError("unable to locate embedded manifest")


def read_markers(raw_log: str) -> list[FlightMarker]:
    markers = []
    for row_text in raw_log.splitlines():
        if not re.match(r"^\d{4},", row_text):
            continue
        columns = row_text.split(",", 9)
        if len(columns) != 10:
            continue
        markers.append(
            FlightMarker(
                row_id=int(columns[0]),
                local_stamp=datetime.strptime(columns[1], "%Y-%m-%d %H:%M:%S"),
                latitude_deg=float(columns[2]),
                longitude_deg=float(columns[3]),
                code=columns[8],
                memo=columns[9],
            )
        )
    return markers


def guess_zone_hours(longitude_deg: float) -> list[int]:
    candidates = {int(round(longitude_deg / 15))}
    if longitude_deg > 150:
        candidates.update({12, 13, 14})
    if longitude_deg < -150:
        candidates.update({-10, -11, -12})
    return sorted(candidates)


def utc_epoch_variants(marker: FlightMarker) -> list[int]:
    epochs = set()
    for zone_hours, day_delta in itertools.product(
        guess_zone_hours(marker.longitude_deg),
        (0, -1),
    ):
        shifted_local = marker.local_stamp + timedelta(days=day_delta)
        utc_stamp = shifted_local - timedelta(hours=zone_hours)
        epochs.add(int(utc_stamp.replace(tzinfo=timezone.utc).timestamp()))
    return sorted(epochs)


def decode_capture_endian(global_header: bytes) -> str:
    magic = global_header[:4]
    if magic == b"\xd4\xc3\xb2\xa1":
        return "<"
    if magic == b"\xa1\xb2\xc3\xd4":
        return ">"
    raise RuntimeError("unsupported pcap magic")


def slice_tcp_appdata(trace_file: Path) -> list[bytes]:
    capture = trace_file.read_bytes()
    if len(capture) < 24:
        return []

    byte_order = decode_capture_endian(capture[:24])
    app_chunks = []
    cursor = 24

    while cursor + 16 <= len(capture):
        _ts_sec, _ts_usec, included_len, _original_len = struct.unpack(
            byte_order + "IIII",
            capture[cursor : cursor + 16],
        )
        cursor += 16
        frame = capture[cursor : cursor + included_len]
        cursor += included_len

        if len(frame) < 14:
            continue
        if struct.unpack("!H", frame[12:14])[0] != 0x0800:
            continue

        ipv4_packet = frame[14:]
        if len(ipv4_packet) < 20 or ipv4_packet[0] >> 4 != 4:
            continue
        header_len = (ipv4_packet[0] & 0x0F) * 4
        total_len = struct.unpack("!H", ipv4_packet[2:4])[0]
        if ipv4_packet[9] != 6 or len(ipv4_packet) < total_len:
            continue

        tcp_segment = ipv4_packet[header_len:total_len]
        if len(tcp_segment) < 20:
            continue
        payload_offset = (tcp_segment[12] >> 4) * 4
        app_data = tcp_segment[payload_offset:]
        if app_data:
            app_chunks.append(app_data)

    return app_chunks


def recover_cipher_blob(trace_file: Path) -> bytes:
    merged_payload = b"".join(slice_tcp_appdata(trace_file))
    located = re.search(rb"CIPH:([0-9a-fA-F]+)", merged_payload)
    if located is None:
        located = re.search(rb"CIPH:([0-9a-fA-F]+)", trace_file.read_bytes())
    if located is None:
        raise RuntimeError("ciphertext marker not found")
    return bytes.fromhex(located.group(1).decode())


def apply_counter_xor(cipher_blob: bytes, material_text: str) -> bytes:
    block_total = (len(cipher_blob) + 31) // 32
    stream = bytearray()
    encoded_material = material_text.encode()

    for counter in range(block_total):
        stream.extend(
            hashlib.sha256(
                encoded_material + struct.pack(">I", counter)
            ).digest()
        )

    return bytes(left ^ right for left, right in zip(cipher_blob, stream))


def pick_relevant_markers(markers: list[FlightMarker]):
    syn_markers = [item for item in markers if item.code == "EVT_A"]
    idl_markers = [item for item in markers if item.code == "EVT_B"]
    anomaly_markers = [
        item
        for item in markers
        if item.code == "EVT_C"
        and ("marker=A2" in item.memo or "marker=X9" in item.memo)
    ]
    return syn_markers, idl_markers, anomaly_markers


def fill_material_template(
    template_text: str,
    syn_epoch: int,
    idl_epoch: int,
    anomaly_epoch: int,
) -> str:
    return (
        template_text.replace("<EVT_A_UTC>", str(syn_epoch))
        .replace("<EVT_B_UTC>", str(idl_epoch))
        .replace("<EVT_C_UTC>", str(anomaly_epoch))
    )


def run() -> None:
    raw_log = LOG_PATH.read_text(encoding="utf-8", errors="ignore")
    manifest = scan_manifest(raw_log)
    markers = read_markers(raw_log)
    cipher_blob = recover_cipher_blob(TRACE_PATH)

    template_match = re.search(r"^KEYMAT:(.+)$", manifest, re.MULTILINE)
    if template_match is None:
        raise RuntimeError("key material template missing")
    material_template = template_match.group(1).strip()

    syn_markers, idl_markers, anomaly_markers = pick_relevant_markers(markers)
    attempt_count = 0

    for syn_marker, idl_marker, anomaly_marker in itertools.product(
        syn_markers,
        idl_markers,
        anomaly_markers,
    ):
        epoch_triplets = itertools.product(
            utc_epoch_variants(syn_marker),
            utc_epoch_variants(idl_marker),
            utc_epoch_variants(anomaly_marker),
        )
        for syn_epoch, idl_epoch, anomaly_epoch in epoch_triplets:
            material_text = fill_material_template(
                material_template,
                syn_epoch,
                idl_epoch,
                anomaly_epoch,
            )
            attempt_count += 1
            candidate_flag = apply_counter_xor(cipher_blob, material_text)
            if FLAG_RE.fullmatch(candidate_flag):
                print("hint:")
                print(manifest)
                print("ciphertext:", cipher_blob.hex())
                print("keymat:", material_text)
                print("flag:", candidate_flag.decode())
                print("tried:", attempt_count)
                return

    raise RuntimeError(f"flag not found after {attempt_count} attempts")


if __name__ == "__main__":
    run()

posted @ 2026-05-19 16:32  MillionMind  阅读(18)  评论(0)    收藏  举报