Hack.lu 2013: ECKA

Hey you!
Come over and help me, please. We discovered a strange key agreement protocol used on this server: ctf.fluxfingers.net:1330. They send a curve which they use later. But we think the robots are a bit UBER-cautious and do not use the curve’s P. So they first exchange their public point with a technique we could not figure out. It looks like they do not need a public key for this step. Afterwards they use another technique to agree on a key which they ultimately use to send some encrypted password.

We need this last password to shut down the robo-factory on our way to the Oktoberfest.

Oh btw, the robots use AES-ECB for symmetric encryption.

Hint: He, we have the latest news for you. The first part of their strange key agreement was designed by the famous SHA-Robot Мир!

First we started to find out what happens when you connect to the service. So we found out ECKA stands for Elliptic Curve Key Agreement and learned all translations for Мир.

After googeling “sha mir” (“mir” is the transcription for “Мир”) we obviously realized, that the first part of the key agreement could be Shamir’s three-pass-protocol. For the second part we guessed a Diffie-Hellman key exchange.

Shamir’s three-pass-protocol with elliptic curves

  1. Alice and Bob generate a secret and its inverse
  2. Alice selects a point
  3. Alice encrypts with her secret:
  4. Bob encrypts with his secret:
  5. Alice decrypts with the known inverse
  6. Bob decrypts with the known inverse
  7. knows now

Diffie-Hellmann with elliptic curves

  1. Alice and Bob generate both a secret
  2. Alice sends to Bob
  3. Bob sends to Alice
  4. Both know the secret

The easiest way to implement this key exchange is, by using as a secret each round. So the following script decrypts the flag b3nDer_<3_3PDHKE with about a 50/50 chance.

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
#!/usr/bin/env python

from sock import Sock
from Crypto.Cipher import AES

s = Sock("ctf.fluxfingers.net", 1330)
# ignore first line
s.read_until("\n")
# get a * P
aP = s.read_until("\n")
# send 1 * a * P == a * P
s.send(aP)
# get 1*P = P
P = s.read_until("\n")
# get b * P
bP = s.read_until("\n")
# send P
s.send(P)
# get cypher
cypher = s.read_until("\n")

# decrypt the cypher using b * P as the key
key = ("%064x" % int(bP[1:-2].split(" : ")[0])).decode("hex")
crypt = AES.new(key, AES.MODE_ECB)
flag = crypt.decrypt(cypher.strip().decode("base64")[:-1])
print flag