#!/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