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
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
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!"
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!")