In this challenge we need to guess the secret key used by an encryption service running over UDP.
We get the source of the server-side.
The encryption algorithm uses a Sbox that is initialized with sequential numbers from 1 to 128:
Then the secret key is mixed in the Sbox using element permutations:
def add_key(sbox, k):
for i, c in enumerate(k):
sbox[i], sbox[ord(c)] = sbox[ord(c)], sbox[i]
for i in xrange(len(sbox)):
sbox[i] = (sbox[i] + 1) % 128
return
The packet format for this service is then one UDP packet per request as this:
[[C1][C2]..[Cn]] where n < 64
Command block is divided in 2 equals parts:
k = data[:mid].rstrip("\x00")
m = data[mid:].rstrip("\x00")
c = encrypt(SALTED_SBOX, k, m)
f.sendto(c.encode("hex"), addr)
One part, k, is used to mix more data in the Sbox part and the other part, m, is used for encryption
sbox = sbox[::]
add_key(sbox, k)
c = ""
for ch in m:
c += chr(sbox[ord(ch)])
sbox = combine(sbox, sbox)
return c
The combine function is full of evil remix so we need to avoid it if we want to keep track of the state of the Sbox:
ret = [-1] * len(b)
for i in range(len(b)):
ret[i] = a[b[i]]
return tuple(ret)
As the Sbox is copied over anew for each request (sbox = sbox[::]), if we send a single m part, we will avoid the combine.
So our strategy:
– Send 127 2-bytes packets with an empty k part (NUL character) and just one m part request to dump the Sbox from the server
– The dump will miss offset 0 of the Sbox so we use one add additionnal packet to swap offset 0 and 1 in the Sbox and request offset 1 again to fill in our local Sbox offset 0
– Revert the mixing algorithm using the dumped Sbox to recover the key
Source code:
import os, sys
import socket
host = sys.argv[1]
addr = (host, 7777)
s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sbox = list(range(128))
for i in xrange(1,128):
msg = chr(0) + chr(i)
s.sendto(msg,addr)
reply, addr = s.recvfrom(1024)
sbox[i] = ord(reply.decode("hex"))
msg = chr(1) + chr(1)
s.sendto(msg,addr)
reply, addr = s.recvfrom(1024)
sbox[0] = ord(reply.decode("hex")) - 1
print "Dumped Sbox=", repr(sbox)
local_sbox = list(range(128))
key_length = sbox[127]
print "Key length=", key_length + 1
for k in xrange(0,128):
sbox[k] = (sbox[k] - key_length - 1) % 128
flag = ''
i = 0
while i < key_length+1:
a = sbox[i]
if a:
c = local_sbox.index(a)
print "Position=", i, "Remote Sbox=", a, "Local Sbox offset=", c, "Char=", repr(chr(c))
flag = flag + chr(c)
local_sbox[i], local_sbox[c] = local_sbox[c], local_sbox[i]
for z in xrange(len(local_sbox)):
local_sbox[z] = (local_sbox[z] + 1) % 128
sbox[z] = (sbox[z] + 1) % 128
i = i + 1
print "flag=", flag
Run it:
Key length= 26
Position= 0 Remote Sbox= 60 Local Sbox offset= 60 Char= '<'
Position= 1 Remote Sbox= 106 Local Sbox offset= 105 Char= 'i'
Position= 2 Remote Sbox= 117 Local Sbox offset= 115 Char= 's'
Position= 3 Remote Sbox= 99 Local Sbox offset= 96 Char= '`'
Position= 4 Remote Sbox= 120 Local Sbox offset= 116 Char= 't'
Position= 5 Remote Sbox= 109 Local Sbox offset= 104 Char= 'h'
Position= 6 Remote Sbox= 55 Local Sbox offset= 49 Char= '1'
Position= 7 Remote Sbox= 9 Local Sbox offset= 115 Char= 's'
Position= 8 Remote Sbox= 11 Local Sbox offset= 96 Char= '`'
Position= 9 Remote Sbox= 116 Local Sbox offset= 107 Char= 'k'
Position= 10 Remote Sbox= 61 Local Sbox offset= 51 Char= '3'
Position= 11 Remote Sbox= 4 Local Sbox offset= 121 Char= 'y'
Position= 12 Remote Sbox= 20 Local Sbox offset= 96 Char= '`'
Position= 13 Remote Sbox= 121 Local Sbox offset= 108 Char= 'l'
Position= 14 Remote Sbox= 62 Local Sbox offset= 48 Char= '0'
Position= 15 Remote Sbox= 125 Local Sbox offset= 110 Char= 'n'
Position= 16 Remote Sbox= 73 Local Sbox offset= 57 Char= '9'
Position= 17 Remote Sbox= 29 Local Sbox offset= 96 Char= '`'
Position= 18 Remote Sbox= 119 Local Sbox offset= 101 Char= 'e'
Position= 19 Remote Sbox= 34 Local Sbox offset= 110 Char= 'n'
Position= 20 Remote Sbox= 34 Local Sbox offset= 48 Char= '0'
Position= 21 Remote Sbox= 10 Local Sbox offset= 117 Char= 'u'
Position= 22 Remote Sbox= 125 Local Sbox offset= 103 Char= 'g'
Position= 23 Remote Sbox= 28 Local Sbox offset= 104 Char= 'h'
Position= 24 Remote Sbox= 87 Local Sbox offset= 63 Char= '?'
Position= 25 Remote Sbox= 87 Local Sbox offset= 62 Char= '>'
flag= <is`th1s`k3y`l0n9`en0ugh?>