2022. 11. 13. 17:06, CTF/Crypto
prob.py
import os
import signal
import random
import secrets
FLAG = os.getenv("FLAG", "fake{cast a special spell}")
def janken(a, b):
return (a - b + 3) % 3
signal.alarm(1000)
print("kurenaif: Hi, I'm a crypto witch. Let's a spell battle with me.")
witch_spell = secrets.token_hex(16)
witch_rand = random.Random()
witch_rand.seed(int(witch_spell, 16))
print(f"kurenaif: My spell is {witch_spell}. What about your spell?")
your_spell = input("your spell: ")
your_random = random.Random()
your_random.seed(int(your_spell, 16))
for _ in range(666):
witch_hand = witch_rand.randint(0, 2)
your_hand = your_random.randint(0, 2)
if janken(your_hand, witch_hand) != 1:
print("kurenaif: Could you come here the day before yesterday?")
quit()
print("kurenaif: Amazing! Your spell is very powerful!!")
print(f"kurenaif: OK. The flag is here. {FLAG}")
It is well-known that python random module is not cryptophically secure. All we need is to find a integer seed which generates 666 target values. When
I first tried to analyze the exact logic for the random module in python but it is too boring. Luckily I find this repository and it contains almost everything I want.
Since the whole code is too long, I will introduce a handmade method in Breaker class.
mersenne.py (partial)
class BreakerPy(Breaker):
# ...
def state_recovery_rand_partial(self, outputs):
"""
state recovery for given prob
"""
MT = [BitVec(f'MT[{i}]',32) for i in range(624)]
values = []
start_time = time()
S = Solver()
for i in range(len(outputs)):
if i%624==0:
self.twist_state(MT)
S.add(LShR(self.tamper_state(MT[i%624]),30)==outputs[i])
if S.check()==sat:
print("time taken :",time()-start_time)
model = S.model()
mt = {str(i): model[i].as_long() for i in model.decls()}
mt = [mt[f'MT[{i}]'] for i in range(len(model))]
return mt
# ...
I find a candidate mersenne state using state_recovery_rand_partial method then find a corresponding seed.
solver.py
from pwn import *
from mersenne import *
import random
def conv(a):
return (a+1)%3
r = remote("janken-vs-kurenaif.seccon.games", 8080)
r.recvuntil("spell is ")
spell = r.recvuntil(".")[:-1]
print(spell)
witch_rand = random.Random()
witch_rand.seed(int(spell, 16))
outputs = [conv(witch_rand.randint(0, 2)) for _ in range(666)]
b = BreakerPy()
mt = b.state_recovery_rand_partial(outputs)
print("mt recovered")
R = random.Random()
R.setstate((3, tuple(mt) + (624,), None))
for i in range(666):
assert(R.randint(0,2) == outputs[i])
print("assert passed")
R = random.Random()
#R.seed((1))
R.setstate((3, tuple(mt) + (624,), None))
output32 = [R.getrandbits(32) for _ in range(624)]
seeds = b.get_seeds_python(output32, 624)
if not seeds:
exit()
print("!!! seeds!!!!", seeds)
seed_int = 0
for i in range(len(seeds)):
seed_int |= (seeds[i] << (32*i))
r.sendline(hex(seed_int))
r.interactive()
'CTF > Crypto' 카테고리의 다른 글
[RCTF 2022] superguess (0) | 2022.12.13 |
---|---|
[RCTF 2022] magicsign (0) | 2022.12.13 |
[RCTF 2022] guess (2) | 2022.12.13 |
[SECCON CTF 2022] this_is_not_lsb (0) | 2022.11.13 |
[LINE CTF 2022] lazy_stek (0) | 2022.03.27 |
[LINE CTF 2022] Forward-or (0) | 2022.03.27 |
Comments