Skip to content

Latest commit

 

History

History

srpp

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

Srpp (Crypto, 231p)

###ENG PL

We are given quite complicated file, but after simplification, the only important part is this:

params = getParams(nbit)
N, g, k = params
email = '[email protected]'
client.send('params = (N, g, k) = ' + str(params) + '\n')
salt = urandom(32)
N, g, _ = params
x = Hash(salt, email, password)
verifier = pow(g, x, N)

client.send('Send the email address and the public random positive value A seperated by "," as "email, A": ' + '\n')
ans = client.recv(_bufsize).strip()
print ans
email, A = ans.split(',')
A = int(A)
assert (A != 0 and A != N), client.send('Are you kidding me?! :P' + '\n')
assert email == '[email protected]', client.send('You should login as [email protected]' + '\n')
b = getRandomRange(1, N)
B = (k * verifier + pow(g, b, N)) % N

client.send('(salt,  public_ephemeral) = (%s, %d) \n' % (salt.encode('base64')[:-1], B))

u = Hash(A, B)

client.send('Send the session key: ' + '\n')
K_client = client.recv(_bufsize).strip()
assert K_client.isdigit(), client.send('Please send a valid positive integer as session key.' + '\n')
K_client = int(K_client)

S_s = pow(A * pow(verifier, u, N), b, N)
print 'S_s', S_s
K_server = Hash(S_s)
print 'K_server', K_server

client.send('Send a POC of session key: ' + '\n')
M_client = client.recv(_bufsize).strip()

assert M_client.isdigit(), client.send('Please send valid positive integer as POC.' + '\n')
M_client = int(M_client)

assert (K_server == K_client), client.send('The session key is not correct!' + '\n')
assert (M_client == Hash(Hash(N) ^ Hash(g), Hash(email), salt, A, B, K_client)), client.send('The POC is not correct!' + '\n')

M_server = Hash(A, M_client, K_server) # TODO: check server POC in clinet side

client.send('Great, you got the flag: ' + flag + '\n')
client.close()

This looks complicated, and hard to break, but let's look again. There are basically two checks we have to pass:

assert (K_server == K_client), client.send('The session key is not correct!' + '\n')
assert (M_client == Hash(Hash(N) ^ Hash(g), Hash(email), salt, A, B, K_client)), client.send('The POC is not correct!' + '\n')

And the second one is no-issue (because every variable used in assert is given to us by challenge!). So we only have to calculate K_client. How are we going to achieve this? Well this looks impossible - algorithm here is almost bulletproof. Almost - except one small scar...

S_s = pow(A * pow(verifier, u, N), b, N)

A here is controlled by us. What if we could send A=0? Recovering S_s would be trivial (0*anything == 0). Alternatively we could send A=N. Unfortunatelly, challenge authors have thought about it:

assert (A != 0 and A != N), client.send('Are you kidding me?! :P' + '\n')

But wait, what if we could send A=2N?

Well, this wasn't thought about, and this is how we solved this challenge.

The only nontrivial part is this:

A = 2*N
K_client = 43388321209941149759420236104888244958223766953174235657296806338137402595305  # hardcoded K_client assuming that S_s = 0
s.send(email + ', ' + str(A) + '\n')
s.send(str(K_client) + '\n')

The rest is just basic mathematical operations on numbers given to us by challenge:

def proof_of_work(s):
    data = recvuntil(s, ["Enter X:"])
    x_suffix, hash_prefix = re.findall("X \+ \"(.*)\"\)\.hexdigest\(\) = \"(.*)\.\.\.\"", data)[0]
    len = int(re.findall("\|X\| = (.*)", data)[0])
    print(data)
    print(x_suffix, hash_prefix, len)
    for x in itertools.product(string.ascii_letters + string.digits, repeat=len):
        c = "".join(list(x))
        h = hashlib.sha512(c + x_suffix).hexdigest()
        if h.startswith(hash_prefix):
            return c

print s.recv(9999)

proof = proof_of_work(s)
print proof
s.send(proof)

kot = recvuntil(s, ["public random"])
print kot

email = '[email protected]'
ls = kot.split('\n')
for l in ls:
    if 'params' in l:
        data = l[21:]
        N, g, k = eval(data)
        print N, g, k

A = 2*N
K_client = 43388321209941149759420236104888244958223766953174235657296806338137402595305
s.send(email + ', ' + str(A) + '\n')
s.send(str(K_client) + '\n')

# almost done, just need to calculate POC

kot = recvuntil(s, ['session key'])
print kot
ls = kot.split('\n')
for l in ls:
    if 'salt' in l:
        data = l[28:]
        salt, B = re.findall("\((.*),(.*)\)", data)[0]
        salt = salt.decode('base64')
        B = B.strip()
        print salt, B

poc = Hash(Hash(N) ^ Hash(g), Hash(email), salt, A, B, K_client)

print poc
s.send(str(poc))

print s.recv(999)

And that's it.

###PL version

Dostajemy całkiem skomplikowany plik, ale po uproszczeniu, jedyna skomplikowana część to to:

params = getParams(nbit)
N, g, k = params
email = '[email protected]'
client.send('params = (N, g, k) = ' + str(params) + '\n')
salt = urandom(32)
N, g, _ = params
x = Hash(salt, email, password)
verifier = pow(g, x, N)

client.send('Send the email address and the public random positive value A seperated by "," as "email, A": ' + '\n')
ans = client.recv(_bufsize).strip()
print ans
email, A = ans.split(',')
A = int(A)
assert (A != 0 and A != N), client.send('Are you kidding me?! :P' + '\n')
assert email == '[email protected]', client.send('You should login as [email protected]' + '\n')
b = getRandomRange(1, N)
B = (k * verifier + pow(g, b, N)) % N

client.send('(salt,  public_ephemeral) = (%s, %d) \n' % (salt.encode('base64')[:-1], B))

u = Hash(A, B)

client.send('Send the session key: ' + '\n')
K_client = client.recv(_bufsize).strip()
assert K_client.isdigit(), client.send('Please send a valid positive integer as session key.' + '\n')
K_client = int(K_client)

S_s = pow(A * pow(verifier, u, N), b, N)
print 'S_s', S_s
K_server = Hash(S_s)
print 'K_server', K_server

client.send('Send a POC of session key: ' + '\n')
M_client = client.recv(_bufsize).strip()

assert M_client.isdigit(), client.send('Please send valid positive integer as POC.' + '\n')
M_client = int(M_client)

assert (K_server == K_client), client.send('The session key is not correct!' + '\n')
assert (M_client == Hash(Hash(N) ^ Hash(g), Hash(email), salt, A, B, K_client)), client.send('The POC is not correct!' + '\n')

M_server = Hash(A, M_client, K_server) # TODO: check server POC in clinet side

client.send('Great, you got the flag: ' + flag + '\n')
client.close()

Wygląda na skomplikowane i trudne do złamania. Ale w sumie, są tylko dwa testy które musimy przejść:

assert (K_server == K_client), client.send('The session key is not correct!' + '\n')
assert (M_client == Hash(Hash(N) ^ Hash(g), Hash(email), salt, A, B, K_client)), client.send('The POC is not correct!' + '\n')

I drugi z nich to żaden problem (bo każda zmienna użyta w assercie jest wysyłana do nas przez serwer zadania) - więc musimy tylko obliczyć K_client. Jak zamierzamy to zrobić? Cóż, wygląda to na niemożliwe - algorytm jest prawie że kuloodporny. Prawie że - ma jeden drobny problem...

S_s = pow(A * pow(verifier, u, N), b, N)

A tutaj jest kontrolowane przez nas. Co jeśli wysłalibyśmy A=0? Odzyskanie S_s byłoby trywialne (0*cokolwiek = 0). Albo moglibyśmy wysłać A=N. Niestety, twórcy zadania pomyśleli o tym:

assert (A != 0 and A != N), client.send('Are you kidding me?! :P' + '\n')

Hmm, ale co jeśli wyślemy A=2N?

Cóż, nikt o tym widać nie pomyślał, i w ten sposób rozwiązaliśmy to zadanie.

Jedyna nietrywialna część naszego kodu to to:

A = 2*N
K_client = 43388321209941149759420236104888244958223766953174235657296806338137402595305  # hardcoded K_client assuming that S_s = 0
s.send(email + ', ' + str(A) + '\n')
s.send(str(K_client) + '\n')

Reszta to podstawowe operacje matematyczne na liczbach które dostajemy od zadania:

def proof_of_work(s):
    data = recvuntil(s, ["Enter X:"])
    x_suffix, hash_prefix = re.findall("X \+ \"(.*)\"\)\.hexdigest\(\) = \"(.*)\.\.\.\"", data)[0]
    len = int(re.findall("\|X\| = (.*)", data)[0])
    print(data)
    print(x_suffix, hash_prefix, len)
    for x in itertools.product(string.ascii_letters + string.digits, repeat=len):
        c = "".join(list(x))
        h = hashlib.sha512(c + x_suffix).hexdigest()
        if h.startswith(hash_prefix):
            return c

print s.recv(9999)

proof = proof_of_work(s)
print proof
s.send(proof)

kot = recvuntil(s, ["public random"])
print kot

email = '[email protected]'
ls = kot.split('\n')
for l in ls:
    if 'params' in l:
        data = l[21:]
        N, g, k = eval(data)
        print N, g, k

A = 2*N
K_client = 43388321209941149759420236104888244958223766953174235657296806338137402595305
s.send(email + ', ' + str(A) + '\n')
s.send(str(K_client) + '\n')

# almost done, just need to calculate POC

kot = recvuntil(s, ['session key'])
print kot
ls = kot.split('\n')
for l in ls:
    if 'salt' in l:
        data = l[28:]
        salt, B = re.findall("\((.*),(.*)\)", data)[0]
        salt = salt.decode('base64')
        B = B.strip()
        print salt, B

poc = Hash(Hash(N) ^ Hash(g), Hash(email), salt, A, B, K_client)

print poc
s.send(str(poc))

print s.recv(999)

I to w sumie tyle, wystarczyło to do zdobycia flagi.