Codebase list poshc2 / 06a953b poshc2 / server / Core.py
06a953b

Tree @06a953b (Download .tar.gz)

Core.py @06a953braw · history · blame

import os, base64, random, codecs, glob, readline, re, gzip, io
from poshc2.server.Config import POST_200_Responses, PayloadsDirectory, BeaconDataDirectory, ModulesDirectory
from poshc2.Utils import randomuri
from poshc2.client.cli.TabComplete import tabCompleter
from poshc2.Colours import Colours
from poshc2.server.database.DB import get_cred_by_id, insert_cred


def number_of_days(date1, date2):
    return (date2 - date1).days


def default_response():
    return bytes((random.choice(POST_200_Responses)).replace("#RANDOMDATA#", randomuri()), "utf-8")


def load_module(module_name):
    if module_name.startswith("/"):
        module_source = codecs.open(module_name, 'r', encoding='utf-8-sig')
    else:
        module_source = codecs.open(("%s%s" % (ModulesDirectory, module_name)), 'r', encoding='utf-8-sig')
    return module_source.read()


def load_module_sharp(module_name):
    if module_name.startswith("/"):
        module_source = open(module_name, 'r+b')
    else:
        module_source = open(("%s%s" % (ModulesDirectory, module_name)), 'r+b')
    return base64.b64encode(module_source.read()).decode("utf-8")


def get_images():
    images = ""
    for root, dirs, filenames in os.walk(BeaconDataDirectory):
        count = 1
        for f in filenames:
            if count == 5:
                with open(BeaconDataDirectory + f, "rb") as image_file:
                    image = image_file.read()
                    if len(image) < 1500:
                        images += "\"%s\"" % (base64.b64encode(image).decode("utf-8"))
            if count < 5:
                with open(BeaconDataDirectory + f, "rb") as image_file:
                    image = image_file.read()
                    if len(image) < 1500:
                        images += "\"%s\"," % (base64.b64encode(image).decode("utf-8"))
            count += 1
    return images


# Decrypt a string from base64 encoding
def get_encryption(key, iv='0123456789ABCDEF'):
    from Crypto.Cipher import AES
    iv = os.urandom(AES.block_size)
    bkey = base64.b64decode(key)
    aes = AES.new(bkey, AES.MODE_CBC, iv)
    return aes

# Decrypt a string from base64 encoding


def decrypt(key, data):
    iv = data[0:16]
    aes = get_encryption(key, iv)
    data = aes.decrypt(base64.b64decode(data))
    return data[16:].decode("utf-8")

# Decrypt a string from base64 encoding


def decrypt_bytes_gzip(key, data):
    iv = data[0:16]
    aes = get_encryption(key, iv)
    data = aes.decrypt(data)
    import gzip
    data = gzip.decompress(data[16:])
    try:
        data = data.decode("utf-8")
    except Exception:
        data = data
    return data

# Encrypt a string and base64 encode it


def encrypt(key, data, gzipdata=False):
    if not gzipdata:
        try:
            data = base64.b64encode(data)
        except TypeError:
            data = base64.b64encode(bytes(data, 'utf-8'))
    if gzipdata:
        data = bytes(data, 'utf-8')
        print("Gzipping data - pre-zipped len, " + str(len(data)))
        out = io.BytesIO()
        with gzip.GzipFile(fileobj=out, mode="w") as f:
            f.write(data)
        data = out.getvalue()

    # Pad with zeros
    mod = len(data) % 16
    if mod != 0:
        newlen = len(data) + (16 - mod)
        try:
            data = data.ljust(newlen, '\0')
        except TypeError:
            data = data.ljust(newlen, bytes('\0', "utf-8"))
    aes = get_encryption(key, os.urandom(16))
    data = aes.IV + aes.encrypt(data)
    if not gzipdata:
        data = base64.b64encode(data)
    return data


def filecomplete(text, state):
    os.chdir(PayloadsDirectory)
    return (glob.glob(text + '*') + [None])[state]


def shellcodefilecomplete(text, state):
    os.chdir(PayloadsDirectory)
    return (glob.glob(text + '*' + ".bin") + [None])[state]


def get_creds_from_params(params, user):
    if "-credid" in params:
        p = re.compile(r"-credid (\w*)")
        credId = re.search(p, params)
        params = p.sub("", params)
        if credId:
            credId = credId.group(1)
        else:
            print(Colours.RED, "Please specify a credid", Colours.GREEN)
        creds = get_cred_by_id(credId)
        if creds is None:
            print(Colours.RED, "Unrecognised CredID: %s" % credId, Colours.GREEN)
        return (creds, params)
    else:
        print(Colours.RED, "Command does not contain -credid", Colours.GREEN)


def creds(accept_hashes=True):
    '''
    Decorator around commands that allows credentials to be passed as '-credid <id>' parameters with an appropriate ID.

    Wraps the function replacing '-credid <id>' with '-domain <domain> -user <user> -pass <password>' (or '-hash <hash>' if the credid is a hash type).

    The wrapped function must take the arguments 'user, command, randomuri'.

    The decorator can take an 'accept_hashes' argument, e.g. '@creds(accept_hashes = False)' to disable the use of hash credential IDs.
    '''

    def decorator(func):

        def wrapper(*args, **kwargs):

            user = args[0]
            command = args[1]
            randomuri = args[2]

            if "-credid" in command:
                creds, command = get_creds_from_params(command, user)
                if creds is None:
                    return
                if creds['Password']:
                    command = command + " -domain %s -user %s -pass %s" % (creds['Domain'], creds['Username'], creds['Password'])
                elif not accept_hashes:
                    print_bad("This command does not support hash authentication")
                    return
                else:
                    command = command + " -domain %s -user %s -hash %s" % (creds['Domain'], creds['Username'], creds['Hash'])
            output = func(user, command, randomuri)
            return output

        return wrapper

    return decorator


def print_good(message):
    print(Colours.GREEN)
    print(message)


def print_bad(message):
    print(Colours.RED)
    print(message)
    print(Colours.GREEN)


def process_mimikatz(lines):
    # code source https://github.com/stufus/parse-mimikatz-log/blob/master/pml.py
    main_count = 0
    current = {}
    for line in lines.split('\n'):
        main_count += 1
        val = re.match(r'^\s*\*\s+Username\s+:\s+(.+)\s*$', line.strip())
        if val is not None:
            current = {}
            current['Username'] = val.group(1).strip()
            if current['Username'] == '(null)':
                current['Username'] = None
            continue

        val = re.match(r'^\s*\*\s+Domain\s+:\s+(.+)\s*$', line.strip())
        if val is not None:
            current['Domain'] = val.group(1).strip()
            if current['Domain'] == '(null)':
                current['Domain'] = None
            continue

        val = re.match(r'^\s*\*\s+(NTLM|Password)\s+:\s+(.+)\s*$', line.strip())
        if val is not None and "Username" in current and "Domain" in current:
            if val.group(2).count(" ") < 10:
                current[val.group(1).strip()] = val.group(2)
                if val.group(1) == "Password":
                    if val.group(2) == '(null)':
                        continue
                    insert_cred(current['Domain'], current['Username'], current['Password'], None)
                elif val.group(1) == "NTLM":
                    if val.group(2) == '(null)':
                        continue
                    insert_cred(current['Domain'], current['Username'], None, current['NTLM'])