古剑山2025&2024 WP

    古剑山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提醒无效排除很有启发性

    评论

    发表回复

    您的邮箱地址不会被公开。 必填项已用 * 标注