Buckeye CTF
solutions of some challenges in Patriot CTF
Hashbrown
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
...
def aes(block: bytes, key: bytes) -> bytes:
assert len(block) == len(key) == 16
return AES.new(key, AES.MODE_ECB).encrypt(block)
def pad(data):
padding_length = 16 - len(data) % 16
return data + b"_" * padding_length
def hash(data: bytes):
data = pad(data)
state = bytes.fromhex("f7c51cbd3ca7fe29277ff750e762eb19")
for i in range(0, len(data), 16):
block = data[i : i + 16]
state = aes(block, state)
return state
def sign(message, secret):
return hash(secret + message)
...
From the code, we can see that it uses the last state to encrypt the next block. To get the flag, we must send a message containing $french fry$ and its signature. Therefore, I use a length extension attack to achieve this.
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
def aes(block: bytes, key: bytes) -> bytes:
assert len(block) == len(key) == 16
return AES.new(key, AES.MODE_ECB).encrypt(block)
def pad(data):
padding_length = 16 - len(data) % 16
return data + b"_" * padding_length
def hash(data: bytes, state):
# data = pad(data)
# state = bytes.fromhex("f7c51cbd3ca7fe29277ff750e762eb19")
# state = bytes.fromhex(state)
for i in range(0, len(data), 16):
block = data[i : i + 16]
state = aes(block, state)
return state
def sign(message, secret):
return hash(secret + message)
# context.log_level = "debug"
# io = process(["python", "hashbrown.py"])
io = remote("challs.pwnoh.io", 13419)
io.recvuntil(b"Recipe for hashbrowns:\n")
msg = io.recvline().decode()
io.recvuntil(b"Signature:\n")
hsh = bytes.fromhex(io.recvline().decode())
msg = pad(b'1'*16 + my_message)
len_m = len(msg)
new_msg = pad(msg + b"french fry")[len_m:]
new_hsh = hash(new_msg, hsh)
send_m = msg[16:] + b"french fry"
io.sendlineafter(b"Give me recipe for french fry? (as hex)", send_m.hex())
io.sendlineafter(b"Give me your signiature?", new_hsh.hex())
io.interactive()
zkwarmup
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
from math import gcd
import random
import time
# from flag import flag
flag = b"bctf{?????????????????}"
class Verifier:
def __init__(self, y, n):
self.y = y
self.n = n
self.previous_ss = set()
self.previous_zs = set()
def flip_coin(self) -> int:
return random.randrange(2)
def verify(self, s, z, b) -> bool:
if s in self.previous_ss :
print("Bad: repeated s")
return False
if z in self.previous_zs:
print("Bad: repeated r")
return False
self.previous_ss.add(s)
self.previous_zs.add(z)
n = self.n
y = self.y
if s == 0:
print("Bad: s = 0")
return False
if gcd(s, n) != 1:
print("Bad: gcd(s, n) != 1")
return False
return pow(z, 2, n) == (s * pow(y, 1 - b)) % n
def main():
print("Welcome to zkwarmup!")
# p = getPrime(1024)
# q = getPrime(1024)
# n = p * q
n = 19261756194530262169516227535327268535825703622469356233861243450409596218324982327819027354327762272541787979307084854543427241827543331732057807638293377995167826046761991463655794445629511818504788588146049602678202660790161211079215140614149179394809442098536009911202757117559092796991732111808588753074002377241720729762405118846289128192452140379045358673985940236403266552967867241351260376075804662700969038717732248036975281084947926661161892037413944872628410488986135370175176475239647256670545733839891394321932103696968961386864456665963903759123610214930997530883831800104920546270573046968308379346633
print(f"n = {n}")
random.seed(int(time.time()))
x = random.randrange(1, n)
y = pow(x, 2, n)
print(f"y = {y}")
print("Can you prove that you know sqrt(y) without revealing it to the verifier?")
verifier = Verifier(y, n)
n_rounds = 128
for i in range(n_rounds):
s = int(input("Provide s: ")) % n
b = verifier.flip_coin()
print(f"b = {b}")
z = int(input("Provide z: ")) % n
if verifier.verify(s, z, b):
print(f"Verification passed! {n_rounds - i - 1} rounds remaining")
else:
print("Verification failed!")
return
print("You've convinced the verifier you know sqrt(y)!")
print(flag)
if __name__ == "__main__":
main()
- The random seed uses $int(time.time())$ means when we connect to server we can also get the seed $\to$ $x$
- The verify server give the $b$ so we can choose $s = x^{2*i}$ and $z = x^{i}$ or $x^{i} * x$ depend on the $b$
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
io = remote('challs.pwnoh.io', 13421)
io.recvuntil(b"y = ")
y = eval(io.recvline().decode())
for i in range(-10, 10):
random.seed(int(time.time())+i)
n = 19261756194530262169516227535327268535825703622469356233861243450409596218324982327819027354327762272541787979307084854543427241827543331732057807638293377995167826046761991463655794445629511818504788588146049602678202660790161211079215140614149179394809442098536009911202757117559092796991732111808588753074002377241720729762405118846289128192452140379045358673985940236403266552967867241351260376075804662700969038717732248036975281084947926661161892037413944872628410488986135370175176475239647256670545733839891394321932103696968961386864456665963903759123610214930997530883831800104920546270573046968308379346633
x = random.randrange(1, n)
if y == pow(x, 2, n):
break
assert y == pow(x, 2, n)
previous_ss = []
previous_zs = []
for i in range(2, 300):
a = pow(x, 2*i, n)
b = (pow(x, i, n))%n
if a in previous_ss or b in previous_zs:
continue
previous_ss.append(a)
previous_zs.append(b)
previous_s_send = set()
previous_z_send = set()
for s, z in zip(previous_ss[2:], previous_zs[2:]):
if s in previous_s_send or z in previous_z_send:
continue
io.sendlineafter(b"Provide s: ", str(s).encode())
io.recvuntil(b"b = ")
b = eval(io.recvline().decode())
if b == 1:
z_send = z%n
assert pow(z_send, 2, n) == (s * pow(y, 1 - b)) % n
else:
z_send = (z*x)%n
assert pow(z_send, 2, n) == (s * pow(y, 1 - b)) % n
previous_s_send.add(s)
previous_z_send.add(z_send)
io.sendlineafter(b"Provide z: ", str(z_send).encode())
io.interactive()
This post is licensed under CC BY 4.0 by the author.