华为杯2023 Crypto

next-prime

题目描述:

from Crypto.Util.number import *
from gmpy2 import next_prime, iroot
from flag import flag
assert flag[0:4]==b'flag'

m = bytes_to_long(flag)
assert size(m)<500

p = getPrime(512)
q = next_prime(p)

n = p * q
print('n=', n>>520)
e = 0x10001
c = pow(m, e, n)
print('c=', c)

n= 28576274811010794362153160897556935178530640825011441539841241257190782139295561904323347128956873569754645071205043238985141474388531008367238218822591
c= 49502875285578675438052554215266678403659290915102322948363030271494959804587081871467110614683972929037615883922743651431683465100061968204901334627149795829429950385848753728500177164800064208215503246868631076011505268371936586645321659884527055007299822625570713613996139223348709621258028349513737798120

题目分析:
对n开方可得到p的高位,之后爆破得完整p
其实提示的很明显,size(m)<500<512说明只需求出p即可得到flag
高位攻击啥的也不用想,n都不知道,只能往简单点想

from Crypto.Util.number import *
from gmpy2 import *
e = 0x10001
n= 28576274811010794362153160897556935178530640825011441539841241257190782139295561904323347128956873569754645071205043238985141474388531008367238218822591
c= 49502875285578675438052554215266678403659290915102322948363030271494959804587081871467110614683972929037615883922743651431683465100061968204901334627149795829429950385848753728500177164800064208215503246868631076011505268371936586645321659884527055007299822625570713613996139223348709621258028349513737798120
n_high = n << 520
p_high = iroot(n_high,2)[0]
for i in range(1000):
    p= p_high + i
    phi = (p - 1)
    d = invert(e, phi)
    flag=long_to_bytes(pow(c,d,p))
    if b'flag' in flag:
      print(flag)
      break
# flag{90b344ca-867d-002e-915c-4a897faf0bbe}

StepBystep

题目描述:

from Crypto.Util.number import *
import random
import base64
import os

flag = b'xxx'
flag = base64.b64encode(flag)
part_size = len(flag)//3
part1 = flag[:part_size]
part2 = flag[part_size:2 * part_size]
part3 = flag[2 * part_size:]

def gen_keys(bits):
    while True:
        p = getPrime(bits)
        q = getPrime(bits)
        n = p * q
        k = random.randint(1, n)

        if pow(q ** 2 * k, (p - 1) // 2, p) + pow(p ** 2 * k, (q - 1) // 2, q) == p + q - 2:
            break
        return p, q, n, k

def bit_enc(m,n):
    enc = []
    m = bin(bytes_to_long(m))[2:]
    for x in m:
        while True:
            r = random.randint(1, n)
            if GCD(r, k) == 1:
                enc.append((k ** int(x) * r ** 2) % n)
                break
    return enc
if __name__ == '__main__':
    f = open('stepbystep.txt','w')
    f.write(f'part_size = {part_size}\n') # 32 
    p, q, n, k = gen_keys(1024)
    y = p ^ (bytes_to_long(part1) << 0x100)
    f.write(f'n = {n}\n')
    f.write(f'y = {y}\n')

    enc1 = bit_enc(part2,n)
    f.write(f'enc1 = {enc1}\n')

    e = 3
    r = getPrime(1024)
    m = bytes_to_long(part3 + os.urandom(128))
    M = m % r
    f.write(f'r = {r}\n')
    f.write(f'M = {M}\n')
    enc2 = pow(m, e, n)
    f.write(f'enc2 = {enc2}\n')

题目分析:
每个part长度为32
part1:
bytes_to_long(part1)为32 * 8 = 256bit,故通过y可以得到p的高512位和低256位,高位攻击得完整p,之后和y对应位异或即可得到part1

n = 28170615394813994128829444311922294768047711140557403853945487107089509134737767942656361548230221829951460844271639575394883663959935517401286468610803533794281322932272016151070004303694003064267127894498582758474889399072435632149814773794006700377495059535551601562497404157228212268055301420382159240876926718366155907065534007141933306379265213201601388196980959461243178574169497366868119687674891554278269534759630337252574501733517956676373291013127781435489132751470026441547613876124640645867844110455115246109355929378020562498143759261535211073747144498765678339037517382156239422037334011910477313579931
y = 159220553107177193400023378107257900962478698517628796905876914005522464561698874246291191230998317763194901894991385290134307075261722076436131488895536910117912445041560442858379826501161163372467275224931272546634069502187842680578039864204499755434023932362315829340273816781101788682876410192428287564079
p_ = ((y >> 512) << 512) + (y & (2 ^ 256 - 1))
PR.<x> = PolynomialRing(Zmod(n))
f = p_ + (x * 2 ** 256)
f = f.monic()
p0 = f.small_roots(2 ^ 256,0.4)
print(p_ + p0[0] * 2 ** 256)

p = 159220553107177193400023378107257900962478698517628796905876914005522464561698874246291191230998317763194901894991385290134307075261722076436131488895536914601862873373189346623370102585921882621795643375613240095719969918295959844527488766867194677670844144950629130942802580496956921624007957883073349196079
part1 = ((y // 2 ** 256) % 2 ** 256) ^^ ((p // 2 ** 256) % 2 ** 256)
print(long_to_bytes(part1))
# ZmxhZ3s4ZjYzNzc3MTMyZWRkNmJlNmM2

part2:

二次剩余,又是二次剩余

二次剩余知识导入:

  • 设m是正整数,若同余式:
    x 2 ≡ a m o d    p a ≡ x 2 m o d    p 其中 , ( a , p ) = 1 x^{2} \equiv a \mod p \\ a \equiv x^{2} \mod p \\ \\ 其中,(a,p) = 1 x2amodpax2modp其中,(a,p)=1
    有解,则a叫做模p的二次剩余(或平方剩余),否则为二次非剩余(平方非剩余)
  • 勒让得符号之运算性质(欧拉判别法则):
    ( a p ) = a p − 1 2 = { 1 , a 是模 p 的二次剩余, x 有解 − 1 , a 是模 p 的二次非剩余, x 无解 0 , p ∣ a ( a 比 p 大 ) \left ( \frac{a}{p} \right ) = a^{\frac{p-1}{2}} = \begin{cases} \quad1,a是模p的二次剩余,x有解\\ -1,a是模p的二次非剩余,x无解\\ \quad0,p|a (a比p大) \end{cases} (pa)=a2p1= 1a是模p的二次剩余,x有解1a是模p的二次非剩余,x无解0pa(ap)

pow(p ** 2 * k, (p - 1) // 2, p) + pow(q ** 2 * k, (q - 1) // 2, q) == p + q - 2
从这串中可以得出:
( k ∗ q 2 ) p − 1 2 % p + ( k ∗ p 2 ) q − 1 2 % q = p + q − 2 ⇒ (费马小定理) ( k ∗ q 2 ) p − 1 2 ≡ k p − 1 2 ≡ − 1 m o d    p = p − 1 ( k ∗ p 2 ) q − 1 2 ≡ k q − 1 2 ≡ − 1 m o d    q = q − 1 故 k 是模 p 和 q 的二次剩余 (k * q^{2}) ^ {\frac{p - 1}{2}} \%p + (k * p^{2}) ^ {\frac{q - 1}{2}}\%q= p + q - 2\\ \Rightarrow (费马小定理)\\ (k * q^{2}) ^ {\frac{p - 1}{2}} \equiv k ^ {\frac{p-1}{2}} \equiv -1 \mod p = p - 1\\ (k * p^{2}) ^ {\frac{q - 1}{2}} \equiv k ^ {\frac{q-1}{2}} \equiv -1 \mod q = q - 1\\ 故k是模p和q的二次剩余\\ (kq2)2p1%p+(kp2)2q1%q=p+q2(费马小定理)(kq2)2p1k2p11modp=p1(kp2)2q1k2q11modq=q1k是模pq的二次剩余
enc.append((k ** int(x) * r ** 2) % n)
从这串我们可以得出:
k x ∗ r 2 = { k ∗ r 2 ,   x = 1        r 2 ,   x = 0 ⇒ (费马小定理) ( k ∗ r 2 ) p − 1 2 ≡ − 1 m o d    p      ( r 2 ) p − 1 2 ≡      1 m o d    p 若 p o w ( i , ( p − 1 ) / / 2 , p ) = − 1 , 则 x = 1 若 p o w ( i , ( p − 1 ) / / 2 , p ) =     1 , 则 x = 0 其中 i 为 e n c 1 中的元素,由此可得到 p a r t 2 k^x * r^2 = \begin{cases} k * r^2 , \ x = 1\\ \ \ \ \ \ \ r^2, \ x = 0 \end{cases}\\ \Rightarrow(费马小定理)\\ (k * r^2)^\frac{p-1}{2} \equiv -1 \mod p\\ \ \ \ \ (r^2)^\frac{p-1}{2} \equiv \ \ \ \ 1 \mod p\\ 若pow(i,(p-1)//2,p) = -1,则x = 1\\ 若pow(i,(p-1)//2,p) = \ \ \ 1, 则x = 0\\\\ 其中i为enc1中的元素,由此可得到part2 kxr2={kr2, x=1      r2, x=0(费马小定理)(kr2)2p11modp    (r2)2p1    1modppow(i,(p1)//2,p)=1,x=1pow(i,(p1)//2,p)=   1,x=0其中ienc1中的元素,由此可得到part2

with open('stepbystep.txt') as f:
    exec(f.read())
part2 = ''
for i in enc1:
    if pow(i,(p-1)//2,p) == 1:
        part2 += '0'
    else:
        part2 += '1'
part2 = int(part2,2)
print(long_to_bytes(part2))
# Yjg4NGQyMTEyMjI5YWI2OWEwYjNkOTgz

part3:
普普通通的RSA求解,p,q,n第一部分已求出

enc2 = 3308959603655783037626701651449897306691054491531277011007239956503226856416291050447522118208505181328835654445067869891209022570372449235908057926971720202776819863989791528582562270047426782371075056370483172178853004208276586218043687123444373535519007051969960162889822736022485151723842157816601130568321361182105375979062860034971261145125640309251608819317332361754077108485716298746870151841624987872455719817354570871513465617937552265477428311051737704837212011740627884815342732601532604444076319807464313504969470749988374578293008881546119868431456512406342629832650219747911308702089872865087547016692
phi = (p - 1) * (q - 1)
d = invert(3,phi)
print(long_to_bytes(pow(enc2,d,n)))
# Yzc4NDZjYWMwYjA2NjQ2OGJhNmIwfQ==

唉,这M,r也没用到啊,出题人应该是想要我们用下面这种解法。这种解法挺不错,不过我并有没想到。

m = M + k ∗ r e n c 2 ≡ ( M + k ∗ r ) 3 m o d    n 一个未知数,解方程 m = M + k * r\\ enc2 \equiv (M + k * r) ^ 3 \mod n\\ 一个未知数,解方程\\ m=M+krenc2(M+kr)3modn一个未知数,解方程

from Crypto.Util.number import *
r = 93549208091522943157523889855147971756339952408014900465237079549147321642184605352106076825075471647946551417457797445371594401297547607606554921501239662351004696055416766322360074764155873771691904894277891317076059387107629281872952247903070858893854519963946004933092428964179404235822710307390762443997
n = 28170615394813994128829444311922294768047711140557403853945487107089509134737767942656361548230221829951460844271639575394883663959935517401286468610803533794281322932272016151070004303694003064267127894498582758474889399072435632149814773794006700377495059535551601562497404157228212268055301420382159240876926718366155907065534007141933306379265213201601388196980959461243178574169497366868119687674891554278269534759630337252574501733517956676373291013127781435489132751470026441547613876124640645867844110455115246109355929378020562498143759261535211073747144498765678339037517382156239422037334011910477313579931
M = 41114562773057591509703798019081726715938195388792207483716010974789826658609043370184495922299754629166062481798971234172682345725464787827933687171665115253731281252387251668805119315677910253579731618701676377111463086179935234982471957816896707984220800374578967269143044956222576420557100741943571288295
enc2 = 3308959603655783037626701651449897306691054491531277011007239956503226856416291050447522118208505181328835654445067869891209022570372449235908057926971720202776819863989791528582562270047426782371075056370483172178853004208276586218043687123444373535519007051969960162889822736022485151723842157816601130568321361182105375979062860034971261145125640309251608819317332361754077108485716298746870151841624987872455719817354570871513465617937552265477428311051737704837212011740627884815342732601532604444076319807464313504969470749988374578293008881546119868431456512406342629832650219747911308702089872865087547016692
PR.<k> = PolynomialRing(Zmod(n))
f = (M + k * r) ** 3 - enc2
f = f.monic()
kk = f.small_roots(2 ** 256)[0]
m = M + kk * r
print(long_to_bytes(int(m)))
# Yzc4NDZjYWMwYjA2NjQ2OGJhNmIwfQ==

之后每一部分拼接base64解密得到flag
flag{8f63777132edd6be6c6b884d2112229ab69a0b3d983c7846cac0b066468ba6b0}

LCG

题目来源:https://blog.csdn.net/weixin_52640415/article/details/133466943?spm=1001.2014.3001.5502
(跟着别人博客学习是一种很好的提升方法,目前来说通常没有比赛我都是刷别人博客做题)
题目描述:

from Crypto.Util.number import*
from secret import flag
 
assert(flag[0:5]==b"flag{" )
assert(flag[-1:]==b"}" )
flag = flag[7:-1]
print(len(flag))
 
 
class LCG:
    def __init__(self):
        self.a = getRandomNBitInteger(32)
        self.b = getRandomNBitInteger(32)
        self.c = getRandomNBitInteger(32)
        self.m = getPrime(32)
        self.seed1 = getRandomNBitInteger(32)
        self.seed2 = getRandomNBitInteger(32)
 
    def next(self):
        tmp = self.seed2
        self.seed2 = (self.a*self.seed1+self.b*self.seed2 +self.c) % self.m
        self.seed1 = tmp
        #return self.seed2 >> 16
        return self.seed2   #从给出的T来看,是32位的
 
    def output(self):
        print("m = {}".format(self.m))
        print("self.seed1= {}".format(self.seed1))
        print("self.seed2= {}".format(self.seed2))
 
L = LCG()
T = [0]
 
for k in range(random(10)):
    T.append(L.next())
 
L.output()

enc = []
for i in range(9):
    tmp = bytes_to_long(flag[i*4:i*4+4])
    tt = L.next()
    T.append(tt)
    enc.append(tmp^(tt>>16))

print('T = ',T[:6])
print('enc = ',enc)
print("Have a good time!")
 
'''
m = 3533156827
self.seed1 = 2970464585
self.seed2 = 3350124366
enc = [909619317, 912378641, 761422938, 841844503, 1701111746, 1701194992, 959752815, 892545567, 1667598316]
T = [0,3180180532,86337434,1850726346,2970464585,3350124366]
'''

题目分析:
二元LCG

import gmpy2
from Crypto.Util.number import *
m = 3533156827
enc = [909619317, 912378641, 761422938, 841844503, 1701111746, 1701194992, 959752815, 892545567, 1667598316]
T = [0,3180180532,86337434,1850726346,2970464585,3350124366]

data = T[1:]
n = m

tmp = [0]
for i in range(1,5):
    tmp.append(data[i] - data[i-1])

b = ((tmp[3] * tmp[2] - tmp[4] * tmp[1]) * gmpy2.invert(tmp[2] * tmp[2] - tmp[3] * tmp[1],n)) % n
a = ((tmp[3] - b * tmp[2]) * gmpy2.invert(tmp[1],n)) % n
c = (data[2] - b * data[1] - a * data[0]) % n
print(a)
print(b)
print(c)
# b = 2578155191
# a = 3222280379
# c = 3272724155

未知数都出来了,那之后就是简单的异或了

b = 2578155191
a = 3222280379
c = 3272724155
seed1 = 2970464585
seed2 = 3350124366
m = b''
for i in range(9):
    tmp = seed2
    seed2 = (a * seed1 + b * seed2 + c) % n
    seed1 = tmp
    m += long_to_bytes(enc[i] ^ (seed2 >> 16))
print(m)
# flag{67456ac9-b362-11ed-aef7-94085339ce84}

记录佬求未知数的方法:
groebner_basis()第二次遇到,下次多个式子解方程用它

m = 3533156827
seed1 = 2970464585
seed2 = 3350124366
enc = [909619317, 912378641, 761422938, 841844503, 1701111746, 1701194992, 959752815, 892545567, 1667598316]
T = [0,3180180532,86337434,1850726346,2970464585,3350124366]
T = T[1:]

P.<a,b,c,s1,s2> = PolynomialRing(Zmod(m))
G = []
for i in range(len(T)):
    s1,s2 = s2,a * s1 + b * s2 + c
    G.append(s2 - T[i])

B = Ideal(G).groebner_basis()
print(B)

res = [x.constant_coefficient() for x in B]
print(res)

a = -res[0] % m
b = -res[1] % m
c = -res[2] % m

print(a)
print(b)
print(c)

浅记一下:
考点:高位,二次剩余,解方程,二元LCG,groebner_basis()求参
二次剩余,第二次遇到,更熟悉了,期待再一次和它相遇

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值