def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.logger = create_logger_with_prefix("MS_SAMR Client")

        options = options_parser.parse_args(args or None)
        self._target = options_parser.process_target(options)

        self.file = getattr(options, "file", None)
        self.connection_manager = lambda: MS_RPC_ConnectionManager(self.target)
    def process_target(options):
        """
        Processes an AD_Objects.Target object from given options.
        Options can be given from commandline or shell commands.
        :param options: argparse.Namespace (=object) with the parsed options as class variables
        :return: An object representing the target Active Directory Domain
         :rtype AD_Objects.Target object
        """
        if not options:
            return

        logger = create_logger_with_prefix("Target Processor")

        logger.info("Started processing target from given options.")

        domain, username, password, address = re.compile(
            '(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
                options.target).groups('')

        # In case the password contains '@'
        if '@' in address:
            password = password + '@' + address.rpartition('@')[0]
            address = address.rpartition('@')[2]

        if options.target_ip is None:
            options.target_ip = address

        if domain is None:
            domain = ''

        if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
            from getpass import getpass
            password = getpass("Password:"******"Finished processing target.")

        return target
Beispiel #3
0
    def __init__(self, target, handles_manager_closing_func=lambda connection, handle: None):
        """
        :param target: The targetted domain
         :type target: AD_Objects.Target
        :param handles_manager_closing_func: A function that closes handles. needed for closing them automatically.
         :type handles_manager_closing_func: func(connection, handle) -> void
        """
        self.logger = create_logger_with_prefix("RPC_Connection Manager")
        self.__dict__.update(target._asdict())
        self.__dict__.update(vars(target.options))
        self._handles_interface = namedtuple(self._CONNECTION_HANDLES_CLASS_NAME, self._CONNECTION_HANDLES_NAMES)
        self._handles_manager_func = handles_manager_closing_func

        self._handles = None
        self.domain_name = None
        self.domain_handle = None
        self.connection = None
Beispiel #4
0
    def list_all(cls, connection):
        """
        List all entries of the used class type.
        :param connection: (dce, domain_handle)
        :return: entries_list of the used class type
        """
        entries_list = []
        page = 1
        logger = create_logger_with_prefix(cls.__name__)

        status = STATUS_MORE_ENTRIES
        while status == STATUS_MORE_ENTRIES:
            try:
                resp = cls.ENUMERATE_FUNC(*connection)
            except DCERPCException as e:
                if str(e).find("STATUS_MORE_ENTRIES") < 0:
                    raise
                resp = e.get_packet()

            entries_raw_info = resp["Buffer"]["Buffer"]
            page_entries = [
                cls.create(connection, entry_raw_info=entry_raw_info)
                for entry_raw_info in entries_raw_info
            ]

            logger.debug(
                f"Found {len(page_entries)} AD entries of type {cls.__name__} in page {page}"
            )
            page += 1

            entries_list.extend(page_entries)

            try:
                status = resp['ErrorCode']
            except KeyError as err:
                error_msg = f"Received error on page {page}, while listing entries of type {cls.__name__}. "
                error_msg += f"AD Error message: {str(err)}"
                raise RuntimeError(error_msg)

            if status != STATUS_SUCCESS:
                raise ConnectionError(
                    f"Received status {status} on page {page} while listing entries of type {cls.__name__}"
                )

        return entries_list
    def parse_args(args=None):
        """
        Optional: Parse method for options given from the commandline. Run with -h option for help.
        :return: options namespace, as parsed from commandline
         :rtype argparse.Namespace (=object) with the parsed options as class variables
        """
        logger = create_logger_with_prefix("Args Parser")
        logger.info("Starting to parse arguments.")

        parser = argparse.ArgumentParser(
            add_help=True, description="SMB client arg_parser implementation.")

        parser.add_argument(
            'target',
            action='store',
            help='[[domain/]username[:password]@]<targetName or address>',
            default=None)
        parser.add_argument(
            '-file',
            type=argparse.FileType('r'),
            help='input file with commands to execute in the mini shell')
        parser.add_argument('-debug',
                            action='store_true',
                            help='Turn DEBUG output ON')

        authentication_group = parser.add_argument_group('authentication')

        authentication_group.add_argument(
            '-hashes',
            action="store",
            metavar="LMHASH:NTHASH",
            help='NTLM hashes, format is LMHASH:NTHASH')
        authentication_group.add_argument(
            '-k',
            action="store_true",
            help=
            'Use Kerberos authentication. Grabs credentials from ccache file '
            '(KRB5CCNAME) based on target parameters. If valid credentials '
            'cannot be found, it will use the ones specified in the command '
            'line')
        authentication_group.add_argument(
            '-aesKey',
            action="store",
            metavar="hex key",
            help='AES key to use for Kerberos Authentication '
            '(128 or 256 bits)')
        authentication_group.add_argument(
            '-dc-ip',
            action='store',
            metavar="ip address",
            help=
            'IP Address of the domain controller. If omitted it will use the domain part (FQDN) specified in '
            'the target parameter')
        authentication_group.add_argument(
            '-target-ip',
            action='store',
            metavar="ip address",
            help=
            'IP Address of the target machine. If omitted it will use whatever was specified as target. '
            'This is useful when target is the NetBIOS name and you cannot resolve it'
        )
        authentication_group.add_argument(
            '-port',
            choices=['139', '445'],
            nargs='?',
            default='445',
            metavar="destination port",
            help=
            'Destination port to connect to SMB Server. If omitted it will use 445 by default'
        )

        if len(sys.argv) == 1:
            parser.print_help()
            print("No arguments given - need at least target to start.")
            args = input("Please add arguments: ").split()
            options = parser.parse_args(args)

        else:
            options = parser.parse_args(args)

        if options.debug is True:
            os.environ["SAMR_DEBUG"] = "1"
            logger.setLevel(DEBUG)
            logger.debug(impacket_version.getInstallationPath())
        else:
            os.environ["SAMR_DEBUG"] = "0"

        logger.info("Finished parsing arguments.")

        return options
from logging import DEBUG

from general_tools import create_logger_with_prefix
from ms_rpc_connection_manager import MS_RPC_ConnectionManager
from ms_samr_parser import MS_SAMR_OptionsParser as options_parser
from tests.conftest import *

pytestmark = [pytest.mark.unit_tests]
logger = create_logger_with_prefix("SAMR_CLIENT_UNIT_TESTS", DEBUG)


class Test_SAMR_Parser:
    @pytest.mark.parametrize("args, expected_options",
                             [((DEFAULT_TARGET, ), PARSER_EXPECTED_OPTIONS)])
    def test_parse_args(self, args, expected_options):
        options = options_parser.parse_args(args)
        non_empty_options = {
            op: val
            for op, val in vars(options).items() if val
        }

        assert non_empty_options == expected_options, "Parsed options do not match expected!"

    def test_process_target(self, options_namespace, target_asdict):
        target_asdict["options"] = options_namespace
        target = options_parser.process_target(options_namespace)

        assert target._asdict(
        ) == target_asdict, "Processed target is different than expected!"

Beispiel #7
0
 def __init__(self, target):
     super().__init__(target,
                      handles_manager_closing_func=samr.hSamrCloseHandle)
     self.logger = create_logger_with_prefix("MS_RPC_Connection Manager")
from logging import DEBUG

import pytest

from general_tools import create_logger_with_prefix
from ms_samr_client import MS_SAMR_Client
from tests.conftest import ENV_TARGET_INPUT, get_entries_list_from_stdout

pytestmark = [pytest.mark.e2e_tests, pytest.mark.success]
logger = create_logger_with_prefix("SAMR_E2E_TESTS", DEBUG)


@pytest.mark.parametrize("entry_type", MS_SAMR_Client.ENTRY_TYPES)
def test__e2e__connect__add_entry__get_entries(entry_type,
                                               mock_env_creds, credentials_fixture, random_computer_name_fixture,
                                               capsys):
    entry_name_to_add = random_computer_name_fixture

    client = MS_SAMR_Client(credentials_fixture[ENV_TARGET_INPUT])

    logger.debug(f"Adding {entry_type} {entry_name_to_add}")
    client.do_add_entry(args_str=f"{entry_type} {entry_name_to_add}")
    logger.debug("Entry added!")

    logger.debug("Getting list of entries:")
    client.do_list_entries(f"{entry_type}")
    entries_by_type = get_entries_list_from_stdout(capsys)
    logger.debug(f"Entries list: {entries_by_type}")

    assert entry_name_to_add in entries_by_type[entry_type], "The entry was not created."
    logger.debug("Success!")