from Crypto.Random.random import randint, choice import os
# In a realm where curiosity roams free, our fearless cat sets out on an epic journey. # Even the cleverest feline must respect the boundaries of its world—this magical limit holds all wonders within. limit = 0xe5db6a6d765b1ba6e727aa7a87a792c49bb9ddeb2bad999f5ea04f047255d5a72e193a7d58aa8ef619b0262de6d25651085842fd9c385fa4f1032c305f44b8a4f92b16c8115d0595cebfccc1c655ca20db597ff1f01e0db70b9073fbaa1ae5e489484c7a45c215ea02db3c77f1865e1e8597cb0b0af3241cd8214bd5b5c1491f
# Through cryptic patterns, our cat deciphers its next move. defwalking(x, y, part): # Each step is guided by a fragment of the cat's own secret mind. epart = [int.from_bytes(part[i:i+2], "big") for i inrange(0, len(part), 2)] xx = epart[0] * x + epart[1] * y yy = epart[2] * x + epart[3] * y return xx, yy
# Enter the Cat: curious wanderer and keeper of hidden paths. classCat: def__init__(self): # The cat's starting position is born of pure randomness. self.x = randint(0, 2**256) self.y = randint(0, 2**256) # Deep within, its mind holds a thousand mysterious fragments. whileTrue: self.mind = os.urandom(1000) self.step = [self.mind[i:i+8] for i inrange(0, 1000, 8)] iflen(set(self.step)) == len(self.step): break
# The epic chase begins: the cat ponders and strides toward the horizon. defmoving(self): for _ inrange(30): # A moment of reflection: choose a thought from the cat's endless mind. part = choice(self.step) self.step.remove(part) # With each heartbeat, the cat takes a cryptic step. xx, yy = walking(self.x, self.y, part) self.x, self.y = xx, yy # When the wild spirit reaches the edge, it respects the boundary and pauses. ifself.x > limit orself.y > limit: self.x %= limit self.y %= limit break
# When the cosmos beckons, the cat reveals its secret coordinates. defposition(self): return (self.x, self.y)
# Adventurer, your quest: find and connect with 20 elusive cats. forroundinrange(20): try: print(f"👉 Hunt {round+1}/20 begins!") cat = Cat()
# At the start, you and the cat share the same starlit square. human_pos = cat.position() print(f"🐱✨ Co-location: {human_pos}") print(f"🔮 Cat's hidden mind: {cat.mind.hex()}")
# But the cat, ever playful, dashes into the unknown... cat.moving() print("😸 The chase is on!")
print(f"🗺️ Cat now at: {cat.position()}")
# Your turn: recall the cat's secret path fragments to catch up. mind = bytes.fromhex(input("🤔 Path to recall (hex): "))
# Step by step, follow the trail the cat has laid. for i inrange(0, len(mind), 8): part = mind[i:i+8] if part notin cat.mind: print("❌ Lost in the labyrinth of thoughts.") exit() human_pos = walking(human_pos[0], human_pos[1], part)
# At last, if destiny aligns... if human_pos == cat.position(): print("🎉 Reunion! You have found your feline friend! 🐾") else: print("😿 The path eludes you... Your heart aches.") exit() except Exception: print("🙀 A puzzle too tangled for tonight. Rest well.") exit()
# Triumph at last: the final cat yields the secret prize. print(f"🏆 Victory! The treasure lies within: {open('flag.txt').read()}")
from ast import literal_eval from typing importList, Tuple
from Crypto.Util.number import getPrime
deffind_candidate_prev_points( mind: bytes, endpoint: Tuple[int, int], limit: int ) -> List[Tuple[int, int, int]]: """ For each 8-byte part in 'mind', treat it as a 2x2 matrix over F_p (p=limit), compute its inverse, apply it to 'endpoint', and collect those previous points whose coordinates are both strictly less than the corresponding coordinates of endpoint. Returns a list of tuples: (index, prev_x, prev_y). """ x_n, y_n = endpoint candidates = []
if prev_x < x_n and prev_y < y_n: candidates.append((i, prev_x, prev_y))
return candidates
defrecover_mind_sequence( mind: bytes, start: Tuple[int, int], end: Tuple[int, int], limit: int, steps: int = 30 ) -> List[bytes]: """ Reverse-engineer the sequence of 'parts' used by the cat to move from 'start' to 'end'. Returns the list of 8-byte parts in forward order. """ current = end seq_indices = [] seq_coords = []
# Verify we reached the start coordinate if current != start: raise ValueError(f"After reverse ({steps} steps), reached {current}, expected start {start}")
# Reverse indices to forward order and extract parts seq_indices.reverse() recovered_parts = [mind[i * 8:(i + 1) * 8] for i in seq_indices] return recovered_parts
defsimulate_walking_sequence( start: Tuple[int, int], parts: List[bytes] ) -> Tuple[int, int]: """ Simulate the cat's walking forward using the recovered parts to verify correctness. """ x, y = start for part in parts: # parse matrix e0 = int.from_bytes(part[0:2], "big") e1 = int.from_bytes(part[2:4], "big") e2 = int.from_bytes(part[4:6], "big") e3 = int.from_bytes(part[6:8], "big") xx = e0 * x + e1 * y yy = e2 * x + e3 * y x, y = xx, yy return (x, y)
if __name__ == "__main__": import os from pwn import *
conn = remote('catch.chal.idek.team', 1337)
foriterinrange(20): conn.recvuntil(b'Co-location: ') x0, y0 = literal_eval(conn.recvline().decode()) #print(x0, y0) conn.recvuntil(b'Cat\'s hidden mind: ') mind_hex = conn.recvline().decode() mind = bytes.fromhex(mind_hex) conn.recvuntil(b'Cat now at: ') xn, yn = literal_eval(conn.recvline().decode()) limit = 0xe5db6a6d765b1ba6e727aa7a87a792c49bb9ddeb2bad999f5ea04f047255d5a72e193a7d58aa8ef619b0262de6d25651085842fd9c385fa4f1032c305f44b8a4f92b16c8115d0595cebfccc1c655ca20db597ff1f01e0db70b9073fbaa1ae5e489484c7a45c215ea02db3c77f1865e1e8597cb0b0af3241cd8214bd5b5c1491f
conn.recvuntil(b'Path to recall (hex):') parts = recover_mind_sequence(mind, (x0, y0), (xn, yn), limit) print("Recovered sequence of indices and parts:") sendpart = ''.join(part.hex() for part in parts) print(sendpart)
#Some magic from Willy Wonka p = 170829625398370252501980763763988409583 a = 164164878498114882034745803752027154293 b = 125172356708896457197207880391835698381
defchocolate_generator(m:int) -> int: return (pow(a, m, p) + pow(b, m, p)) % p
#Willy Wonka are making chocolates for i inrange(1337): chocolate_bag.append(getRandomRange(1, p))
#And he put the golden ticket at the end chocolate_bag.append(flag_chocolate)
#Augustus ate lots of chocolates, but he can't eat all cuz he is full now :D remain = chocolate_bag[-5:]
#Compress all remain chocolates into one remain_bytes = b"".join([c.to_bytes(p.bit_length()//8, "big") for c in remain])
#The last chocolate is too important, so Willy Wonka did magic again P = getPrime(512) Q = getPrime(512) N = P * Q e = bytes_to_long(b"idek{this_is_a_fake_flag_lolol}") d = pow(e, -1, (P - 1) * (Q - 1)) c1 = pow(bytes_to_long(remain_bytes), e, N) c2 = pow(bytes_to_long(remain_bytes), 2, N) # A small gift
#How can you get it ? print(f"{N = }") print(f"{c1 = }") print(f"{c2 = }")
#Some magic from Willy Wonka p = 170829625398370252501980763763988409583 a = 164164878498114882034745803752027154293 b = 125172356708896457197207880391835698381
L = p.bit_length() // 8# =128//8 =16 chunks = [remain_bytes[i*L:(i+1)*L] for i inrange(5)] print(bytes_to_long(chunks[-1])) #99584795316725433978492646071734128819
a = 164164878498114882034745803752027154293 for y in roots: # 把 a, y 视为 GF(p) 中的元素 F = GF(p) A = F(a) Y = F(y) # 调用 discrete_log x = discrete_log(Y, A) assert chocolate_generator(x) == c print("解得 x =", x) #4807895356063327854843653048517090061
defis_all_printable(bs: bytes) -> bool: returnall(0x20 <= b <= 0x7Efor b in bs)
import multiprocessing # 上一部分结果 BASE = 4807895356063327854843653048517090061 from tqdm import tqdm import os
# 子任务:在给定区间内查找满足条件的 j defworker(start: int, end: int, queue): results = [] for j inrange(start, end): val = BASE + (p-1)//2 * j text = long_to_bytes(val) if is_all_printable(text): results.append((j, text)) if j % 10000 == 0: queue.put(10000) return results
if __name__ == '__main__': multiprocessing.set_start_method('spawn') # For compatibility, especially on Windows cpu_count = multiprocessing.cpu_count() total = 2**33 chunk = total // cpu_count
# 使用 Manager 和 Queue 跟踪进度 with multiprocessing.Manager() as manager: queue = manager.Queue() pool = multiprocessing.Pool(processes=cpu_count)
# 用 tqdm 显示总进度 with tqdm(total=total, desc="Brute-forcing", unit="j") as pbar: results = [] tasks = [] for i inrange(cpu_count): start = i * chunk end = start + chunk if i < cpu_count - 1else total tasks.append(pool.apply_async(worker, (start, end, queue)))
.rodata:0000000000403013 aCorrect db 'Correct!',0 ; DATA XREF: sub_401050:loc_401159↑o .rodata:000000000040301C aWrong db 'Wrong!',0 ; DATA XREF: sub_401050+CE↑o
for t in tokens: if t.isdigit(): stack.append(BitVecVal(int(t), WIDTH)) elif re.fullmatch(r'[iIl]+', t): if t notin var_cache: var_cache[t] = BitVec(t, WIDTH) stack.append(var_cache[t]) elif t in OPS: if t == '~': iflen(stack) < 1: raise ValueError('stack underflow') a = stack.pop() stack.append(~a) else: iflen(stack) < 2: raise ValueError('stack underflow') b = stack.pop() a = stack.pop() stack.append({'&': a & b, '^': a ^ b, '|': a | b}[t]) else: raise ValueError(f'bad token {t}')
for t in tokens: if re.fullmatch(r'\d+|[iIl]+', t): # 数字或变量 output.append(t)
elif t in prec: # 运算符 while ops and ops[-1] in prec: top = ops[-1] if (prec[top] > prec[t] or (prec[top] == prec[t] and t notin right_assoc)): output.append(ops.pop()) else: break ops.append(t)
elif t == '(': ops.append(t)
elif t == ')': # 弹出直到左括号 while ops and ops[-1] != '(': output.append(ops.pop()) ifnot ops or ops[-1] != '(': raise ValueError("括号不匹配") ops.pop() # 丢弃 '('
else: raise ValueError(f"未知 token: {t}")
# 最后把剩余运算符都弹出 while ops: if ops[-1] in ('(', ')'): raise ValueError("括号不匹配") output.append(ops.pop())
return' '.join(output)
conn = remote('gacha-gate.chal.idek.team', 1337) context.log_level = 'debug' conn.recvuntil(b'lets play a game!\n') for i inrange(50): expression = conn.recvline().decode() #print(f'expression: {expression}') ans = infix_to_rpn(expression) conn.sendline(ans.encode()) conn.recvuntil(b'let me see..\n') conn.interactive()
后记
太弱啦太弱啦!crypto和re双修的我根本做不出来几道呢,我很想看看那几道椭圆曲线的题目是如何解的,一点思路都没有,以及那个5美元外包的vm,虽然已经有一些进展了(指看到了输入 i 可以调出寄存器),但是后面的工程似乎还很大,猪脑过载了喵>_<