Wednesday, October 17, 2012

hackyou CTF: Crypto 300

This was a fun network crypto challenge. The server is shown below.
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import os, sys, re
from socket import *

PORT = 7777 or int(sys.argv[1])
KEY = open("_flag.txt").read()

SBOX = list(range(128))
SALTED_SBOX = list(range(128))

# ----------------------------------------------------------------
# SERVICE
# ----------------------------------------------------------------
def main():
    add_key(SALTED_SBOX, KEY)
    serve()

def serve():
    f = socket(AF_INET, SOCK_DGRAM)
    f.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    f.bind(("0.0.0.0", PORT))

    print "[*] Server started on %d" % PORT
    while True:
        data, addr = f.recvfrom(4096)
        client(data, addr, f)
    return

def client(data, addr, f):
    print "[.] GOT", repr(data), "FROM", addr

    if len(data) % 2 or len(data) > 64:
        return

    for ch in data:
        if ord(ch) >= 128:
            return

    mid = len(data) >> 1
    k = data[:mid].rstrip("\x00")
    m = data[mid:].rstrip("\x00")

    c = encrypt(SALTED_SBOX, k, m)
    f.sendto(c.encode("hex"), addr)
    print "[+] RESULT SENT", addr, "\n"
    return

# ----------------------------------------------------------------
# CRYPTO
# ----------------------------------------------------------------
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

def add_key(sbox, k):
    for i, c in enumerate(k):
 print i, sbox[ord(c)], ord(c)
        sbox[i], sbox[ord(c)] = sbox[ord(c)], sbox[i]
        for i in xrange(len(sbox)):
            sbox[i] = (sbox[i] + 1) % 128
    return

def combine(a, b):
    ret = [-1] * len(b)
    for i in range(len(b)):
        ret[i] = a[b[i]]
    return tuple(ret)


if __name__ == "__main__":
    main()
The big weakness in this crypto was the fact that we can steal the SALTED_SBOX by sending special packets to the server. My script to steal the sbox is shown below.
from socket import *
import binascii

 # Create a list of numbers so I can figure out which one I didn't download
nums = list(range(128))
SALT = list()

# This iterates through all possible spots of the SBOX
for i in range(1,128):
 print i
 # Create a string that looks like \x00\x01
 send = '\x00' + binascii.unhexlify(hex(i)[2:].zfill(2))
 s = socket(AF_INET, SOCK_DGRAM)
 # You actually run this against their server intially
 s.connect(('localhost', 7777))
 s.sendall(send)
 sbox = int(s.recv(1024), 16)
 SALT.append(sbox)
 s.close()
 nums.remove(sbox)

SALT.insert(0, nums[0])
length = SALT[-1]

flag = ''
for i in range(0, length):
 val = (SALT[i]-(length+1))%128
 flag += chr(val)
print flag

for i in range(128):
 SALT[i] = (SALT[i] - (length+1))%128

hexSALT = []
for i in SALT:
 hexSALT.append(hex(i)[2:].zfill(2))

print hexSALT

### Everything below this line was added merely so I could easily see the
### Salted SBOX of the server after I had downloaded it.
print
print ['3c', '69', '73', '60', '74', '68', '31', '02', '03', '6b', '33', '79', '08', '6c', '30', '6e', '39', '0c', '65', '0f', '0e', '75', '67', '05', '3f', '3e', '1a', '1b', '1c', '1d', '1e', '1f', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '2a', '2b', '2c', '2d', '2e', '2f', '14', '06', '32', '0a', '34', '35', '36', '37', '38', '10', '3a', '3b', '00', '3d', '19', '18', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '4a', '4b', '4c', '4d', '4e', '4f', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '5a', '5b', '5c', '5d', '5e', '5f', '11', '61', '62', '63', '64', '12', '66', '16', '17', '01', '6a', '09', '0d', '6d', '13', '6f', '70', '71', '72', '07', '04', '15', '76', '77', '78', '0b', '7a', '7b', '7c', '7d', '7e', '7f']
The server's stolen sbox is at the bottom of the script and (if there are no duplicate chars) then the key used to salt the sbox is at the beginning of the array where each value subtracts the final value in the array mod 128 (ie. 3c-7f % 128). So now, instead of running against their server, I ran a local one with the key that I knew up to that point (the first few characters were legible) and slowly changed the _flag.txt until the sboxs matched. So all I did was download there sbox and then create a string that resulted in their sbox, which was the flag - .

-- suntzu_II

No comments:

Post a Comment