
古剑山2025
1.CBC
题干:
Good job!
####*####*####*####Game Start####*####*####*####
# '#' for conditions
# '@' for questions
####@####
# c1=aes_cbc_encode(password,iv,m1)
# c1.encode('base64')=cHtNHbeZs4hMvPY1I3U/H5Xw9dTAYiQbNxi6hhKpD8fG+9XHS1y3vurJv+8n/eJS30zwAyUB/hZx
+jrgTG6oYaUoxrlMnDhdtblJMiI0SXpUYkEFTzON+v5v0kKBtlY/7deS+nhRWlWm1AEfLLFx6N0z
q22s9F0r2/HDEGTEdsiLnogqVO/f7e/GtLO7Kl6e++GQnpaKasa2KFDCaocMj7V5LfEh/Nha6N9E
Y6zMelUIEpy56YeeZBYctJ+CT8jeNZ6lXNOEO+ZD0KNeh9xyB7++PDgwhoeumGiJIh3S+EgZ/y2U
0vgtJ0JC6/5v27Km3btyV/dDEIGhIvx9pe1wCA==
# m1.encode('base64')=UUxjWW9QRm1SYXhCWG9WdVhtU1JXemtza3dJR0V4ZkpDaVNMZWJ2dmVGaWhoQ0V4Slp2cHhLU2VT
WkdLck1YY1ZCZmZPYkRmdWltWUdaTWdCcHhzaVZhYVBhWmtrd0xTbmNmY3pzeGpVRUNab0lWYkh4
T1J2UHlRWXhlaFZqQnVWRkxHaVJDYk5FVGRxQUZjU0Vld0pRZkplZXZEUVpxc3JvUWx2WmFqVmZF
YmVVbXdnaExXWG5iSXRHTG1WeURxQldlQWJtZnZiUEhNWVBVa2RGdmtSRU5EbldYY3NqRHhiR2tr
b2hySHFKY3hyUENVV0tFRURBYVlCRkN3T3hvRw==
# m2=aes_cbc_decode(password,iv,c2)
# m2[31]=n
# m2[54]=E
# m2[82]=C
# m2[117]=A
# m2[145]=H
# m2[189]=c
# m2[216]=i
# m2[248]=t
WP:
如题上所述,要控制2,4等相间的block中的特定字符(即PN(INDEX)),我们只知道一组C,P
part1:如何利用已知的C,P:
由CBC的解密可得:C0 XOR D(C1)=P1即D(C1)=P1 XOR C0,这时我们得到一个中间量S=D(C1)
part2:控制特定位置:
P(N)=C(N-1) XOR D(C(N)),我想通过S来控制C(N),即令C1=CN,同时解:D(C1)=D(CN)
即S=D(CN),带会得到P(N)=C(N-1) XOR S
加入INDEX控制,就是CN(INDEX)=PN(INDEX) XOR S(INDEX)
from pwn import *
import hashlib
import base64
import re
from collections import defaultdict
context.log_level = "info"
BLOCK_SIZE = 16
HOST = "47.107.168.16"
PORT = 42057
# ================== 工具函数 ==================
def xor_bytes(a: bytes, b: bytes) -> bytes:
return bytes(x ^ y for x, y in zip(a, b))
def solve_pow(prefix: str) -> bytes:
"""
题目:x = chr(rand(0,0xff)) + chr(rand(0,0xff)) + chr(rand(0,0x1f))
sha256(x).hexdigest()[0:8] == prefix
暴力枚举,返回 x(bytes,长度 3)
"""
log.info(f"[*] Solving PoW for prefix {prefix} ...")
target = prefix.lower()
for b0 in range(256):
for b1 in range(256):
for b2 in range(0x20): # 0x00 ~ 0x1f
x = bytes([b0, b1, b2])
h = hashlib.sha256(x).hexdigest()
if h.startswith(target):
log.success(f"Found x = {x.hex()} (sha256={h})")
return x
raise RuntimeError("No PoW solution found (should not happen).")
def build_c2(c1_b64: str, m1_b64: str, constraints):
"""
根据已知的 c1, m1 和 m2 的若干位置约束自动构造 c2。
constraints: [(index, char), ...] 例如 [(31,'n'), (54,'E'), ...]
返回: (c2_bytes, c2_base64_str)
"""
c1 = base64.b64decode(c1_b64)
m1 = base64.b64decode(m1_b64)
assert len(c1) == len(m1), "c1 和 m1 长度不一致,检查一下题目数据"
assert len(c1) % BLOCK_SIZE == 0, "长度不是 16 的倍数?"
nblocks = len(c1) // BLOCK_SIZE
C1 = [c1[i*BLOCK_SIZE:(i+1)*BLOCK_SIZE] for i in range(nblocks)]
P1 = [m1[i*BLOCK_SIZE:(i+1)*BLOCK_SIZE] for i in range(nblocks)]
# 选 C1[1] 作为参考块:S = D(C1[1]) = P1[1] XOR C1[0]
src_block_index = 1
assert nblocks > 1, "至少需要两个块才行"
S = xor_bytes(P1[src_block_index], C1[src_block_index - 1])
src_cipher_block = C1[src_block_index]
# 把约束按块分组:block_index -> [(offset, ord(ch)), ...]
by_block = defaultdict(list)
for pos, ch in constraints:
if pos < BLOCK_SIZE:
raise ValueError(
f"题目在第 0 块位置 {pos} 上给了约束,目前构造没处理第 0 块"
)
block_idx = pos // BLOCK_SIZE
offset = pos % BLOCK_SIZE
by_block[block_idx].append((offset, ord(ch)))
if not by_block:
raise ValueError("没有任何约束?")
max_block = max(by_block.keys())
num_blocks2 = max_block + 1 # 0-based
# 检查是否存在连续的约束块(例:3 和 4 同时有约束,会冲突)
constrained_blocks = sorted(by_block.keys())
for i in range(len(constrained_blocks) - 1):
if constrained_blocks[i] + 1 == constrained_blocks[i + 1]:
raise ValueError(
f"连续约束块 {constrained_blocks[i]} 和 {constrained_blocks[i+1]},"
"当前构造策略会冲突,需要手动改算法。"
)
# 初始化 C2,全 0
C2 = [bytearray(BLOCK_SIZE) for _ in range(num_blocks2)]
# 所有有约束的块,都设置为 src_cipher_block
for b in constrained_blocks:
C2[b][:] = src_cipher_block
# 根据每个受约束块,反推出"前一块"的对应字节
for b, items in by_block.items():
if b == 0:
raise ValueError("不应该出现 b == 0 的约束(前面已经检查过)")
prev_b = b - 1
for offset, ch_ord in items:
# P2[b][offset] = ch_ord
# P2[b] = S XOR C2[prev_b]
# => C2[prev_b][offset] = S[offset] XOR ch_ord
need = S[offset] ^ ch_ord
if C2[prev_b][offset] != 0 and C2[prev_b][offset] != need:
raise ValueError(
f"冲突:块 {prev_b} 偏移 {offset} 之前已为 "
f"{C2[prev_b][offset]}, 现在需要 {need}"
)
C2[prev_b][offset] = need
c2 = b"".join(bytes(block) for block in C2)
c2_b64 = base64.b64encode(c2).decode()
return c2, c2_b64
def parse_and_solve_round(text):
"""
从一轮题目文本中解析 c1, m1, 约束,并构造 c2
返回: (c2_bytes, c2_b64) 或 None(解析失败)
"""
# 解析 c1
m_c1 = re.search(
r"c1\.encode\('base64'\)=([A-Za-z0-9+/=\s]+?)#\s*m1\.encode\('base64'\)=",
text, re.S
)
if not m_c1:
return None
c1_b64_raw = m_c1.group(1).strip().replace("\n", "").replace("\r", "").replace(" ", "")
# 解析 m1
m_m1 = re.search(
r"m1\.encode\('base64'\)=([A-Za-z0-9+/=\s]+?)#\s*m2=",
text, re.S
)
if not m_m1:
return None
m1_b64_raw = m_m1.group(1).strip().replace("\n", "").replace("\r", "").replace(" ", "")
# 解析约束 m2[xxx]=c
constraints = []
for idx, ch in re.findall(r"m2\[(\d+)\]=(.)", text):
pos = int(idx)
constraints.append((pos, ch))
if not constraints:
return None
log.info(f"c1_b64 length = {len(c1_b64_raw)}")
log.info(f"m1_b64 length = {len(m1_b64_raw)}")
log.info(f"Parsed constraints: {constraints}")
try:
c2_bytes, c2_b64 = build_c2(c1_b64_raw, m1_b64_raw, constraints)
return c2_bytes, c2_b64
except Exception as e:
log.error(f"build_c2 failed: {e}")
return None
# ================== 主攻击逻辑 ==================
def main():
io = remote(HOST, PORT)
# ---------- 1. 读取 PoW 提示,解析前缀 ----------
data = io.recvuntil(b"x.encode('hex')=")
text = data.decode(errors="ignore")
log.info("PoW banner:\n" + text)
m = re.search(r"hexdigest\(\)\[0:8\]=='([0-9a-fA-F]{8})'", text)
if not m:
log.error("无法从题面解析出 PoW 前缀")
return
prefix = m.group(1)
x = solve_pow(prefix)
# 发送 x.encode('hex')
io.sendline(x.hex().encode())
round_num = 0
accumulated_text = ""
while True:
round_num += 1
log.info(f"========== Round {round_num} ==========")
# 读取本轮题目,直到出现 @c2.encode('base64')=
try:
data = io.recvuntil(b"@c2.encode('base64')=", timeout=10)
text = data.decode(errors="ignore")
log.info(f"Round {round_num} text:\n{text}")
except EOFError:
log.warning("EOF received, checking for flag...")
break
except Exception as e:
log.warning(f"Exception while receiving: {e}")
break
# 解析并构造 c2
result = parse_and_solve_round(text)
if result is None:
log.error("Failed to parse round, trying interactive...")
io.interactive()
return
c2_bytes, c2_b64 = result
log.success(f"Sending c2 (base64): {c2_b64[:80]}... (len={len(c2_b64)})")
io.sendline(c2_b64.encode())
# 读取响应,检查是否成功
try:
response = io.recvuntil(b"Good job!", timeout=5)
log.success(f"Round {round_num} passed!")
except:
# 可能是最后一轮或者出错了
log.info("No 'Good job!' received, checking remaining output...")
try:
remaining = io.recv(timeout=2)
log.info(f"Remaining data: {remaining.decode(errors='ignore')}")
if b"flag" in remaining.lower() or b"FLAG" in remaining or b"{" in remaining:
log.success(f"Possible flag found!")
except:
pass
break
# 最后尝试获取所有剩余输出
try:
io.settimeout(3)
final = io.recvall(timeout=3)
log.info(f"Final output: {final.decode(errors='ignore')}")
except:
pass
io.interactive()
if __name__ == "__main__":
main()
常见的CBC约束控制
2.RSA
题干:
N = 162178605357818616394571566923155907889899677780239882906511996614607940884142045197452389471499799373787832649318837814454679970724845203557871078001956378966434166323827984964942729898095347038272003371167123553368531662277059263517900162297903110415768403265100411543878859321181606008503516896600638590699
e1 = 35422
c1 = 153249315480380808558746807096025628082875635601515291525075274335055878390662930254941118045696231628008256877302589689883059616503108946971165183674522403835250738176157466145855833767128209866527507862726083268576304163200171600023472544755768741118904892489037291247455823396160705615280802805803254323033
e2 = 1033
c2 = 5823189490163315770684717059899864988806118565674660089157163486577056500243194221873916232616081138765317598078910078375360361118674333149663483360677725162911935082290640547407140413703664960164356579153623498735889314476063673352676918268911309402784919521792079943937126634436658784515914270266106683548
WP:
#一眼顶真,是共模攻击,并且互质,起飞
from Crypto.Util.number import *
n = 162178605357818616394571566923155907889899677780239882906511996614607940884142045197452389471499799373787832649318837814454679970724845203557871078001956378966434166323827984964942729898095347038272003371167123553368531662277059263517900162297903110415768403265100411543878859321181606008503516896600638590699
e1 = 35422
c1 = 153249315480380808558746807096025628082875635601515291525075274335055878390662930254941118045696231628008256877302589689883059616503108946971165183674522403835250738176157466145855833767128209866527507862726083268576304163200171600023472544755768741118904892489037291247455823396160705615280802805803254323033
e2 = 1033
c2 = 5823189490163315770684717059899864988806118565674660089157163486577056500243194221873916232616081138765317598078910078375360361118674333149663483360677725162911935082290640547407140413703664960164356579153623498735889314476063673352676918268911309402784919521792079943937126634436658784515914270266106683548
s1=pow(e1+e2,-1,e2)
s2=(1-e1*s1)//e2
t1=pow(c1,s1,n)
t2=pow(c2,s2,n)
m=(t1*t2)%n
m=long_to_bytes(m)
print(m)
共模攻击
3.近似方程
题干:
from Crypto.Util.number import *
import hashlib
p,x,a,b,c=getPrime(2025), getPrime(1024),getPrime(512),getPrime(1024),getPrime(1500)
y=x**4+a*x**2+b*x+c
print(f"y={y}")
print(f"b={b}")
print(f"c={c}")
print(f"p={p}")
flag=b'flag{'+hashlib.md5((str(a)).encode()).hexdigest().encode()+b'}'
flag
'''
y=198522960435841482189432265177909868426431703074708363449219028697324701510160286719731988116839697036160581630187160949885168801231636762705713983033164840216512010109089390010468198557792366011939222563881564708155342114969230636341034212421633874868858789397938243061335664034978831542864101231007091988193863118052712382091154270467198016237288512835005671248249864343182623298497353279333896185090300689372993251851698985725825619040354352099701531339567909888327001736937441117830890673409609636796619930226667955511073115983145641056494143455805357766933615911269415380543240132925744315363193308341329870002528062545716167903459438493154302274735882612908825416096548207130441684370171413794313131775960498691561791175984389699661776179746486882233034632979256741127422204726199986374102650551713456275685579171314911783130052174900534533297618292233601037654694611306838303066884626125878765611689352475739052132349596965687310082111652949904102268706219733830038063864703811394198182986558883397487207721093367621647038297033723455527107418011037322197091733780060565430632034276705456268207898108856563488519654411483838153783153351440293627133844440601677480833571012370843618968387929873540183054860492446381685034534436
b=174037789483961573123221843591212086149338262121485604600830887974881925500571571986993457692950300053374733237615433793104052491813933671943903946487842930332507742237430616633825365722075237337670353892075697891908329460336348924680104502864021820883975695834115061923434753902679746280494899854826747016131
c=33472511251772997775575923606329341691183359500908766818057589728522254242596438955590397842265158831677351198820346034459567637818737702918129437591421137895764213815168420429024803177742927091662422333575499875132388315943460516679655107714170719351306979285358080183600281172752401859701765071852769889627868633217209348758684870450300228084878260106253395883903968044935866937201968348531962567937222914516096107266208665856669507873113157476771707
p=3634071908867506965069315261429753443464716245738410040251875581513557838289276325218462157875991826224641777953866288921260305341272244121296068932207702190214606897491230548638292125061653257219859677992258434311277440482616823776560279337140379719082621412099620617217597998553091192312010588644683350990634085010622936842730027266359749795759745712453727285078472696717945991398633345949433118817923844577431455021644322229917758424788807006347476518982567210018019855197933776595555771047454297485885425728424948978147686731066201483794340469411549870237470492555193178687911573753715584786785924202386461
'''
WP:
from Crypto.Util.number import *
import hashlib
from decimal import Decimal,getcontext#引入小数处理
y=198522960435841482189432265177909868426431703074708363449219028697324701510160286719731988116839697036160581630187160949885168801231636762705713983033164840216512010109089390010468198557792366011939222563881564708155342114969230636341034212421633874868858789397938243061335664034978831542864101231007091988193863118052712382091154270467198016237288512835005671248249864343182623298497353279333896185090300689372993251851698985725825619040354352099701531339567909888327001736937441117830890673409609636796619930226667955511073115983145641056494143455805357766933615911269415380543240132925744315363193308341329870002528062545716167903459438493154302274735882612908825416096548207130441684370171413794313131775960498691561791175984389699661776179746486882233034632979256741127422204726199986374102650551713456275685579171314911783130052174900534533297618292233601037654694611306838303066884626125878765611689352475739052132349596965687310082111652949904102268706219733830038063864703811394198182986558883397487207721093367621647038297033723455527107418011037322197091733780060565430632034276705456268207898108856563488519654411483838153783153351440293627133844440601677480833571012370843618968387929873540183054860492446381685034534436
b=174037789483961573123221843591212086149338262121485604600830887974881925500571571986993457692950300053374733237615433793104052491813933671943903946487842930332507742237430616633825365722075237337670353892075697891908329460336348924680104502864021820883975695834115061923434753902679746280494899854826747016131
c=33472511251772997775575923606329341691183359500908766818057589728522254242596438955590397842265158831677351198820346034459567637818737702918129437591421137895764213815168420429024803177742927091662422333575499875132388315943460516679655107714170719351306979285358080183600281172752401859701765071852769889627868633217209348758684870450300228084878260106253395883903968044935866937201968348531962567937222914516096107266208665856669507873113157476771707
p=3634071908867506965069315261429753443464716245738410040251875581513557838289276325218462157875991826224641777953866288921260305341272244121296068932207702190214606897491230548638292125061653257219859677992258434311277440482616823776560279337140379719082621412099620617217597998553091192312010588644683350990634085010622936842730027266359749795759745712453727285078472696717945991398633345949433118817923844577431455021644322229917758424788807006347476518982567210018019855197933776595555771047454297485885425728424948978147686731066201483794340469411549870237470492555193178687911573753715584786785924202386461
getcontext().prec=3000#设置精度为3000位的10进制数
x_root4=int(Decimal(y).sqrt().sqrt())#精确得出开方
for move in range(-10,10):#设置左右偏移检查量
x_try=x_root4+move
if x_try<=0:#只考虑整数
continue
if isPrime(x_try):
a=(y-x_try**4-b*x_try-c)//(x_try**2)
if isPrime(a):
if a.bit_length()==512:
print("解出a")
md5a=hashlib.md5(str(a).encode()).hexdigest()
print(f"flag{{{md5a}}}")#3重{}才可以输出{}
如题名
4.哈希控制
题干:
#目标是输入值使sha256的后20位是0
WP:
#数据来源:前导0的网站:https://www.bitmex.com/blog/bitcoins-lowest-block-hash-values
import hashlib
import base64
#自己构造b'0'*20吗,开什么玩笑
text="00004020b97d5e09984585663a48d8de73233254ab2ee13bd72f07000000000000000000a48018a3bd388812511e9d068d9cd711a82b78d3918482cd2ee3c9bbd0b2b70b283ee75e357f141704176980"
text=hashlib.sha256(bytes.fromhex(text)).hexdigest()
print(text)
text="7c4dea8e47e58b2a1f8ecf9020b95df164dbfccc0f5cfff21993a1df7d0946ae"
text=hashlib.sha256(bytes.fromhex(text)).hexdigest()
print(text)
print(len(b'0000000000000000000000'))
t="7c4dea8e47e58b2a1f8ecf9020b95df164dbfccc0f5cfff21993a1df7d0946ae".encode()
t=base64.b64encode(t)
print(t)
查询数据
古剑山2024part
1.guess the key
题干:
jhjnjcjikmkfjjjkkejkkekdjgjcjnjhjnjcjiko
WP:
这是一个已知明文攻击,知道flag开头是flag{
大胆猜测,j和k代表的设不同的分组,或者不同的加密
这种情况下,没有密钥,大概率是编码或者恺撒
编码不对,太过整齐
恺撒处理前4个,发现偏移量是2,得到j的偏移是2,k对应{,得到偏移12
取出来,放在恺撒处理器里面,得到flag
比较好猜的恺撒
2.sign in RSA
题干:
Crypto
babyRSA#
题目信息
p=105570604806073931560404187362816308950408774915960751676958845800335871518600455146040240314204606944641098914858159386588868785987100524581699043605351952348586132553458702298393907476955946990849442034441882748278181148503329309660427627438266645843535466462936882505833453738522673603026747578372964995367
q=143288358949089585215266953016278524463612148007190135453434243047032456958207376091733443584531919755660149037321119885867830905350806865862130270602628423547624190876473872180462192096873381471710899246073298439365336797201672838785511965949336068032011363484044690916570288372443303750426476423930352603827
e=33
c=2015688184356018702340063509729974600786840546364122789630929715337660295810961474144939260049892177486759513271253257458112942844435453563521353664294799145214833075575682837104619902593712323020851788866158742546117774717790910939444986189326649593286629046732910564929050955013637088916579175350308099506220813882274665233182543235264034038626524248300357250276274833999274982434790398489181021739913941634249265775997527901903799819193254397528348894700572493228638350806878366316533768758273322476421225382269304595973225131694907875536065669987510951854843686992445396860396865892650745594418900376500862332616
WP:
from Crypto.Util.number import *
import re#这个可以去掉无效字符
p=105570604806073931560404187362816308950408774915960751676958845800335871518600455146040240314204606944641098914858159386588868785987100524581699043605351952348586132553458702298393907476955946990849442034441882748278181148503329309660427627438266645843535466462936882505833453738522673603026747578372964995367
q=143288358949089585215266953016278524463612148007190135453434243047032456958207376091733443584531919755660149037321119885867830905350806865862130270602628423547624190876473872180462192096873381471710899246073298439365336797201672838785511965949336068032011363484044690916570288372443303750426476423930352603827
e=33
c=2015688184356018702340063509729974600786840546364122789630929715337660295810961474144939260049892177486759513271253257458112942844435453563521353664294799145214833075575682837104619902593712323020851788866158742546117774717790910939444986189326649593286629046732910564929050955013637088916579175350308099506220813882274665233182543235264034038626524248300357250276274833999274982434790398489181021739913941634249265775997527901903799819193254397528348894700572493228638350806878366316533768758273322476421225382269304595973225131694907875536065669987510951854843686992445396860396865892650745594418900376500862332616
n=p*q
phi=(p-1)*(q-1)
d=pow(e,-1,phi)
m=pow(c,d,n)
m=long_to_bytes(m)
m=re.search(rb"flag\{.*?\}",m)#re.search()寻找,rb"flag\}(这里的\是转译,表示{是
print(m) #目标而非寻找符)开头,.*?表示找多个字符,\}停止于
#第一个}
不同于普通的签到,这里还考了无效排除
我比较喜欢的比赛,考的比较接近我学的,也有题扩展了视野,2024的RSA提醒无效排除很有启发性
发表回复