da Vinci after rsa

[题解分析]

1
2
factor(0x1d42aea2879f2e44dea5a13ae3465277b06749ce9059fd8b7b4b560cd861f99144d0775ffffffffffff)
# 9749 * 11237753507624591 * 9127680453986244150392840833873266696712898279308227257525736684312919750469261

e=5,取最大的素因子p,发现e|(p-1),AMM整出b'flag{weadfa9987_adwd23123_454f}'

然后出了个古典的达芬奇密码可还行…

1
2
3
4
5
6
7
8
9
10
11
fb = ['1', '1', '2', '3', '5', '8', '13', '21', '34', '55', '89', '144', '233', '377', '610', '987', '1597', '2584', '4181', '6765', '10946', '17711', '28657', '46368', '75025', '121393', '196418', '317811', '514229', '832040', '1346269', '2178309']
fb = [int(i) for i in _]
t = [1,28657,2,1,3,17711,5,8,13,21,46368,75025,34,55,89,610,377,144,233,1597,2584,4181,6765,10946,987]
m = 'weadfa9987_adwd23123_454f'
s = '?' * 25
s = 'list(s)'
for i in range(25):
# s[fb.index(t[i])] = m[i]
s[i] = m[fb.index(t[i])]
print(''.join(s))
# w5awd4fa994f87_dwad3123_2

由于斐波那契前两个均为1,且t[0]=t[3]=1,因此将s[3]替换为m[1],得到flag{w5aed4fa994f87_dwad3123_2}

elgaml_rsa

[题解分析]

r以LCG生成

因此$c2^{B}\cdot h^{A}\cdot(c22)^{-1}=m^{B-1}(mod\ p)$,且在这题里检验发现gcd(B-1,p-1)=1,所以直接还原得到m

得到m,即secret后,知pow(flag,e,secret)=c,yafu跑次secret,得到分解结果

(ps:yafu挺离谱的…分解出的因子幂次都不告诉的…只显示出来一部分…歪日)

发现secret的一个因子m0=653551912583,且幂次为15

由于gcd(e,(m*(m0-1))//m0)=2,化简得到c’后,c’即为$pow(flag,2,m0^{15})$,AMM求二次根得到结果

对$p^{k}$的AMM,按照如下paper给出的算法,将q替换成$q^{k}$,phi等替换下即可

[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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
from Crypto.Util.number import *

g, h, A, B, p, q =
c1, c2 =
c11, c22 =
M = (pow(c2, B, p) * pow(h, A, p) * inverse(c22, p)) % p
assert(GCD(B-1, p-1) == 1)
secret = int(pow(M, inverse(B-1, p-1), p))
# yafu factor
m0 = 653551912583
m = m0**15
c =
e = 0x1296
assert(GCD(e, (m*(m0-1))//m0) == 2)
c = int(pow(c, inverse(e//2,(m*(m0-1))//m0), m))

def AMM(delta, r, q, k = 1): # get one special a s.t. a**r \equiv delta % q**k where b_x | phi(q**k)
# r, q are supposed to be primes

phi = (q - 1) * q**(k - 1)
mod = q ** k
while 1:
rho = getRandomRange(1, mod)
if(pow(rho, phi // r, mod) == 1):
continue
t = 0
s = phi
while s % r == 0:
s //= r
t += 1
assert gcd(r, s) == 1
alpha = inverse(r, s)
a, b, c, h = pow(rho, phi // r, mod),pow(delta, r * alpha - 1, mod), pow(rho, s, mod), 1
j, k = 0, phi // (r * s)

for i in range(1, t):
k //= r
d = pow(b, k, mod)
if d == 1:
j = 0
else:
j = phi - discrete_log(d, a)
b, h, c = b * pow(c, r * j, mod) % mod, h * pow(c, j, mod), pow(c, r, mod)
return pow(delta, alpha, mod) * h % mod

def allroot(root, r, q, k = 1):
# find all roots satisfy a**r \equiv delta % q**k

phi = (q - 1) * q**(k - 1)
mod = q ** k
all_root = set()
all_root.add(root)
while len(all_root) < r:
new_root = root
unity = pow(getRandomRange(2, mod), phi // r, mod)
for i in range(r - 1):
new_root = (new_root * unity) % mod
all_root.add(new_root)
return all_root

for flag in list(allroot(AMM(c, 2, m0, 15), 2, m0, 15)):
print(long_to_bytes(flag))
# b'you_4re_good_at_b0th_el94mal_and_rs4'

what_r_the_noise

[题解分析]

可无限次获取flag带偏差的ascii值,取平均就行- -就这就这

看出的flag的意思,应该和差分隐私有关,彳亍

[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
from pwn import *
from tqdm import tqdm

io = remote("124.71.145.165", "9999")

io.sendlineafter(":", "2")
flag = [eval(_) for _ in io.recvline().strip().decode()[:-1].split(",")]

def remove_noise(flag, i):
flag_with_noise = [0] * len(flag)
for j in range(2**i):
io.sendlineafter(":", "2")
new_flag_with_noise = [eval(_) for _ in io.recvline().strip().decode()[:-1].split(",")]
flag_with_noise = [flag_with_noise[i] + new_flag_with_noise[i] for i in range(len(flag))]
flag_with_noise = [_ / (2**i) for _ in flag_with_noise]
flag = [(flag[i] + flag_with_noise[i]) / 2 for i in range(len(flag))]
return flag

for i in tqdm(range(10)):
flag = remove_noise(flag, i)
print(bytes([round(_) for _ in flag]))

'''
[+] Opening connection to 124.71.145.165 on port 9999: Done
0%| | 0/10 [00:00<?, ?it/s]b'k^gufxyrt\\gnlvfrwcf_]anvo_ejodftensicq^ptjvfcy\x80'
10%|████████████████▊ | 1/10 [00:00<00:01, 7.45it/s]b'j_euf{ypx_hnmvanvch__`n}pabliffqfnshao_orjueaz~'
20%|█████████████████████████████████▌ | 2/10 [00:00<00:01, 5.56it/s]b'hbdug|xpv`jnnwanvcf`banyr`dkgfdqfoshan`qqjvb`{|'
30%|██████████████████████████████████████████████████▍ | 3/10 [00:00<00:02, 3.47it/s]b'hacug|xou`kmow_lubh`bbows`dkegdrfntian_qqivabx}'
40%|███████████████████████████████████████████████████████████████████▏ | 4/10 [00:02<00:03, 1.84it/s]b'g`cuf{xot`kmow_mvcg`aaovt_dkfffrentiam_privaby}'
50%|████████████████████████████████████████████████████████████████████████████████████ | 5/10 [00:04<00:05, 1.02s/it]b'g`ctf{xou_jnow_mvcg_about_djfffrentiam_priv`cy}'
60%|████████████████████████████████████████████████████████████████████████████████████████████████████▊ | 6/10 [00:08<00:08, 2.04s/it]b'gactf{you_jnow_much_abous_difffrential_privacy}'
70%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▌ | 7/10 [00:17<00:12, 4.13s/it]b'gactf{you_jnow_much_about_differential_privacy}'
80%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▍ | 8/10 [00:35<00:16, 8.27s/it]b'gactf{you_know_much_about_differential_privacy}'
'''

EzAES

[题解分析]

Encryption

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
key = b'T0EyZaLRzQmNe2**' # $O(2^{16})$

def encrypt(message,passphrase,iv):
aes = AES.new(passphrase, AES.MODE_CBC, iv)
return aes.encrypt(message)

h = hashlib.md5(key).hexdigest()
SECRET = binascii.unhexlify(h)[:10]
with open('flag','rb') as f:
IV = f.read().strip(b'gactf{').strip(b'}')
message = b'AES CBC Mode is commonly used in data encryption. What do you know about it?'+SECRET
print("Encrypted data: ", binascii.hexlify(encrypt(pad(message),key,IV)))
'''
Encrypted data: b'a8**************************b1a923**************************011147**************************6e094e**************************cdb1c7**********a32c412a3e7474e584cd72481dab9dd83141706925d92bdd39e4'
'''

分段print下cipher发现:

1
2
3
4
5
6
b'a8**************************b1a9'
b'23**************************0111'
b'47**************************6e09'
b'4e**************************cdb1'
b'c7**********a32c412a3e7474e584cd'
b'72481dab9dd83141706925d92bdd39e4'

于是只要在$O(2^{16})$下爆破key,取最后一块作ECB解密,再与已知的倒二块后10bytes异或,当异或结果为message末位padding(b'\x10'*10),即找到正确key

接下来从后往前逐块进行ECB解密(message已全部得到),即可推出iv

[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
37
38
39
40
from Crypto.Cipher import AES
import binascii, sys, hashlib

key = list(b'T0EyZaLRzQmNe2**')

KEYSIZE = 16
def pad(message):
p = bytes((KEYSIZE - len(message) % KEYSIZE) * chr(KEYSIZE - len(message) % KEYSIZE),encoding='utf-8')
return message + p

cipher = b'a8**************************b1a923**************************011147**************************6e094e**************************cdb1c7**********a32c412a3e7474e584cd72481dab9dd83141706925d92bdd39e4'

def xor(a, b):
return bytes(x ^ y for x, y in zip(a, b))

for i in range(0x100):
for j in range(0x100):
k = bytes(key[:-2] + [i, j])
h = hashlib.md5(k).hexdigest()
SECRET = binascii.unhexlify(h)[:10]
aes = AES.new(k, AES.MODE_ECB)
if xor(aes.decrypt(binascii.unhexlify(cipher[-32:]))[-10:], binascii.unhexlify(cipher[-52:-32])) == bytes([10] * 10):
print(k)
print(SECRET)
'''
b'T0EyZaLRzQmNe2pd'
b'\xfc\x89\xb4\xd5\xe2\x0b\xd2\xc6U\xae'
'''

key = b'T0EyZaLRzQmNe2pd'
SECRET = b'\xfc\x89\xb4\xd5\xe2\x0b\xd2\xc6U\xae'
message = pad(b'AES CBC Mode is commonly used in data encryption. What do you know about it?'+SECRET)
ct = binascii.unhexlify(cipher[-32:])
aes = AES.new(key, AES.MODE_ECB)
for i in range(5):
ct = xor(aes.decrypt(ct), message[-16:])
message = message[:-16]
iv = xor(aes.decrypt(ct), message[-16:])
iv
# b'9j_for_aes_cbc!!'

square

[题解分析]

1
'Please give me 100 (x,y) which satisfies x**2 = ( 1**2 + 2**2 + ... + y**2) / y\n'

找满足$6x^{2}=(y+1)(2y+1)$的100组(x, y)

直接暴力跑出前三组$(x, y)$,然后上oeis找到递推式

https://oeis.org/search?q=1%2C337%2C65521&sort=&language=&go=Search

1
2
3
4
5
6
FORMULA	
a(n) = ((7/2 + 2*sqrt(3))*(97 + 56*sqrt(3))^n + (7/2 - 2*sqrt(3))*(97 - 56*sqrt(3))^n - 3)/4.

a(n) = (floor((7/2 + 2*sqrt(3))*(97 + 56*sqrt(3))^n) - 2)/4.

a(n+3) = 195*(a(n+2) - a(n+1)) + a(n).

[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 *
from pwnlib.util.iters import mbruteforce
import string, re
from hashlib import md5
from gmpy2 import iroot

io = remote("124.71.158.89", "8888")

def proof_of_work():
msg = io.recvline().strip().decode()
suffix = re.findall(r"md5\(str \+ (.*)\)", msg)[0]
cipher = re.findall(r"== (.*)", msg)[0]
proof = mbruteforce(lambda x: md5((x + suffix).encode("latin-1")).hexdigest()[:5] ==
cipher, string.ascii_lowercase + string.digits, length=5, method='fixed')
io.sendlineafter("Give me xxxxx: ", proof)

def send_data():
# a(n+3) = 195*(a(n+2) - a(n+1)) + a(n)
data = [(1, 1), (195, 337), (37829, 65521)]
for i in range(97):
y = 195 * (data[-1][1] - data[-2][1]) + data[-3][1]
x = int(iroot(((y + 1) * (2 * y + 1)) // 6, 2)[0])
data.append((x, y))
for i in range(100):
print(i)
io.sendlineafter("[>] x: ", str(data[i][0]))
io.sendlineafter("[>] y: ", str(data[i][1]))

proof_of_work()
send_data()
io.interactive()
'''
[*] Switching to interactive mode
You are right!
flag is: gactf{congrts_you_solve_it}
'''

babycrypto

[题解分析]

完全出原题可还行,甚至p都一样

https://keltecc.github.io/ctf/writeup/2020/05/24/m0lecon-ctf-2020-teaser-king-exchange.html

基点g,啥提示没有,原题也是屑题,盲猜$x_{g}^{2}+y_{g}^{2}=1(mod\ p)$,由add function可知:

也就是说在g为基点时,mutiply得到的整个域上均满足$x^{2}+y^{2}\equiv 1(mod\ p)$

给出两个点A,B,利用$gcd(x_{A}^{2}+y_{A}^{2}-1,x_{B}^{2}+y_{B}^{2}-1)$得到p

得到p后,想要得到shared = multiply(A, b)[0]

由于前述点集的通项和add function,可将其表示为单位⚪上的点

sage对某个域存在extend函数,参数可传入一个polynomial,作用大概就是将这个域引入该polynomial对应的所有根(包括虚根),再重新形成环的定义后返回一个扩展域(和先前的商环quotient不同)

因此用该扩展域表示单位圆上的点,求解DLP即可得到b,进而获得share

[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
37
38
39
40
41
42
43
44
45
46
A = (68279847973010227567437241690876400434176575735647388141445319082120661, 36521392659318312718307506287199839545959127964141955928297920414981390)
B = (84698630137710906531637499064120297563999383201108850561060383338482806, 10975400339031190591877824767290004140780471215800442883565278903964109)

from Crypto.Util.number import *

p = GCD(A[0]^2+A[1]^2-1, B[0]^2+B[1]^2-1)
factor(p)
# 2^2 * 108848362000185157098908557633810357240367513945191048364780883709439999

p = 108848362000185157098908557633810357240367513945191048364780883709439999
g = (29223879291878505213325643878338189297997503744039619988987863719655098, 32188620669315455017576071518169599806490004123869726364682284676721556)

def add_points(P, Q):
return ((P[0]*Q[0]-P[1]*Q[1]) % p, (P[0]*Q[1]+P[1]*Q[0]) % p)

def multiply(P, n):
Q = (1, 0)
while n > 0:
if n % 2 == 1:
Q = add_points(Q, P)
P = add_points(P, P)
n = n//2
return Q

F = GF(p)
R.<w> = PolynomialRing(F)
K.<w> = F.extension(w^2 + 1)
g_K = g[0] + g[1]*w
B_K = B[0] + B[1]*w
b = discrete_log(B_K, g_K)
print(b)
print(multiply(g, b) == B)
'''
42167952593276919058888649873311585235839247920808784925745017581061391
True
'''

from Crypto.Cipher import AES
from hashlib import sha256

shared = multiply(A, b)[0]
key = sha256(long_to_bytes(shared)).digest()
aes = AES.new(key, AES.MODE_ECB)
plaintext = aes.decrypt(bytes.fromhex("26b1b05962d188f1f2abdfad2cef049d45cfc27d9e46f40ebe52e367941bcfa05dd0ef698f528375be2185759e663431"))
print(plaintext)
# b'gactf{354b6ce4c03387a828a3c30061213204}\t\t\t\t\t\t\t\t\t'