TSCTF-J2024 密码向WP(5/8)

ezRSA

part 1

#part1
p = getPrime(512)
q = getPrime(512)
n = p * q
phi = (p-1) * (q-1)
d = getPrime(250)
e = inverse(d, phi)
c = pow(bytes_to_long(parts[0]), e, n)
print(f'n = {n}')
print(f'e = {e}')
print(f'c = {c}')

# n = 124695452995031270645183837267530422032624508784074534979189655228220709056309638648794696369835930482818980808128467814617220810217534821336503942838791498456112882717378013827550680516551959078234339477401303759763506487676709408813412867364414651706461400252466842182365340612883444737530603407481042520627
# e = 38587713366842463962841747677614707312145479235042165803103947138237921458583509748629042842505955223421671992698976799249425980974893271454942323501453571320592126625468760278770909411858284131095166550317734018153286242950401311537208914820833671566620645882329390747892971373265592565291598852744678229891
# c = 75650426098322108299742769179799276679353245586432433359812352366165787480762021753671012472067826915659840541954505167382050569833945309043431554352347482992118091659840982421987700648585142917347916194810259117115753813661282178558428786374835684668840019166776178494086580424196110694158066034697988927644

容易发现

\[\frac{1}{3}n^{\frac{1}{4}} > d \]

\[\min(p, q) < \max(p, q) < 2\min(p, q) \]

因此采用 \(\text{Wiener's Attack}\)

part 2

#part2
p = getPrime(80)
q = getPrime(80)
n = p * q
e = 2
c = pow(bytes_to_long(parts[1]), e, n)
print(f'n = {n}')
print(f'c = {c}')
print(f'p,q = {p,q}')

# n = 649329048322675374317593820985753646586809799201
# c = 283668420132846011615755415362202578109404366325
# p,q = (647260709632957671018359, 1003195526406802286444839)

发现 \(e = 2\)\(p, q\) 较小,采用 \(\text{Rabin}\) 算法。

part 3


#part3
p = getPrime(512)
q = getPrime(512)
n = p * q
e = 65537
c = pow(bytes_to_long(parts[2]), e, n)
print(f'n = {n}')
print(f'c = {c}')
print(f'p^q = {p^q}')

# n = 85836893651204560884454211125508692415276042143801480450535044733242333318334455339451808653755272841343378345709375676280488068099971805717946097641116078266013229365158341178806480395974457905053516603153056936745020102883744430977333247681929718298626185526512756702624513738698825219177049538628421559753
# c = 75578834548096799626696300881096262997184146142305165096930004492293642496308047534319034721187289042138332386120962890635270628767192988167590315544321566944902760623837249074921079890026503778053466720161607743010204269544291059577848021218234039433294700788160167294093796603060247381385159054508170520277
# p^q = 5068548570505625142069285468439450186210992627026138517591800598908379828953823253346928574075223127205617451260708785889033493211347183583859669806824864

\(p \oplus q\) 已知,考虑从低到高搜索 \(p, q\) 的每一位,对于当前为 \(i\) 满足异或后第 \(i\) 位与 \(p \oplus q\) 相等,且 \(p \times q \le n\),实现剪枝。

part 4

#part4
p = getPrime(512)
q = getPrime(512)
n = p * q
e = 3
m = bytes_to_long(os.urandom(32)+parts[3])
c = pow(m, e, n)
print(f'n = {n}')
print(f'c = {c}')
print(f'mleak = {m>>128}')

# n = 136909292741753142871542219643510188168311518562065789353531367466023357011784735447560466352669948897633633920102617017949126915203615330337196942328793619163571419292670395849251337340787480297972818664454785647844715189207055430597030032696044932960884594660634280475822683286430432169515120767378120972393
# c = 14763067643592454478187771324072634160297758439803081056226124797870386993054732731754324146768654813122274647054179017048620683751626439261028839186682159969656271385676822426489416369402998625865982105676257668946313225156476831065481561281126267398712822218497384996243578386630794573662378684549962709522
# mleak = 287621732882458207416007037901690948810437972193283953524078189541847647258

明文 \(m\) 的高 \(\text{bit}\) 位已知,低 \(128\) 位未知,根据 \(\text{Coppersmith}\) 定理可求。

ezNumberTheory

part 1

p = getPrime(512)
q = getPrime(512)
n = p * q
e = 65537
gift = pow(p+q,q,n)-p
m = bytes_to_long(flag[:part_len])
c = pow(m,e,n)
print("n =",n)
print("gift =",gift)
print("c =",c)

已知

\[(p+q)^q - p \equiv gift (\text{mod } n) \]

\[n=pq \]

考虑二项式展开得到

\[\sum_{i=0}^{q} \binom{q}{i} {p}^{i} {q}^{q-i} - p \equiv gift (\bmod pq) \]

化简得

\[p^q + q^q - p \equiv gift (\text{mod } pq) \]

不妨考虑拆分模数 \(pq\),得到两个式子。

\[q^q \equiv gift (\text{mod } p) \]

\[p^q - p \equiv gift (\text{mod } q) \]

由于 \(p, q\) 为质数,由费马小定理可得

\[q^q\equiv gift (\text{mod } p) \]

\[0 \equiv gift (\text{mod } q) \]

\(gift\) 因式分解得到 \(q\),进而 \(p = \frac{n}{q}\)

已知 \(e,p,q\),可求出密文 \(m\)

part 2

p = getPrime(512)
q = getPrime(512)
n = p * q
e = 65537
m = bytes_to_long(flag[part_len:2*part_len])
gift = pow(n-1,m,n**2)
c = pow(m,e,n)
print("n =",n)
print("gift =",gift)
print("c =",c)

# n = 101825028937892274755066568298675753051915211984336844010001051946487425488926444636462308770518438066690807207349397829434761111870976777194195377994066094978667372161712559828354279213717904934626695765400184018995342438568172381388749973725572499090794995578069624189552411881722996041605959223833931977161
# gift = 10368336518202599155478611962986419931032852827247241544520944151147424100095888793813818133261771295138537638975002709013348196763698154979511238402220870231916534915445557931918512473116396709289462544729254616655147690181935999744990349056920271954901882532521970967307761055252723712876050094383313331140406445004997917905258680326374893836686879404331037481540004243801858344196072242572121743245711481444665694028492376994909442739868671793792049626000877294534619734785195159330183197692177383792865340479971388526007871832995878259364333594949083550807440342049213394072558604884076160696389453987350453566282
# c = 6206002756473719752481539026703107851866702754011241324450943403363532188346664095512595594879972008741307201868011790850666619709952602312346601585431717191460826191100496612662950423560204126590072745286069584415447406165328489464592884522745095517397118876988652114937810284710552486235169949976593170616

已知

\[(n-1)^m \equiv gift (\text{mod }n^2) \]

不妨先考虑

\[(n-1)^m \equiv gift (\text{mod }n) \]

同上,有

\[(-1)^m \equiv gift (\text{mod }n) \]

带入 \(gift, n\) 可得

\[1 \equiv gift (\text{mod }n) \]

于是 \(m\) 为偶数。

直接展开原式有

\[\sum_{i=0}^{m} \binom{m}{i} (-1)^{i} {n}^{m-i} \equiv gift (\text{mod }n^2) \]

化简得

\[(-1)^{m-1}nm + (-1)^m \equiv gift (\text{mod }n^2) \]

\[1 - nm \equiv gift (\text{mod }n^2) \]

求得

\[nm \equiv n^2 - gift + 1 (\text{mod }n^2) \]

根据第一部分 \(m\) 的大小推断 \(m,n\) 大小接近,试除后发现 \(m = \frac{n^2 - gift + 1}{n}\) 恰为整数,故 \(m\) 为所求值。

part 3

p = getPrime(512)
q = next_prime(p)
r = getPrime(512)
n = p * q
e = 65537
m = bytes_to_long(flag[2*part_len:])
gift = (pow(1+m*n,1,n**2)*pow(r,n,n**2))%(n**2)
print("n =",n)
print("gift =",gift)
print("p =",p)
print("q =",q)

# n = 120085278656547434097495160698983440086977117014813250068332400723955573086081539793659925690983763612644098938270646361630753175121916904514302168476109276991065963359696653413330575343172587757112308794397643577457876878076258704713342584783867357600427211623403743168388707834963362860793081706620920977197
# gift = 755365096368274234067764643236574524026724653923904235465391689910808113641191521577417355583242717443863451427293486428108305560959934240539916731737071269870765365812729624798503913486014927177398814766178335408644263304959990001131184599054858122656321624816269434361664736002888774514548687701448375859895656340630928111228145226768972365422917506757004452665088879334351527814068349598913876771933729841797767944898598495453091334481416909244894797229851139025287531257727280929467263018600829848751441007477417666581057493947789435287529403836954674188989033519154270698877075630430517874818318137411379414838

已知

\[(nm + 1)r^n \equiv gift (\text{mod } n^2) \]

考虑

\[(nm + 1)r^n \equiv gift (\text{mod } n) \]

\[r^n \equiv gift (\text{mod } n) \]

再考虑将 \(n\) 分解有

\[r^n \equiv gift (\text{mod } p) \]

\[r^n \equiv gift (\text{mod } q) \]

由费马小定理有

\[( r^{q} )^{p} \equiv gift (\bmod p) \]

\[r^q \equiv gift (\text{mod } p) \]

考虑欧拉定理 \(a^n \equiv a^{n \text{ mod } \varphi(p)} (\text{mod p})\)

\[r^{q \bmod \varphi(p)} \equiv gift (\bmod p) \]

\[r^{q \bmod (p - 1)} \equiv gift (\bmod p) \]

\(q^{-1}\)\(q\)\(p - 1\) 的逆元,可知

\[( {r} ^ {q \bmod (p - 1)} ) ^ {q ^ {-1}} \equiv gift ^ {q ^ {-1}} (\text{mod } p) \]

\[{r} ^ {q {q} ^ {-1} \bmod (p - 1)} \equiv gift ^ {q ^ {-1}} (\text{mod } p) \]

\[r \equiv {gift} ^ {q ^ {-1}} (\text{mod } p) \]

由于 \(r\)\(p\) 大小相近,可解出唯一 \(r\)

回到原式,记 $ ( r^n ) ^ {-1} $ 为 \(r^n\)\(n^2\) 的逆元,有

\[nm + 1 \equiv gift \times (r ^ n) ^ {-1} (\bmod n ^ 2 ) \]

\[nm \equiv gift \times (r ^ n) ^ {-1} - 1 (\text{mod } n ^ 2 ) \]

依旧有 $ m = \frac{(gift \times (r ^ n) ^ {-1} - 1) \text{ mod } n ^ 2 }{n} $,此题完。

ezPwntools

挺过了前面的分P狂魔,单题可以开始轻松了不少。

import random
from Crypto.Util.number import *
from typing import Callable
with open("flag.txt", "r") as f:
    flag = f.read().strip()

length = 128

class LCG:
    def __init__(self, length=128):
        self.length = length
        self.setparam()

    def setparam(self):
        self.m = getPrime(length+1)
        self.a = getPrime(length)
        self.b = getPrime(length)
        self.seed = random.randint(0, self.m - 1)
        self.begin = self.seed

    def generate(self):
        for i in range(10):
            self.seed = (self.a * self.seed + self.b) % self.m
        seed_list = []
        for i in range(5):
            self.seed = (self.a * self.seed + self.b) % self.m
            seed_list.append(self.seed)
        return seed_list

    def __call__(self):
        return self.begin

def challenge(input:Callable[[str],None], print:Callable[[str],None]):
    print("Welcome to TSCTF-J! If you can recover the seed, you will get the flag!")
    for i in range(512):
        lcg = LCG()
        print("Here is your gift:{}".format(lcg.generate()))
        i = input(f"Give me the seed: ")
        if int(i) != lcg():
            print(f"Oh, you are wrong!")
            return
    print(f"Congurations!{flag}")

观察到 generate 函数每次做 \(15\) 次线性同余变换并返回最后 \(5\) 次结果,可以列出方程。

\[x_{i+1} \equiv ax_i + b (\text{mod } m)(i = 1,2,3,4) \]

考虑相邻两项相减消去 \(b\)

\[x_5 - x_4 \equiv a(x_4 - x_3)(\text{mod } m) \]

\[x_4 - x_3 \equiv a(x_3 - x_2)(\text{mod } m) \]

则对于 \(a\)

\[a \equiv \frac{x_5 - x_4}{x_4 - x_3} \equiv \frac{x_4 - x_3}{x_3 - x_2}(\text{mod } m) \]

交叉相乘可得

\[(x_5 - x_4)(x_3 - x_2) \equiv (x_4 - x_3)^2 (\text{mod } m) \]

不难发现

\[m \mid (x_5 - x_4)(x_3 - x_2) - (x_4 - x_3)^2 \]

取出上式的最大质因子即可得到 \(m\),接着易于得到 \(a, b\) 的值。

然后解 \(10\) 次一元线性同余方程

\[x_{i-1} \equiv (x_i - b)a^{-1}(\text{mod } m) \]

对于 \(512\) 次操作,求出 \(m, a, b\),并解出其初值,用 pwntools 与交互库交互即可。

如下是一份代码

from pwn import *
from gmpy2 import *
from sympy import *
con = remote('challenges.hazmat.buptmerak.cn', 21747)
t = con.recvline()

for _ in range(512):
    t = con.recvline()
    t = t[19:len(t)-1]
    ls = t.split()
    xi = []
    for i in ls:
        xi.append(int(i[:len(i)-1]))
    x1, x2, x3, x4, x5 = xi
    m = gmpy2.gcd((x5-x4)*(x3-x2)-(x4-x3)**2, (x4-x3)*(x2-x1)-(x3-x2)**2)
    while is_prime(m) == False:
        div = primefactors(m)
        m //= div[0]
    a = (x5 - x4) * gmpy2.invert(x4 - x3, m) % m
    b = (x5 - a * x4) % m
    x = x1
    for i in range (10, -1, -1):
        x = (x - b) * gmpy2.invert(a, m) % m   
    t = con.recv()
    print(_ + 1)
    con.sendline(str(x))
t = con.recv()
print(t)

ezECC

#sage
from Crypto.Util.number import *
from sage.all import *
msg = b'???'
flag = b'TSCTF-J{'+msg+b'}'
m = bytes_to_long(flag)
(p,a,b) = (getPrime(len(bin(bytes_to_long(flag)))) for i in range(3))
E = EllipticCurve(GF(p),[a,b])
for i in range(4):
    print(E.random_point())
e = 114514
K = E.lift_x(m+1)
eK = e*K
print(f"eK={eK}")

# (121542556343240612458886464797113519174471159973947349739843570016310276547868092596053087152046638137671892191449161589255393370014868931365353180578227117593735, 98840327394835055943257602264613388284774639725847294267402852043945104193135363216749971927532657727021952911622427228151863672295675502233797082353097959401869)
(99873546068894326234875062311058443230105529641172112880280159410919160848811689840010700811925066814781044568587853533466146854172381759747204462070834544581290, 54021704113721739503680080012966661376438637715420865450114718929076757346120207862947548620569435334895396324086781218017455445315421875871714429627637052871264)
(157617948261622464254152314994703598559915540865514420750304511730242939622181332932412360735409968696715108030369878546783001121709970056186086411214753185288604, 172554749510163658517336910991986476106096485848063383199885085041705448141288872247307505628175707155625972718684398442883637022240936050198926361252214588735919 )
(35601692031837010013294196210817440996871532774395053520652383520080490377765688796593595849840043210266473831404618732851470643297944496494254634489110294026906, 83109137089012676168973029782730032714034653700896174331183408691033951856596721848482516578501834370646437639802694578133409539742068584938899820955422945845189 )
# eK=(148943471114336569351357302344843611917995236697008317506292328720097140911033713257363114040728547610446354966023690433690295644571622343830380563979394775174929, 5570936030363753742907439216925560949272269404612411400083981446597152991432704283038752432067099389895408182978262946903003923985122728700021027170168725851130 )

构式题,椭圆曲线密码,做法与 ezNumberTheoryezPwntools 完全一样。

给定域 \(GF(p)\),椭圆曲线 \(E_p: y^2 = x^3 + ax + b\) 上任意 \(4\)\(A_1,A_2,A_3,A_4\),与 \(e\) 倍基点 \(K(m+1, y_{lift\_x})\)\(eK\) 的坐标,求 \(m\)

对于点 \(A_i(x_i, y_i)\)

\[y_i^2 \equiv x_i^3 + ax_i + b (\text{mod } p) \]

一样是先消去 \(b\),用 \(a\) 联立两个方程得到 \(p\),然后解出 \(a, b\)

利用 sage 自带的求椭圆曲线阶的函数 E.order(),求得阶为 \(P\),这个题就转化为了:

\(K\) 为椭圆曲线 \(E_p\) 上一点,已知 \(eK\)\(e\),求 \(K\)

求出 \(e\) 关于阶 \(P\) 的逆元即可。

ezFakekey

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from typing import Callable
with open("flag.txt", "r") as f:
    flag = f.read().strip()

def pad(data):
    pad_len = 16 - len(data) % 16
    return data + bytes([pad_len] * pad_len)

def unpad(data):
    pad_len = data[-1]
    return data[:-pad_len]

def generate_key():
    return get_random_bytes(32) 

def generate_iv():
    return get_random_bytes(16) 

def encrypt(message, key, iv):
    assert len(key) == 32
    assert len(iv) == 16
    cipher = AES.new(key, AES.MODE_CBC, iv)
    ciphertext = cipher.encrypt(pad(message))
    return ciphertext

def decrypt(ciphertext, key, iv):
    assert len(key) == 32
    assert len(iv) == 16
    cipher = AES.new(key, AES.MODE_CBC, iv)
    plaintext = unpad(cipher.decrypt(ciphertext))
    return plaintext

def challenge(input:Callable[[str],None], print:Callable[[str],None]):
    print("Welcome to TSCTF-J! If you are Administrator, I will give you a flag!")
    key = generate_key()
    iv = generate_iv()
    while True:
        print('[E]ncrypt the message')
        print('[D]ecrypt the message')
        print('[G]et the flag')
        print('[Q]uit')
        option = input('> ').upper()
        if option == 'E':
            input_message = input('Plz input your message: ')
            m = input_message.encode()
            if b'Administrator' in m:
                print('Permission denied!')
                return
            ciphertext = encrypt(m, key, iv)
            print(f'Your ciphertext: {ciphertext.hex()}')
        elif option == 'D':
            ciphertext = bytes.fromhex(input('Plz input the ciphertext: '))
            m = ciphertext
            plaintext = decrypt(m, key, iv)
            print(f'Your plaintext: {plaintext}')
        elif option == 'G':
            ciphertext = bytes.fromhex(input('Plz input the ciphertext: '))
            m = ciphertext
            plaintext = decrypt(m, key, iv)
            if b'Administrator' in plaintext:
                print(f'Congurations!Here is flag:{flag}')
                return
            else:
                print('Permission denied!')
        elif option == 'Q':
            return
        else:
            print('Unknown option:', option)

又是我们的交互题,这是一道 AES-CBC 的已知文本攻击题目,我们需要构造一条密文,使得密文经过解密后出现一条子串 Administrator

由于 CBC 加密只有相邻的两个块有异或关系,我们可以先通过加密得到含有 administrator 的循环密文,其循环节为 \(16\)CBC加密的块大小),找到对应字节所在块的位置,进行遍历搜索解密。

考虑 AES 编码只存在十六进制数,因此找到一个十六进制两位数对其做替换解密,直到出现子串 Administrator,即是我们所需要的密文,最后发现替换第 \(72,73\) 位可以做到。

代码如下(非预期解)

from pwn import *
from gmpy2 import *
from sympy import *
con = remote('challenges.hazmat.buptmerak.cn', 21815)
ls = [b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd', b'e', b'f']
t = con.recvline()
t = con.recvline()
t = con.recvline()
t = con.recvline()
t = con.recvline()
t = con.recv()
con.sendline("E")
t = con.recv()
t = b'administrator' + b'\x03' * 3 + b'administrator'+ b'\x03' * 3 + b'administrator'
con.sendline(str(t))
t = con.recv()
Administ = t[17:]
Administ = Administ[:len(Administ) - 1]
for i in ls:
    for j in ls:
        A = Administ
        ad = A[:72] + i + j + A[74:]
        t = con.recvline()
        t = con.recvline()
        t = con.recvline()
        t = con.recvline()
        t = con.recv()
        con.sendline("D")
        t = con.recv()
        c = str(ad, "utf-8")
        con.sendline(c)
        t = con.recvline()
        ans = t[20:]
        if (b'Administrator' in ans):
            t = con.recvline()
            t = con.recvline()
            t = con.recvline()
            t = con.recvline()
            t = con.recv()
            con.sendline("G")
            t = con.recv()
            con.sendline(str(c))
            t = con.recv()
            print(t)
            break

Conclusion

对于新生赛而言,题目难度是比较高的,\(8\) 题里面我只做了 \(5\) 题(在此期间和 ACM 队友去集训了一天,不然可能还能做一道题),甚至不乏有 ezECC 这种题目解法与其他题目完全重合的这种题,同时也见识到了各种密码题的解题思路,收益还是比较大的,虽然我想学密码学的原因有一部分是数学,但我希望不要所有的密码题都是数学,ezRSA 中对搜索的考察就是很不错的一点。

成就:在比赛期间快把 NanachiPYthok 烦死了(不会做题导致的),现在其实也是什么都不会呐,还是任重而道远啊。

posted @ 2024-09-25 21:33  YipChip  阅读(107)  评论(0)    收藏  举报