def log_message(self, format, *args): server_logger = CMXLogAdapter( extra={ 'module': self.server.module.name.upper(), 'host': self.client_address[0] }) server_logger.info("- - %s" % (format % args))
def main(): setup_logger() logger = CMXLogAdapter() first_run_setup(logger) args = gen_cli_args() module = None module_server = None targets = [] server_port_dict = {'http': 80, 'https': 443, 'smb': 445} current_workspace = cfg.WORKSPACE if args.verbose: setup_debug_logger() logging.debug('Passed args:\n' + pformat(vars(args))) if hasattr(args, 'username') and args.username: for user in args.username: if Path(user).is_file(): #If it was a file passed in args.username.remove(user) args.username.append(open(user, 'r')) if hasattr(args, 'password') and args.password: for passw in args.password: if Path(passw).is_file(): #If it was a file passed in args.password.remove(passw) args.password.append(open(passw, 'r')) elif hasattr(args, 'hash') and args.hash: for ntlm_hash in args.hash: if Path(ntlm_hash).is_file(): #If it was a file passed in args.hash.remove(ntlm_hash) args.hash.append(open(ntlm_hash, 'r')) if hasattr(args, 'cred_id') and args.cred_id: for cred_id in args.cred_id: if '-' in str(cred_id): start_id, end_id = cred_id.split('-') try: for n in range(int(start_id), int(end_id) + 1): args.cred_id.append(n) args.cred_id.remove(cred_id) except Exception as e: logger.error( 'Error parsing database credential id: {}'.format(e)) sys.exit(1) if hasattr(args, 'target') and args.target: for target in args.target: if Path(target).is_file(): #If it was a file passed in target_file_type = identify_target_file(target) if target_file_type == 'nmap': targets.extend(parse_nmap_xml(target, args.protocol)) elif target_file_type == 'nessus': targets.extend(parse_nessus_file(target, args.protocol)) else: with open(target, 'r') as target_file: for target_entry in target_file: targets.extend(parse_targets(target_entry)) else: targets.extend(parse_targets(target)) # Clean obfuscation scripts if hasattr(args, 'clear_obfscripts') and args.clear_obfscripts: shutil.rmtree(cfg.OBF_PATH) os.mkdir(cfg.OBF_PATH) logger.success('Cleared cached obfuscated PowerShell scripts') if hasattr(args, 'obfs') and args.obfs: powershell.obfuscate_ps_scripts = True p_loader = protocol_loader() protocol_path = p_loader.get_protocols()[args.protocol]['path'] protocol_db_path = p_loader.get_protocols()[args.protocol]['dbpath'] protocol_object = getattr(p_loader.load_protocol(protocol_path), args.protocol) protocol_db_object = getattr(p_loader.load_protocol(protocol_db_path), 'database') db_path = (cfg.WS_PATH / current_workspace / args.protocol).with_suffix('.db') # set the database connection to autocommit w/ isolation level db_connection = sqlite3.connect(db_path, check_same_thread=False) db_connection.text_factory = str db_connection.isolation_level = None db = protocol_db_object(db_connection) setattr(protocol_object, 'config', cfg.__dict__) if hasattr(args, 'module'): loader = module_loader(args, db, logger) if args.list_modules: modules = loader.get_modules() for name, props in sorted(modules.items()): logger.info('{:<25} {}'.format(name, props['description'])) sys.exit(0) elif args.module and args.show_module_options: modules = loader.get_modules() for name, props in modules.items(): if args.module.lower() == name.lower(): logger.info('{} module options:\n{}'.format( name, props['options'])) sys.exit(0) elif args.module: modules = loader.get_modules() for name, props in modules.items(): if args.module.lower() == name.lower(): module = loader.init_module(props['path']) setattr(protocol_object, 'module', module) break if not module: logger.error('Module not found') exit(1) if getattr(module, 'opsec_safe') is False: ans = raw_input( highlight( '[!] Module is not opsec safe, are you sure you want to run this? [Y/n] ', 'red')) if ans.lower() not in ['y', 'yes', '']: sys.exit(1) if getattr(module, 'multiple_hosts') is False and len(targets) > 1: ans = raw_input( highlight( "[!] Running this module on multiple hosts doesn't really make any sense, are you sure you want to continue? [Y/n] ", 'red')) if ans.lower() not in ['y', 'yes', '']: sys.exit(1) if hasattr(module, 'on_request') or hasattr( module, 'has_response'): if hasattr(module, 'required_server'): args.server = getattr(module, 'required_server') if not args.server_port: args.server_port = 443 context = Context(db, logger, args) module_server = CMXServer(module, context, logger, args.server_host, args.server_port, args.server) module_server.start() setattr(protocol_object, 'server', module_server.server) try: ''' Open all the greenlet threads **Unless we interactive ''' pool = Pool(args.threads) jobs = [] for target in targets: jobs.append(pool.spawn(protocol_object, args, db, str(target))) for job in jobs: job.join(timeout=args.timeout) except (KeyboardInterrupt, gevent.Timeout): logging.info("Timed out") pass if module_server: module_server.shutdown()
class winrm(connection): def __init__(self, args, db, host): self.hostname = None self.os_arch = None self.local_ip = None self.domain = None self.server_os = None self.os_arch = 0 self.hash = None self.lmhash = '' self.nthash = '' self.remote_ops = None self.bootkey = None self.output_filename = None self.smbv1 = None self.signing = False self.debug = args.verbose self.dc_ip = '' connection.__init__(self, args, db, host) @staticmethod def proto_args(parser, std_parser, module_parser): winrm_parser = parser.add_parser('winrm', help="own stuff using WINRM", parents=[std_parser, module_parser]) winrm_parser.add_argument("-H", '--hash', metavar="HASH", dest='hash', nargs='+', default=[], help='NTLM hash(es) or file(s) containing NTLM hashes') winrm_parser.add_argument("--continue-on-success", action='store_true', help="continues authentication attempts even after successes") dgroup = winrm_parser.add_mutually_exclusive_group() dgroup.add_argument("-d", metavar="DOMAIN", dest='domain', type=str, default=None, help="domain to authenticate to") dgroup.add_argument("--local-auth", action='store_true', help='authenticate locally to each target') cgroup = winrm_parser.add_argument_group("Command Execution", "Options for executing commands") cgroup.add_argument('--no-output', action='store_true', help='do not retrieve command output') cgroup.add_argument("-x", metavar="COMMAND", dest='execute', help="execute the specified command") cgroup.add_argument("-X", metavar="PS_COMMAND", dest='ps_execute', help='execute the specified PowerShell command') return parser def proto_flow(self): self.proto_logger() if self.create_conn_obj(): self.enum_host_info() self.print_host_info() if self.login(): if hasattr(self.args, 'module') and self.args.module: self.call_modules() else: self.call_cmd_args() def proto_logger(self): print('test') self.logger = CMXLogAdapter(extra={'protocol': 'WINRM', 'host': self.host, 'port': 'NONE', 'hostname': self.hostname}) def enum_host_info(self): #smb_conn = SMBConnection(self.host, self.host, None) # #try: # smb_conn.login('' , '') #except SessionError as e: # if "STATUS_ACCESS_DENIED" in str(e): # pass # #self.domain = smb_conn.getServerDomain() #self.hostname = smb_conn.getServerName() #self.server_os = smb_conn.getServerOS() #self.signing = smb_conn.isSigningRequired() #self.os_arch = smb_conn.get_os_arch() # #self.output_filename = os.path.expanduser('~/.cme/logs/{}_{}_{}'.format(self.hostname, self.host, datetime.now().strftime("%Y-%m-%d_%H%M%S"))) # #if not self.domain: # self.domain = self.hostname # #self.db.add_computer(self.host, self.hostname, self.domain, self.server_os) # #try: # ''' DC's seem to want us to logoff first, windows workstations sometimes reset the connection # ''' # smb_conn.logoff() #except: # pass # #if self.args.domain: # self.domain = self.args.domain # #if self.args.local_auth: # self.domain = self.hostname print('todo') def print_host_info(self): #>>> s.__dict__ # {'url': 'http://win10a.ocean.depth:5985/wsman', 'protocol': <winrm.protocol.Protocol object at 0x7f7ae595f110>} #>>> s.protocol.__dict__ # {'read_timeout_sec': 30, 'operation_timeout_sec': 20, 'max_env_sz': 153600, 'locale': 'en-US', 'transport': <winrm.transport.Transport object at 0x7f7ae60a7f50>, 'username': '******', 'password': '******', 'service': 'HTTP', 'keytab': None, 'ca_trust_path': None, 'server_cert_validation': 'validate', 'kerberos_delegation': False, 'kerberos_hostname_override': None, 'credssp_disable_tlsv1_2': False} # self.logger.info("{}".format(self.conn.url)) def create_conn_obj(self): #pywinrm will try and guess the correct endpoint url: https://pypi.org/project/pywinrm/0.2.2/ print('pass={}'.format(self.args.password)) self.conn = pywinrm.Session(self.host, auth=('{}\\{}'.format(self.domain, self.args.username[0]), self.args.password[0]), transport='ntlm', server_cert_validation='ignore') return True def plaintext_login(self, domain, username, password): try: self.conn.run_cmd('hostname') self.admin_privs = True out = '{}\\{}:{} {}'.format(domain, username, password, highlight('({})'.format(cfg.pwn3d_label) if self.admin_privs else '')) self.logger.success(out) if not self.args.continue_on_success: return True except SessionError as e: error, desc = e.getErrorString() self.logger.error('{}\\{}:{} {} {}'.format(domain, username, password, error, '({})'.format(desc) if self.args.verbose else '')) if error == 'STATUS_LOGON_FAILURE': self.inc_failed_login(username) return False def parse_output(self, response_obj): if response_obj.status_code == 0: buf = StringIO(response_obj.std_out).readlines() for line in buf: self.logger.highlight(line.decode('utf-8').strip()) return response_obj.std_out else: buf = StringIO(response_obj.std_err).readlines() for line in buf: self.logger.highlight(line.decode('utf-8').strip()) return response_obj.std_err def execute(self, payload=None, get_output=False): r = self.conn.run_cmd(self.args.execute) self.logger.success('Executed command') self.parse_output(r) def ps_execute(self, payload=None, get_output=False): r = self.conn.run_ps(self.args.ps_execute) self.logger.success('Executed command') self.parse_output(r)