示例#1
0
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 ' ') ) )
示例#2
0
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)
示例#3
0
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)
示例#4
0
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