0%

ImaginaryCTF-Round54-wp-crypto

听说maple出了道MLFSR,作为老粉之一当然就来了,然后发现其他题质量也都不错,就写篇wp记录下。

univariate

题目

题目描述:

1
I love univariate polynomials...

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from Crypto.Util.number import getPrime, bytes_to_long
from secret import flag

p = getPrime(512)
q = getPrime(512)
n = p*q

m = bytes_to_long(flag.encode())
e = 65537
c = pow(m,e,n)

P.<x> = PolynomialRing(ZZ)
x = P.gens()[0]

terms = [x**i for i in range(137)]

T = RealDistribution('gaussian', 2)
coefs = [round(T.get_random_element()) for _ in range(len(terms))]

f = sum([term*coef for term,coef in zip(terms,coefs)])
w = pow(2,f(p),n)

with open('out.txt', 'w') as file:
file.write(f'{n = }\n')
file.write(f'{e = }\n')
file.write(f'{c = }\n')
file.write(f'{f = }\n')
file.write(f'{w = }\n')

题目生成一个单变量随机多项式$f(x)$,并计算:

其中$n=pq$,给出$n,f,w$,要分解掉$n$从而解RSA得到flag。

思路

由费马小定理知道:

所以如果把指数关于$p$的多项式$f(p)$分解成:

那么有:

而$k$只需要将$f(x)$模掉$x-1$就能得到,所以求$gcd(w-2^k, n)$就能得到$p$了。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from Crypto.Util.number import *

P.<x> = PolynomialRing(ZZ)
x = P.gens()[0]

n = 151510886600487624888537926759375027338192556324330182365859112926770109752858284462159488504727238764120612593911292154858008775463001345641311051184326218974685701057787672193003745574697137968457609530135969033403360561333863943223407215732526198691453110628598401583407984162075630768455052482583101773637
e = 65537
c = 74468088842131664480394073891613024559473817230309311952320910922177130990996003196602702376336093457990873018154873841543712071422931358036924937335888815556064840522100618318507080665149514719351519909821468981883880543654015414713368018500970500498936910817336501949914675483148862843329341461828563728789
f = -x^136 + x^135 - 2*x^134 - 4*x^132 + 2*x^130 - x^128 - 3*x^127 + 4*x^126 + 3*x^125 + 3*x^124 + x^123 + x^122 - 5*x^121 - 3*x^120 - x^119 - x^118 + x^117 + x^116 - 4*x^114 - 2*x^112 + 2*x^110 + x^109 + 2*x^108 - 2*x^107 + 3*x^106 - x^104 + 2*x^103 - x^102 + x^101 - 2*x^100 + 3*x^99 - 2*x^98 - x^97 - x^96 - 3*x^95 - x^94 - 2*x^93 - 2*x^91 + 3*x^90 - 2*x^89 - 2*x^88 + x^86 + x^85 - 2*x^84 - 3*x^83 + 2*x^82 + 3*x^79 - x^76 + 2*x^75 - x^74 + x^71 - 5*x^70 - x^67 + x^66 + x^65 + x^63 - x^61 + x^59 - 2*x^58 + 6*x^56 + x^55 + 3*x^54 - x^53 + 2*x^52 + 3*x^51 + x^50 + 2*x^49 + 3*x^47 + 2*x^46 - 4*x^45 + 3*x^44 + 3*x^43 - x^42 - 2*x^40 - 5*x^39 + x^38 + x^37 + 2*x^36 + 2*x^35 + x^34 - x^33 + x^32 - 5*x^31 + x^30 + x^29 + 2*x^28 - 2*x^27 + 3*x^26 - x^25 - x^23 - x^22 - 3*x^21 + 2*x^20 - x^19 - x^17 + 2*x^16 - 2*x^15 - 2*x^14 - 2*x^13 - 2*x^12 + 2*x^11 - 2*x^9 + 3*x^8 - 4*x^7 + 2*x^6 - 2*x^5 - 5*x^4 - 3*x^3 + 5*x^2 - 2
w = 86258923706084556733053644452456806418792871483898871193707132372143291757396867798433017660985422614532352743658877188445517898648519256573663299464811234251773841741466280567326570167017786562044635756348763128567054349991798640926148221279889174229551074668002853442182664523748992260830782387602048836221


a = f % (x-1)

q = GCD(w * pow(2, -a, n) - 1, n)
p = n // q

print(long_to_bytes(int(pow(c, inverse(e, (p-1)*(q-1)), n))))


#ictf{p-1_g0es_aB$olU7eLy_w1lD!!!}



bivariate

题目

题目描述:

1
I love bivariate polynomials...

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from Crypto.Util.number import getPrime, bytes_to_long
from secret import flag

p = getPrime(512)
q = getPrime(512)
n = p*q

m = bytes_to_long(flag.encode())
e = 65537
c = pow(m,e,n)

P.<x,y> = PolynomialRing(ZZ)
x, y = P.gens()

terms = []
for i in range(16):
terms += [(x**i)*(y**j) for j in range(16-i)]

T = RealDistribution('gaussian', 2)
coefs = [round(T.get_random_element()) for _ in range(len(terms))]

f = sum([term*coef for term,coef in zip(terms,coefs)])
w = pow(2,f(p,q),n)

with open('out.txt', 'w') as file:
file.write(f'{n = }\n')
file.write(f'{e = }\n')
file.write(f'{c = }\n')
file.write(f'{f = }\n')
file.write(f'{w = }\n')

题目生成一个双变量随机多项式$f(x, y)$,并计算:

其中$n=pq$,给出$n,f,w$,要分解掉$n$从而解RSA得到flag。

思路

相比于上一道题,这道题的变化就是把单变量变成了双变量。

由于还拥有等式$n=pq$,所以可以把$f(p,q)$消去一元,变成单变量多项式,然后就和上一题一样了。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from Crypto.Util.number import *

P.<x,y> = PolynomialRing(ZZ)
x, y = P.gens()

n = 98488948227534213135365379684862624429673068552821962383206603053375602239567322517902539151497074614991106864222481349938278930598229083057490442318255136829928469581554377192371866029487604479323565889518717446151982565992066276503827461143275322745847799672825450812329543611683563104108219738025321562523
e = 65537
c = 33689078336368731933599049868377598889513853827091255415228112692581639226201157033763841134090287166200344255205518269253856257850799105681895141225429744730522910522719711967250214161994104603730456295060918937791062152710510634082416634814375504799932491459723391812528758183583260665132261524658305233101
f = x^14*y + x^13*y^2 - x^12*y^3 - 3*x^10*y^5 - x^8*y^7 - x^6*y^9 + x^5*y^10 + x^4*y^11 - x^2*y^13 - 2*x*y^14 + y^15 - x^14 + 3*x^13*y + x^12*y^2 + 2*x^11*y^3 - x^9*y^5 + x^8*y^6 + x^7*y^7 - x^6*y^8 + x^5*y^9 - x^4*y^10 - 2*x^3*y^11 + 2*x^2*y^12 - 2*x*y^13 + x^11*y^2 - 3*x^10*y^3 - x^7*y^6 - x^6*y^7 + x^5*y^8 + 3*x^4*y^9 + 4*x^3*y^10 + 2*x^2*y^11 - 2*x*y^12 + y^13 + 2*x^12 + 2*x^11*y - 5*x^9*y^3 - 3*x^8*y^4 + 4*x^7*y^5 - 2*x^6*y^6 - x^5*y^7 - x^3*y^9 - x^2*y^10 + x*y^11 - y^12 - x^11 - x^10*y + 3*x^9*y^2 + 3*x^8*y^3 - 2*x^7*y^4 - 2*x^6*y^5 + 2*x^3*y^8 - 3*x^2*y^9 + y^11 + x^10 + x^8*y^2 - x^7*y^3 + 3*x^6*y^4 - 5*x^5*y^5 - x^4*y^6 + x^3*y^7 - x^2*y^8 - 2*y^10 - x^8*y - x^7*y^2 - x^5*y^4 - x^4*y^5 - x^2*y^7 - x*y^8 - 4*x^8 + 4*x^7*y - 2*x^6*y^2 - x^5*y^3 - 2*x^4*y^4 - x^3*y^5 - 3*x^2*y^6 + x*y^7 - 3*y^8 + 4*x^6*y + x^5*y^2 - 2*x^3*y^4 - 5*x^2*y^5 + x*y^6 + x^5*y + 2*x^4*y^2 - x^2*y^4 - x*y^5 - 2*y^6 + 3*x^5 + 4*x^4*y + 2*x^3*y^2 - y^5 - x^4 - 3*x^3*y - 3*x^2*y^2 + x*y^3 + 2*x^3 + x*y^2 + y^3 + x^2 - 4*x*y + y^2 + 2*x + y
w = 15989670860487110440149242708963326378222417402274922693109917884050744558426262015391394948248787707275063436118071762352120567031556081355002123446258001515700442370180130234425828020876205206138411646169530066935290884923582522784445410472996067725745321733529202293253140485085104533833013732203780391490

g = x*y - n
h = f.sylvester_matrix(g, x).det()
a = h % (y-1)

q = GCD(w * pow(2, -a, n) - 1, n)
p = n // q

print(long_to_bytes(int(pow(c, inverse(e, (p-1)*(q-1)), n))))


#ictf{symmetry_of_bivariate_polynomials_is_zany}



Prime Cuts

题目

题目描述:

1
You can keep the primes, I'll keep the rest...

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from Crypto.Hash import SHA256
from Crypto.Cipher import AES

flag = b'ictf{REDACTED}'

def bits_to_bytes(bits):
byte_array = bytearray()
for i in range(0, len(bits), 8):
byte = 0
for bit in bits[i:i+8]:
byte = (byte << 1) | int(bit)
byte_array.append(byte)
return byte_array



F = GF(2)

M = random_matrix(F,64,64)

initial_value = random_vector(F,64)
sequence = [(M^i * initial_value)[-1] for i in range(1024)]



key = bits_to_bytes(sequence)

hashed = SHA256.new(key).digest()
cipher = AES.new(hashed,AES.MODE_ECB)
encrypted = cipher.encrypt(flag)

print(f'encrypted = {encrypted}')

poly = M.charpoly()
prime_cuts = []
for i in range(1024):
if (is_prime(i)):
prime_cuts.append(sequence[i])


print(f'prime_cuts = {prime_cuts}')
print(f'poly = {poly}')

题目在$GF(2)$下生成了一个64阶的随机方阵$M$以及一个长64的初始向量$\textbf{v}$,并用如下值的序列当作AES的key:

并给出其中当$i$为素数时的序列值,同时额外给出$M$的特征多项式$f$,要求还原flag。

思路

我们知道$f$以$M$的特征值为根,再取$f$的伴随矩阵$X$,$X$的特征值和$M$完全相等,因此两个矩阵相似,所以存在:

取$i$次幂则是:

同时右乘$\textbf{v}$:

此时可以把$P^{-1}\textbf{v}$看做一个整体,记为$\textbf{t}$,因此得到:

$P,X$虽然是不可交换的,但由于整个式子取的是得到结果的最后一个分量,所以我们看看等式右边究竟做了什么。

把他写开成两个方阵乘一个向量的形式:(这里没管$i$次幂,因为只是要看看怎么乘的而已)

因为是取最后一个分量,因此只需要关注:

记最后得到的分量是$y$,则:

这样可能不够直观,我们记$P$的最后一行为向量$\textbf{p}_{64}$,然后$X$的第$i$个列向量是$\textbf{x}_{i}$,那么就是:

而$t_i$仅仅是个数字,所以可以交换上面的运算顺序,让$t_i$先和$\textbf{p}_{64}$做运算:

不仅如此,$t_i$和$\textbf{p}_{64}$还都是固定值,而$\textbf{x}_{i}$的具体值我们是知道的。

所以实际上这是关于$t_i$以及$\textbf{p}_{64}$这128个未知数的方程,而方程数有172个,因为是在$GF(2)$下,所以即使是二次方程,groebner基可能也有机会解掉。

但实际上这也等价于对$X^i$的列空间做了个固定的线性组合,因此实际上只有64个未知数,所以直接用$X$的幂次来搜集线性方程也可以做,这也是我的做法。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from Crypto.Util.number import *
from tqdm import *

F2 = GF(2)
PR.<x> = PolynomialRing(F2)

prime_cuts = [0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0]
encrypted = b'O\xb4\x92\xdd~/&\xad\x99\xfd\xb3\x1dV\x95Y\x91\xba$;\xbb\x7fS\r\xc6\xf1zL\xdcO\xdb\x8ao\xa0\xbbm\xa2\x1f\x99\x17\xee\xa5\x828.>2\x03\x10X\x91o\x83\x8b\xf8\x910\x17x\x1b\x8a\x08\xa0p@'
poly = x^64 + x^63 + x^62 + x^61 + x^59 + x^56 + x^55 + x^54 + x^53 + x^50 + x^49 + x^47 + x^40 + x^38 + x^37 + x^32 + x^27 + x^25 + x^24 + x^20 + x^19 + x^16 + x^15 + x^14 + x^12 + x^9 + x^8 + x^7 + x + 1


M = companion_matrix(poly.list())

L = Matrix(F2, 0, 64)
for i in trange(1024):
if (is_prime(i)):
L = L.stack((M^i)[-1])
1
2
3
4
R = vector(F2, prime_cuts)
res = L.solve_right(R)
print(res)
print(L.dimensions())
1
2
3
4
5
from Crypto.Hash import SHA256
from Crypto.Cipher import AES

initial_value = res
sequence = [(M^i * initial_value)[-1] for i in range(1024)]
1
2
3
4
5
6
temp = []
for i in range(1024):
if (is_prime(i)):
temp.append(sequence[i])
print(temp)
print(prime_cuts)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def bits_to_bytes(bits):
byte_array = bytearray()
for i in range(0, len(bits), 8):
byte = 0
for bit in bits[i:i+8]:
byte = (byte << 1) | int(bit)
byte_array.append(byte)
return byte_array

key = bits_to_bytes(sequence)

hashed = SHA256.new(key).digest()
cipher = AES.new(hashed,AES.MODE_ECB)
flag = cipher.decrypt(encrypted)
print(flag)


#ictf{m041_m4n_15_4_w45h3d_4u7h0r_wh0_0nly_m4k35_lf5r_ch4ll3n635}



MLFSR

题目

题目描述:

1
Following the steps of @moai_man, I made another masked linear feedback shift register challenge! Except the register is a bit bigger than what you might expect 😛

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from sage.all import *
from Crypto.Cipher import AES

p = 2**127 - 1
F = GF(p)


def mask(x):
return int(x) >> 63


def mlfsr(n):
M = random_matrix(F, n, n)
v = random_vector(F, n)
while True:
v = M * v
yield mask(v[0])


if __name__ == "__main__":
with open("flag.txt", "rb") as f:
flag = f.read().strip()
rng = mlfsr(16)
key = (next(rng) << 64) + next(rng)
aes = AES.new(key.to_bytes(16, "big"), AES.MODE_CTR)
ct = aes.encrypt(flag)
iv = aes.nonce
print(f"{ct = }")
print(f"{iv = }")

output = []
for _ in range(256):
output.append(next(rng))
print(f"{output = }")

和上一道题目类似,题目在$GF(2^{127}-1)$下生成16阶的随机方阵$M$以及长16的初始向量$\textbf{v}$,并取$M^{i}\textbf{v}$的第一个分量的高64bit作为第$i$个随机数。

给出连续256个随机数,要求能向前还原两个随机数,从而解AES得到flag。

思路

这个题目的主要问题在于不仅没有$\textbf{v}$,我们甚至没有$M$的任何有关信息,只有256个随机数而已。

我的思路是利用特征多项式。由于$M$阶为16,所以存在度为16的特征多项式$f$满足:

不妨就假设$f$为:

也就是说满足:

而虽然我们系数$a_i$和矩阵$M$都不知道,但是我们可以在上式右边同时乘上$\textbf{v}$得到:

为什么要这样做?假如最终取随机数不去掉低63位的状态为$s_i$,那么对于上式来说,我们取其第一个分量就能得到:

而又因为对任意的自然数$k$,下式也成立:

所以就得到了一个关系:

这个关系很强,如果简单移项一下会发现,新状态是由前16个状态共同决定的:

所以这本质上是个MRG,而取高位生成随机数的操作其实也就是truncated MRG,因此可以搜到论文,然后照着实现一下就好了。

exp

1
2
3
4
5
6
7
8
from Crypto.Cipher import AES

ct = b'\x02oI]\x1ah$\xc4\xf8yj\x1b\xe1-\x90\x8b\xf8\xfd\xf0\xf0\xf9\xc2R\x88\x12l\xf9\x13|\xe1/\xf0\xad\x85%\x86\x90\xabo\xe7\xae\x86\xdfM\x08\xcb\t'
iv = b'\xa8_\x067h#\x18<'
output = [16571946204224940283, 11392336404833892558, 2543157721392714866, 814053091972463518, 15841696289489162943, 11259291121448767593, 17403356973533804622, 17041641038731278534, 13312359638430307662, 972458339406199623, 2392795526167786442, 7349775907372092045, 2309616376607635422, 9817973281538770790, 1166695604833697786, 1169165220184018001, 17513888251176116991, 13379548526865267302, 16362257580026098401, 6327292440675265730, 11574944786052394394, 13612888339731664601, 6477417939185242093, 8500965147337658193, 8418096057416477441, 9555509495909557269, 1079429192264462908, 13102296469089663705, 719698280231378441, 4272778232125933754, 7011722142721927503, 13293000912677726450, 593614131498595036, 17393797427592075075, 6809119453585789855, 13774483085306710863, 12805277117772622516, 14989350347848139762, 10333766328349885607, 9373112307599110023, 12329265409028640672, 11245220458387892439, 17722870094805139034, 16229576728611454183, 11360620590190704585, 4217344604760762101, 16343259316830055641, 11817132521003703395, 13737919528533399306, 7275811701971217465, 1541911178906894061, 10055554670227351090, 16333131956597619586, 16519371340584689146, 17112666629779684142, 15168926771356146219, 5734235197497376583, 499287216215754015, 13765245602914235160, 5658121109578087000, 2962217689530474289, 4938338170260991333, 15137545170169806527, 11488469978354626873, 9757870379648999366, 3019591568620343760, 18183075819457815862, 3982898340925303636, 18356442455906708677, 6326359571004098712, 14013282540085751521, 2495328940948650050, 12298442222964443927, 12513774292643112385, 2796474100746485293, 2120250918968911142, 5753295582948654484, 17027117423231727265, 2387359940846350734, 16205002087311285859, 9777967765440686022, 9030721784737525348, 5335337921092692477, 17520185928364881671, 10904570848521003817, 14334450630680370330, 805174543534634818, 11673978745947168045, 18370026079941475036, 15728500344927779371, 6384153077841873326, 18021340594577522675, 1342999949849111126, 3066051764797777634, 18421736088967203146, 10638875771690350208, 16787814423378822701, 4744819462077975531, 5271000334360578737, 10231268653626853377, 15655565043478746136, 10882005239316400710, 9639721109531611826, 16872495794380094186, 1939228805868475943, 10028102252678507528, 1694245389001616647, 8653763291581803481, 11710154118157564728, 17415294231070549035, 16034295156456625356, 10807439294295017777, 217427117073635494, 13661722734003353600, 17370772873267848823, 5726484825514307780, 5079929256769056604, 10920775869371029055, 7226666754874367929, 7966039793007469854, 13619353398069183389, 8549728891159081989, 14283670796003008025, 8265867208695352204, 16489304161347635475, 3310461849350296912, 9436224526112907140, 14589390672372960736, 8246112494960736115, 15217550419845118198, 4006062446410836474, 5510719433463489313, 11005122696244366645, 240623852175095959, 722460217782652985, 4977768488427339654, 13617559859652882125, 17493108660691918792, 15968268889053575161, 5906598363697029000, 2258595434919016609, 15838424410031657126, 9758576753003134787, 8691257769393124272, 5570274611946744336, 17862929210584391659, 13523897418276361354, 1258262664488373354, 13917872262138223996, 18421624875215407044, 18268911265429835934, 12754979068568359541, 15768709298887555645, 18285979235543966880, 6570751075972111155, 5588435749877560605, 8810154983458760012, 11843494761154535393, 8260623677326375215, 15717504651076748210, 5357848710115996697, 9005337460832483286, 10913829268076200421, 396999260501336619, 7064529303216688201, 8149316853585683733, 10435170275159484420, 7876657835886499038, 4265088953277353087, 15388645063673306617, 6116864909336773497, 8747086796155804322, 13762380685256939761, 10466091771058929947, 18343769691232417850, 4652123173529996097, 6664355400453648298, 795838954301756994, 8848080737415675632, 17240558279604979031, 2236071517844111736, 15901808564385051803, 5559440615528024766, 1741845584208709097, 7931374221831783444, 4499229304096868021, 14083108125588666506, 6759395054811993270, 8262557999625393951, 11962935307561161203, 7556156849739603986, 3419483089996837015, 8331905655194322647, 9565916150827476017, 10902107447463569903, 4029743711698757137, 2104645061619579930, 5317775186894040070, 5793515096942041167, 8054537955680738213, 7001355019332136442, 1367620161029089929, 16795880913080764180, 10530891455161550007, 2981118490743777658, 320329097694193093, 12379408766034349661, 16059694350349708333, 918310558057032928, 5956456251994353992, 16303056360391461266, 7557662304749342718, 3906294183864019620, 17822879947750863293, 10029589440208543869, 2450874748881475277, 10212147015431740462, 5756523816817730644, 11587712451937073247, 16264116787009277617, 4809377173474478475, 3775821254065580142, 15187638466111071127, 37922582801596314, 14168639860476521920, 10348043871637510141, 10212071379394875228, 8895713658003940834, 4323083509292363214, 2728783808623159372, 1041026631901754907, 7767449778813937318, 15118582699345517176, 9281276032915614101, 15608326172092580555, 9036407238384709903, 996607405847220921, 10286705618005832438, 17623660126697468160, 4901405463629150900, 2433394384895722776, 16557651125971180326, 8368256748006061199, 13266517755059347166, 2315402701773975178, 15946875676241322236, 1643359733056896734, 12514556825378688782, 4227305030247806071, 1147368828771697060, 6394633031977985934, 14303335405947619925, 17173690372640961719, 10056890902537565446, 1329826347356472498, 3169782457848355776]

p = 2**127 - 1
F = GF(p)
1
2
3
4
5
6
7
8
9
10
11
12
13
#https://eprint.iacr.org/2022/1134.pdf
n = 16
t = 65
r = 256 - t

Y = []
for i in range(r):
Y.append(output[i:i+t])
Y = Matrix(ZZ, Y)
L = block_matrix(ZZ, [
[Y, 1]
])
L = L.LLL()
1
2
3
4
5
6
7
8
9
10
PR.<x> = PolynomialRing(F)

μs = []
for i in range(120):
μ = vector(ZZ, L[i][t:])
μs.append(PR(μ.list()))

print(gcd(μs[0], μs[1]))
print(gcd(μs[1], μs[2]))
print(gcd(μs[1], μs[2]).list())
1
2
3
4
5
6
7
8
from re import findall
from subprocess import check_output

def flatter(M):
# compile https://github.com/keeganryan/flatter and put it in $PATH
z = "[[" + "]\n[".join(" ".join(map(str, row)) for row in M) + "]]"
ret = check_output(["flatter"], input=z.encode())
return matrix(M.nrows(), M.ncols(), map(int, findall(b"-?\\d+", ret)))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
coef = gcd(μs[0], μs[1]).list()

nums = 120
L2 = Matrix(ZZ, nums+1+nums-n, nums+1+nums-n)
for i in range(nums+1):
L2[i, i] = 1
for i in range(nums-n):
Ci = int(2^63*vector(F, coef)*vector(F, output[i:i+n+1]))
L2[nums, nums+1+i] = -Ci
for j in range(n+1):
L2[i+j, nums+1+i] = coef[j]
L2[-i-1, -i-1] = p
L2[:, -(nums-n):] *= p

print(L2.dimensions())
res = flatter(L2)[0]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from Crypto.Util.number import *

ls = list(map(abs, res[:nums]))
a = [output[i]*2^63+ls[i] for i in range(nums)]
a = [(-int(vector(F, coef[1:])*vector(F, a[:n])))*inverse(coef[0], p) % p] + a
a = [(-int(vector(F, coef[1:])*vector(F, a[:n])))*inverse(coef[0], p) % p] + a

key = (int(a[0] >> 63) << 64) + int(a[1] >> 63)
aes = AES.new(key.to_bytes(16, "big"), AES.MODE_CTR, nonce=iv)
flag = aes.decrypt(ct)
print(flag)


#ictf{lfsr_is_approximately_equal_to_lcg_right?}



Wrong ssh

题目

题目描述:

1
I've heard MD4 is insecure so I made this new super secure hash to replace it, guess my 1 in a 100 million password to win big 🎰🎰🎰🎰 🤑 nc 155.248.210.243 42191

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from Crypto.Hash import MD4
from secrets import choice
from string import printable

flag = b'ictf{REDACTED}'
password = (''.join(choice(printable) for _ in range(4))).encode('utf-8')
#super-secure-hash :)
def ssh(ct):
hasher = MD4.new()
hasher.update(ct)
hashed = hasher.digest()
for i in range(20000):
hasher.update(hashed)
intermediate = hasher.digest()
hashed = bytes(i ^ j for i,j in zip(hashed,intermediate))
return hashed

def password_checker(A):
for i in range(len(flag)):
if ssh(A[:i + 1]) != ssh(password[:i + 1]):
return False
return True

def main():
while(True):
your_flag = str(input('gimme your password:\t')).encode()
if password_checker(your_flag):
print('yep!',flag)
return
else:
print('Nope!')

if __name__ == '__main__':
main()

连接上靶机后,题目随机生成一个长度为4并由printable组成的4位password,如果猜对该password就可以拿到flag,否则会返回’Nope!’。

思路

问题出在他对password猜测成功与否的check上,他不直接判断字符串是否完全相等,而是:

  • 逐步判断前1个字符,前2个字符,前3个字符,前4个字符
  • 每次判断也不直接判断相等,而是判断一个复杂的hash过程后的值是否相等

所以这很明显是个时间侧信道,相等的时候会进入下一步判断,从而显著增长时间。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from pwn import *
import time
from string import printable
from secrets import choice
from tqdm import *

if(1):
sh = remote("155.248.210.243", 42191)

password = ""
for i in range(4):
table = [0 for _ in range(len(printable))]
for j in trange(len(printable)):
sh.recvuntil(b'gimme your password:\t')

t1 = time.time()
sh.sendline((password+printable[j]).encode())
res = sh.recvline().strip().decode()
t2 = time.time()

if('yep!' in res):
print(res)
exit()

temp_time = float(t2-t1)
table[j] = temp_time

password = password + printable[table.index(max(table))-i]
print(sorted(table)[::-1][:5])
print(password)

print("Done!")
sh.close()


#ictf{y37_4n07h3r_64mbl1n6_ch4ll?!?}



Long Rods

题目

题目描述:

1
Don't you hate it when your favorite cipher needs padding to encrypt, me, I just make my messages longer... Anyways go decrypt this code for the flag, shouldn't be too hard, even Plutarch knew how to do it

思路

题目输出有点长,但简单来说就是个密码棒,只是密文很长,所以是个很长的密码棒XD。

而解决思路也很简单,factor一下密文发现正好是$1009\times 1013$,所以合理猜测密码棒的长度和宽度就是这两个值,因此解码即可。

exp

1
2
3
4
M = Matrix(ZZ, 1009, 1013, list(enc.encode())).T
print("".join(list(map(chr, M.list()))))

#ictf{f4c70r1z4710n_pr0bl3m_1n51d3_4n_4nc13n7_c1ph3r}



bitD2cision

题目

题目描述:

1
Make your decision bit by bit, again!

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from Crypto.Util.number import *
from random import SystemRandom
from sage.all import *

random = SystemRandom()
# BLS12-381 curve
p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
K = GF(p)
E = EllipticCurve(K, (0, 4))
flag = open("flag.txt", "rb").read().strip()
flag_bin = bin(bytes_to_long(flag))[2:].zfill(len(flag)*8)
assert flag.decode().startswith("ictf")
output = []
n = E.order()
sn = 52437899
s = n//sn**2
print(n)
for c in flag_bin:
if c == "0":
output.append(random.getrandbits(512) % p)
else:
P1 = s*E.random_element()
P2 = s*E.random_element()
output.append(P1.weil_pairing(P2, sn))

print(output)

题目基于$BLS12-381$曲线,取曲线阶的因子$sn$,之后根据flag每一bit进行decision:

  • 当前bit为0,则给出一个$GF(p)$下的随机数
  • 当前bit为1,则给出$e(sQ_1, sQ_2)$,其中$e$为weil配对函数,$Q_1,Q_2$是曲线上的随机点

思路

这个套路很熟了,当前bit为1的weil配对值显然是个$sn$次单位根。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from Crypto.Util.number import *

p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
K = GF(p)
E = EllipticCurve(K, (0, 4))
n = E.order()
sn = 52437899
s = n//sn**2

flag = ""
for i in enc:
if(pow(i, sn, p) == 1):
flag += "1"
else:
flag += "0"
print(long_to_bytes(int(flag, 2)))

#ictf{chEck_t0rsion_aft3r_pa1ring_3f1d7aec}