K1nd4SUSCTF

Posted by Ma3t1n on 2025-03-20
Estimated Reading Time 23 Minutes
Words 4.1k In Total
Viewed Times

Matrices Matrices Matrices

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from sage.all import GF, Matrix
import os, random

assert("FLAG" in os.environ)
FLAG = os.environ["FLAG"]
assert(FLAG.startswith("KSUS{") and FLAG.endswith("}"))

q = 271
qf = GF(q)
m = 70
n = 30

def key_gen():
a = Matrix(qf, [[qf.random_element() for _ in range(n)] for _ in range(m)])
s = Matrix(qf, [[ord(c)] for c in FLAG])
e = Matrix(qf, [[int(round(random.gauss(0, 2/3)))] for _ in range(m)])
b = a * s + e
return s, (a,b)

sk, pk = key_gen()
a, b = pk

print(f"a={[list(a[i]) for i in range(m)]}")
print(f"b={[list(b[i]) for i in range(m)]}")

​ 已知$a_{70 \times 30}*s_{30 \times 1}+e=b$,已知a,b求s

​ 由于q是271,而我们的flag是在[32,256]的,所以不能直接对flag进行格规约,只能规约出e后再对方程进行求解得到flag,所以构造下面的格

$\begin{bmatrix}k_0&k_1&\dots&k_{m-1}&-1\end{bmatrix}\times\begin{bmatrix}a_{0,0} & a_{1,0} & \dots & a_{m-1,0} \\a_{0,1} & a_{1,1} & \dots & a_{m-1,1} \\\vdots & \vdots & \ddots & \vdots \\a_{0,n-1} & a_{1,n-1} & \dots & a_{m-1,n-1} \\p & & & \\& p & & \\& & \ddots & \\& & & p\end{bmatrix}=\begin{bmatrix}e_0&e_1&\dots&e_{m-1}\end{bmatrix}$

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pk import *
from sage.all import *
q = 271
qf = GF(q)
m = 70
n = 30
L=zero_matrix(ZZ,m+n+1,m)
for i in range(m):
L[n+i,i]=q
A=matrix(ZZ,a)
B=matrix(ZZ,b)
L[:n,:]=A.T
L[-1,:]=B.T
t=[-2,-1,0,1,2]
res=L.LLL()
for i in res:
if all(j in t for j in i) and not all(j==0 in t for j in i):
e=i
E=matrix(ZZ,e)

flag=(A.T.change_ring(qf)).solve_left((B.T-E).change_ring(qf))
for k in flag[0]:
print(chr(k),end="")
#KSUS{I_gu3ss_p4r4ms_m4773r_:/}

key in the haystack

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
from Crypto.Util import number
from base64 import b64encode

prime = lambda: number.getPrime(512)
def b64enc(x):
h = hex(x)[2:]
if len(h) % 2:
h = '0' + h
return b64encode(bytes.fromhex(h)).decode()


p = prime()
q = prime()
with open("flag.txt") as f:
flag = f.readline().strip()

n = p * q
m = int(flag.encode().hex(), 16)
c = pow(m, 65537, n)

print("ciphertext:", hex(c)[2:])

bale = [p, q]
bale.extend(prime() for _ in range(1<<6))

def add_hay(stack, straw):
x = stack[0]
for i in range(1, len(stack)):
y = stack[i]
stack[i] = y + (straw * x)
x = y
stack.append(straw * x)

stack = [1]
add_hay(stack, p)
add_hay(stack, q)
for straw in bale:
add_hay(stack, straw)

print("size:", len(stack))
for x in stack:
print(b64enc(x))

​ 先理解题目大意,前面就是一个常规的rsa加密,所以攻击点肯定是后面add_hay对于p,q的一个泄露

1
2
3
4
5
6
7
def add_hay(stack, straw):
x = stack[0]
for i in range(1, len(stack)):
y = stack[i]
stack[i] = y + (straw * x)
x = y
stack.append(straw * x)

​ 仔细阅读add_hay函数,$stack[i,j]=stack[i-1,j]+straw\times stack[i-1,j-1]$,所以
$$
\sum_{j=1}^n{stack[i,j]}=\sum_{j=1}^n{stack[i-1,j]+straw\times stack[i-1,j-1]}=(1+straw)\times \sum_{j=1}^n{stack[i-1,j]}
$$
​ 设$f(x)=\sum_{j=1}^n{stack[x,j]}$,所以$f(x)=(1+straw)*f(x-1)$,题目中给出的$stack$就是$f(x)$的系数,现在分析清楚$stack$后,回去阅读源码可以看出来p,q都经

​ 过了两次了add_hay,其他素数只经过了一次,所以
$$
f(x)=(1+p)^2\times(1+q)^2\times (1+x_0)\times \dots \times (1+x_{63})
$$
​ 我们要求的素数就是方程$f(x)$的根,但是直接对这个方程求解是求解不出来的,所以考虑对$f(x)$求导,设$g(x)=(1+x_0)\times \dots \times (1+x_{63})$
$$
f’(x)=2(1+p)\times 2(1+q) \times g(x)+(1+p)^2\times(1+q)^2\times g’(x)=(1+p)\times (1+q)\times (4\times g(x)+(1+p)\times (1+q)\times g’(x))
$$
​ 所以$gcd(f(x),f’(x))=(1+p)\times (1+q)$

​ 然后对于上的方程求解就可以拿到p,q了,再进行常规的rsa解密即可

代码如下:

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 output import *
from Crypto.Util.number import *
from sympy import gcd, Poly,solve
from sympy.abc import x
import base64

s=tt.split("\n")[:-1]
coeffs=[]
for i in s:
coeffs.append(bytes_to_long(base64.b64decode(i.encode())))
f = Poly(coeffs, x)
f_prime = f.diff(x)
g = gcd(f, f_prime)
a, b, c = g.all_coeffs()
f = (a * x**2 + b * x + c, 0)
solution = solve(f, x)
p = abs(solution[0][0])
q = abs(solution[1][0])
phi = (p - 1) * (q - 1)
e = 65537
d = inverse(e, int(phi))
c = int(ciphertext, 16)
m = pow(c, d, int(p * q))
flag = long_to_bytes(m)
print(flag)
#KSUS{6465726976617469766573206172652061206e69636520747269636b}

Lightning Fast Scrambling

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
from hashlib import sha256
from base64 import b64encode, b64decode

# utility wrapper for hashing

def digest(message):
"Gives a bytes object representing the sha256 encoding of its argument (a sequence of bytes)"
return sha256(message).digest()

# utility wrapper for encoding and decoding

def base64_encode(x):
"Encodes a sequence of bytes in a string, using base64 encoding"
return b64encode(x).decode()

def base64_decode(x):
return b64decode(x, validate=True)

# crypto magic

def create_key(passphrase):
h = passphrase.encode()
h = digest(h)
k = 0
for i in range(8):
k <<= 8
k |= h[i]
return k if k else 1

def secret_byte_stream(key):
x = key
mask = 255
while True:
y = x # 64
a = y & mask
yield a
y >>= 8
x = y
y >>= 1 # 45
a ^= y & mask
y >>= 14 # 31
a ^= y & mask
y >>= 17 # 14
a ^= y & mask
x |= a << 56

def scramble(message, key):
stream = secret_byte_stream(key)
return bytes(x ^ y for x, y in zip(message, stream))

# user-facing stuff

def encrypt(text, passphrase):
message = text.encode()
hash = digest(message)
key = create_key(passphrase)
e = scramble(message, key)
return '#'.join(map(base64_encode, [e, hash]))

def decrypt(text, passphrase):
e, hash = map(base64_decode, text.split('#'))
key = create_key(passphrase)
message = scramble(e, key)
if hash != digest(message):
raise ValueError("Wrong key")
return message.decode()

def create_flag(secret):
return "".join(["KSUS{", secret.encode().hex(), "}"])

if __name__ == "__main__":
secret = input("secret > ")
passphrase = input("passphrase > ")
flag = create_flag(secret)
print("flag :", flag)
challenge = encrypt(flag, passphrase)
assert flag == decrypt(challenge, passphrase)
print("challenge :", challenge)

​ 题目大意是生成一个key然后对flag进行加密,我们需要通过密文还原key后进行解密即可

​ 主要是观察secret_byte_stream函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def secret_byte_stream(key):
x = key
mask = 255
while True:
y = x # 64
a = y & mask
yield a
y >>= 8
x = y
y >>= 1 # 45
a ^= y & mask
y >>= 14 # 31
a ^= y & mask
y >>= 17 # 14
a ^= y & mask
x |= a << 56

​ 第一个a就是x的低八位,key有8字节长,的对于后面的a我们先设$x=\sum_{i=0}^{64}e_i\times 2^i,e_i\in [0,1]$
$$
a=x_{0-7}\\
y=e_8+e_9\times 2+ \dots+e_{63}\times 2^{55}\\
x = e_8+e_9\times 2+ \dots+e_{63}\times 2^{55}\\
y=e_9+e_{10}\times 2 \dots+e_{63}\times 2^{54}\\
a=x_{0-7}\oplus x_{9,16}\\
y=e_{23}+e_{24}\times 2 \dots+e_{63}\times 2^{40}\\
a=x_{0-7}\oplus x_{9-16} \oplus x_{23-30}\\
y=e_{40}+e_{41}\times 2 \dots+e_{63}\times 2^{23}\\
a=x_{0-7}\oplus x_{9-16} \oplus x_{23-30}\oplus x_{40,47}\\
x=e_8+e_9\times 2+ \dots+e_{63}\times 2^{55}+a\times 2^{56}\\
$$

​ 所以在前面8次的secret_byte_stream就是获得key的值,我们已知message的前缀是KSUS{,所以可以通过异或的方式还原key的前40比特,而key是63个比特,所以还需要遍历23个比特就可以拿到正确的key还原flag

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import base64
from Crypto.Util.number import *
from tqdm import *
e=b'VERY/Rjwj1U4DQZ/zyyHxSsMY1iYuOZHs//qWPVYInUz/5cxidrFCrSqco4bbVLpWjHHI4Z+JZOwOfsT'
hash='SUS/PDQPS4DlVum2aO+5+SuczHag7/rnYMBUr+pEqEU='
e=base64.b64decode(e)
t=e.hex()
hash=base64.b64decode(hash)
head=b'KSUS{'
key=b''
s=[t[i*2:(i+1)*2] for i in range(len(t)//2)]
for i in range(len(head)):
key=long_to_bytes(int(s[i],16)^head[i])+key
t=256*256*128
for i in trange(t):
k=long_to_bytes(i).zfill(3)+key
message = scramble(e, bytes_to_long(k))
if hash == digest(message):
print(message)
print(k)
break
#78%|███████▊ | 6548678/8388608 [03:51<01:05,28232.16it/s]
#b'KSUS{6c6673725f6172655f6e6f745f7365637572653038363834363137}
#b'c\xec\xc6c\xae\r\x17\x1f'

key in the big haystack

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
from Crypto.Util import number
from base64 import b64encode

prime = lambda: number.getPrime(512)
def b64enc(x):
h = hex(x)[2:]
if len(h) % 2:
h = '0' + h
return b64encode(bytes.fromhex(h)).decode()


p = prime()
q = prime()
with open("flag.txt") as f:
flag = f.readline().strip()

n = p * q
m = int(flag.encode().hex(), 16)
c = pow(m, 65537, n)

print("ciphertext:", hex(c)[2:])

bale = [p, q]
bale.extend(prime() for _ in range(1<<9))

def add_hay(stack, straw):
x = stack[0]
for i in range(1, len(stack)):
y = stack[i]
stack[i] = y + (straw * x)
x = y
stack.append(straw * x)

stack = [1]
add_hay(stack, p)
add_hay(stack, q)
for straw in bale:
add_hay(stack, straw)
for straw in bale[2:]:
add_hay(stack, straw + 2)

print("size:", len(stack))
for x in stack:
print(b64enc(x))

​ 这个题和前面的key in the haystack类似,只是进行的add_hay次数更多了,数据量更大了,所以直接使用之前的方法是不行的,然后考虑使用hgcd,但是发现由于数据量太大了,4gb的内存仍然是会跑崩溃的,所以需要采取优化,将多项式从整数环更换到一个有限域下进行hgcd,将多项式取模后,然后可以通过gcd求得$(p+1)\times(q+1)$

代码如下:

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
from big_output import *
from Crypto.Util.number import *
import base64

s=coefs.split("\n")[:-1]
coeffs=[]
pp=getPrime(1200)
for i in s:
coeffs.append(bytes_to_long(base64.b64decode(i.encode()))%pp)
def HGCD(a, b):
if 2 * b.degree() <= a.degree() or a.degree() == 1:
return 1, 0, 0, 1
m = a.degree() // 2
a_top, a_bot = a.quo_rem(x^m)
b_top, b_bot = b.quo_rem(x^m)
R00, R01, R10, R11 = HGCD(a_top, b_top)
c = R00 * a + R01 * b
d = R10 * a + R11 * b
q, e = c.quo_rem(d)
d_top, d_bot = d.quo_rem(x^(m // 2))
e_top, e_bot = e.quo_rem(x^(m // 2))
S00, S01, S10, S11 = HGCD(d_top, e_top)
RET00 = S01 * R00 + (S00 - q * S01) * R10
RET01 = S01 * R01 + (S00 - q * S01) * R11
RET10 = S11 * R00 + (S10 - q * S11) * R10
RET11 = S11 * R01 + (S10 - q * S11) * R11
return RET00, RET01, RET10, RET11

def GCD(a, b):
print(a.degree(), b.degree())
q, r = a.quo_rem(b)
if r == 0:
return b
R00, R01, R10, R11 = HGCD(a, b)
c = R00 * a + R01 * b
d = R10 * a + R11 * b
if d == 0:
return c.monic()
q, r = c.quo_rem(d)
if r == 0:
return d
return GCD(d, r)

PR.<x> = PolynomialRing(Zmod(pp))
f1=PR(coeffs)
f2=f1.diff()
res = GCD(f1,f2)
p=pp-inverse(res[0][0],pp)
q=pp-inverse(res[1][0],pp)
phi=(p-1)*(q-1)
e = 65537
d = inverse(e, int(phi))
c = int(ciphertext, 16)
m = pow(c, d, int(p * q))
flag = long_to_bytes(m)
print(flag)
#KSUS{43525420697320612076657279206e69636520747269636b}

Feistel <3

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
64
65
66
67
68
69
70
71
72
from Crypto.Util.number import bytes_to_long, getPrime, long_to_bytes
from Crypto.Util.Padding import pad
import os, signal

assert("FLAG" in os.environ)
FLAG = os.environ["FLAG"]
assert(FLAG.startswith("KSUS{") and FLAG.endswith("}"))

def xor_bytes(bytes_a, bytes_b):
return bytes(a ^ b for a, b in zip(bytes_a, bytes_b)).ljust(2, b'\x00')

def f(sub_block, round_key, modulus):
return long_to_bytes((bytes_to_long(sub_block) + pow(65537, bytes_to_long(round_key), modulus)) % (1<<17-1)).ljust(2, b'\x00')

def encrypt_block(block, key, modulus, rounds=8, shortcut=False):
sub_block_1 = block[:2].ljust(2, b'\x00')
sub_block_2 = block[2:4].ljust(2, b'\x00')
sub_block_3 = block[4:].ljust(2, b'\x00')
for i in range(0, rounds):
round_key = key[i*2:i*2+2]
new_sub_block_1 = xor_bytes(sub_block_1, sub_block_2)
new_sub_block_2 = f(sub_block_3, round_key, modulus)
new_sub_block_3 = xor_bytes(sub_block_2, round_key)
sub_block_1 = new_sub_block_1
sub_block_2 = new_sub_block_2
sub_block_3 = new_sub_block_3
if shortcut and sub_block_1 == b"\xff\xff":
break
return sub_block_1 + sub_block_2 + sub_block_3

def encrypt(plaintext, key, modulus):
iv = os.urandom(6)
padded = pad(plaintext.encode(), 6)
blocks = [padded[i:i+6] for i in range(0, len(padded), 6)]
res = []
for i in range(len(blocks)):
if i == 0: block = xor_bytes(blocks[i], iv)
else: block = xor_bytes(blocks[i], bytes.fromhex(res[-1]))
res.append(encrypt_block(block, key, modulus).hex())
return iv.hex() + "".join(res)

def handle():
key = os.urandom(16)
N = getPrime(1024)
print("flag =", encrypt(FLAG, key, N))
print("N =", N)

encrypted = []
while True:
print("[1] Encrypt")
print("[2] Exit")
opt = input("> ")

if opt == "1":
plaintext = input("Enter your fantastic plaintext (in hex): ")
if len(plaintext) % 2 != 0 or len(plaintext) < 2 or len(plaintext) > 12:
print("It doesn't look fine to me :/")
elif plaintext in encrypted:
print("Nah, you've already encrypted it!")
else:
encrypted.append(plaintext)
ciphertext = encrypt_block(bytes.fromhex(plaintext).rjust(6, b"\x00"), key, N, shortcut=True)
print("Here it is: " + ciphertext.hex())
elif opt == "2":
print("Bye (^-^)")
exit(0)
else:
print("Nope :/")

if __name__ == "__main__":
signal.alarm(300)
handle()

​ 主要是encrypt_block函数

1
if shortcut and sub_block_1 == b"\xff\xff":

​ 所以如果sub_block_1等于b"\xff\xff"就会提前跳出循环,输出密文,我们可以依次拿到sub_block_2的值,也就是f(sub_block_3, round_key, modulus)的值了

1
2
def f(sub_block, round_key, modulus):
return long_to_bytes((bytes_to_long(sub_block) + pow(65537, bytes_to_long(round_key), modulus)) % (1<<17-1)).ljust(2, b'\x00')

$$
(subblock+(65537^{key}\bmod N))\bmod (1<<17-1)
$$

​ 由于知道subblock,上面式子的未知数只有一个key了,但是由于$65537^{key}\bmod N$后是远大于$1<<17-1$的,但是无法使用rsa的解密方法,只有通过遍历两个字节的key寻找正确的key,这个部分可以先进行预处理来优化。

​ 然后通过改变明文,使得加密函数在指定的循环次数后跳出就可以拿到对应的密文,可以从密文中拿到sub_block_2,但是sub_block_3需要的是上一组的,我们可以自己通过现在已知的key自己跑一下函数拿到sub_block_3,但是对于同一组sub_block_2,sub_block_3可能有多个key满足,所以用dfs即可找到正确的key。

​ 找到key后的问题就是这个题只给了加密函数,没有解密函数,还需要自己写一个解密函数,由于对于相同的位置可能有多个字符满足解密的条件,所以有的时候解密结果会有几个字符是乱码,有乱码的时候就需要对那两个字符进行遍历了,然后通过加密通过密文就可以找到正确的flag。

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
def decrypt_block(cipher_block, key, modulus, rounds=8):
s1 = cipher_block[:2].ljust(2, b"\x00")
s2 = cipher_block[2:4].ljust(2, b"\x00")
s3 = cipher_block[4:6].ljust(2, b"\x00")

for i in reversed(range(rounds)):
round_key = key[i * 2 : (i + 1) * 2]
# Calculate previous sub_blocks
prev_s2 = xor_bytes(s3, round_key)
K_i_val = bytes_to_long(round_key)
exponent = pow(65537, K_i_val, modulus)
s2_val = bytes_to_long(s2)
prev_s3_val = (s2_val - exponent) % 65536
prev_s3 = long_to_bytes(prev_s3_val).ljust(2, b"\x00")
prev_s1 = xor_bytes(s1, prev_s2)
# Update for next iteration
s1, s2, s3 = prev_s1, prev_s2, prev_s3
return s1 + s2 + s3


def decrypt(ciphertext_hex, key, modulus):
iv = bytes.fromhex(ciphertext_hex[:12])
ciphertext = bytes.fromhex(ciphertext_hex[12:])
blocks = [ciphertext[i : i + 6] for i in range(0, len(ciphertext), 6)]

previous = iv
plaintext_blocks = []
for block in blocks:
decrypted = decrypt_block(block, key, modulus)
plaintext = xor_bytes(decrypted, previous)
plaintext_blocks.append(plaintext)
previous = block
padded = b"".join(plaintext_blocks)
return padded

代码如下:

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
from Crypto.Util.number import  *
from tqdm import *
from Crypto.Util.Padding import pad
from pwn import *
def encrypt(plaintext, key, modulus):
iv = os.urandom(6)
padded = pad(plaintext.encode(), 6)
blocks = [padded[i:i+6] for i in range(0, len(padded), 6)]
res = []
for i in range(len(blocks)):
if i == 0: block = xor_bytes(blocks[i], iv)
else: block = xor_bytes(blocks[i], bytes.fromhex(res[-1]))
res.append(encrypt_block(block, key, modulus).hex())
return iv.hex() + "".join(res)
def decrypt_block(cipher_block, key, modulus, rounds=8):
s1 = cipher_block[:2].ljust(2, b"\x00")
s2 = cipher_block[2:4].ljust(2, b"\x00")
s3 = cipher_block[4:6].ljust(2, b"\x00")

for i in reversed(range(rounds)):
round_key = key[i * 2 : (i + 1) * 2]
# Calculate previous sub_blocks
prev_s2 = xor_bytes(s3, round_key)
K_i_val = bytes_to_long(round_key)
exponent = pow(65537, K_i_val, modulus)
s2_val = bytes_to_long(s2)
prev_s3_val = (s2_val - exponent) % 65536
prev_s3 = long_to_bytes(prev_s3_val).ljust(2, b"\x00")
prev_s1 = xor_bytes(s1, prev_s2)
# Update for next iteration
s1, s2, s3 = prev_s1, prev_s2, prev_s3
return s1 + s2 + s3
def decrypt(ciphertext_hex, key, modulus):
iv = bytes.fromhex(ciphertext_hex[:12])
ciphertext = bytes.fromhex(ciphertext_hex[12:])
blocks = [ciphertext[i : i + 6] for i in range(0, len(ciphertext), 6)]

previous = iv
plaintext_blocks = []
for block in blocks:
decrypted = decrypt_block(block, key, modulus)
plaintext = xor_bytes(decrypted, previous)
plaintext_blocks.append(plaintext)
previous = block
padded = b"".join(plaintext_blocks)
return padded
def f(sub_block, round_key, modulus):
return long_to_bytes((bytes_to_long(sub_block) + pow(65537, bytes_to_long(round_key), modulus)) % (1<<17-1)).ljust(2, b'\x00')
def xor_bytes(bytes_a, bytes_b):
return bytes(a ^ b for a, b in zip(bytes_a, bytes_b)).ljust(2, b'\x00')
def encrypt_block(block, key, modulus, rounds=8, shortcut=False):
sub_block_1 = block[:2].ljust(2, b'\x00')
sub_block_2 = block[2:4].ljust(2, b'\x00')
sub_block_3 = block[4:].ljust(2, b'\x00')
for i in range(0, rounds):
round_key = key[i*2:i*2+2]
new_sub_block_1 = xor_bytes(sub_block_1, sub_block_2)
new_sub_block_2 = f(sub_block_3, round_key, modulus)
new_sub_block_3 = xor_bytes(sub_block_2, round_key)
sub_block_1 = new_sub_block_1
sub_block_2 = new_sub_block_2
sub_block_3 = new_sub_block_3
if shortcut and sub_block_1 == b"\xff\xff":
break
return sub_block_1 + sub_block_2 + sub_block_3
def getblock(key,N):
while 1:
block=os.urandom(6)
sub_block_1 = block[:2].ljust(2, b'\x00')
sub_block_2 = block[2:4].ljust(2, b'\x00')
sub_block_3 = block[4:].ljust(2, b'\x00')
for i in range(0, len(key)//2):
round_key = key[i*2:i*2+2]
new_sub_block_1 = xor_bytes(sub_block_1, sub_block_2)
new_sub_block_2 = f(sub_block_3, round_key, N)
new_sub_block_3 = xor_bytes(sub_block_2, round_key)
sub_block_1 = new_sub_block_1
sub_block_2 = new_sub_block_2
sub_block_3 = new_sub_block_3
if xor_bytes(sub_block_1, sub_block_2) == b"\xff\xff":
return block,sub_block_3

r=remote("chall.ctf.k1nd4sus.it" ,31013)
s=r.recvuntil(b'flag =')
enc=r.recvuntil(b'\n')[1:-1].decode()
print(enc)
s=r.recvuntil(b'N =')
N=r.recvuntil(b'\n')
N=int(N[1:-1].decode())
print(N)
block=b'\xff\xff\x00\x00\x00\x00'
table={}
for i in range(256*256):
table[pow(65537,i,N)%(1<<17-1)]=i
def getkey(blo,N,key_gauss):
block,sub_block_3=blo
if len(key_gauss)==16:
print(key_gauss.hex())
print(decrypt(enc,key_gauss,N))
return
s=r.recvuntil(b'> ')
r.sendline(b'1')
s=r.recvuntil(b'Enter your fantastic plaintext (in hex): ')
r.sendline(block.hex().encode())
s=r.recvuntil(b'\n')
s=bytes.fromhex(s[12:-1].decode())
sub_block_2 = s[2:4].ljust(2, b'\x00')
for i in range(256*256):
if (bytes_to_long(sub_block_3)+table[i]) % (1<<17-1)==bytes_to_long(sub_block_2):
getkey(getblock(key_gauss+long_to_bytes(i),N),N,key_gauss+long_to_bytes(i))
getkey((block,block[4:]),N,b'')
# [x] Opening connection to chall.ctf.k1nd4sus.it on port 31013
# [x] Opening connection to chall.ctf.k1nd4sus.it on port 31013: Trying 93.51.172.89
# [+] Opening connection to chall.ctf.k1nd4sus.it on port 31013: Done
# 78ebcce695b0a20acb13ca2325fee2b7843bfa35d29e3016549d55fbd4e7921ba4e032d6bae329d42d5c5a21893ddf1c6d39efd1ab8f
# 160567778636678363533387608589227022367589625364449765706869244724671309007529798864529549780027199542599083542499712081507485065989357127460874901093077781176489298361491082655502233043626890749817831452922576411457036925692230037642615730016579409199901040153611870207185696871839223986353183916394898475311
# 697604db1ebb3f1398d77cbd1d46c376
# b'\x12\x8f\x87\x93FT5*\xa5BG\xff\xb4\x07\x92\x1f\xc5\xb29Fv\x93\xed\x8e\x92r2\xa9\x96\xaes(L\x1e\x04\xad\x03\x14\xa3k\xddgu\xe2\xa3\xc6e\xdc'
# 697604db1ebb3f1398d7b1350d7f60a6
# b'e\x1fe\xb3\x1f\x14\xdf\xf4eRl\xa1t\xcf2a\x98\x12166c\xe8\x16!:\xa3S\xf6\x8e\xe1@\xeft.=A\x14\xc3k\xbew\xccb\x06fB\xcc'
# 697604db1ebb3f1398d7b13565ee5184
# b'KSUS{N3veR_Ev3r_5hOr7cuT_F3ist3l_Ne7w0rks}\x06\x06\x06\x06\x06\x06'

Back the Hank

1
2
3
4
5
6
7
8
9
10
11
The Kindasus Bank prioritizes the security of its users by implementing two separate databases to store partial user information.

However, the bank has introduced two distinct login methods, which is kinda sus.

Embark on a journey through the bank's website!

HINTS: go to the discord "announcements" channel

VGhpcyBpcyB0aGUgc2Vjb25kIGhpbnQg

http://chall.ctf.k1nd4sus.it:31337

​ 题目只有这些,也没有附件,感觉需要web的相关知识,算了不看了😁😁

总体来说题目质量还是不错的,国际赛还是挺好玩的


如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !