CryptoCTF 2023 (part1)

和朋友们组了个"养老选手摸鱼看题"的队伍,成功ak了赛题。这里放一个榜,以后再接再厉。

image
part1主要是比赛过程中做出或弄懂的题,由于本人刚刚写了一版没保存(恼),这一版估计就写的很简略了。

Easy

Did it!

将0-127分为若干不相交的集合,每次给server发一组,根据did函数逻辑来筛选每次返回的结果。

from pwn import *

n = 127
lists = [[0, 16, 2, 32, 30, 34, 53, 12, 20, 23, 5, 41, 63, 62, 6, 61, 26, 60, 38, 7],
        [1, 125, 95, 97, 93, 74, 115, 107, 104, 122, 86, 64, 65, 121, 66, 101, 67, 89, 120, 59],
        [126, 3, 4, 48, 28, 36, 44, 17, 52, 13, 47, 68, 21, 58, 8, 24, 18, 31, 57, 40],
        [111, 124, 123, 79, 99, 91, 83, 110, 75, 114, 80, 106, 69, 119, 103, 109, 96, 70, 87, 29],
        [51, 14, 43, 33, 98, 9, 46, 50, 27, 15, 10, 22, 19, 42, 49, 25, 45, 54, 39],
        [76, 113, 84, 94, 118, 81, 77, 100, 112, 117, 105, 108, 85, 78, 102, 82, 73, 88],
        [35, 56, 37, 55, 11],
        [92, 71, 90, 72, 116]]
D = []
io = remote('00.cr.yp.toc.tf', 11337)
for i in range(3):
    io.recvline()
for i in range(8):
    io.sendline(str(lists[i]).encode()[1:-1])
    io.recvuntil(b'[')
    did = [int(_) for _  in io.recvuntil(b']', drop = True).decode().split(', ')]
    for j in lists[i]:
        if (pow(j,2,n) not in did) and (pow(j,2,n)+1 not in did):
            D.append(j)
io.sendline(str(D).encode()[1:-1])
print(io.recvall())

Blue Office

爆破seed即可,估计非预期。

enc = b'b0cb631639f8a5ab20ff7385926383f89a71bbc4ed2d57142e05f39d434fce'
head = b'CCTF{'

import binascii

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

# for s in range(2147483647):
#     if encrypt(s,head) == binascii.unhexlify(enc)[:5]:
#         print(s)

s = 10364460

assert s <= 2 ** 32
c, d = 0, s
l = len(binascii.unhexlify(enc))

dec = b''
while c < l:
    d = reseed(d)
    dec += (binascii.unhexlify(enc)[c] ^ ((d >> 16) & 0xff)).to_bytes(1, 'big')
    c += 1
print(dec)
# CCTF{__B4ck_0r!F1c3__C1pHeR_!!}

Suction

n,e,c的末尾8位均未知,所以需要爆破。主要麻烦的是对n的爆破,因为在爆破的同时需要调用factordb的api来分解n,直到获得2个128位比特的素数因子。

from Crypto.Util.number import *
from tqdm import tqdm
import requests

def queryFactors(n):
    s=[]
    url="http://factordb.com/api?query="+str(n)
    r = requests.get(url)
    factors=r.json()['factors']
    for f in factors:
        for i in range(f[1]):
            s.append(int(f[0]))
    return s


PKEY = 55208723145458976481271800608918815438075571763947979755496510859604544396672
ENC = 127194641882350916936065994389482700479720132804140137082316257506737630761

print(bin(PKEY))
n_h = bin(PKEY)[2:(258-9)]
e_h = bin(PKEY)[-8:]

rs = []
for i in tqdm(range(2^8)):
    n = int(n_h,2) * 2 ^ 8 + i
    r = queryFactors(n)
    if len(r) == 2 and isPrime(r[0]) and isPrime(r[1]):
        rs.append(r)
print(rs)

from Crypto.Util.number import *

PKEY = 55208723145458976481271800608918815438075571763947979755496510859604544396672
ENC = 127194641882350916936065994389482700479720132804140137082316257506737630761
p,q = [188473222069998143349386719941755726311, 292926085409388790329114797826820624883]
e_h = bin(PKEY)[-8:]
print(e_h)
phi = (p-1)*(q-1)
print(isPrime(p),isPrime(q),int(p).bit_length(),int(q).bit_length())
n = p * q

for i in range(2**8):
    for j in range(2**8):
        c = ENC * 2 ** 8 + i
        e = int(e_h, 2) * 2 ** 8 + j
        if not isPrime(e):
            continue
        d = inverse(e,phi)
        flag = long_to_bytes(pow(c,d,n))
        try:
            print(flag.decode())
        except:
            continue
# 注意flag掐头去尾了
# 6oRYGy&Dc$G2ZS

Medium

Derik

e和d的求解是二元一次线性方程,可以找个求解器求通解,再进行遍历找到一组解为(3,73),题目的O列表中的前3个元素恰好为(C[0] * p - C[1] * q)等的值。那么这里用sage的solve解一下约束即可获得pqr,最后解rsa即可。

from Crypto.Util.number import *

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

print(c.bit_length())
print(C[6],C[7])

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

assert O[0] ** 3 + O[1] ** 3 + O[2] ** 3 == 73 * O[0] * O[1] * O[2]

# sage
# C = [5960650533801939766973431801711817334521794480800845853788489396583576739362531091881299990317357532712965991685855356736023156123272639095501827949743772, 6521307334196962312588683933194431457121496634106944587943458360009084052009954473233805656430247044180398241991916007097053259167347016989949709567530079, 1974144590530162761749719653512492399674271448426179161347522113979158665904709425021321314572814344781742306475435350045259668002944094011342611452228289, 2613994669316609213059728351496129310385706729636898358367479603483933513667486946164472738443484347294444234222189837370548518512002145671578950835894451, 8127380985210701021743355783483366664759506587061015828343032669060653534242331741280215982865084745259496501567264419306697788067646135512747952351628613, 5610271406291656026350079703507496574797593266125358942992954619413518379131260031910808827754539354830563482514244310277292686031300804846114623378588204, 10543, 4]
# p,q,r = var('p q r')
# f1 = C[0] * p - C[1] * q - 1391526622949983
# f2 = C[2] * q - C[3] * r - 2848691279889518
# f3 = C[4] * r - C[5] * p - 89200900157319
# solve([f1==0,f2==0,f3==0],p,q,r)
e = 3
d = 73
p = 9758621034843917661145412977193922808892309951663464821517963113005483457886774294910761723767526582514514505278091600074371768233672585649562672245905811
q = 8919642442779618620315315582249815126044061421894622037450496385178083791083142991676417756698881509754110765444929271564991855378540939292428839562446571
r = 6736304432663651651650099104581016800112378771266600017972326085742513966258250417227421932482058281545032658577816441378170466639375931780967727070265551
f = (p-1)*(q-1)*(r-1)*(e-1)*(d-1)
dd = inverse(65537,f)
print(long_to_bytes(pow(c,dd,p*q*r*e*d)))
# CCTF{____Sylvester____tHE0r3m_Of_D3r!va7i0n!}

Insights

没啥说的,d都给了,估计非预期。

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

n = 12765231982257032754070342601068819788671760506321816381988340379929052646067454855779362773785313297204165444163623633335057895252608396010414744222572161530653104640020689896882490979790275711854268113058363186249545193245142912930804650114934761299016468156185416083682476142929968501395899099376750415294540156026131156551291971922076435528869024742993840057342092865203064721826362149723366381892539617642364692012936270150691803063945919154346756726869466855557344213050973081755499746750276623648407677639812809665472258655462846021403503851719008687214848550916999977775070011121527941755954255781343103086789
e = 459650454686946706615371845737527916539205656667844780634386049268800615782964920944229084502752167395446158290854047696006034750210758341744841762479191173017773034647739346927390580848998121830029134542880713409306092967282675122699586503684943407535067216738556403169403622104762516293879994387324370835718056251706150557820106296417750402984941838652433642298378976899556042987560946508887315484380807248331504458640857234708123277403252632993828101306072382329879857946191508782246793011691530554606521701055094223574951862129713872918021549814674387049788995785872980320871421550616327471735316980754238323013
c = 10992248752412909788626396175372747713079469256270100576886987393986576680666320383209810005318254336440105142571546847427454822405793626080251363454531982746373841267986148332456716023293306870382809568309620264499225135226626560298741596462262513921032733814032790312163314776421380481083058518893602887082464123177575742160690315666730642727773288362853901330620841098230284739614618790097180848133698381487679399364400048499041582830157094876815030301231505774900176910650887780842536610942820066913075027528705150102760422836458745949063992228680293226303245265232017738712226154128654682937687199768621565945171

d = next_prime(int(pow(n, 0.2919)))
print(long_to_bytes(ZZ(pow(c,d,n))))
## CCTF{RSA_N3w_rEc0rd5_4Nd_nEw_!nSi9h75!}

TPSD

一个特殊的不定方程,参考知乎写脚本,注意令b=2a,在求解的时候需要满足xyz中有素数且最小的一个值在某个比特范围内。

from pwn import *
from Crypto.Util.number import *
import random

def attack(nn,l,h):
    while True:
        a = random.getrandbits(nn)
        # 假定a和b的关系
        # w为1
        b = 2 * a
        for c in range(1, 4):
            d = c * (a ** 3 + (a - b) ** 3 + c ** 3)
            if c * (-1 * a ** 3 - b ** 3 + c ** 3) % d == 0 and (
                    -1 * (a ** 2 - a * b + b ** 2) ** 2 + (a + b) * c ** 3) % d == 0 and (
                    (a ** 2 - a * b + b ** 2) ** 2 + (2 * a - b) * c ** 3) % d == 0:
                temp_x = c * (-1 * a ** 3 - b ** 3 + c ** 3) // d
                temp_y = (-1 * (a ** 2 - a * b + b ** 2) ** 2 + (a + b) * c ** 3) // d
                temp_z = ((a ** 2 - a * b + b ** 2) ** 2 + (2 * a - b) * c ** 3) // d
                assert temp_x ** 3 + temp_y ** 3 + temp_z ** 3 == 1
                x, y, z = (abs(temp_x), abs(temp_y), abs(temp_z))
                if (isPrime(x) or isPrime(y) or isPrime(z)) and min(x, y, z).bit_length() >= l and min(x, y, z).bit_length() <= h:
                    # if (isPrime(x) or isPrime(y) or isPrime(z)) :
                    return (temp_x, temp_y, temp_z)

s1 = [-876023, -40296966, 40297104]
io = remote('05.cr.yp.toc.tf',11137)
print(io.recvuntil(b'level 1: \n'))
io.sendline(str(s1)[1:-1].encode())
#io.interactive()
for i in range(50):
    if i == 18:
        io.interactive()
    print(f'i = {i}')
    rev = io.recvlines(3)
    print(rev)
    p1 = rev[2].find(b'(')
    p2 = rev[2].find(b',')
    p3 = rev[2].find(b')')
    l = int(rev[2][p1+1:p2])
    h = int(rev[2][p2+2:p3])
    print(l,h)
    nn = l//3
    ans = attack(nn,l,h)
    io.sendline(str(ans)[1:-1].encode())

该不定方程的求解是有论文的,更深入的学习可参考苏氨酸师傅

Trex

目标为求解不定方程\(x^2+y^2-xy=az^3\),每轮a由server给定,可以令\(x=at^3,y=kat^3,z=mat^2\),代入展开消掉a得到\(k^2-k+1-m^3=0\),将m看作常量进行遍历,同时把k看作未知数求判别公式,直到找到一组整数解,这里可以找到一组(k,m)为(19,7),带入求xyz即可。本题主要参考了tsuppari师傅的做法,感谢。

from pwn import *

def attack(a):
    t = 1
    h = a ** 2
    x = h * t ** 3
    y = h * 19 * t ** 3
    z = a * 7 * t ** 2
    return (x,y,z)

io = remote('03.cr.yp.toc.tf',31317)
#io.interactive()
banner = io.recvlines(6)
l1 = banner[-1]
print(l1)
p1 = l1.find(b'= ')
p2 = l1.find(b'*')
a = int(l1[p1+2:p2])

ans1 = attack(a)
print(ans1)
io.sendline(str(ans1)[1:-1].encode())

for i in range(18):
    print(f'第{i}次')
    rev = io.recvlines(2)
    print(rev)
    l = rev[-1]
    p1 = l.find(b'= ')
    p2 = l.find(b'*')
    a = int(l[p1 + 2:p2])
    print(a)
    ans = attack(a)
    print(ans)
    io.sendline(str(ans)[1:-1].encode())

io.interactive()
io.close()
# CCTF{T3rn3ry_Tr3x_3Qu4t!0n}

另一个做法同样参见苏氨酸师傅知乎。

Keymoted

分为两个步骤,第一是要分解n,第二是要解ecrsa。n的因子生成算法如下:

def gen_koymoted(nbit):
	p = getPrime(nbit)
	a, b = [randint(1, p - 1) for _ in '__']
	Ep = EllipticCurve(GF(p), [a, b])
	tp = p + 1 - Ep.order()
	_s = p ^^ ((2 ** (nbit - 1)) + 2 ** (nbit // 2))
	q = next_prime(2 * _s + 1)
	Eq = EllipticCurve(GF(q), [a, b])
	n = p * q
	tq = q + 1 - Eq.order()
	e = 65537
	while True:
		if gcd(e, (p**2 - tp**2) * (q**2 - tq**2)) == 1:
			break
		else:
			e = next_prime(e)
	pkey, skey = (n, e, a, b), (p, q)
	return pkey, skey

p为256比特,记p去掉最高位的比特后为p1,\(\small p=p_1+2^{255}\),那么\(\small q=nextprime(2(p_1+2^{128})+1)\),这是由于gen的过程有异或操作把p的最高位去掉了。我的思路是将q直接看作\(\small 2(p_1+2^{128})+1\),利用n=pq这一关系得到一个关于p1的方程,这样解出来的p1和p也只是近似值,因为q是取的近似,因此得到p之后还要进行爆破,得到正确的p和q。后一部分就是经典的ecrsa求解,求d需要求阶phi,phi就等于ecc在modp和modq下的阶的乘积(一个结论)。完整exp:

from gmpy2 import *

n = 6660938713055850877314255610895820875305739186102790477966786501810416821294442374977193379731704125177528590285016474818841859956990486067573436301232301

k = 2 ** 129 + 1
a = 2
b = k + 2 ** 256
c = k * 2 ** 255 - n
delta = b ** 2 - 4*a*c

p_ = (-b + iroot(delta,2)[0])//(2*a) + 2**255
print(p_.bit_length())
for i in range(-2**20,2**20):
    p = p_ + i
    if gcd(p,n) > 1:
        print(p)
        break

p=93511613846272978051774379195449772332692693333173612296021789501865098047641
n,e,a,b = (6660938713055850877314255610895820875305739186102790477966786501810416821294442374977193379731704125177528590285016474818841859956990486067573436301232301, 65537, 5539256645640498184116966196249666621079506508209770360679460869295427007578, 20151017657582479433586370393795140515103572865771721775868586710594524816458)
enc = (6641320679869421443758875467781930795132746694454926965779628505713445486895274490835545942727970688359873955019634877304270220728625521646208912044469433 , 2856872654927815636828860866843721158889474116106462420201092148493803550131351543372740950198853438539317164093538508795630146854596724019329887894933972)

E = EllipticCurve(Zmod(n), [a, b])
C = E(enc)

q = n//p
assert p * q == n
Ep = EllipticCurve(Zmod(p), [a, b])
Eq = EllipticCurve(Zmod(q), [a, b])

print('start...')
ordp = Ep.order()
ordq = Eq.order()
phi = ordp*ordq
print(phi)

d = inverse_mod(e,phi)
from Crypto.Util.number import *
m = (C*d)[0]
print(long_to_bytes(int(m)))

Hard

Big

剪枝分解n,然后二次剩余判定即可。

# https://kt.gy/blog/2015/10/asis-2015-finals-rsasr/
n = 6528060431134312098979986223024580864611046696815854430382374273411300418237131352745191078493977589108885811759425485490763751348287769344905469074809576433677010568815441304709680418296164156409562517530459274464091661561004894449297362571476259873657346997681362092440259333170797190642839587892066761627543
def t(a, b, k):
    # sqrt(n) has 155 digits, so we need to figure out 77 digits on each side
    if k == 77:
        if a*b == n:
            print(a, b)
        return
    for i in range(10):
        for j in range(10):
            # we try to guess the last not-already-guessed digits of both primes
            a1 = a + i*(10**k) + j*(10**(154-k))
            b1 = b + j*(10**k) + i*(10**(154-k))
            if a1*b1 > n:
                # a1 and b1 are too large
                continue
            if (a1+(10**(154-k)))*(b1+(10**(154-k))) < n:
                # a1 and b1 are too small
                continue
            if ((a1*b1)%(10**(k+1))) != (n%(10**(k+1))):
                # The last digits of a1*b1 (which won't change later) doesn't match n
                continue
                # this a1 and b1 seem to be a possible match, try to guess remaining digits
            t(a1, b1, k+1)

# the primes have odd number of digits (155), so we try all possible middle digits (it simplifies the code)
for i in range(10):
    t(i*(10**77), i*(10**77), 0)

from tqdm import tqdm

p = 9397241380094769307017158485931177341961951476061947013977394739417988689050439874435488908822482074028128151771446818457295188445665208696820950505470967 
q = 7690745050590286968025665448815927548186441771518218204702842288098845344789340509868897149374937793107491606741591691437711395848517107039674900831427939 

a, C = 

m = ''
for _ in tqdm(range(len(C))):
    Rp.<x1> = PolynomialRing(Zmod(p))
    Rq.<x2> = PolynomialRing(Zmod(q))
    f1 = x1^2-a-C[_]*x1
    f2 = x2^2-a-C[_]*x2
    r1 = int(f1.roots()[0][0])
    r2 = int(f2.roots()[0][0])
    t = crt([r1, r2], [p, q])
    sign = kronecker(t, p*q)
    m += str((sign+1)//2)

print(bytes.fromhex(hex(int(m,2))[2:]))
posted @ 2023-07-09 12:13  ZimaB1ue  阅读(326)  评论(0编辑  收藏  举报