2025第八届强网拟态国际精英挑战赛 -- Crypto -- WriteUp

2025第八届强网拟态国际精英挑战赛 -- Crypto -- WriteUp

前言

这次比赛也是完成了为数不多的密码方向的AK,本来想着赛后写一写WP不过因为学业任务比较繁重,各种课程的实验都没来得及完成。拖着拖着,终于拖到了今天晚上,对比赛结束后提交的WP进行了完善补充。感觉本次赛题还是学到了不少的东西,也是纯手工搓了搓解密脚本,还是认为有记录一下的必要的。

Unsafe Parameters

task

from Crypto.Util.number import *
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from hashlib import sha3_512
from secret import *

def genParams():
    ns = []
    es = []
    ps = []
    d = getPrime(425) # Common Private Exponent 问题
    for _ in 'flag{': # 获取 5 组 密钥对,返回公钥
        p, q, r = [getPrime(512) for _ in range(3)]
        ps.append((p, q, r))
        n = p * q * r
        totient = (p - 1) * (q - 1) * (r - 1)
        es.append(inverse(d, totient))
        ns.append(n)

    return (ns, es), (d, ps)

pub, priv = genParams()
# 需分解每个 ns[i] 对每个大素因子求和
key = sha3_512(str(sum(sum(psum) for psum in priv[1])).encode()).digest()[:16]
ct = AES.new(key, AES.MODE_ECB).encrypt(pad(flag, 16))

print(f'ns = {pub[0]}')
print(f'es = {pub[1]}')
print(f'ct = {ct}')

"""
ns = [729626364576206469704240917876675932841677846807662743683194531189219993605123671836962855605283722577718230552963049472251011326675202612492908848548419883361685662678347011887752523869081347313358470192291106167923273619672010347904232948623232956703976722596246251219832472749781700661621717970912452690860710243752051416797956410009096107757901714990018414837758557784033898837218196380413347278999394220278929595840196278059116531409774355756772349502091527, 1905432596115201099512716986634374621489368222604315919606798930577721863294916275385323430940054377575273762764157350871093106918016952598143825002497857813393515175754983371150373414986745909170502391681896252141058196242286758315439213464335711981585004206505072743627148478873299089587473670712150938286701206231734068594554186483978059254161949801153805752489828395472590106405761648181381945795038085612905557546907903561767813822101495476457137965288880829, 1364349980724204783960363890369037262456777329397270902364257972605993939460160766530889520645888701966401869980072151818996346007664249855901114579605632358191242379651843548926065735575249122023543206153088606751338328933651430885645444839242513718915066302971957689904414300766000572990594260836480347717506097403302570129593533017205416936506156113683938133811098995666977893386015199252455279325252257306124895958614122639652100229863446635612470942945471629, 801368910415539931617837996119032301790585643652894417707002521182569449104238101253556548156062846942011258499343192564944616333555000780965111384091131074358231330717413121484815002612991437436336271715078003900216545055505405363415778086330672269759661284535787363323398929120499505051378299467980018690275341014026390376705451595348674549031968858607633947674244014656615045639930798213161815130251215148286379903187335369475976106390101774566278602818405563, 966507016385573035667733231340091844033410158675175976938028218854439065352997895173668326599042719776214706325758565722305289931861889759808022284179479582700755314576250841330755684569639628012527976286442288571350327307582007959428925460653350857512795573323486273071975979456828134582982048746188934676698845075430701350305713747666370252912970653968385390246217234899448990236850880873105998296512557366752267827688836849041157572772272378435636367162425013]
es = [271264973728317298627822473902975461834475616151421882685140849409171660245987005602120200976835072966082573357282000859946014645129524701781302678299643424924092704444611429422064341444927220441684375194952864825982915148126084102832224731498348832396834993134927447743362540828447462072582023959357845229307404791804881057335208287771899121154533179273274029972648764343120454360366227609296867085751393693129946684282269737380796082741762647814800935844443493, 1003585299728442138241618739786172474341836074346983356412903232651178484164007865852681323340512406956827176253325249434459143223478630158795320316445722303141000749369087588146876087005139101967705654538148200848735795226715484701026716263131990473014248826031226350016169675911701420383849573743077161936130739955632072650333006768068396767746102320645544271946493533626477749495460960365939362830334782483616521427745117179433129272929291131199903349977861093, 528794486797786998573050589647661205854628331390578568482905276781799068613879068393786314280307765489986817827366340249545226369795434498075653554440390174127680889163962386407937584138342055553885382937486807537913781141805602397908008361192107987266566035872632130183102501118938531330140154852996630732644530954025739132771439783366022539476194754352734861357158197716623228211860189868592877935421689151152269864018399507621324672725433395533000425391708101, 63082374875671578481986217433413118060930080658278695174993640397998104913203950822096713822227514918774669078130439943184022889683633824002111416144422250644695809731240622289790840695469043596475009958463780096705571401137513331089894183226911665633164170027318352120002851940432163291498705806239158684141492080194438572549164494596965748695244152843943149994989589419519395650481254722496058552304954140980743159877918387972030837603120258130045817858729621, 120373323462979513980656435521004077312964483601298713849302793507801996545855844801219890178210569842943901523332883574699699439655256681068585396438543716728771762620808023696864023456625376138578749985774885652016790204471223404504866831356254849552890279653807176865165230965065842479979722094076544915080001416276324550993965426099480075249719779502220020343237480355441663501565965298776708377675024327374331823503627493136287447543537154779812201209689733]
ct = b'J\x08\x0c\xfe\x0eC\n\x96!\xb3\x05\xa9\x9b\xf9\n\xe3-\xf2m\xdb&.\xb5h\xe1\x0e\xd6\x89\x14\x87Z\x0b0p\xbb\xd9\x93\xd7\x1cT\xa2\xf36\x02\x8e:\\\x92'
"""

analysis

  • 首先,解题思路很清晰,就是想办法分解ns数组中的每一个n,最终AES解密获取flag即可,而且这里des的生成方式,是一个典型的Common Private Exponent问题,参考博客Common Private Exponent Attack - sevensnight - 博客园
  • 我们通过Common Private Exponent获得相应的欧拉函数后,已知φ(N),分解N

exp

# sage 10.7

from Crypto.Cipher import AES
from hashlib import sha3_512

def factorize_multi_prime(N, phi):
    """
    Recovers the prime factors from a modulus if Euler's totient is known.
    This method works for a modulus consisting of any number of primes, but is considerably be slower than factorize.
    More information: Hinek M. J., Low M. K., Teske E., "On Some Attacks on Multi-prime RSA" (Section 3)
    :param N: the modulus
    :param phi: Euler's totient, the order of the multiplicative group modulo N
    :return: a tuple containing the prime factors
    """
    prime_factors = set()
    factors = [N]
    while len(factors) > 0:
        # Element to factorize.
        N = factors[0]

        w = randrange(2, N - 1)
        i = 1
        while phi % (2 ** i) == 0:
            sqrt_1 = pow(w, phi // (2 ** i), N)
            if sqrt_1 > 1 and sqrt_1 != N - 1:
                # We can remove the element to factorize now, because we have a factorization.
                factors = factors[1:]

                p = gcd(N, sqrt_1 + 1)
                q = N // p

                if is_prime(p):
                    prime_factors.add(p)
                elif p > 1:
                    factors.append(p)

                if is_prime(q):
                    prime_factors.add(q)
                elif q > 1:
                    factors.append(q)

                # Continue in the outer loop
                break

            i += 1

    return tuple(prime_factors)

ns = [729626364576206469704240917876675932841677846807662743683194531189219993605123671836962855605283722577718230552963049472251011326675202612492908848548419883361685662678347011887752523869081347313358470192291106167923273619672010347904232948623232956703976722596246251219832472749781700661621717970912452690860710243752051416797956410009096107757901714990018414837758557784033898837218196380413347278999394220278929595840196278059116531409774355756772349502091527, 1905432596115201099512716986634374621489368222604315919606798930577721863294916275385323430940054377575273762764157350871093106918016952598143825002497857813393515175754983371150373414986745909170502391681896252141058196242286758315439213464335711981585004206505072743627148478873299089587473670712150938286701206231734068594554186483978059254161949801153805752489828395472590106405761648181381945795038085612905557546907903561767813822101495476457137965288880829, 1364349980724204783960363890369037262456777329397270902364257972605993939460160766530889520645888701966401869980072151818996346007664249855901114579605632358191242379651843548926065735575249122023543206153088606751338328933651430885645444839242513718915066302971957689904414300766000572990594260836480347717506097403302570129593533017205416936506156113683938133811098995666977893386015199252455279325252257306124895958614122639652100229863446635612470942945471629, 801368910415539931617837996119032301790585643652894417707002521182569449104238101253556548156062846942011258499343192564944616333555000780965111384091131074358231330717413121484815002612991437436336271715078003900216545055505405363415778086330672269759661284535787363323398929120499505051378299467980018690275341014026390376705451595348674549031968858607633947674244014656615045639930798213161815130251215148286379903187335369475976106390101774566278602818405563, 966507016385573035667733231340091844033410158675175976938028218854439065352997895173668326599042719776214706325758565722305289931861889759808022284179479582700755314576250841330755684569639628012527976286442288571350327307582007959428925460653350857512795573323486273071975979456828134582982048746188934676698845075430701350305713747666370252912970653968385390246217234899448990236850880873105998296512557366752267827688836849041157572772272378435636367162425013]
es = [271264973728317298627822473902975461834475616151421882685140849409171660245987005602120200976835072966082573357282000859946014645129524701781302678299643424924092704444611429422064341444927220441684375194952864825982915148126084102832224731498348832396834993134927447743362540828447462072582023959357845229307404791804881057335208287771899121154533179273274029972648764343120454360366227609296867085751393693129946684282269737380796082741762647814800935844443493, 1003585299728442138241618739786172474341836074346983356412903232651178484164007865852681323340512406956827176253325249434459143223478630158795320316445722303141000749369087588146876087005139101967705654538148200848735795226715484701026716263131990473014248826031226350016169675911701420383849573743077161936130739955632072650333006768068396767746102320645544271946493533626477749495460960365939362830334782483616521427745117179433129272929291131199903349977861093, 528794486797786998573050589647661205854628331390578568482905276781799068613879068393786314280307765489986817827366340249545226369795434498075653554440390174127680889163962386407937584138342055553885382937486807537913781141805602397908008361192107987266566035872632130183102501118938531330140154852996630732644530954025739132771439783366022539476194754352734861357158197716623228211860189868592877935421689151152269864018399507621324672725433395533000425391708101, 63082374875671578481986217433413118060930080658278695174993640397998104913203950822096713822227514918774669078130439943184022889683633824002111416144422250644695809731240622289790840695469043596475009958463780096705571401137513331089894183226911665633164170027318352120002851940432163291498705806239158684141492080194438572549164494596965748695244152843943149994989589419519395650481254722496058552304954140980743159877918387972030837603120258130045817858729621, 120373323462979513980656435521004077312964483601298713849302793507801996545855844801219890178210569842943901523332883574699699439655256681068585396438543716728771762620808023696864023456625376138578749985774885652016790204471223404504866831356254849552890279653807176865165230965065842479979722094076544915080001416276324550993965426099480075249719779502220020343237480355441663501565965298776708377675024327374331823503627493136287447543537154779812201209689733]
ct = b'J\x08\x0c\xfe\x0eC\n\x96!\xb3\x05\xa9\x9b\xf9\n\xe3-\xf2m\xdb&.\xb5h\xe1\x0e\xd6\x89\x14\x87Z\x0b0p\xbb\xd9\x93\xd7\x1cT\xa2\xf36\x02\x8e:\\\x92'


M = int(pow(max(ns), 2 / 3))

L = matrix(ZZ, 6, 6)
L[0, 0] = M
for i in range(5):
    L[0, 1 + i] = es[i]
    L[i + 1, i + 1] = -ns[i]

L = L.LLL()

for i in range(6):
    d = abs(L[i][0]) // M
    if is_prime(d):
        ps = []
        for j in range(5):
            tmp = factorize_multi_prime(ns[j], es[j] * d - 1)
            ps.append(tmp)
        key = sha3_512(str(sum(sum(psum) for psum in ps)).encode()).digest()[:16]
        print(AES.new(key, AES.MODE_ECB).decrypt(ct))
# flag{9b31dd3e-aa6a-4c4d-b796-bff4e4dfe0cc}

image-20251025211416498

blockchain

  • 其实问题本身也不难,就是连续猜对01即可,起初我的思路还停留在是不是随机数的生成上可预测?最后发现,表面上如果我们选用固定的01组合,尝试1024次能一定获取flag,但是真正猜对的概率是比1/1024高的,其中存在生日攻击的思想,在这里就不证明了。
    image-20251025213645189

image-20251025213654651

image-20251025213729390

image-20251025213736458

flag{ineedyou}

FMS

task

import os
import pty
from Crypto.Util.number import *
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from hashlib import sha256
from random import randrange
from string import ascii_letters, digits

flag = os.getenv('FLAG')

class PRNG:
    def __init__(self, a = None, b = None, p = None, seed = None):
        """a, b, p, seed 的选择不依赖于 PRNG 而是依赖于 random 库函数"""
        self.p = p if p else getPrime(128) # 此处 getPrime 来自于 pycryptodome 库中的函数,不存在自身循环调用
        self.a = a if a else randrange(1, self.p)
        self.b = b if b else randrange(0, self.p)
        self.state = seed if seed else randrange(0, self.p) # seed randrange(0, self.p)

    def next(self):
        self.state = (self.a * self.state + self.b) % self.p # LCG --> state = (a * state + b) % p
        return self.state

    def randbytes(self, n):
        out = b''
        for _ in range(n):
            out += bytes([self.next() % 256]) # next % 256 for i in range(n)
        return out

    def choices(self, seq, k):
        return [seq[self.next() % len(seq)] for _ in range(k)]

    def getPrime(self, n):
        while True:
            num = self.randbytes(n // 8)
            p = bytes_to_long(num) | (1 << (n - 1)) | 1
            if isPrime(p):
                return p

    def getrandbits(self, n):
        num = self.randbytes((n + 7) // 8)
        return bytes_to_long(num) & ((1 << n) - 1)

    def __repr__(self):
        return f'a = {self.a}\nb = {self.b}\np = {self.p}'

prng = PRNG()

Account = {"admin": sha256(("".join(prng.choices(ascii_letters + digits, 32))).encode()).hexdigest(),
           "guest": "84983c60f7daadc1cb8698621f802c0d9f9a3c3c295c810748fb048115c186ec"}
# guest --> password:guest

user = None

menu = """Flag Management System
[L]ogin
[G]et Public Key
[R]ead Flag
[E]xit"""

# Get Public key 获得 a, b, p
# 目标为还原 seed ,Account 初始化为 prng = PRNG() 之后直接采用的 choices
# L 操作可以获得验证码,采用了 PRNG 的 randbytes() 函数

if __name__ == "__main__":
    while True:
        print(menu)
        op = input(">>> ").strip().upper()

        if op == "L" or op == "LOGIN":
            vcode = prng.randbytes(4).hex().rjust(8, '0')
            print(f"Verification code: {vcode}")
            username = input("Username: ").strip() # 用户名
            password = input("Password: ").strip() # 密码
            verify = input("Verification code: ").strip() # 验证码

            if verify != vcode:
                print("Verification code error!")
                continue

            if username in Account and Account[username] == sha256(password.encode()).hexdigest():
                print(f"Welcome, {username}!")
                user = username

            else:
                print("Login failed!")

        elif op == "G" or op == "GET PUBLIC KEY":
            print(prng)

        elif op == "R" or op == "READ FLAG":
            if user == "admin":
                key = prng.randbytes(16)
                iv = prng.randbytes(16)
                aes = AES.new(key, AES.MODE_CBC, iv)
                print(aes.encrypt(pad(flag.encode(), 16)).hex())
            else:
                print("Permission denied!")

        elif op == "E" or op == "EXIT":
            print("Goodbye!")
            exit(0)
            
        else:
            print("Invalid option!")

analysis

  • 根据题目描述或者代码分析,获取flag的思路就是,首先你需要进行随机数预测求取seed,之后登录该seed进行admin登录,然后获取得到AES加密的flag,既然有seed了,那我们就直接往后消耗next()就可以恢复keyiv了。
  • 这里所说的随机数就是每次L产生输出的Verification(验证码)
  • 这里也很明显,next()为一个LCG问题,再详细点就是一个HNP问题,需要注意的就是,我们需要获取足够多的数据,但是这里接收的数据必须保证HNP能够恢复seed。所以这里我是接收了200组的Verification进行规约,本地尝试了一下,200组足够了,写一个交互程序,不断地收集数据,之后再去另外一个终端求解seed,直接利用main里面的password的生成方式得到登录密码,最后获取AES加密后的密文即可。
  • 这里不得不说一下,这个靶机,如果你有一段时间不操作它,他就会挂掉,第一次求解的结果就被这样挂掉了。
  • 再多说一句,在你保证靶机连接还活着的时候,最好利用G来让他或者,用L的还得后续爆破。😂😂😂

get_data.py

# @Author  : chatgpt-o5
from pwn import *
import re
import json
import time

HOST = "pwn-0a325d807c.challenge.xctf.org.cn"
PORT = 9999
USE_SSL = True
TARGET_CODES = 200
OUTPUT_FILE = "output.txt"

context.log_level = "error"

def get_prng_params(io):
    """发送 G 获取 a,b,p"""
    io.sendlineafter(b">>>", b"G")
    data = io.recvuntil(b"E]xit", drop=True).decode(errors="ignore")

    # 匹配 a,b,p
    a_match = re.search(r"a\s*=\s*(\d+)", data)
    b_match = re.search(r"b\s*=\s*(\d+)", data)
    p_match = re.search(r"p\s*=\s*(\d+)", data)
    if not (a_match and b_match and p_match):
        raise ValueError("无法匹配到 a,b,p 数据")

    a, b, p = map(int, (a_match.group(1), b_match.group(1), p_match.group(1)))
    return a, b, p


def get_verification_codes(io, count=200):
    """连续 L,收集验证码"""
    codes = []
    for i in range(count):
        io.sendlineafter(b">>>", b"L")
        try:
            line = io.recvuntil(b"Verification code:", timeout=2).decode(errors="ignore")
            code_line = io.recvline(timeout=1).decode(errors="ignore")
        except EOFError:
            break
        code_match = re.search(r"([0-9a-fA-F]{8})", code_line)
        if code_match:
            code = code_match.group(1)
            codes.append(code)
            if i % 10 == 0:
                print(f"[*] Collected {i+1}/{count} codes ...")
        else:
            continue
        # 跳过 Username/Password 输入
        io.sendline(b"L")  # Username
        io.sendline(b"L")  # Password
        io.sendline(b"L")  # Verification code -> 错误也无所谓

    return codes


def main():
    print(f"[*] Connecting to {HOST}:{PORT} ...")
    io = remote(HOST, PORT, ssl=USE_SSL)
    io.recvuntil(b"E]xit")

    # Step 1: 获取 a,b,p
    a, b, p = get_prng_params(io)
    print(f"[+] Got PRNG params:")
    print(f"    a = {a}\n    b = {b}\n    p = {p}")

    # Step 2: 收集 200 组验证码
    print("[*] Collecting verification codes ...")
    codes = get_verification_codes(io, TARGET_CODES)
    print(f"[+] Collected {len(codes)} verification codes.")

    # Step 3: 写入文件
    result = {
        "a": a,
        "b": b,
        "p": p,
        "codes": codes
    }

    with open(OUTPUT_FILE, "w") as f:
        json.dump(result, f, indent=2)

    print(f"[+] Data saved to {OUTPUT_FILE}")
    print("[*] Ready to manually input admin credentials (if needed).")

    io.interactive()  # 保留连接,等待你手动操作


if __name__ == "__main__":
    main()

search_seed.py

# sage 10.7

from Crypto.Util.number import *
from subprocess import *
from string import ascii_letters, digits
from hashlib import sha256

def flatter(M):
    # compile https://github.com/keeganryan/flatter and put it in PATH
    z = "[[" + "]\n[".join(" ".join(map(str, row)) for row in M) + "]]"
    ret = check_output(["flatter"], input=z.encode())
    from re import findall
    return matrix(M.nrows(), M.ncols(), map(int, findall(b"-?\d+", ret)))

l = ['6b1bfeed', 'aef74de8', '9d23b3e3', '05338612', '5a3308f9', 'b1284a52', 'baf0a4c2', 'a42e953f', 'b1499b86', '03eb39b2', 'b7e7c3fb', 'b9dc9574', '027da1dd', 'f737b643', '85999ec6', 'cdd77858', 'd61ae49b', 'be80ab10', 'c458371d', '362fb80c', 'cc8af6bc', '4e06a93a', 'f6395bf1', '0ad0c995', '7bcd236c', '6fe615df', 'f195bb42', '8e4aefc2', 'a2492c0b', '14a75bb9', '085abee3', 'f29eea68', 'f1b5d238', '57ab0a5e', '6b22938e', 'f881b527', '178525b7', '73cd921a', 'd44c915c', '23d1ca81', '1f036582', '6a011a63', '7040b891', '7f07731a', '8dd1e594', '4cbbb4c1', 'dc1b5006', 'b036527a', '519699fc', '794abd14', '0ec9e404', 'b4fb2b32', '144befeb', '5ee886fa', '62572a81', '81b8f2c4', '8c5e0759', '65937d8c', 'e61f6979', '61d9d260', 'ca19730a', '7971b54f', '560924b9', '224dde30', '3e7e2f5e', 'bb73f3df', '51b2e175', '27eb0702', '62e581a6', '19097cd5', '80d1ed74', '24e9b0a8', '97619ffd', '1776ad1a', '15a8e79a', '6423ffd9', 'f8db28c7', 'a8188dbd', '90fbcf2f', '6bbbad77', '99520ec8', '0cf6af59', '6aeee4bd', '3e14cc66', '61b0cfa9', 'e8d90de7', '33964a36', '2d483f22', 'e2e89c61', '15fda641', '23a32073', 'ca133e61', 'd9e70213', '6405e9e0', '8a14ef9a', 'dee58027', '981956e7', '0384bed9', '953e7e6c', 'f2a02d17', '0e3ac6e6', 'a20dbb35', '0920eb64', '392d9ab1', 'd80f2ea6', 'd2e17762', 'd4b9c9be', '64dfd9d4', '2e87fbf9', 'aaa72398', '9b857bc5', '734185af', 'b8206d9f', '2e068625', 'b78544a2', '732a577b', '799b7ed3', 'd9cc7ab6', '968ee92f', 'ac36c477', 'c6bf60a9', '7c7c013a', 'c51d39fd', '01352f37', '74d9962b', 'e944438a', 'ebfc1eca', '2731a382', '1dab526d', 'c8902e5e', '62d3d744', '241bd54c', '0c4d8261', '9e22b9c5', '4ce28765', 'd330d89d', 'ff145c0c', 'e51c0d4c', '5b04f549', '397945ce', '31f3c093', 'e9821f54', '98ecf5ff', 'e55f08f8', '715ec3a3', 'ebf6f248', '5da34706', 'beb92d6d', '5f1b5cff', 'b4f6a9cb', 'f7935569', 'cc9e49c8', '6471c1d5', 'e9ec6acf', 'c5813e33', '026fe96a', 'a2817802', 'e385a6fb', '596f1744', '08b756c5', '23efd6ea', 'b4eb18f3', 'e6b4f89d', '3737fe03', '6229d4ce', '72e729d9', '89f74694', 'ab16e83c', 'fb80fb7a', '13544771', '9a59ac0f', '6bb57732', '0f25a800', '0518832c', 'c21982a3', 'f61f1d2e', '37d8defa', '3a3317fd', '56bd0eb9', '83dc44f5', 'c2a167e8', '5d3a2eee', '84f7bead', '26db914f', 'a47ea8ec', '09436d9b', '1ff38454', '86d6498c', 'e02b7943', 'd77a0f01', '161ad139', '3023d8c3', '165d345b', '1d66937d', '9f7d2aa9', 'ba4a1ddc', '36dea6d5', 'b3dd87a1', 'ed692080', '290c022e']

r = []
for rr in l:
    tmp = bin(int(rr,16))[2:].zfill(32)
    for i in range(4):
        r.append(int(tmp[i*8:(i+1)*8],2))

h = [0] + r[:100]
a= 70662413661960838978939233394038738837
b= 190473167030056339865973654388602296753
p= 321544457139506412049945479442144216681

A = [1]
B = [0]
for i in range(1, len(h)-1):
	A.append(a * A[i - 1] % p)
	B.append((a * B[i - 1] + a * h[i] + b - h[i + 1]) % p)

for i in range(len(B)):
    B[i] = B[i] * inverse(2 ** 8, p) % p


A = A[1:]
B = B[1:]

# print(len(A))

M = matrix(ZZ, len(A) + 2, len(A) + 2)

for i in range(len(A)):
    M[i, i] = p
    M[len(A), i] = A[i]
    M[len(A)+1, i] = B[i]
    M[i, len(A)] = M[i, len(A) + 1] = 0
M[len(A), len(A)] =  1
M[len(A)+1, len(A)+1] = 2^120
M[len(A), len(A)+1]= 0

L=flatter(M)
v = L[0]

s1 = v[-2]*2**8+h[1]
print(s1)

table = ascii_letters + digits

passwd = ''

for i in range(32):
    tmp = (s1-b)*inverse(a, p)%p
    passwd += table[tmp%len(table)]
    s1 = tmp
passwd = passwd[::-1]

print(passwd)
# 2NtzdOLgbU9eJzZA9Hhp5FZ2bs9YrsZL

solve_flag.py

# @Author  : chen_xing

from Crypto.Cipher import AES
from Crypto.Util.number import *

def fun(a, b, p, s):
    return (a * s + b) % p

enc = 0x72242d138afe30fa4384dfa3250db2e876f90bb84aa39f11aae84b609291ca2a7cf6d5068c85ef51148fdfdbfe1babbd
a = 70662413661960838978939233394038738837
b = 190473167030056339865973654388602296753
p = 321544457139506412049945479442144216681

for r in range(800,1100):
    key = b''
    iv = b''
    s = 279404651228606932570902027246647631723

    for _ in range(r):
        s = fun(a,b,p,s)
    for i in range(16):
        s = fun(a,b,p,s)
        key+=bytes([s % 256])
    for i in range(16):
        s = fun(a,b,p,s)
        iv+=bytes([s % 256])
    aes = AES.new(key, AES.MODE_CBC, iv)
    flag = aes.decrypt(long_to_bytes(enc))
    if flag.startswith(b'flag{'):
        print(flag)
# flag{brZVj2wDZL7J9uxGTeyFU4ICNgrSITTb}

image-20251025221418483

image-20251025221437700

image-20251025221445290

image-20251025221509914

  • 这里后续发现看官方给的WP好像看有师傅用CUSO只取了32组验证码就进行了求解,而且脚本是一体化的,但是短时间内也没法参悟一下师傅的思路,后续有时间再学习一下。想了解思路的师傅可以移步官方WP
posted @ 2025-11-07 00:51  chen_xing  阅读(10)  评论(0)    收藏  举报