APT_CyberCriminal_Campagin_.../2017/2017.02.15.deep-dive-dragonok-rambo-backdoor/Modified_PyTEA.py
2017-02-28 14:44:07 +08:00

405 lines
7.3 KiB
Python

#!/usr/bin/env python
#################################################################################
# Python implementation of the Tiny Encryption Algorithm (TEA)
# By Moloch
#
# About: TEA has a few weaknesses. Most notably, it suffers from
# equivalent keys each key is equivalent to three others,
# which means that the effective key size is only 126 bits.
# As a result, TEA is especially bad as a cryptographic hash
# function. This weakness led to a method for hacking Microsoft's
# Xbox game console (where I first encountered it), where the
# cipher was used as a hash function. TEA is also susceptible
# to a related-key attack which requires 2^23 chosen plaintexts
# under a related-key pair, with 2^32 time complexity.
#
# Block size: 64bits
# Key size: 128bits
#
##################################################################################
import os
import getpass
import platform
import struct
from random import choice
from hashlib import sha256
from ctypes import c_uint32
from string import ascii_letters, digits
if platform.system().lower() in ['linux', 'darwin']:
INFO = "\033[1m\033[36m[*]\033[0m "
WARN = "\033[1m\033[31m[!]\033[0m "
else:
INFO = "[*] "
WARN = "[!] "
### Magical Constants
DELTA = 0x9e3779b9
SUMATION = 0xc6ef3720
ROUNDS = 32
BLOCK_SIZE = 2 # number of 32-bit ints
KEY_SIZE = 4
### Functions ###
def encrypt_block(block, key, verbose=False):
'''
Encrypt a single 64-bit block using a given key
@param block: list of two c_uint32s
@param key: list of four c_uint32s
'''
assert len(block) == BLOCK_SIZE
assert len(key) == KEY_SIZE
sumation = c_uint32(0)
delta = c_uint32(DELTA)
for index in range(0, ROUNDS):
sumation.value += delta.value
block[0].value += ((block[1].value << 4) + key[0].value) ^ (block[1].value + sumation.value) ^ ((block[1].value >> 5) + key[1].value)
block[1].value += ((block[0].value << 4) + key[2].value) ^ (block[0].value + sumation.value) ^ ((block[0].value >> 5) + key[3].value)
if verbose: print("\t--> Encrypting block round %d of %d" % (index + 1, ROUNDS))
return block
def decrypt_block(block, key, verbose=False):
'''
Decrypt a single 64-bit block using a given key
@param block: list of two c_uint32s
@param key: list of four c_uint32s
'''
assert len(block) == BLOCK_SIZE
assert len(key) == KEY_SIZE
sumation = c_uint32(SUMATION)
delta = c_uint32(DELTA)
for index in range(0, ROUNDS):
block[1].value -= ((block[0].value << 4) + key[2].value) ^ (block[0].value + sumation.value) ^ ((block[0].value >> 5) + key[3].value);
block[0].value -= ((block[1].value << 4) + key[0].value) ^ (block[1].value + sumation.value) ^ ((block[1].value >> 5) + key[1].value);
sumation.value -= delta.value
if verbose: print("\t<-- Decrypting block round %d of %d" % (index + 1, ROUNDS))
return block
def to_c_array(data):
''' Converts a string to a list of c_uint32s '''
c_array = []
for index in range(0, len(data)/4):
chunk = data[index*4:index*4+4]
packed = struct.unpack(">L", chunk)[0]
c_array.append(c_uint32(packed))
return c_array
def to_string(c_array):
''' Converts a list of c_uint32s to a Python (ascii) string '''
output = ''
for block in c_array:
output += struct.pack(">L", block.value)
return output
def random_chars(nchars):
chars = ''
for n in range(0, nchars):
chars += choice(ascii_letters + digits)
return chars
def add_padding(data, verbose=False):
pad_delta = 4 - (len(data) % 4)
if verbose:
print(INFO + "Padding delta: %d" % pad_delta)
data += random_chars(pad_delta)
data += "%s%d" % (random_chars(3), pad_delta)
return data
def encrypt(data, key, verbose=False):
'''
Encrypt string using TEA algorithm with a given key
'''
data = add_padding(data, verbose)
data = to_c_array(data)
key = to_c_array(key.encode('ascii', 'ignore'))
cipher_text = []
for index in range(0, len(data), 2):
if verbose:
print(INFO + "Encrypting block %d" % index)
block = data[index:index + 2]
block = encrypt_block(block, key, verbose)
for uint in block:
cipher_text.append(uint)
if verbose:
print(INFO + "Encryption completed successfully")
return to_string(cipher_text)
def decrypt(data, key, verbose=False):
data = to_c_array(data)
key = to_c_array(key.encode('ascii', 'ignore'))
plain_text = []
for index in range(0, len(data), 2):
if verbose:
print(INFO + "Encrypting block %d" % index)
block = data[index:index + 2]
decrypted_block = decrypt_block(block, key, verbose)
for uint in decrypted_block:
plain_text.append(uint)
data = to_string(plain_text)
if verbose:
print(INFO + "Decryption compelted successfully")
return data
def get_key(password=''):
''' Generate a key based on user password '''
if 0 == len(password):
password = getpass.getpass(INFO + "Password: ")
sha = sha256()
sha.update(password + "Magic Static Salt")
sha.update(sha.hexdigest())
return ''.join([char for char in sha.hexdigest()[::4]])
def encrypt_file(fpath, key, verbose=False):
with open(fpath, 'rb+') as fp:
data = fp.read()
cipher_text = encrypt(data, key, verbose)
fp.seek(0)
fp.write(cipher_text)
fp.close()
def decrypt_file(fpath, key, verbose=False):
with open(fpath, 'rb+') as fp:
data = fp.read()
plain_text = decrypt(data, key, verbose)
fp.close()
fp = open(fpath, 'w')
fp.write(plain_text)
fp.close()
### UI Code ###
if __name__ == '__main__':
from argparse import ArgumentParser
parser = ArgumentParser(
description='Python implementation of the TEA cipher',
)
parser.add_argument('-e', '--encrypt',
help='encrypt a file',
dest='epath',
default=None
)
parser.add_argument('-d', '--decrypt',
help='decrypt a file',
dest='dpath',
default=None
)
parser.add_argument('--verbose',
help='display verbose output',
default=False,
action='store_true',
dest='verbose'
)
args = parser.parse_args()
if args.epath is None and args.dpath is None:
print('Error: Must use --encrypt or --decrypt')
elif args.epath is not None:
print(WARN + 'Encrypt Mode: The file will be overwritten')
if os.path.exists(args.epath) and os.path.isfile(args.epath):
key = get_key()
encrypt_file(args.epath, key, args.verbose)
else:
print(WARN + 'Error: target does not exist, or is not a file')
elif args.dpath is not None:
print(WARN + 'Decrypt Mode: The file will be overwritten')
if os.path.exists(args.dpath) and os.path.isfile(args.dpath):
key = get_key()
decrypt_file(args.dpath, key, args.verbose)
else:
print(WARN + 'Error: target does not exist, or is not a file')