Secure Vault - WICSME CTF 2022
Challenge:
#!/usr/bin/python3
import os
import random
import ecdsa
from hashlib import sha256
from sys import exit
get_k = (
lambda: os.urandom(32)[random.randrange(0, 32)]
^ os.urandom(32)[random.randrange(0, 32)] + 1
)
def b2l(x): return int.from_bytes(x, "big")
def l2b(x): return x.to_bytes(64, "big")
sk = ecdsa.SigningKey.generate(curve=ecdsa.SECP256k1, hashfunc=sha256)
vk = sk.get_verifying_key()
users = {"admin": ["flag{XXXXXXXXXXXXX}"]}
connected = 0
User = None
def welcome():
print(
"This is the vault of secrets! You can use it to keep secrets inside, don't worry they are perfectly safe thanks to latest signing encryption. This is a beta version, you can only store 10 secrets."
)
def view():
if len(users[User]):
print("\n".join(f"{i+1}] {secret}" for i,
secret in enumerate(users[User])))
else:
print("No secrets to show")
def login():
global connected, User
data = input("login -> ")
if "-" not in data:
print("Wrong format")
return
creds, name = data.split("-", 1)
if not creds.isdigit():
print("Credentials must be integer")
return
if name in users and vk.verify(l2b(int(creds)), name.encode()):
connected = 1
User = name
print(f"Succesfully logged as user : {name}")
return
print("Wrong signing key")
def register():
global users
name = input("name -> ")
if name not in users:
creds = b2l(sk.sign(name.encode(), k=get_k()))
users[name] = []
print("succesfully registered")
print(f"login credentials : {creds}-{name}")
return
print("User already exist")
def add_secret():
if len(users[User]) < 11:
secret = input("secret -> ")
users[User].append(secret)
print("Secret has been succesfully added")
return
print("Max secrets limit reached")
def main():
welcome()
funcs = {0: [exit, login, register], 1: [exit, view, add_secret]}
while True:
choice = None
if connected:
print("1. View secrets")
print("2. Add secret")
else:
print("1. login")
print("2. register")
print("0. Exit")
while choice not in ["1", "2", "0"]:
choice = input("> ")
funcs[connected][int(choice)]()
if __name__ == "__main__":
main()
Solve:
\[\text{priv} \equiv \frac{s1 \cdot k1 - z1}{r1} \equiv \frac{s2 \cdot k2 - z2}{r2} \ (mod \ n)\]from pwn import remote
from hashlib import sha256
from tqdm import tqdm
from ecdsa import SECP256k1
from Crypto.Util.number import long_to_bytes, bytes_to_long
def read():
print(io.read().decode())
def b2l(x):
return int.from_bytes(x, "big")
def l2b(x):
return x.to_bytes(64, "big")
io = remote("127.0.0.1", 1337)
read()
# Collect 2 signatures
io.sendline(b"2")
read()
io.sendline(b"random name 1")
S1 = l2b(int(io.read().decode().split()[5][:-7]))
io.sendline(b"2")
read()
io.sendline(b"random name 2")
S2 = l2b(int(io.read().decode().split()[5][:-7]))
r1 = b2l(S1[:32])
s1 = b2l(S1[32:])
r2 = b2l(S2[:32])
s2 = b2l(S2[32:])
n = SECP256k1.order
z1 = b2l(sha256(b"random name 1").digest())
z2 = b2l(sha256(b"random name 2").digest())
# bruteforce attack
for k1 in tqdm(range(256)):
for k2 in range(256):
if ((s1*k1-z1) * pow(r1, -1, n)) % n == ((s2*k2-z2) * pow(r2, -1, n)) % n:
priv = ((s1*k1-z1) * pow(r1, -1, n)) % n
# send admin signature to get flag
k = 2
x = int((SECP256k1.generator * k).x())
y = int((SECP256k1.generator * k).y())
r = x % n
s = pow(k, -1, n) * (bytes_to_long(sha256(b"admin").digest()) + r * priv) % n
admin_sig = str(b2l(long_to_bytes(r) + long_to_bytes(s))) + "-admin"
io.sendline(b"1")
read()
io.sendline(admin_sig.encode())
read()
io.sendline(b"1")
read()
# flag{S4M3_K3yS_M34n5_7rouBLes_F0R_EC}