[CPCTF 2025] Crypto

上上周的比赛,有事没写,补上一部分。最近都是每周3个,难度感觉越来越大。

Heroic code

Caeser爆下即可

'''
Xubbe qdt jxqda oek veh zeydydw jxu SFSJV! Jxu vbqw yi SFSJV{qbuq_zqsjq_uij}.
Hello and thank you for joining the CPCTF! The flag is CPCTF{alea_jacta_est}.
'''

add-and-multiple

plaintext = input()
a = [ord(i) for i in plaintext]
cipher = 0
for i,chr in enumerate(a,1000):
    cipher += chr
    cipher *= i
f = open('cipher.txt', 'w')
f.write(str(cipher))
f.close()

这个for i,chr in enumerate(a,1000)式子不常见,测试一下是说i从1000开始,那么这个式子就是:

c = a0*1000*1001... + a1*1001*... + an*1029

第个的参数是1000*....*1029,第2个参数是1001*...*1029这样显然第1个因子最大,所以从大到小除一下就得到flag

c = 103200264548574214569124695908951019136986646123214535931636006688814109904122192900997137101
base = prod(range(1000,1030))
flag = ''
i = 1000
while c>1000:
    k = c//base
    c -= k*base
    flag+=chr(k)
    print(flag)
    base//=i
    i+=1

#CPCTF{C14ssic4l_Ciph3r_15_fun}

Anomaly

from Crypto.Util.number import getPrime, bytes_to_long

flag = "CPCTF{fake_flag}"

p = getPrime(512)
e = getPrime(512)
q = 0x10001
n = p * q
c = pow(bytes_to_long(flag.encode()), e, n)

with open("output.py", "w") as f:
    f.write(f"e = {e}\n")
    f.write(f"n = {n}\n")
    f.write(f"c = {c}\n")

这个就比较简单了,n的一个小因子已经,去掉后拿另一个就能直接求解

p = n//0x10001
m = pow(c,inverse_mod(e,p-1),p)
long_to_bytes(int(m))

#CPCTF{You_h4v3_escap3d!_8fa245b1007bc564}

One More PPC Problem

from secrets import randbelow

m = 80701148760020250419  # prime
a = 20250418
b = 240240240240

MAX_N = 10**5
MAX_Q = 100


class myRand:
    def __init__(self):
        self.v = randbelow(m)
    
    def random(self):
        self.v = (a*self.v+b) % m
        return self.v


def genCase(h, w, rd):
    A = [[rd.random() % MAX_N+1 for _ in range(w)] for _ in range(h)]
    A[rd.random() % h][rd.random() % w] = 0
    return (A, h, w)


def judge(case):
    A, H, W = case
    cnt = 0
    x, y = 0, 0
    print(f"{H} {W}")
    while cnt < MAX_Q:
        cnt += 1
        op = input()
        if op == '>':
            y += 1
        elif op == '<':
            y -= 1
        elif op == '^':
            x -= 1
        elif op == 'v':
            x += 1
        else:
            print("Invalid Input")
            return False
        if x >= 0 and x < H and y >= 0 and y < W:
            print(A[x][y])
            if A[x][y] == 0:
                return True
        else:
            print("Out of Stage")
            return False
    print("Query Over")
    return False


def main():
    r = myRand()
    caseHW = [(5, 5), (10, 10), (50, 50), (50, 50), (50, 50),
              (50, 50), (50, 50), (50, 50), (50, 50), (50, 50), (50, 50)]
    for i in range(len(caseHW)):
        h, w = caseHW[i]
        case = genCase(h, w, r)
        if not judge(case):
            print(f"WA on Case {i}")
            exit(0)
    print("AC!")
    with open("flag.txt") as f:
        print(f.read())
    exit(0)


if __name__ == '__main__':
    try:
        main()
    except:
        print("Judge Error")
        exit()

这个没弄成,后来感觉刚开始方法有点问题。

这是一个lcg模式(a,b,m已知)的伪随机数,给出后5位10进制数。

每关当除放置w*w个随机数外会随机生成一个坐标,在坐标对应格内填0,当移动到为0的格(小于100步)时过关。

一开始发现用第1关25格里的10个完成不了就没接着作。其实可以先路过第1关,(只有5*5个格动一遍不会到100)用第2关的20个格来求应该是可以的。回头试好了再补上

#from pwn import remote,context as ctx
#ctx.log_level = 'debug'
#from sage.all import *

m = 80701148760020250419  # prime
a = 20250418
b = 240240240240

MAX_N = 10**5
MAX_Q = 100


class myRand:
    def __init__(self,seed):
        self.v = seed
    
    def random(self):
        self.v = (a*self.v+b) % m
        return self.v


def genCase(h, w, rd):
    A = [[rd.random() % MAX_N+1 for _ in range(w)] for _ in range(h)]
    A[rd.random() % h][rd.random() % w] = 0
    return (A, h, w)

def get_seed(k3):
    dim = len(k3)-1
    k3 = [i-1 for i in k3]
    A = [1]
    B = [0]
    for i in range(1, dim):
        A.append(a*A[i-1] % m)
        B.append((a*B[i-1]+(a*k3[i]+b-k3[i+1])*inverse_mod(100000,m)) % m)
    A = A[1:]
    B = B[1:]
    
    M = matrix(ZZ, dim, dim)
    for i in range(dim-2):
        M[i, i] = m
        M[-2, i] = A[i]
        M[-1, i] = B[i]
        M[i, -2] = M[i, -1] = 0
    M[-2,-2] =  1
    M[-1,-1] = m//100000  #高位的规模
    
    ll = M.LLL()[0]
    l1 = ll[-2]
    h1 = k3[1]
    s1 = l1*100000+h1
    
    seed = ((s1-b)*inverse_mod(a,m))%m
    seed = ((seed-b)*inverse_mod(a,m))%m
    print(seed)
    return seed


#test
seed = 61998074543891883238
rd = myRand(seed)
A,h,w = genCase(10,10,rd)

#用10*10来求seed 当前20个值为含0时
k3 = A[0]+A[1]
seed2 = get_seed(k3)

assert seed == seed2

RSA Trial

from Crypto.Util.number import getPrime, bytes_to_long
from sympy import nextprime
from secret import flag

p = getPrime(512)
q = getPrime(512)
r = nextprime(p + q)

n = p * q * r
hint = p**3 + q**3 + r**3
e = 0x10001
c = pow(bytes_to_long(flag.encode()), e, n)

with open("output.py", "w") as f:
    f.write(f"e = {e}\n")
    f.write(f"n = {n}\n")
    f.write(f"c = {c}\n")
    f.write(f"hint = {hint}\n")

这个题给出的r=next_prime(p+q),给出的hint = p^3+q^3+r^3可以根据3次方展开公式得到:

2*(p+q)^3 = hint + 3*n 

由于r只是略大于p+q可以直接开3次方得到p+q从面分解n

p_q = iroot((hint+3*n)//2,3)[0]
r = next_prime(p_q)
assert gcd(r,n) == r
#17758421139024913359737842854715476386497379733049987554858008465450704659866266822649115010114307927129443930502131087390672063301921117757614648780099317
m = pow(c,invert(65537,r-1),r)
#1995359735599891780688837994124480968965315009678458446439030574806791881327928957
long_to_bytes(m)
#b'CPCTF{tr1pl3_RSA_8011aed45d7c060f}'

Prime Tester

from math import gcd

def is_prime(n):
    if n == 2: 
        return True
    if n == 1 or n & 1 == 0: 
        return False
    d = n - 1
    while d & 1 == 0:
        d >>= 1
    for a in range(500):
        if gcd(a, n) != 1:
            continue
        t = d
        y = pow(a, t, n)
        while t != n - 1 and y != 1 and y != n - 1:
            y = (y * y) % n
            t <<= 1
        if y != n - 1 and t & 1 == 0:
            return False
    return True

if __name__ == '__main__':
    n = int(input("What is your favorite prime number?: "))
    if n <= 2 or 4096 <= n.bit_length():
        print("Hmm... I don't like this.")
        exit(0)
    if not is_prime(n):
        print(":(")
        exit(0)
    x = int(input("What is your favorite number?: "))
    if x <= 1 or x >= n - 1:
        print(":(")
        exit(0)
    if pow(x, 2, n) == x:
        print("Wow! How did you do that?")
        with open("flag.txt") as f:
            print(f.read())
    else:
        print("Nice!")
    exit(0)

要求输入一个n和一个x使得 x^2=x mod n并且n能通上边函数的伪素数测试。

感觉就是找一个500内base的伪素数就行,当n为素数里显然x^2==x只有0和1两个解达不到要求,当是合数里就有解,这个就模p,q分别得0,1

如果是300内的倒是有个存着的数

p = 29674495668685510550154174642905332730771991799853043350995075531276838753171770199594238596428121188033664754218345562493168782883
n = p *(313*(p - 1) + 1)*(353 * (p - 1) + 1)

500以内的搜了半天没搜着,放弃了。等哪天看着了再补上。

后来看他有3个hint,指向一篇论文,

Constructing Carmichael Numbers which are Strong Pseudoprimes to Several Bases

但打开不知道论文内容是啥,不过搜到另外一个文章:

Constructing Miller–Rabin Pseudoprimes with SageMath

这里边有几个生成的例子,直接用541(>500)的那个即可

P = 7787732278755017824602699104690213508779614394369890477845377286825786597995065267843507802259844752423265555266243395693314487288078622041297083410934849005783165970061493954764862875292520625075354020623135144404282468700013671547
Q = 32529357728359709453365474160291021826172449325283032525960140927071310619825387623782332090039371530871980224347098663810974613402304404266497917407474864297156284256946860249052832230096858650939753744142835498176687871759957106047643
R = 16206270872089192092998216836860334311770377554683742084396230133884461910427730822382339736502736929792815620509052506437787448046491612467939230578155420781034768383697968919865679643483735420781811716916744235505311817364728450487227

#n = P*Q*R
n = 4105533452433021765013837810250129497906776520439478762444757535787150166441808983131547549061042819241803677734684089387936615218512623246819260378395895806190969367738434747226833314918439125195560533069527626034315805252829069712946717489170662443699387652432168043013276992556383661463839991504034566570743939526431419627777024666651653328024067508705964704654262029757671485757644661464119465787544601305942781717049259725682702584999377699680541279376069665052863573819455144839742780771486371611198963915144096863366918510828427548413169928095028815881158255689153934576571383186599819333534474537810962373427256838686635300737173281200715737713827696261842481543872169300021385829998511793741667

x = crt([0,0,1],[P,Q,R])
#1531366200181857377711253471591901546605387878653347058218367918899607190461573193774806122702526922633475207812515387104410225065505299232264145284679931809647250559662552357937882813391742908457819909520410271610494598873622898582376109230868035330321739604649879686263758296364934129304872988377327320975681824510252422811945467449312545894524670151200577073909395338222544281619489797225047347782037973971554822098059238445615511329358004762839987872520530558881754025345678961468010903818101033188743863511475142658563659779475437162133039943938077147640118449176233077231755249379875098399536038795818364620535923984824500857838465466897011059723446084866589594699779679030111619356355660402100543

asset x^2%n == x 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值