Crypto100 - Schneier's Algorithm



cry100.jpg

Download file cry100.jpg
This is a code that Bruce Schneier likes to insert as an autograph.
Read from the last column to the first to discover the flag.

Flag: hackyouisthebestestctfftw

Crypto200 - XOROWbIu WbI(|)P



Download file cry200.txt.enc
The title suggests it's XOR, so let's launch a XOR analysis, here with Cryptool:
cryptool_xor.png
We are not done yet, but that's quite a good start!
We can assume the first word is "Congratulations", and deduce the final XOR key from it and the corresponding bytes in the ciphertext:

awe@awe-laptop ~ % hexdump -C cry200.txt.enc|head -n  1
00000000  d5 cb 44 a4 e4 12 e2 d1  46 a2 e2 1a f9 ca 59 e2  |..D.....F.....Y.|
awe@awe-laptop ~ % echo "Congratulations"|hexdump -C|head -n 1
00000000  43 6f 6e 67 72 61 74 75  6c 61 74 69 6f 6e 73 0a  |Congratulations.|
>>> s1 = "d5 cb 44 a4 e4 12 e2 d1  46 a2 e2 1a f9 ca 59 e2".replace(' ','').decode("hex")
>>> s2 = "43 6f 6e 67 72 61 74 75  6c 61 74 69 6f 6e 73 0a".replace(' ','').decode("hex")
>>> ' '.join('%02x' % (ord(s1[i]) ^ ord(s2[i])) for i in range(len(s1)))
'96 a4 2a c3 96 73 96 a4 2a c3 96 73 96 a4 2a e8'

96 a4 2a c3 96 73 is repeated, that's our XOR key.

Now let's decrypt the whole file:

#!/usr/bin/env python2
 
txt = open("cry200.txt.enc", "rb").read()
key = "96 a4 2a c3 96 73".replace(" ", "").decode("hex")
 
def my_xor(cipher, key):
    keylen = len(key)
    res = ""
 
    for pos, c in enumerate(cipher):
        res += chr(ord(c) ^ ord(key[pos % keylen]))
 
    return res
 
print my_xor(txt, key)

Congratulations! While the quick brown fox jumps over the lazy dog, the plain xor cipher is still very unsecure when the key is much shorter than the message. Your flag: Foxie Dogzie Crypto Pwnd

Flag: Foxie Dogzie Crypto Pwnd

Crypto300 - Hardcore



Download file cry300.py.

Here is what the algorithm does:

  • - Init sbox (SALTED_BOX) with range(128).
  • - add_key(SALTED_SBOX, KEY)
  • - every time it receives something from a user, it splits user input into two equal size blocks (k and m), and calls encrypt(SALTED_BOX, k, m)
  • - return hex-encoded result of the encrypt call to the user

If you are too lazy to open the file, here is add_key function:

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

Notice this is all reversible as long as we have an idea of the inital sbox, which is the case here, we know it was range(128).
Now we must submit an even sized string, let's study the encrypt() function:

def encrypt(sbox, k, m):
    sbox = sbox[::]
    add_key(sbox, k)
 
    c = ""
    for ch in m:
        c += chr(sbox[ord(ch)])
        sbox = combine(sbox, sbox)
    return c

The sbox is copied (sbox = sbox[::]) so that every time you will encrypt something, sbox will remain in the same state as above, salted with our challenge flag.
What about if we submit a string of 2 characters? 'k' will contain the first one, 'm' the last. So in the for loop, we will bypass the combine() call.
Simply perform 127 queries to extract the result of add_key(sbox, k). It is then trivial to reverse it and obtain sbox (SALTED_BOX).
It is indeed possible to optimize this process and to save queries by detecting a constant increment in dumped sbox characters, but I prefered the easy dirty way ;)
We only need to revert add_key(range(128), KEY), now that we have its output.
A quick bruteforce does it well, for each possible key length.
Once again, the optimization could save this but ... :D

Here is my code:

#!/usr/bin/env python2
 
import socket
import string
 
#HOST = 'localhost'
HOST = '93.191.13.142'
PORT = 7777
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect((HOST, PORT))
 
res = [-1]
 
for i in xrange(1, 128):
    txt = '1' + chr(i)
    s.sendall(txt)
    data = s.recv(1024)
    res += [ord(data.rstrip().decode("hex"))]
 
s.close()
 
for i in xrange(0, 128):
    if i not in res:
        res[0] = i
        break
 
res[0], res[ord('1')] = res[ord('1')], res[0]
 
res = map(lambda x: (x - 1) % 128, res)
 
print res
 
ref = res[:]
 
for bf in xrange(1, 128):
    pwd = ""
    dump = True
    res = ref[:]
 
    for pos in reversed(xrange(0, bf)):
        res = map(lambda x: (x - 1) % 128, res)
 
        for i, c in enumerate(res):
            if c == 2 * pos:
                break
 
        res[pos], res[i] = res[i], res[pos]
 
        if not chr(i) in string.printable:
            dump = False
            break
 
        pwd = chr(i) + pwd
 
    if dump:
        print "-" * 80
        print pwd

And its output:

% ./sploit_cry300.py
[86, 3, 13, 122, 14, 2, 75, 28, 29, 5, 77, 19, 34, 6, 74, 8, 83, 38, 127, 41, 40, 15, 1, 31,
89, 88, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
73, 46, 32, 76, 36, 78, 79, 80, 81, 82, 42, 84, 85, 26, 87, 51, 50, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
114, 115, 116, 117, 118, 119, 120, 121, 43, 123, 124, 125, 126, 44, 0, 48, 49, 27, 4,
35, 39, 7, 45, 9, 10, 11, 12, 33, 30, 47, 16, 17, 18, 37, 20, 21, 22, 23, 24, 25]
--------------------------------------------------------------------------------
jj	
--------------------------------------------------------------------------------
m	
--------------------------------------------------------------------------------
<is`th1s`k3y`l0n9`en0ugh?>
--------------------------------------------------------------------------------
1sek3yhl >9`en0ugh?> !"#$% !"#$%
--------------------------------------------------------------------------------
snk3y?l"!9`en0ugh?>!"#$%&'!"#$%&'
--------------------------------------------------------------------------------
0k3y>l$#9`en0ugh?>"#$%&'()"#$%&'()
--------------------------------------------------------------------------------
k3y#l&%9`en0ugh?>#$%&'()*+#$%&'()*+
--------------------------------------------------------------------------------
3y%l('9`en0ugh?>$%&'()*+,-$%&'()*+,-
--------------------------------------------------------------------------------
y'l*)9`en0ugh?>%&'()*+,-./%&'()*+,-./

Flag: <is`th1s`k3y`l0n9`en0ugh?>