2023 CCTF --- writeup

文章提供了多个涉及加密算法的挑战,包括RSA、Easy、Suction、MediumInsights、Derik和Risk等题目,要求通过解析给定的代码和信息,使用数学方法(如素数分解、丢番图方程求解)和加密库来解密并获得隐藏的flag。解题过程涉及了对加密原理的深入理解和编程技巧。
摘要由CSDN通过智能技术生成


没打比赛,持续复现更新ing

Easy

blue office

题目:

#!/usr/bin/enc python3

import binascii
from secret import seed, flag

def gen_seed(s):
	i, j, k = 0, len(s), 0
	while i < j:
		k = k + ord(s[i])
		i += 1
	i = 0
	while i < j:
		if (i % 2) != 0:
			k = k - (ord(s[i]) * (j - i + 1))            
		else:
			k = k + (ord(s[i]) * (j - i + 1))
	
		k = k % 2147483647
		i += 1

	k = (k * j) % 2147483647
	return k

def reseed(s):
	return s * 214013 + 2531011

def encrypt(s, msg):
	assert s <= 2**32
	c, d = 0, s 
	enc, l = b'', len(msg)
	while c < l:
		d = reseed(d)
		enc += (msg[c] ^ ((d >> 16) & 0xff)).to_bytes(1, 'big')
		c += 1
	return enc

enc = encrypt(seed, flag)
print(f'enc = {binascii.hexlify(enc)}')
enc = b'b0cb631639f8a5ab20ff7385926383f89a71bbc4ed2d57142e05f39d434fce'

爆破seed即可

解题代码:

from tqdm import *
from binascii import *

def encrypt(s, msg):
	assert s <= 2**32
	c, d = 0, s
	enc, l = b'', len(msg)
	while c < l:
		d = reseed(d)
		enc += (msg[c] ^ ((d >> 16) & 0xff)).to_bytes(1, 'big')
		c += 1
	return enc
def reseed(s):
	return s * 214013 + 2531011

enc = b'b0cb631639f8a5ab20ff7385926383f89a71bbc4ed2d57142e05f39d434fce'
enc = unhexlify(enc)
for d in trange(2**32):
    if encrypt(d,b'CCTF')==enc[:4]:
        break
#d = 10364460
flag = b''
for i in range(len(enc)):
    d = reseed(d)
    flag += (enc[i]^((d>>16) & 0xff)).to_bytes(1,'big')
print(flag)

flag:

CCTF{__B4ck_0r!F1c3__C1pHeR_!!}
Suction

题目:

#!/usr/bin/env python3

from Crypto.Util.number import *
from flag import flag

def keygen(nbit, r):
	while True:
		p, q = [getPrime(nbit) for _ in '__']
		e, n = getPrime(16), p * q
		phi = (p - 1) * (q - 1)
		if GCD(e, phi) == 1:
			N = bin(n)[2:-r]
			E = bin(e)[2:-r]
			PKEY = N + E
			pkey = (n, e)
			return PKEY, pkey

def encrypt(msg, pkey, r):
	m = bytes_to_long(msg)
	n, e = pkey
	c = pow(m, e, n)
	C = bin(c)[2:-r]
	return C

r, nbit = 8, 128
PKEY, pkey = keygen(nbit, r)
print(f'PKEY = {int(PKEY, 2)}')
FLAG = flag.lstrip(b'CCTF{').rstrip(b'}')
enc = encrypt(FLAG, pkey, r)
print(f'enc = {int(enc, 2)}')

n、e、c的低8bit均未知,所以我们得先爆破n的低8bit,然后分解n得到p q。这题最麻烦的一点在于爆破n以及分解,刚开始想在sage中用factor()去分解,但是有点慢。接下来想在python中直接用命令调用本地的yafu去分解,但是测试了一下,对于分解的n不好获取数据。最后选择用factordb的api进行分解,对于无法分解的n它也会返回当前的n,对于可以分解的n我们只需要判断是否只存在两个因子,以及每个因子是否都是128bit的素数即可找到符合条件的n。
PS:由于flag的内容不是CCTF{}格式,所以在最后爆破ce的时候还需要判断m的字符串格式是否都是可打印字符
解题代码:

from Crypto.Util.number import *
import gmpy2
import requests
from tqdm import *
PKEY = 55208723145458976481271800608918815438075571763947979755496510859604544396672
ENC = 127194641882350916936065994389482700479720132804140137082316257506737630761
c_high = ENC<<8
n_high = (PKEY>>8)<<8
e_high = (PKEY & (2**8-1))<<8
def factor_n(n):
    url = "http://factordb.com/api?query={}".format(str(n))
    try:
        response = requests.get(url)
        fac = response.json()['factors']
        fac_data = [int(i[0]) for i in fac]
        return fac_data
    except:
        pass

p_q = []
for i in trange(2**8):
    n = n_high+i
    fac_data = factor_n(n)
    if len(fac_data)==2:
        if fac_data[0].bit_length()==128 and fac_data[1].bit_length()==128 and isPrime(fac_data[0]) and isPrime(fac_data[1]):
            p_q = fac_data
            break
p,q = int(fac_data[0]),int(fac_data[1])
n = p*q
phi = (p-1)*(q-1)
for i in range(2**8):
    for j in range(2**8):
        c = c_high + i
        e = e_high + j
        if isPrime(e):
            try:
                d = gmpy2.invert(e, phi)
                m = pow(c,d,n)
                flag = long_to_bytes(m)
                if all(k<=127 and k >=33 for k in flag):
                    print(b"CCTF{"+flag+b"}")
                    break
            except:
                pass

flag:

CCTF{6oRYGy&Dc$G2ZS}

Medium

Insights

题目:

#!/usr/bin/env sage

from Crypto.Util.number import *
from flag import flag

def getRandomNBits(n):
	nb = '1' + ''.join([str(randint(0, 1)) for _ in range(n - 1)])
	return nb

def getLeader(L, n):
	nb = L + getRandomNBits(n)
	return int(nb, 2)

def genPrime(L, nbit):
	l = len(L)
	assert nbit >= l
	while True:
		p = getLeader(L, nbit - l)
		if is_prime(p):
			return p

def genKey(L, nbit):
	p, q = [genPrime(L, nbit) for _ in '__']
	n = p * q
	d = next_prime(pow(n, 0.2919))
	phi = (p - 1) * (q - 1)
	e = inverse(d, phi)
	pubkey, privkey = (n, e), (p, q)
	return pubkey, privkey

def encrypt(msg, pubkey):
	n, e = pubkey
	m = bytes_to_long(msg)
	c = pow(m, e, n)
	return c

nbit = 1024
L = bin(bytes_to_long(b'Practical'))[2:]
pubkey, privkey = genKey(L, nbit)
p, q = privkey
c = encrypt(flag, pubkey)

print('Information:')
print('-' * 85)
print(f'n = {p * q}')
print(f'e = {pubkey[1]}')
print(f'c = {c}')
print(f'p = {bin(p)[2:len(L)]}...[REDACTED]')
print(f'q = {bin(q)[2:len(L)]}...[REDACTED]')
print('-' * 85)

关键代码d = next_prime(pow(n, 0.2919)),我们已知n,那么直接可以求出d,然后RSA解密即可得到flag

解题代码:

from Crypto.Util.number import *
import gmpy2

n = gmpy2.mpz(12765231982257032754070342601068819788671760506321816381988340379929052646067454855779362773785313297204165444163623633335057895252608396010414744222572161530653104640020689896882490979790275711854268113058363186249545193245142912930804650114934761299016468156185416083682476142929968501395899099376750415294540156026131156551291971922076435528869024742993840057342092865203064721826362149723366381892539617642364692012936270150691803063945919154346756726869466855557344213050973081755499746750276623648407677639812809665472258655462846021403503851719008687214848550916999977775070011121527941755954255781343103086789)
e = 459650454686946706615371845737527916539205656667844780634386049268800615782964920944229084502752167395446158290854047696006034750210758341744841762479191173017773034647739346927390580848998121830029134542880713409306092967282675122699586503684943407535067216738556403169403622104762516293879994387324370835718056251706150557820106296417750402984941838652433642298378976899556042987560946508887315484380807248331504458640857234708123277403252632993828101306072382329879857946191508782246793011691530554606521701055094223574951862129713872918021549814674387049788995785872980320871421550616327471735316980754238323013
c = 10992248752412909788626396175372747713079469256270100576886987393986576680666320383209810005318254336440105142571546847427454822405793626080251363454531982746373841267986148332456716023293306870382809568309620264499225135226626560298741596462262513921032733814032790312163314776421380481083058518893602887082464123177575742160690315666730642727773288362853901330620841098230284739614618790097180848133698381487679399364400048499041582830157094876815030301231505774900176910650887780842536610942820066913075027528705150102760422836458745949063992228680293226303245265232017738712226154128654682937687199768621565945171

d = gmpy2.next_prime(int(pow(n, 0.2919)))
m = pow(c,d,n)
flag = long_to_bytes(m)
print(flag)

flag:

CCTF{RSA_N3w_rEc0rd5_4Nd_nEw_!nSi9h75!}
Derik

题目:

#!/usr/bin/env python3

from Crypto.Util.number import *
from secret import C, e, d, p, q, r, flag

O = [1391526622949983, 2848691279889518, 89200900157319, 31337]

assert isPrime(e) and isPrime(d) and isPrime(p) and isPrime(q) and isPrime(r)
assert C[0] * p - C[1] * q >= 0
assert C[2] * q - C[3] * r >= 0
assert C[4] * r - C[5] * p >= 0
assert (C[0] * p - C[1] * q) ** e + (C[2] * q - C[3] * r) ** e + (C[4] * r - C[5] * p) ** e == d * (C[0] * p - C[1] * q) * (C[2] * q - C[3] * r) * (C[4] * r - C[5] * p)
assert C[6] * e - C[7] * d == O[3]

n = e * d * p * q * r
m = bytes_to_long(flag)
c = pow(m, 65537, n)
print(f'C = {C}')
print(f'c = {c}')
C = [5960650533801939766973431801711817334521794480800845853788489396583576739362531091881299990317357532712965991685855356736023156123272639095501827949743772, 6521307334196962312588683933194431457121496634106944587943458360009084052009954473233805656430247044180398241991916007097053259167347016989949709567530079, 1974144590530162761749719653512492399674271448426179161347522113979158665904709425021321314572814344781742306475435350045259668002944094011342611452228289, 2613994669316609213059728351496129310385706729636898358367479603483933513667486946164472738443484347294444234222189837370548518512002145671578950835894451, 8127380985210701021743355783483366664759506587061015828343032669060653534242331741280215982865084745259496501567264419306697788067646135512747952351628613, 5610271406291656026350079703507496574797593266125358942992954619413518379131260031910808827754539354830563482514244310277292686031300804846114623378588204, 10543, 4]
c = 80607532565510116966388633842290576008441185412513199071132245517888982730482694498575603226192340250444218146275844981580541820190393565327655055810841864715587561905777565790204415381897361016717820490400344469662479972681922265843907711283466105388820804099348169127917445858990935539611525002789966360469324052731259957798534960845391898385316664884009395500706952606508518095360995300436595374193777531503846662413864377535617876584843281151030183895735511854

C[6] * e - C[7] * d == O[3],即10543 * e - 4 * d = 31337,该方程为一个丢番图方程,我们可以先求e和d的通解,然后去爆破符合条件的e和d。求出的通解为e = -31337 - 4 * k,d = -82604332 - 10543 * k。
求出e和d之后,根据前三组条件,三个未知数,用z3约束器求解一下即可。

解题代码:

from Crypto.Util.number import *
import gmpy2
from z3 import *

O = [1391526622949983, 2848691279889518, 89200900157319, 31337]
C = [5960650533801939766973431801711817334521794480800845853788489396583576739362531091881299990317357532712965991685855356736023156123272639095501827949743772, 6521307334196962312588683933194431457121496634106944587943458360009084052009954473233805656430247044180398241991916007097053259167347016989949709567530079, 1974144590530162761749719653512492399674271448426179161347522113979158665904709425021321314572814344781742306475435350045259668002944094011342611452228289, 2613994669316609213059728351496129310385706729636898358367479603483933513667486946164472738443484347294444234222189837370548518512002145671578950835894451, 8127380985210701021743355783483366664759506587061015828343032669060653534242331741280215982865084745259496501567264419306697788067646135512747952351628613, 5610271406291656026350079703507496574797593266125358942992954619413518379131260031910808827754539354830563482514244310277292686031300804846114623378588204, 10543, 4]
c = 80607532565510116966388633842290576008441185412513199071132245517888982730482694498575603226192340250444218146275844981580541820190393565327655055810841864715587561905777565790204415381897361016717820490400344469662479972681922265843907711283466105388820804099348169127917445858990935539611525002789966360469324052731259957798534960845391898385316664884009395500706952606508518095360995300436595374193777531503846662413864377535617876584843281151030183895735511854
#10543 * e - 4 * d = 31337

# for k in range(-10000,1000):
#     e = -31337 - 4 * k
#     d = -82604332 - 10543 * k
#     if C[6] * e - C[7] * d == O[3] and isPrime(e) and isPrime(d):
#         print(e,d)
e = 3
d = 73

p, q, r = Ints('p q r')
s = Solver()
s.add(C[0] * p - C[1] * q == 1391526622949983)
s.add(C[2] * q - C[3] * r == 2848691279889518)
s.add(C[4] * r - C[5] * p == 89200900157319)

if s.check()==sat:
    result = s.model()
    p = result.eval(p).as_long()
    q = result.eval(q).as_long()
    r = result.eval(r).as_long()

n = p*q*r
phi = (p-1)*(q-1)*(r-1)
d1 = gmpy2.invert(65537,phi)
m = pow(c,d1,n)
flag = long_to_bytes(m)
print(flag)

flag:

CCTF{____Sylvester____tHE0r3m_Of_D3r!va7i0n!}
Risk

题目代码:

#!/usr/bin/env python3

from Crypto.Util.number import *
from secret import m, flag

def genPrime(m, nbit):
	assert m >= 2
	while True:
		a = getRandomNBitInteger(nbit // m)
		r = getRandomNBitInteger(m ** 2 - m + 2)
		p = a ** m + r
		if isPrime(p):
			return (p, r)

def genkey(m, nbit):
	p, r = genPrime(m, nbit // 2)
	q, s = genPrime(m, nbit // 2)
	n = p * q
	e = r * s
	return (e, n)

def encrypt(msg, pkey):
	e, n = pkey
	m = bytes_to_long(msg)
	c = pow(m, e, n)
	return c

pkey = genkey(m, 2048)
enc = encrypt(flag, pkey)

print(f'pkey = {pkey}')
print(f'enc = {enc}')

output.txt:

pkey = (150953688, 373824666550208932851344358703053061405262438259996622188837935528607451817812480600479188884096072016823491996056842120586016323642383543231913508464973502962030059403443181467594283936168384790367731793997013711256520780338341018619858240958105689126133812557401122953030695509876185592917323138313818881164334490044163316692588337720342646339764174333821950301279825316497898035760524780198230860089280791887363472060435443944632303774987556026740232641561706904946900169858650106994947597642013168757868017723456208796677559254390940651802333382820063200360490892131573853635471525711894510477078934343423255983)
enc = 275574424285842306309073814038154403551700455145115884031072340378743712325975683329051874910297915882286569143815715537085387363420246497061870251520240399514896001311724695996978111559476733709139080970977190150345474341853495386364275702356438666152069791355990718058189043717952080875207858163490627801836274404446661613239167700736337269924479349700031535265765885064606399858172168036794462235707003475360358004643720927563261787867952228496769300443415094124132722170498229611285689671203272698693505808912907778910378274197503048226322090611405601517624884408718689404556983397217070272851442351897456769883

e = r*s,且e的bit长度为28,那么r以及s的bit长度则为14,由此可知m ** 2 - m + 2=14,可计算出m = 4。之后利用sage中的divisors函数分解e计算出r和s。因为p = a ** m + r,其中r对于a**m来说非常小,所以 n 4 ≈ a ∗ b \sqrt[4]{n} \approx a*b 4n ab;再联立 n = ( a 4 + r ) ∗ ( b 4 + s ) n = (a^4+r)*(b^4+s) n=(a4+r)(b4+s)构造方程组,进而解出a和b,此时p和q也计算出来了

# m ** 2 - m + 2 = 14, m = 4
div = divisors(e)
rs = []
for i in div:
	if int(i).bit_length()==14:
		rs.append(int(i))
s,r = rs[0],rs[1]
a_b =  gmpy2.iroot(n,4)[0]
a,b = symbols('a b')
eq = [a*b-a_b,a**4*b**4+a**4*s+b**4*r+r*s-n]
result = list(nonlinsolve(eq, [a, b]))[0]
a = abs(result[0])
b = abs(result[1])
p = gmpy2.mpz(a**4+r)
q = gmpy2.mpz(b**4+s)

经计算,gcd(e,p-1)和gcd(e,q-1)都不为1,p的bit长度为1021远远大于flag的长度且gcd(e,p-1)较小,于是我们将其转换在模p下的多项式环下有限域开根,再遍历flag

from  Crypto.Util.number import *
import gmpy2

n = 373824666550208932851344358703053061405262438259996622188837935528607451817812480600479188884096072016823491996056842120586016323642383543231913508464973502962030059403443181467594283936168384790367731793997013711256520780338341018619858240958105689126133812557401122953030695509876185592917323138313818881164334490044163316692588337720342646339764174333821950301279825316497898035760524780198230860089280791887363472060435443944632303774987556026740232641561706904946900169858650106994947597642013168757868017723456208796677559254390940651802333382820063200360490892131573853635471525711894510477078934343423255983
e = 150953688
c = 275574424285842306309073814038154403551700455145115884031072340378743712325975683329051874910297915882286569143815715537085387363420246497061870251520240399514896001311724695996978111559476733709139080970977190150345474341853495386364275702356438666152069791355990718058189043717952080875207858163490627801836274404446661613239167700736337269924479349700031535265765885064606399858172168036794462235707003475360358004643720927563261787867952228496769300443415094124132722170498229611285689671203272698693505808912907778910378274197503048226322090611405601517624884408718689404556983397217070272851442351897456769883

p = 15040222622096320078383580808680733765955114958694997949647342925417877088612792495485641348591026281373930569798925789027166056695954731923306109646611840570310396750856642056018981080439916663195842593441587057719678555907050674529272376248049062724657792390788687452049496308886252188791975094655675938807
q = 24854995563762799317055160315647073592768859410925406616067526817964296709994775588158311030813096922905657553370793515214591086698010302872311633588541111630338981703494212247996116660819640489213219705595382514374022123356637290058228183400682431815794876393612877273757515867990847040787313812864434536969
phi = gmpy2.mpz(p-1)
d = gmpy2.invert(gmpy2.mpz(e//72),phi)
m = pow(c,d,p)
P.<x>=PolynomialRing(Zmod(p),implementation='NTL')
f = x**72-m
res = f.monic().roots()
for i in res:
	flag = long_to_bytes(int(i[0]))
	if b"CCTF{" in flag:
		print(flag)
		break

flag:

CCTF{S!mP1E_A7t4cK_0n_SpEc1aL-5trucTur3D_RSA_pR1me5!}

【可能真正喜欢一个人,眼里没有ta的不好吧。】

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值