├── .gitignore ├── BadZure.py ├── Invoke-BadZure.ps1 ├── LICENSE ├── README.md ├── badzure.yml ├── entity_data ├── administrative-units.txt ├── app-core-names.txt ├── app-prefixes.txt ├── app-sufixes.txt ├── first-names.txt ├── group-names.txt └── last-names.txt ├── img ├── BadZure.png └── attack_paths.png ├── requirements.txt ├── src ├── __init__.py └── constants.py └── terraform ├── main.tf ├── outputs.tf └── variables.tf /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | .ionide/ 4 | project.lock.json 5 | *-tests.xml 6 | /debug/ 7 | /staging/ 8 | /Packages/ 9 | *.nuget.props 10 | 11 | # dotnet cli install/uninstall scripts 12 | dotnet-install.ps1 13 | dotnet-install.sh 14 | dotnet-uninstall-pkgs.sh 15 | dotnet-uninstall-debian-packages.sh 16 | 17 | # VS auto-generated solution files for project.json solutions 18 | *.xproj 19 | *.xproj.user 20 | *.suo 21 | 22 | # VS auto-generated files for csproj files 23 | *.csproj.user 24 | 25 | # Visual Studio IDE directory 26 | .vs/ 27 | 28 | # VSCode directories that are not at the repository root 29 | /**/.vscode/ 30 | 31 | # Project Rider IDE files 32 | .idea.powershell/ 33 | 34 | # Ignore executables 35 | *.exe 36 | *.msi 37 | *.appx 38 | *.msix 39 | 40 | # Ignore binaries and symbols 41 | *.pdb 42 | *.dll 43 | *.wixpdb 44 | 45 | #ignore text files 46 | #*.txt 47 | 48 | # Ignore packages 49 | *.deb 50 | *.tar.gz 51 | *.zip 52 | *.rpm 53 | *.pkg 54 | *.nupkg 55 | *.AppImage 56 | 57 | # default location for produced nuget packages 58 | /nuget-artifacts 59 | 60 | # resgen output 61 | gen 62 | 63 | # Per repo profile 64 | .profile.ps1 65 | 66 | # macOS 67 | .DS_Store 68 | .DocumentRevisions-V100 69 | .fseventsd 70 | .Spotlight-V100 71 | .TemporaryItems 72 | .Trashes 73 | .VolumeIcon.icns 74 | .com.apple.timemachine.donotpresent 75 | .AppleDB 76 | .AppleDesktop 77 | Network Trash Folder 78 | Temporary Items 79 | .apdisk 80 | .AppleDouble 81 | .LSOverride 82 | 83 | # TestsResults 84 | TestsResults*.xml 85 | ParallelXUnitResults.xml 86 | xUnitResults.xml 87 | 88 | # Resharper settings 89 | PowerShell.sln.DotSettings.user 90 | *.msp 91 | StyleCop.Cache 92 | 93 | # Ignore SelfSignedCertificate autogenerated files 94 | test/tools/Modules/SelfSignedCertificate/ 95 | 96 | # BenchmarkDotNet artifacts 97 | test/perf/BenchmarkDotNet.Artifacts/ 98 | 99 | # Test generated module 100 | test/tools/Modules/Microsoft.PowerShell.NamedPipeConnection/ 101 | 102 | # Test generated startup profile 103 | StartupProfileData-NonInteractive 104 | 105 | # Ignore logfiles 106 | logfile/* 107 | 108 | # Ignore Python virtual environment 109 | venv/ 110 | .venv/ 111 | 112 | # Ignore Python cache files 113 | __pycache__/ 114 | *.pyc 115 | *.pyo 116 | *.pyd 117 | 118 | # Ignore environment configuration files 119 | .env 120 | *.env 121 | 122 | # Ignore Terraform state files and backups 123 | *.tfstate 124 | *.tfstate.* 125 | *.backup 126 | 127 | # Ignore Terraform configuration files 128 | .terraform/ 129 | .terraform.lock.hcl 130 | 131 | # Ignore Terraform variable files 132 | *.tfvars 133 | *.tfvars.json 134 | 135 | # Ignore config file 136 | local.yml 137 | 138 | # Ignore users file 139 | users.txt 140 | 141 | # Ignore tokens file 142 | tokens.txt -------------------------------------------------------------------------------- /BadZure.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import yaml 4 | import click 5 | from python_terraform import Terraform 6 | from src.constants import ENTRA_ROLES, GRAPH_API_PERMISSIONS, HIGH_PRIVILEGED_ENTRA_ROLES, HIGH_PRIVILEGED_GRAPH_API_PERMISSIONS 7 | import random 8 | import string 9 | import requests 10 | import time 11 | import logging 12 | 13 | # Ensure AZURE_CONFIG_DIR is set the Azure CLI config directory 14 | os.environ['AZURE_CONFIG_DIR'] = os.path.expanduser('~/.azure') 15 | 16 | 17 | 18 | TERRAFORM_DIR = os.path.join(os.path.dirname(__file__), 'terraform') 19 | tf = Terraform(working_dir=TERRAFORM_DIR) 20 | 21 | banner = """ 22 | 23 | ____ _ ______ 24 | | _ \ | |___ / 25 | | |_) | __ _ __| | / /_ _ _ __ ___ 26 | | _ < / _` |/ _` | / /| | | | '__/ _ \\ 27 | | |_) | (_| | (_| |/ /_| |_| | | | __/ 28 | |____/ \__,_|\__,_/_____\__,_|_| \___| 29 | 30 | 31 | by Mauricio Velazco 32 | @mvelazco 33 | 34 | """ 35 | 36 | extra = {'include_timestamp': False} 37 | 38 | def setup_logging(level, include_timestamp=True): 39 | 40 | custom_formats = { 41 | logging.INFO: "{timestamp}[+] %(message)s", 42 | logging.ERROR: "{timestamp}[!] %(message)s", 43 | "DEFAULT": "{timestamp}[%(levelname)s] - %(message)s", 44 | } 45 | custom_time_format = "%Y-%m-%d %H:%M:%S" 46 | 47 | class CustomFormatter(logging.Formatter): 48 | def __init__(self, fmt=None, datefmt=None, style='%'): 49 | super().__init__(fmt, datefmt=custom_time_format, style=style) 50 | 51 | def format(self, record): 52 | if hasattr(record, 'include_timestamp') and not record.include_timestamp: 53 | timestamp = "" 54 | else: 55 | timestamp = f"{self.formatTime(record, self.datefmt)} " 56 | 57 | # Replace the {timestamp} placeholder in the format with the actual timestamp or empty string 58 | self._style._fmt = custom_formats.get(record.levelno, custom_formats["DEFAULT"]).format(timestamp=timestamp) 59 | return super().format(record) 60 | 61 | root_logger = logging.getLogger() 62 | root_logger.handlers.clear() 63 | console_handler = logging.StreamHandler() 64 | console_handler.setFormatter(CustomFormatter()) 65 | root_logger.addHandler(console_handler) 66 | root_logger.setLevel(level) 67 | 68 | 69 | def parse_terraform_output(output): 70 | # Parse the Terraform state output and extract the essential information 71 | resources = [] 72 | 73 | try: 74 | state = json.loads(output) 75 | for module in state.get('values', {}).get('root_module', {}).get('resources', []): 76 | resource_type = module.get('type') 77 | resource_name = module.get('name') 78 | if resource_type == 'azuread_domains' or resource_type == 'azuread_administrative_unit_member' or resource_type == 'azuread_group_member' or resource_type == 'azuread_directory_role_assignment': 79 | #print(module.get('values', {})) 80 | continue 81 | if resource_type == 'azuread_user': 82 | key_attr = module.get('values', {}).get('user_principal_name') 83 | elif resource_type == 'azuread_group': 84 | key_attr = module.get('values', {}).get('display_name') 85 | elif resource_type == 'azuread_application_registration': 86 | key_attr = module.get('values', {}).get('display_name') 87 | elif resource_type == 'azuread_administrative_unit': 88 | key_attr = module.get('values', {}).get('display_name') 89 | elif resource_type == 'azuread_service_principal': 90 | key_attr = module.get('values', {}).get('id') 91 | #print(module.get('values', {})) 92 | else: 93 | key_attr = "N/A" 94 | 95 | #resources.append(f"Resource Type: {resource_type}, Name: {resource_name}, Key Attribute: {key_attr}") 96 | resources.append(f"Resource Type: {resource_type}, Identifier: {key_attr}") 97 | 98 | except json.JSONDecodeError: 99 | logging.error("Failed to parse Terraform state output") 100 | 101 | return resources 102 | 103 | def write_users_to_file(users, domain, file_path): 104 | with open(file_path, 'w') as file: 105 | for user in users.values(): 106 | file.write(f"{user['user_principal_name']}@{domain}\n") 107 | 108 | def get_ms_token_username_pass(tenant_id, username, password, scope): 109 | 110 | # https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth-ropc 111 | 112 | #logging.info("Using resource owner password OAuth flow to obtain a token") 113 | 114 | token_url = f'https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token' 115 | 116 | full_scope = f'{scope} offline_access' 117 | 118 | token_data = { 119 | 120 | 'client_id': '1950a258-227b-4e31-a9cf-717495945fc2', # Microsoft Azure PowerShell 121 | #'client_id': '00b41c95-dab0-4487-9791-b9d2c32c80f2', # Office 365 Management. Works to read emails Graph and EWS. 122 | #'client_id': 'd3590ed6-52b3-4102-aeff-aad2292ab01c', # Microsoft Office. Also works to read emails Graph and EWS. 123 | #'client_id': '00000002-0000-0ff1-ce00-000000000000', # Office 365 Exchange Online 124 | #'client_id': '00000006-0000-0ff1-ce00-000000000000', # Microsoft Office 365 Portal 125 | #'client_id': 'fb78d390-0c51-40cd-8e17-fdbfab77341b', # Microsoft Exchange REST API Based Powershell 126 | # 'client_id': '00000003-0000-0000-c000-000000000000', # Microsoft Graph 127 | #'client_id': 'de8bc8b5-d9f9-48b1-a8ad-b748da725064', # Graph Explorer 128 | #'client_id': '14d82eec-204b-4c2f-b7e8-296a70dab67e', # Microsoft Graph Command Line Tools 129 | 130 | 'grant_type': 'password', 131 | 'username': username, 132 | 'password': password, 133 | 'scope': full_scope 134 | } 135 | 136 | response = requests.post(token_url, data=token_data) 137 | refresh_token = response.json().get('refresh_token') 138 | access_token = response.json().get('access_token') 139 | 140 | if refresh_token and access_token: 141 | return {'access_token': access_token, 'refresh_token': refresh_token} 142 | else: 143 | logging.error (f'Error obtaining token. Http response: {response.status_code}') 144 | logging.error (response.text) 145 | 146 | def create_attack_path(attack_patch_config, users, applications, domain, password): 147 | 148 | app_owner_assignments = {} 149 | user_role_assignments = {} 150 | app_role_assignments = {} 151 | app_api_permission_assignments = {} 152 | 153 | # Pick a random application registration 154 | app_keys = list(applications.keys()) 155 | random_app = random.choice(app_keys) 156 | #app_id = applications[random_app]['display_name'] 157 | 158 | attack_path_id = ''.join(random.choices(string.ascii_lowercase + string.digits, k=6)) 159 | key = f"attack-path-{attack_path_id}" 160 | 161 | # Pick a random user 162 | user_keys = list(users.keys()) 163 | random_user = random.choice(user_keys) 164 | user_principal_name = f"{users[random_user]['user_principal_name']}@{domain}" 165 | password = users[random_user]['password'] 166 | 167 | if attack_patch_config['scenario'] == "direct": 168 | 169 | initial_access_user = { 170 | "user_principal_name": user_principal_name, 171 | "password": password 172 | } 173 | 174 | elif attack_patch_config['scenario'] == "helpdesk": 175 | 176 | helpdesk_admin_role_id = "729827e3-9c14-49f7-bb1b-9608f156bbb8" # ID for "Helpdesk Administrator" 177 | second_random_user = random.choice(user_keys) 178 | second_user_principal_name = f"{users[second_random_user]['user_principal_name']}@{domain}" 179 | second_user_password = users[second_random_user]['password'] 180 | 181 | initial_access_user = { 182 | "user_principal_name": second_user_principal_name, 183 | "password": second_user_password 184 | } 185 | 186 | user_role_assignments[key] = { 187 | 'user_name': second_random_user, 188 | 'role_definition_id': helpdesk_admin_role_id 189 | } 190 | 191 | app_owner_assignments[key] = { 192 | 'app_name': random_app, 193 | 'user_principal_name': user_principal_name, 194 | } 195 | 196 | 197 | if attack_patch_config['method'] == "AzureADRole": 198 | 199 | 200 | if attack_patch_config['entra_role'] != 'random': 201 | 202 | privileged_role_id = attack_patch_config['entra_role'] 203 | 204 | else: 205 | privileged_role_id = random.choice(list(HIGH_PRIVILEGED_ENTRA_ROLES.values())) 206 | 207 | # Assign "Privileged Role Administrator" role to the application 208 | #privileged_role_id = "e8611ab8-c189-46e8-94e1-60213ab1f814" # ID for "Privileged Role Administrator" 209 | 210 | app_role_assignments[key] = { 211 | 'app_name': random_app, 212 | 'role_id': privileged_role_id, 213 | } 214 | 215 | elif attack_patch_config['method'] == "GraphAPIPermission": 216 | 217 | if attack_patch_config['app_role'] != 'random': 218 | 219 | api_permission_id = attack_patch_config['app_role'] 220 | 221 | else: 222 | api_permission_id = random.choice([permission["id"] for permission in HIGH_PRIVILEGED_GRAPH_API_PERMISSIONS.values()]) 223 | 224 | # Assign API permission to the application 225 | #api_permission_id = "9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8" # ID for "RoleManagement.ReadWrite.Directory" 226 | 227 | app_api_permission_assignments[key] = { 228 | 'app_name': random_app, 229 | 'api_permission_id': api_permission_id, 230 | } 231 | 232 | return initial_access_user, app_owner_assignments, user_role_assignments, app_role_assignments, app_api_permission_assignments 233 | 234 | def load_config(file_path): 235 | """Load and return the configuration from a YAML file.""" 236 | try: 237 | with open(file_path, 'r') as file: 238 | config = yaml.safe_load(file) 239 | return config 240 | except FileNotFoundError: 241 | #logging.error(f"Configuration file not found at: {file_path}") 242 | exit(1) 243 | except yaml.YAMLError as e: 244 | #logging.error(f"Error parsing the YAML file: {e}") 245 | exit(1) 246 | 247 | def create_random_assignments(users, groups, administrative_units, applications): 248 | 249 | user_group_assignments = {} 250 | user_au_assignments = {} 251 | user_role_assignments = {} 252 | app_role_assignments = {} 253 | app_api_permission_assignments={} 254 | 255 | user_keys = list(users.keys()) 256 | group_keys = list(groups.keys()) 257 | au_keys = list(administrative_units.keys()) 258 | app_keys = list(applications.keys()) 259 | 260 | # Calculate subset size as one-third of the total users 261 | user_subset_size = max(1, len(user_keys) // 3) 262 | 263 | # Randomly select a subset of users for group assignments 264 | logging.info("Creating random user to group assignments") 265 | group_assigned_users = random.sample(user_keys, user_subset_size) 266 | for user in group_assigned_users: 267 | if groups: 268 | group = random.choice(group_keys) 269 | assignment_key = f"{user}-{group}" 270 | user_group_assignments[assignment_key] = { 271 | 'user_name': user, 272 | 'group_name': group 273 | } 274 | 275 | # Randomly select a subset of users for administrative unit assignments 276 | logging.info("Creating random user to administrative unit assignments") 277 | au_assigned_users = random.sample(user_keys, user_subset_size) 278 | for user in au_assigned_users: 279 | if administrative_units: 280 | au = random.choice(au_keys) 281 | assignment_key = f"{user}-{au}" 282 | user_au_assignments[assignment_key] = { 283 | 'user_name': user, 284 | 'administrative_unit_name': au 285 | } 286 | 287 | # Randomly select a subset of users for role assignments 288 | logging.info("Creating random azure ad role assignments to users") 289 | role_assigned_users = random.sample(user_keys, user_subset_size) 290 | for user in role_assigned_users: 291 | if ENTRA_ROLES: 292 | role_name = random.choice(list(ENTRA_ROLES.keys())) 293 | role_id = ENTRA_ROLES[role_name] 294 | assignment_key = f"{user}-{role_name}" 295 | user_role_assignments[assignment_key] = { 296 | 'user_name': user, 297 | 'role_definition_id': role_id 298 | } 299 | 300 | app_subset_size = max(1, len(app_keys) // 2) 301 | role_assigned_apps = random.sample(app_keys, app_subset_size) 302 | 303 | logging.info("Creating random azure ad role assignments to applications") 304 | for app in role_assigned_apps: 305 | if ENTRA_ROLES: 306 | role_name = random.choice(list(ENTRA_ROLES.keys())) 307 | role_id = ENTRA_ROLES[role_name] 308 | assignment_key = f"{app}-{role_name}" 309 | app_role_assignments[assignment_key] = { 310 | 'app_name': app, 311 | 'role_id': role_id 312 | } 313 | 314 | 315 | logging.info("Creating random Graph api permission assignments to applications") 316 | api_assigned_apps = random.sample(app_keys, app_subset_size) 317 | for app in api_assigned_apps: 318 | if GRAPH_API_PERMISSIONS: 319 | api_name = random.choice(list(GRAPH_API_PERMISSIONS.keys())) 320 | api_permission_id = GRAPH_API_PERMISSIONS[api_name]['id'] 321 | assignment_key = f"{app}-{api_name}" 322 | app_api_permission_assignments[assignment_key] = { 323 | 'app_name': app, 324 | 'api_permission_id': api_permission_id, 325 | } 326 | 327 | return user_group_assignments, user_au_assignments, user_role_assignments, app_role_assignments, app_api_permission_assignments 328 | 329 | def generate_random_password(length=15): 330 | if length < 8: 331 | raise ValueError("Password length must be at least 8 characters") 332 | 333 | password = [ 334 | random.choice(string.ascii_uppercase), 335 | random.choice(string.ascii_lowercase), 336 | random.choice(string.digits), 337 | random.choice(string.punctuation) 338 | ] 339 | 340 | password += random.choices(string.ascii_letters + string.digits + string.punctuation, k=length - 4) 341 | random.shuffle(password) 342 | return ''.join(password) 343 | 344 | def read_lines_from_file(file_path): 345 | with open(file_path, mode='r') as file: 346 | return [line.strip() for line in file.readlines()] 347 | 348 | def generate_user_details(first_names_file, last_names_file, number_of_users): 349 | 350 | first_names = read_lines_from_file(first_names_file) 351 | last_names = read_lines_from_file(last_names_file) 352 | users = {} 353 | 354 | for _ in range(number_of_users): 355 | first_name = random.choice(first_names) 356 | last_name = random.choice(last_names) 357 | key = f"{first_name}.{last_name}".lower() 358 | users[key] = { 359 | 'user_principal_name': key, 360 | 'display_name': f"{first_name.capitalize()} {last_name.capitalize()}", 361 | 'mail_nickname': key, 362 | 'password': generate_random_password() 363 | } 364 | 365 | return users 366 | 367 | def generate_group_details(file_path, number_of_groups): 368 | 369 | groups = {} 370 | 371 | group_names = read_lines_from_file(file_path) 372 | 373 | selected_groups = random.sample(group_names, number_of_groups) 374 | 375 | for group_name in selected_groups: 376 | groups[group_name] = { 377 | 'display_name': group_name 378 | } 379 | 380 | return groups 381 | 382 | def generate_app_details(prefix_file, core_file, suffix_file, number_of_names): 383 | 384 | prefixes = read_lines_from_file(prefix_file) 385 | core_names = read_lines_from_file(core_file) 386 | suffixes = read_lines_from_file(suffix_file) 387 | 388 | app_names = set() 389 | 390 | while len(app_names) < number_of_names: 391 | prefix = random.choice(prefixes) 392 | core_name = random.choice(core_names) 393 | suffix = random.choice(suffixes) if random.random() > 0.5 else "" 394 | app_name = f"{prefix}{core_name}{suffix}" 395 | app_names.add(app_name) 396 | 397 | applications = {} 398 | for name in app_names: 399 | applications[name] = { 400 | 'display_name': name 401 | } 402 | 403 | return applications 404 | 405 | def generate_administrative_units_details(file_path, number_of_aunits): 406 | 407 | aunits = {} 408 | 409 | aunit_names = read_lines_from_file(file_path) 410 | 411 | selected_groups = random.sample(aunit_names, number_of_aunits) 412 | 413 | for group_name in selected_groups: 414 | aunits[group_name] = { 415 | 'display_name': group_name 416 | } 417 | 418 | return aunits 419 | 420 | @click.group() 421 | def cli(): 422 | pass 423 | 424 | @cli.command() 425 | @click.option('--config', type=click.Path(exists=True), default='badzure.yml', help="Path to the configuration YAML file") 426 | @click.option('--verbose', is_flag=True, help="Enable verbose output") 427 | def build(config, verbose): 428 | """Create resources and attack paths""" 429 | azure_config_dir = os.path.expanduser('~/.azure') 430 | os.environ['AZURE_CONFIG_DIR'] = azure_config_dir 431 | 432 | # Load configuration 433 | logging.info(f"Loading BadZure configuration from {config}") 434 | config = load_config(config) 435 | tenant_id = config['tenant']['tenant_id'] 436 | domain = config['tenant']['domain'] 437 | 438 | max_users = config['tenant']['users'] 439 | max_groups = config['tenant']['groups'] 440 | max_apps = config['tenant']['applications'] 441 | max_aunits = config['tenant']['administrative_units'] 442 | 443 | 444 | # Generate random users 445 | logging.info(f"Generating {max_users} random users") 446 | users = generate_user_details('entity_data/first-names.txt', 'entity_data/last-names.txt', max_users) 447 | 448 | # Generate random groups 449 | logging.info(f"Generating {max_groups} random groups") 450 | groups = generate_group_details('entity_data/group-names.txt', max_groups) 451 | 452 | # Generate random application registrations 453 | logging.info(f"Generating {max_apps} random application registrations/service principals") 454 | applications = generate_app_details('entity_data/app-prefixes.txt', 'entity_data/app-core-names.txt','entity_data/app-sufixes.txt', max_apps) 455 | 456 | # Generate random administratuve units 457 | logging.info(f"Generating {max_aunits} random administrative units") 458 | administrative_units = generate_administrative_units_details('entity_data/administrative-units.txt', max_aunits) 459 | 460 | # Create random assignments 461 | #logging.info("Creating random assignments for groups, administrative units, azure ad roles and graph api permissions") 462 | 463 | user_group_assignments, user_au_assignments, user_role_assignments, app_role_assignments, app_api_permission_assignments = create_random_assignments(users, groups, administrative_units, applications) 464 | 465 | attack_path_application_owner_assignments, attack_path_user_role_assignments, attack_path_app_role_assignments, attack_path_app_api_permission_assignments = {}, {}, {}, {} 466 | 467 | user_creds = {} 468 | 469 | for attack_path in config['attack_paths']: 470 | 471 | if config['attack_paths'][attack_path]['enabled']: 472 | 473 | #password = config['attack_paths'][attack_path]['password'] 474 | logging.info(f"Creating assignments for attack path '{attack_path}'") 475 | initial_access, ap_app_owner_assignments, ap_user_role_assignments, ap_app_role_assignments, ap_app_api_permission_assignments = create_attack_path(config['attack_paths'][attack_path], users, applications, domain, "test") 476 | attack_path_application_owner_assignments = {**attack_path_application_owner_assignments, **ap_app_owner_assignments} 477 | attack_path_user_role_assignments = {**attack_path_user_role_assignments, **ap_user_role_assignments} 478 | attack_path_app_role_assignments = {**attack_path_app_role_assignments, **ap_app_role_assignments} 479 | attack_path_app_api_permission_assignments = {**attack_path_app_api_permission_assignments, **ap_app_api_permission_assignments} 480 | user_creds[attack_path] = initial_access 481 | #update_password(users, initial_access['user_principal_name'].split('@')[0], password) 482 | 483 | 484 | # Prepare Terraform variables 485 | user_vars = {user['user_principal_name']: user for user in users.values()} 486 | group_vars = {group['display_name']: group for group in groups.values()} 487 | application_vars = {app['display_name']: app for app in applications.values()} 488 | administrative_unit_vars = {au['display_name']: au for au in administrative_units.values()} 489 | 490 | tf_vars = { 491 | 'tenant_id': tenant_id, 492 | 'domain': domain, 493 | 'users': user_vars, 494 | 'azure_config_dir': azure_config_dir, 495 | 'groups': group_vars, 496 | 'applications': application_vars, 497 | 'administrative_units': administrative_unit_vars, 498 | 'user_group_assignments': user_group_assignments, 499 | 'user_au_assignments': user_au_assignments, 500 | 'user_role_assignments': user_role_assignments, 501 | 'app_role_assignments': app_role_assignments, 502 | 'app_api_permission_assignments' : app_api_permission_assignments, 503 | 'attack_path_application_owner_assignments' : attack_path_application_owner_assignments, 504 | 'attack_path_user_role_assignments' : attack_path_user_role_assignments, 505 | 'attack_path_application_role_assignments' : attack_path_app_role_assignments, 506 | 'attack_path_application_api_permission_assignments' : attack_path_app_api_permission_assignments 507 | } 508 | 509 | # Write the Terraform variables to a file 510 | logging.info(f"Creating terraform.tfvars.json") 511 | with open(os.path.join(TERRAFORM_DIR, 'terraform.tfvars.json'), 'w') as f: 512 | json.dump(tf_vars, f, indent=4) 513 | 514 | 515 | 516 | # Initialize and apply the Terraform configuration 517 | logging.info(f"Calling terraform init.") 518 | return_code, stdout, stderr = tf.init() 519 | if return_code != 0: 520 | logging.error(f"Terraform init failed: {stderr}") 521 | if verbose: 522 | logging.error(stdout) 523 | logging.error(stderr) 524 | return 525 | 526 | logging.info(f"Calling terraform apply to create resources, this may take several minutes ...") 527 | return_code, stdout, stderr = tf.apply(skip_plan=True, capture_output=not verbose) 528 | if return_code != 0: 529 | logging.error(f"Terraform apply failed: {stderr}") 530 | if verbose: 531 | logging.error(stdout) 532 | logging.error(stderr) 533 | return 534 | 535 | logging.info("Azure AD tenant setup completed with assigned permissions and configurations!") 536 | write_users_to_file(users, domain, 'users.txt') 537 | logging.info("Created users.txt file.") 538 | logging.info("Attack Path Details") 539 | 540 | for attack_path in config['attack_paths']: 541 | 542 | if config['attack_paths'][attack_path]['enabled']: 543 | logging.info(f"*** {attack_path} ***") 544 | logging.info(f"Initial access user: {user_creds[attack_path]['user_principal_name']}") 545 | 546 | if config['attack_paths'][attack_path]['initial_access'] == "password": 547 | logging.info(f"Password: {user_creds[attack_path]['password']}") 548 | 549 | elif config['attack_paths'][attack_path]['initial_access'] == "token": 550 | logging.info(f"Obtaining tokens...") 551 | #logging.info(f"Will use {user_creds[attack_path]['user_principal_name']} and {user_creds[attack_path]['password']}") 552 | tokens = get_ms_token_username_pass(tenant_id, user_creds[attack_path]['user_principal_name'], user_creds[attack_path]['password'], "https://graph.microsoft.com/.default") 553 | with open("tokens.txt", "a") as file: 554 | file.write(f"Attack Path : {attack_path}\n") 555 | file.write(f"User : {user_creds[attack_path]['user_principal_name']}\n") 556 | file.write(f"Access Token: {tokens['access_token']}\n") 557 | file.write(f"Refresh Token: {tokens['refresh_token']}\n") 558 | logging.info(f"Tokens saved in tokens.txt!.") 559 | 560 | logging.info("Good bye.") 561 | 562 | @cli.command() 563 | @click.option('--verbose', is_flag=True, help="Enable verbose output") 564 | def show(verbose): 565 | """Show all the created resources in the tenant""" 566 | 567 | # Ensure terraform.tfvars.json exists 568 | tfvars_path = os.path.join(TERRAFORM_DIR, 'terraform.tfvars.json') 569 | if not os.path.exists(tfvars_path): 570 | logging.error("Error: terraform.tfvars.json file not found.") 571 | return 572 | 573 | # Initialize the Terraform configuration 574 | return_code, stdout, stderr = tf.init() 575 | if return_code != 0: 576 | logging.error(f"Terraform init failed: {stderr}") 577 | return 578 | 579 | logging.info(f"Calling terraform show to display the current state ...") 580 | 581 | # Execute the terraform show command 582 | return_code, stdout, stderr = tf.show(json=True, capture_output=not verbose) 583 | 584 | if return_code != 0: 585 | logging.error(f"Terraform show failed: {stderr}") 586 | logging.error(stdout) 587 | logging.error(stderr) 588 | return 589 | 590 | if verbose: 591 | print(stdout) 592 | else: 593 | resources = parse_terraform_output(stdout) 594 | for resource in resources: 595 | logging.info(resource) 596 | #logging.info(stdout) 597 | 598 | logging.info("Current state of Azure AD tenant resources displayed successfully.") 599 | 600 | @cli.command() 601 | @click.option('--verbose', is_flag=True, help="Enable verbose output") 602 | def destroy(verbose): 603 | """Destroy all created resources in the tenant""" 604 | 605 | # Ensure terraform.tfvars.json exists 606 | tfvars_path = os.path.join(TERRAFORM_DIR, 'terraform.tfvars.json') 607 | if not os.path.exists(tfvars_path): 608 | logging.error("Error: terraform.tfvars.json file not found.") 609 | return 610 | 611 | # Initialize and destroy the Terraform configuration 612 | return_code, stdout, stderr = tf.init() 613 | if return_code != 0: 614 | logging.error(f"Terraform init failed: {stderr}") 615 | return 616 | 617 | 618 | logging.info(f"Calling terraform destroy, this may take several minutes ...") 619 | #return_code, stdout, stderr = tf.destroy(force=True, input=False, auto_approve=True, capture_output=verbose) 620 | return_code, stdout, stderr = tf.apply(skip_plan=True, destroy=True, auto_approve=True, capture_output=not verbose) 621 | 622 | if return_code != 0: 623 | logging.error(f"Terraform apply failed: {stderr}") 624 | logging.error(stdout) 625 | logging.error(stderr) 626 | return 627 | 628 | logging.info("Azure AD tenant resources have been successfully destroyed!") 629 | 630 | # Remove the state files after destroying the resources 631 | logging.info(f"Deleting terraform state files ") 632 | for file in ["terraform.tfstate", "terraform.tfstate.backup", "terraform.tfvars.json"]: 633 | try: 634 | os.remove(os.path.join(TERRAFORM_DIR, file)) 635 | except FileNotFoundError: 636 | pass 637 | 638 | logging.info("Good bye.") 639 | 640 | if __name__ == '__main__': 641 | 642 | setup_logging(logging.INFO) 643 | print (banner) 644 | time.sleep(2) 645 | cli() 646 | -------------------------------------------------------------------------------- /Invoke-BadZure.ps1: -------------------------------------------------------------------------------- 1 | 2 | 3 | $banner = @" 4 | 5 | 6 | ____ _ _____ 7 | | __ ) __ _ __| ||__ /_ _ _ __ ___ 8 | | _ \ / _` | / _` | / /| | | || '__|/ _ \ 9 | | |_) || (_| || (_| | / /_| |_| || | | __/ 10 | |____/ \__,_| \__,_|/____|\__,_||_| \___| 11 | 12 | 13 | By Mauricio Velazco 14 | @mvelazco 15 | 16 | 17 | "@ 18 | 19 | 20 | Function Invoke-BadZure { 21 | 22 | 23 | <# 24 | 25 | .DESCRIPTION 26 | 27 | BadZure is a PowerShell script that leverages the Microsoft Graph SDK to orchestrate the setup of Azure Active Directory environments, populating them with diverse entities while also introducing common security misconfigurations to create vulnerable Azure AD tenants with multiple attack paths. 28 | 29 | .PARAMETER Build 30 | 31 | Used to populate and configure an Azure AD tenant 32 | 33 | .PARAMETER Destroy 34 | 35 | Used to delete entities created by BadZure on an Azure AD tenant. 36 | 37 | .PARAMETER TenantId 38 | 39 | Used to specify the Tenant ID for the initial authentication with Azure AD 40 | 41 | .PARAMETER NoAttackPaths 42 | 43 | If set, no attack paths are configured. 44 | 45 | .PARAMETER RandomAttackPath 46 | 47 | If set, only one random attack path is configured. 48 | 49 | .PARAMETER Password 50 | 51 | If set, Passwords will be leveraged for initial access simulation. Can be either random or user defined. 52 | 53 | .PARAMETER Token 54 | 55 | If set, Tokens will be leveraged for initial access simulation. 56 | 57 | 58 | .EXAMPLE 59 | 60 | .LINK 61 | 62 | https://github.com/mvelazc0/BadZure/ 63 | 64 | 65 | #> 66 | 67 | [CmdletBinding()] 68 | 69 | param 70 | ( 71 | [Parameter(Mandatory = $false)] 72 | [switch]$Build, 73 | [Parameter(Mandatory = $false)] 74 | [switch]$Destroy, 75 | [Parameter(Mandatory = $false)] 76 | [switch]$NoAttackPaths, 77 | [Parameter(Mandatory = $false)] 78 | [String]$Password, 79 | [Parameter(Mandatory = $false)] 80 | [Switch]$Token, 81 | [Parameter(Mandatory = $false)] 82 | [Switch]$RandomAttackPath, 83 | [Parameter(Mandatory = $true, ValueFromPipeline=$true)] 84 | [string]$TenantId 85 | 86 | ) 87 | $Verbose = $false 88 | if ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent){$Verbose = $true} 89 | 90 | Write-Host $banner 91 | 92 | if($Build -eq $true){ 93 | 94 | 95 | 96 | Connect-Graph -Scopes "Application.ReadWrite.All", "Directory.AccessAsUser.All","EntitlementManagement.ReadWrite.All","RoleManagement.ReadWrite.Directory","Group.Read.All" -TenantId $TenantId | Out-Null #Added the -TenantId parameter here 97 | 98 | CheckDomain 99 | # create principals 100 | CreateUsers 101 | CreateGroups 102 | CreateApps 103 | CreateAdministrativeUnits 104 | 105 | # assign random groups and permissions 106 | AssignGroups 107 | AssignUserRoles 108 | AssignAppRoles 109 | AssignAppApiPermissions 110 | 111 | # create attack paths 112 | if($NoAttackPaths -eq $false){ 113 | 114 | # choose a random attack path 115 | if ($RandomAttackPath -eq $true) 116 | { 117 | $path = Get-Random (1,3) 118 | Switch ($path){ 119 | 1 {CreateAttackPath1 $Password $Token} 120 | 2 {CreateAttackPath2 $Password $Token} 121 | 3 {CreateAttackPath3 $Password $Token} 122 | 123 | } 124 | } 125 | else 126 | { 127 | CreateAttackPath1 $Password $Token 128 | CreateAttackPath2 $Password $Token 129 | CreateAttackPath3 $Password $Token 130 | $global:attackpath_apps =@() 131 | } 132 | } 133 | } 134 | elseif($Destroy-eq $true){ 135 | 136 | 137 | 138 | Connect-Graph -Scopes "Application.ReadWrite.All", "Directory.AccessAsUser.All","EntitlementManagement.ReadWrite.All","RoleManagement.ReadWrite.Directory","Group.Read.All" -TenantId $TenantId | Out-Null #Added the -TenantId parameter here 139 | 140 | CheckDomain 141 | # remove principals 142 | DeleteUsers 143 | DeleteGroups 144 | DeleteApps 145 | DeleteeAdministrativeUnits 146 | } 147 | else{ 148 | 149 | Get-Help Invoke-BadZure 150 | 151 | } 152 | 153 | } 154 | 155 | ## Global var 156 | 157 | $global:attackpath_apps = @() 158 | $global:tenantDomain = "" 159 | 160 | ## Create functions 161 | 162 | Function CheckDomain([Boolean]$Verbose) { 163 | 164 | 165 | $account = (Get-MgContext | Select-Object Account).Account 166 | if ([string]::IsNullorEmpty($account) -eq $false){ 167 | $pos=$account.IndexOf('@') 168 | $global:tenantDomain = $account.Substring($pos+1) 169 | } 170 | else { 171 | $global:tenantDomain = Read-Host -Prompt "Enter your tenant's domain name. Example badzure.com" 172 | } 173 | 174 | } 175 | 176 | 177 | Function CreateUsers([Boolean]$Verbose) { 178 | 179 | Write-Host [!] Creating Users 180 | # set a random password for each user 181 | $randomString = -join ((33..47) + (48..57) + (65..90) + (97..122) + (123..126) | Get-Random -Count 15 | % { [char]$_ }) 182 | $PasswordProfile = @{ 183 | Password = $randomString 184 | } 185 | 186 | $users = Import-Csv -Path "Csv\users.csv" 187 | <# 188 | $checkdomain = (Get-MgContext | Select-Object Account).Account 189 | #if $checkdomain has a value, use it as part of the newly created users' email addresses 190 | if ([string]::IsNullorEmpty($checkdomain) -eq $false){ 191 | $checkdomain = $account 192 | } 193 | #if get-mgcontext does not have the .account, ask them to enter their domain in the form of an email. code flow proceeds. 194 | else{ 195 | $account = Read-Host -Prompt "Enter a verified email domain in the format hello@emaildomain" 196 | } 197 | 198 | $pos=$account.IndexOf('@') 199 | $domain=$account.Substring($pos+1) 200 | #> 201 | $upns=@() 202 | 203 | 204 | foreach ($user in $users) { 205 | $displayName = -join($user.FirstName,'.',$user.LastName) 206 | #$upn = -join($displayName,'@',$domain) 207 | $upn = -join($displayName,'@',$global:tenantDomain) 208 | $upns+=$upn 209 | New-MgUser -DisplayName $displayName -PasswordProfile $PasswordProfile -AccountEnabled -MailNickName $displayName -UserPrincipalName $upn | Out-Null 210 | Write-Verbose "`t[+] Created User $upn" 211 | } 212 | $upns | Out-File -FilePath users.txt 213 | } 214 | 215 | Function CreateApps([Boolean]$Verbose){ 216 | 217 | Write-Host [!] Creating application registrations and service principals 218 | $apps = Import-Csv -Path "Csv\apps.csv" 219 | foreach ($app in $apps) { 220 | 221 | $new_app= New-MgApplication -DisplayName $app.DisplayName 222 | $new_sp= New-MgServicePrincipal -AppId $new_app.Appid 223 | Write-Verbose "`t[+] Created application with displayname $($app.DisplayName) and Service Principal $($new_sp.Id)" 224 | 225 | } 226 | } 227 | 228 | Function CreateGroups([Boolean]$Verbose){ 229 | 230 | Write-Host [!] Creating Groups 231 | $groups = Import-Csv -Path "Csv\groups.csv" 232 | foreach ($group in $groups) { 233 | 234 | $nickName= $group.DisplayName -replace (' ','') 235 | #$new_group = New-MgGroup -DisplayName $group.DisplayName -MailEnabled:$False -MailNickName $nickName -SecurityEnabled -IsAssignableToRole 236 | $new_group = New-MgGroup -DisplayName $group.DisplayName -MailEnabled:$False -MailNickName $nickName -SecurityEnabled 237 | Write-Verbose "`t[+] Created group with displayname $($new_group.DisplayName) and Id $($new_group.Id)" 238 | 239 | } 240 | } 241 | 242 | Function CreateAdministrativeUnits([Boolean]$Verbose){ 243 | 244 | Write-Host [!] Creating administrative units 245 | $a_units = Import-Csv -Path "Csv\a_units.csv" 246 | foreach ($a_unit in $a_units) { 247 | 248 | $params = @{ 249 | displayName = $a_unit.DisplayName 250 | } 251 | $new_adunit = New-MgDirectoryAdministrativeUnit -BodyParameter $params 252 | Write-Verbose "`t[+] Created administrative unit with displayname $($new_adunit.DisplayName) and Id $($new_adunit.Id)" 253 | } 254 | 255 | } 256 | 257 | 258 | ## Delete functions 259 | 260 | 261 | Function DeleteGroups([Boolean]$Verbose){ 262 | 263 | Write-Host [!] Removing groups 264 | $groups = Import-Csv -Path "Csv\groups.csv" 265 | foreach ($group in $groups) { 266 | 267 | $displayName = $group.DisplayName 268 | $groups = Get-MgGroup -Filter "DisplayName eq '$displayName'" 269 | 270 | # in case groups were created more than once 271 | if ($groups -is [Array]) { 272 | foreach ($group in $groups){ 273 | 274 | Remove-MgGroup -GroupId $group.Id 275 | Write-Verbose "`t[+] Deleted group with displayname $($group.DisplayName) and Id $($group.Id)" 276 | 277 | } 278 | } 279 | else { 280 | Remove-MgGroup -GroupId $($groups.Id) 281 | Write-Verbose "`t[+] Deleted group with displayname $($groups.DisplayName) and Id $($groups.Id)" 282 | } 283 | 284 | } 285 | } 286 | 287 | 288 | 289 | Function DeleteApps([Boolean]$Verbose){ 290 | 291 | Write-Host [!] Removing application registrations 292 | 293 | $apps = Import-Csv -Path "Csv\apps.csv" 294 | foreach ($app in $apps) { 295 | 296 | $DisplayName = $app.DisplayName 297 | $app_ids= (Get-MgApplication -Filter "DisplayName eq '$DisplayName'").Id 298 | 299 | # in case apps were created more than once 300 | if ($app_ids -is [Array]) { 301 | foreach ($app_id in $app_ids){ 302 | Remove-MgApplication -ApplicationId $app_id | Out-Null 303 | Write-Verbose "`t[+] Deleted application with Id $app_id" 304 | 305 | } 306 | } 307 | else { 308 | Remove-MgApplication -ApplicationId $app_ids | Out-Null 309 | Write-Verbose "`t[+] Deleted application with Id $app_ids" 310 | } 311 | 312 | } 313 | } 314 | 315 | 316 | Function DeleteUsers([Boolean]$Verbose){ 317 | 318 | Write-Host [!] Removing users 319 | 320 | $users = Import-Csv -Path "Csv\users.csv" 321 | <# 322 | $checkdomain = (Get-MgContext | Select-Object Account).Account 323 | #if $checkdomain has a value, use it as part of the newly created users' email addresses 324 | if ([string]::IsNullorEmpty($checkdomain) -eq $false){ 325 | $checkdomain = $account 326 | } 327 | #if get-mgcontext does not have the .account, ask them to enter their domain in the form of an email. code flow proceeds. 328 | else{ 329 | $account = Read-Host -Prompt "Enter a verified email domain in the format hello@emaildomain" 330 | } 331 | 332 | $pos=$account.IndexOf('@') 333 | $domain=$account.Substring($pos+1) 334 | #> 335 | 336 | foreach ($user in $users) { 337 | $displayName = -join($user.FirstName,'.',$user.LastName) 338 | 339 | #$upn = -join($displayName,'@',$domain) 340 | $upn = -join($displayName,'@',$global:tenantDomain) 341 | $user = Get-MgUser -Filter "UserPrincipalName eq '$upn'" 342 | Remove-MgUser -UserId $user.Id 343 | Write-Verbose "`t[+] Deleted user with ObjectId $($user.Id)" 344 | } 345 | } 346 | 347 | 348 | Function DeleteeAdministrativeUnits([Boolean]$Verbose){ 349 | 350 | Write-Host [!] Removing administrative units 351 | $a_units = Import-Csv -Path "Csv\a_units.csv" 352 | foreach ($a_unit in $a_units) { 353 | 354 | $DisplayName = $a_unit.DisplayName 355 | $admunit_ids= (Get-MgDirectoryAdministrativeUnit -Filter "DisplayName eq '$DisplayName'").Id 356 | 357 | # in case adm units were created more than once 358 | if ($admunit_ids -is [Array]) { 359 | foreach ($admunit_id in $admunit_ids){ 360 | 361 | Remove-MgDirectoryAdministrativeUnit -AdministrativeUnitId $admunit_id | Out-Null 362 | Write-Verbose "`t[+] Deleted administrative unit with Id $admunit_id" 363 | 364 | } 365 | } 366 | else { 367 | Remove-MgDirectoryAdministrativeUnit -AdministrativeUnitId $admunit_ids| Out-Null 368 | Write-Verbose "`t[+] Deleted administrative unit with Id $admunit_ids" 369 | } 370 | } 371 | } 372 | 373 | 374 | ## Assign functions 375 | 376 | Function AssignGroups([Boolean]$Verbose){ 377 | 378 | Write-Host [!] Assigning random users to random groups 379 | $users = Import-Csv -Path "Csv/users.csv" 380 | <# 381 | $checkdomain = (Get-MgContext | Select-Object Account).Account 382 | #if $checkdomain has a value, use it as part of the newly created users' email addresses 383 | if ([string]::IsNullorEmpty($checkdomain) -eq $false){ 384 | $checkdomain = $account 385 | } 386 | #if get-mgcontext does not have the .account, ask them to enter their domain in the form of an email. code flow proceeds. 387 | else{ 388 | $account = Read-Host -Prompt "Enter a verified email domain in the format hello@emaildomain" 389 | } 390 | 391 | $pos=$account.IndexOf('@') 392 | $domain=$account.Substring($pos+1) 393 | #> 394 | 395 | $user_ids = @() 396 | 397 | foreach ($user in $users) { 398 | $displayName = -join($user.FirstName,'.',$user.LastName) 399 | #$upn = -join($displayName,'@',$domain) 400 | $upn = -join($displayName,'@',$global:tenantDomain) 401 | $user = Get-MgUser -Filter "UserPrincipalName eq '$upn'" 402 | $user_ids +=$user.Id 403 | } 404 | 405 | $groups = Import-Csv -Path "Csv\groups.csv" 406 | foreach ($group in $groups) { 407 | 408 | $displayName = $group.DisplayName 409 | $group_id = (Get-MgGroup -Filter "DisplayName eq '$displayName'").Id 410 | $used_users = @() 411 | foreach($i in 1..3){ 412 | do 413 | { 414 | $random_user = (Get-Random $user_ids) 415 | } 416 | until ($used_users -notcontains $random_user) 417 | New-MgGroupMember -GroupId $group_id -DirectoryObjectId $random_user 418 | Write-Verbose "`t[+] Added user with Id $random_user to group with id $group_id" 419 | 420 | $used_users += $random_user 421 | } 422 | 423 | } 424 | 425 | } 426 | 427 | 428 | Function AssignAppRoles([Boolean]$Verbose){ 429 | 430 | Write-Host [!] Assigning random Azure Ad roles to applications 431 | $roles = ('Exchange Administrator', 'Security Operator', 'Network Administrator', 'Intune Administrator', 'Attack Simulation Administrator', 'Application Developer') 432 | $apps = Import-Csv -Path "Csv\apps.csv" 433 | $used_apps =@() 434 | foreach ($role in $roles) 435 | { 436 | do 437 | { 438 | $random_app_dn = (Get-Random $apps).DisplayName 439 | } 440 | until ($used_apps -notcontains $random_app_dn) 441 | 442 | $roleDefinitionId = (Get-MgRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq '$role'").Id 443 | $appSpId = (Get-MgServicePrincipal -Filter "DisplayName eq '$random_app_dn'").Id 444 | $appId = (Get-MgApplication -Filter "DisplayName eq '$random_app_dn'").Id 445 | New-MgRoleManagementDirectoryRoleAssignment -PrincipalId $appSpId -RoleDefinitionId $roleDefinitionId -DirectoryScopeId "/" | Out-Null 446 | Write-Verbose "`t[+] Assigned $role to application with displayName $random_app_dn" 447 | $used_apps += $random_app_dn 448 | 449 | } 450 | 451 | } 452 | 453 | Function AssignAppApiPermissions([Boolean]$Verbose){ 454 | 455 | 456 | Write-Host [!] Assigning random Graph API permissions to applications 457 | $apps = Import-Csv -Path "Csv\apps.csv" 458 | 459 | $permissions = ('d07a8cc0-3d51-4b77-b3b0-32704d1f69fa', '134fd756-38ce-4afd-ba33-e9623dbe66c2', '93283d0a-6322-4fa8-966b-8c121624760d', '798ee544-9d2d-430c-a058-570e29e34338', '6b7d71aa-70aa-4810-a8d9-5d9fb2830017', '7e05723c-0bb0-42da-be95-ae9f08a6e53c' ,'4f5ac95f-62fd-472c-b60f-125d24ca0bc5' , 'eedb7fdd-7539-4345-a38b-4839e4a84cbd') 460 | # 06da0dbc-49e2-44d2-8312-53f166ab848a 461 | # eda39fa6-f8cf-4c3c-a909-432c683e4c9b 462 | # eda39fa6-f8cf-4c3c-a909-432c683e4c9b 463 | # 6323133e-1f6e-46d4-9372-ac33a0870636 464 | 465 | 466 | $used_apps =@() 467 | 468 | foreach ($permission in $permissions) 469 | { 470 | do 471 | { 472 | $random_app_dn = (Get-Random $apps).DisplayName 473 | } 474 | until ($used_apps -notcontains $random_app_dn) 475 | 476 | $resourceId = (Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'" -Property "id,displayName,appId,appRoles").Id 477 | $appSpId = (Get-MgServicePrincipal -Filter "DisplayName eq '$random_app_dn'").Id 478 | $appId = (Get-MgApplication -Filter "DisplayName eq '$random_app_dn'").Id 479 | 480 | $params = @{ 481 | PrincipalId = $appSpId 482 | ResourceId = $resourceId 483 | AppRoleId = $permission 484 | } 485 | New-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $appSpId -BodyParameter $params | Out-Null 486 | Write-Verbose "`t[+] Assigned API permissions $permission to application with displayName $random_app_dn" 487 | $used_apps += $random_app_dn 488 | 489 | } 490 | } 491 | 492 | 493 | Function AssignUserRoles([string]$Password, [Boolean]$Verbose) { 494 | 495 | 496 | Write-Host [!] Assigning random Azure Ad roles to users 497 | $users = Import-Csv -Path "Csv/users.csv" 498 | <# 499 | $checkdomain = (Get-MgContext | Select-Object Account).Account 500 | #if $checkdomain has a value, use it as part of the newly created users' email addresses 501 | if ([string]::IsNullorEmpty($checkdomain) -eq $false){ 502 | $checkdomain = $account 503 | } 504 | #if get-mgcontext does not have the .account, ask them to enter their domain in the form of an email. code flow proceeds. 505 | else{ 506 | $account = Read-Host -Prompt "Enter a verified email domain in the format hello@emaildomain" 507 | } 508 | 509 | $pos=$account.IndexOf('@') 510 | $domain=$account.Substring($pos+1) 511 | #> 512 | 513 | $user_ids = @() 514 | 515 | foreach ($user in $users) { 516 | $displayName = -join($user.FirstName,'.',$user.LastName) 517 | #$upn = -join($displayName,'@',$domain) 518 | $upn = -join($displayName,'@',$global:tenantDomain) 519 | $user = Get-MgUser -Filter "UserPrincipalName eq '$upn'" 520 | $user_ids +=$user.Id 521 | } 522 | 523 | $used_users = @() 524 | $roles = ('Reports Reader', 'Reports Reader', 'Authentication Administrator', 'Directory Readers', 'Guest Inviter', 'Message Center Reader', 'Groups Administrator', 'Guest Inviter', 'Network Administrator') 525 | foreach ($role in $roles) 526 | { 527 | do 528 | { 529 | $random_user = (Get-Random $user_ids) 530 | } 531 | until ($used_users -notcontains $random_user) 532 | 533 | $roleDefinitionId = (Get-MgRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq '$role'").Id 534 | New-MgRoleManagementDirectoryRoleAssignment -PrincipalId $random_user -RoleDefinitionId $roleDefinitionId -DirectoryScopeId "/" | Out-Null 535 | Write-Verbose "`t[+] Assigned $role to user with id $random_user" 536 | $used_users += $random_user 537 | } 538 | } 539 | 540 | 541 | 542 | 543 | ## Attack path functions 544 | 545 | Function CreateAttackPath1 ([String]$Password, [Boolean]$Token){ 546 | 547 | Write-Host [!] Creating attack path 1 548 | 549 | <# 550 | We have to use the Graph beta based on https://github.com/microsoftgraph/msgraph-sdk-powershell/issues/880 551 | Select-MgProfile beta -Verbose:$false 552 | $directoryRole='Privileged Role Administrator' 553 | $directoryRoleId= (Get-MgDirectoryRole -Filter "DisplayName eq '$directoryRole'").Id 554 | $service_principal_id="" 555 | $service_principals= Get-MgDirectoryRoleMember -DirectoryRoleId $directoryRoleId | where { $_.AdditionalProperties."@odata.type" -eq "#microsoft.graph.servicePrincipal"} 556 | Select-MgProfile v1.0 -Verbose:$false 557 | #> 558 | 559 | $role = "Privileged Role Administrator" 560 | $apps = Import-Csv -Path "Csv\apps.csv" 561 | 562 | Do 563 | { 564 | $random_app_dn = (Get-Random $apps).DisplayName 565 | $appSpId = (Get-MgServicePrincipal -Filter "DisplayName eq '$random_app_dn'").Id 566 | $appId = (Get-MgApplication -Filter "DisplayName eq '$random_app_dn'").Id 567 | } 568 | While ($global:attackpath_apps -contains $appId ) 569 | 570 | $roleDefinitionId = (Get-MgRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq '$role'").Id 571 | New-MgRoleManagementDirectoryRoleAssignment -PrincipalId $appSpId -RoleDefinitionId $roleDefinitionId -DirectoryScopeId "/" | Out-Null 572 | Write-Verbose "`t[+] Assigned $role to application with displayName $random_app_dn" 573 | $random_user_id= GetRandomUser 574 | $NewOwner = @{ 575 | "@odata.id"= "https://graph.microsoft.com/v1.0/directoryObjects/{$random_user_id}" 576 | } 577 | 578 | New-MgApplicationOwnerByRef -ApplicationId $appId -BodyParameter $NewOwner 579 | Write-Verbose "`t[+] Created application owner for $appId" 580 | $global:attackpath_apps+=$appId 581 | UpdatePassword $random_user_id $Password $Token 582 | 583 | 584 | 585 | } 586 | 587 | Function CreateAttackPath2([String]$Password, [Boolean]$Token){ 588 | 589 | Write-Host [!] Creating attack path 2 590 | 591 | $random_user_id = GetRandomUser 592 | $role= 'Helpdesk Administrator' 593 | $roleDefinitionId = (Get-MgRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq '$role'").Id 594 | New-MgRoleManagementDirectoryRoleAssignment -PrincipalId $random_user_id -RoleDefinitionId $roleDefinitionId -DirectoryScopeId "/" | Out-Null 595 | Write-Verbose "`t[+] Assigned $role to user with id $random_user_id" 596 | 597 | $NewOwner = @{ 598 | "@odata.id"= "https://graph.microsoft.com/v1.0/directoryObjects/$random_user_id" 599 | } 600 | 601 | 602 | <# 603 | $applications = Import-Csv -Path "Csv\apps.csv" 604 | $service_principal_ids= @() 605 | foreach ($app in $applications) { 606 | 607 | $DisplayName = $app.DisplayName 608 | $service_principal_ids+=(Get-MgServicePrincipal -Filter "DisplayName eq '$DisplayName'").Id 609 | } 610 | 611 | foreach ($service_principal_id in $service_principal_ids){ 612 | $appRoleId = (Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $service_principal_id).AppRoleId 613 | if ($appRoleId -eq '9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8') 614 | { 615 | $DisplayName = (Get-MgServicePrincipal -ServicePrincipalId $service_principal_id).DisplayName 616 | $appId= (Get-MgApplication -Filter "DisplayName eq '$DisplayName'").Id 617 | New-MgApplicationOwnerByRef -ApplicationId $appId -BodyParameter $NewOwner 618 | Write-Host `t[+] Created application owner for $appId 619 | UpdatePassword $user_id $Password $Token 620 | 621 | } 622 | #> 623 | 624 | # RoleManagement.ReadWrite.Directory 625 | $permission = ('9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8') 626 | $apps = Import-Csv -Path "Csv\apps.csv" 627 | 628 | Do 629 | { 630 | $random_app_dn = (Get-Random $apps).DisplayName 631 | $appSpId = (Get-MgServicePrincipal -Filter "DisplayName eq '$random_app_dn'").Id 632 | $appId = (Get-MgApplication -Filter "DisplayName eq '$random_app_dn'").Id 633 | } 634 | While ($global:attackpath_apps -contains $appId ) 635 | 636 | $resourceId = (Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'" -Property "id,displayName,appId,appRoles").Id 637 | 638 | $params = @{ 639 | PrincipalId = $appSpId 640 | ResourceId = $resourceId 641 | AppRoleId = $permission 642 | } 643 | 644 | New-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $appSpId -BodyParameter $params | Out-Null 645 | Write-Verbose "[+] Assigned API permissions $permission to application with displayName $random_app_dn" 646 | New-MgApplicationOwnerByRef -ApplicationId $appId -BodyParameter $NewOwner 647 | Write-Verbose "[+] Created application owner for $appId" 648 | $global:attackpath_apps+=$appId 649 | UpdatePassword $random_user_id $Password $Token 650 | 651 | } 652 | 653 | Function CreateAttackPath3([String]$Password, [Boolean]$Token){ 654 | 655 | Write-Host [!] Creating attack path3 656 | 657 | $random_user_id = GetRandomUser 658 | $role= 'User Administrator' 659 | $roleDefinitionId = (Get-MgRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq '$role'").Id 660 | New-MgRoleManagementDirectoryRoleAssignment -PrincipalId $random_user_id -RoleDefinitionId $roleDefinitionId -DirectoryScopeId "/" | Out-Null 661 | Write-Verbose "`t[+] Assigned $role to user with id $random_user_id" 662 | 663 | 664 | <# 665 | $directoryRole='User Administrator' 666 | $directoryRoleId= (Get-MgDirectoryRole -Filter "DisplayName eq '$directoryRole'").Id 667 | $user_id="" 668 | $users = Get-MgDirectoryRoleMember -DirectoryRoleId $directoryRoleId 669 | 670 | if ($users -is [Array]){ 671 | 672 | $user_id=$users[0].Id 673 | } 674 | 675 | else { 676 | $user_id=$users.Id 677 | } 678 | #> 679 | 680 | $NewOwner = @{ 681 | "@odata.id"= "https://graph.microsoft.com/v1.0/directoryObjects/$random_user_id" 682 | } 683 | 684 | 685 | # AppRoleAssignment.ReadWrite.All 686 | $permission = ('06b708a9-e830-4db3-a914-8e69da51d44f') 687 | $apps = Import-Csv -Path "Csv\apps.csv" 688 | 689 | Do 690 | { 691 | $random_app_dn = (Get-Random $apps).DisplayName 692 | $appSpId = (Get-MgServicePrincipal -Filter "DisplayName eq '$random_app_dn'").Id 693 | $appId = (Get-MgApplication -Filter "DisplayName eq '$random_app_dn'").Id 694 | } 695 | While ($global:attackpath_apps -contains $appId ) 696 | 697 | $resourceId = (Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'" -Property "id,displayName,appId,appRoles").Id 698 | 699 | $params = @{ 700 | PrincipalId = $appSpId 701 | ResourceId = $resourceId 702 | AppRoleId = $permission 703 | } 704 | 705 | New-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $appSpId -BodyParameter $params | Out-Null 706 | Write-Verbose "`t[+] Assigned API permissions $permission to application with displayName $random_app_dn" 707 | New-MgApplicationOwnerByRef -ApplicationId $appId -BodyParameter $NewOwner 708 | Write-Verbose "`t[+] Created application owner for $appId" 709 | $global:attackpath_apps+=$appId 710 | UpdatePassword $random_user_id $Password $Token 711 | 712 | } 713 | 714 | 715 | ## Util functions 716 | 717 | Function GetRandomUser{ 718 | 719 | <# 720 | $checkdomain = (Get-MgContext | Select-Object Account).Account 721 | #if $checkdomain has a value, use it as part of the newly created users' email addresses 722 | if ([string]::IsNullorEmpty($checkdomain) -eq $false){ 723 | $checkdomain = $account 724 | } 725 | #if get-mgcontext does not have the .account, ask them to enter their domain in the form of an email. code flow proceeds. 726 | else{ 727 | $account = Read-Host -Prompt "Enter a verified email domain in the format hello@emaildomain" 728 | } 729 | 730 | $pos=$account.IndexOf('@') 731 | $domain=$account.Substring($pos+1) 732 | #> 733 | 734 | $users = Import-Csv -Path "Csv/users.csv" 735 | $user_ids = @() 736 | 737 | foreach ($user in $users) { 738 | $displayName = -join($user.FirstName,'.',$user.LastName) 739 | #$upn = -join($displayName,'@',$domain) 740 | $upn = -join($displayName,'@',$global:tenantDomain) 741 | $user = Get-MgUser -Filter "UserPrincipalName eq '$upn'" 742 | $user_ids +=$user.Id 743 | } 744 | $random_userid = (Get-Random $user_ids) 745 | return $random_userid 746 | 747 | } 748 | 749 | 750 | Function UpdatePassword ([String]$userId, [String]$Password, [Boolean]$Token) { 751 | 752 | if([string]::IsNullOrEmpty($Password)){ 753 | 754 | $randomString = -join ((33..47) + (48..57) + (65..90) + (97..122) + (123..126) | Get-Random -Count 15 | % { [char]$_ }) 755 | $NewPassword = @{} 756 | $NewPassword["Password"]= $randomString 757 | $NewPassword["ForceChangePasswordNextSignIn"] = $False 758 | Update-Mguser -UserId $userId.Trim() -PasswordProfile $NewPassword 759 | $username = (Get-MgUser -Filter "Id eq '$userId'").UserPrincipalName 760 | 761 | if ($Token -eq $false) 762 | { 763 | Write-Host `t[+] `"$randomString`" assigned as password to random user. 764 | Write-Verbose "t[+] $username" 765 | 766 | } 767 | else{ 768 | GetAccessToken2 $userId $randomString 769 | } 770 | 771 | } 772 | else{ 773 | 774 | $NewPassword = @{} 775 | $NewPassword["Password"]= $Password 776 | $NewPassword["ForceChangePasswordNextSignIn"] = $False 777 | Update-Mguser -UserId $userId.Trim() -PasswordProfile $NewPassword 778 | $username = (Get-MgUser -Filter "Id eq '$userId'").UserPrincipalName 779 | if ($Token -eq $false) 780 | { 781 | Write-Host `t[+] `"$Password`" assigned as password to random user. 782 | Write-Verbose "`t[+] $username" 783 | } 784 | else{ 785 | GetAccessToken2 $userId $Password 786 | } 787 | 788 | } 789 | 790 | } 791 | 792 | Function GetAccessToken ([String]$userId, [String]$Password) { 793 | 794 | Write-Host `t`[!] Obtaining user access token 795 | $username = (Get-MgUser -Filter "Id eq '$userId'").UserPrincipalName 796 | $SecurePassword = ConvertTo-SecureString “$Password” -AsPlainText -Force 797 | $credentials = New-Object System.Management.Automation.PSCredential($username, $SecurePassword) 798 | Connect-AzAccount -Credential $credentials | Out-Null 799 | Write-Host `t`[+] Access token for $username : 800 | Write-Host `t` (Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com/").Token 801 | 802 | } 803 | 804 | Function GetAccessToken2 ([String]$userId, [String]$Password) { 805 | 806 | Write-Host `t`[!] Obtaining access and refresh tokens for $username 807 | $username = (Get-MgUser -Filter "Id eq '$userId'").UserPrincipalName 808 | $tenantId = (Get-MgContext).TenantId 809 | $tokens = Get-CKAccessToken -ClientId 1950a258-227b-4e31-a9cf-717495945fc2 -Resource 'https://graph.microsoft.com/' -TenantId $tenantId -GrantType password -Username $username -Password $Password -Verbose:$false 810 | $access_token = $tokens.access_token 811 | $refresh_token = $tokens.refresh_token 812 | Write-Host `t`[+] access_token:$access_token 813 | Write-Host `t`[+] refresh_token:$refresh_token 814 | 815 | } 816 | 817 | 818 | ## External Functions 819 | 820 | ## credtis to Roberto Rodriguez for this function https://github.com/Azure/Cloud-Katana/blob/main/CloudKatanaAbilities/AzureAD/Authentication/Get-CKAccessToken.ps1 821 | function Get-CKAccessToken { 822 | <# 823 | .SYNOPSIS 824 | A PowerShell script to get a MS graph access token with a specific grant type and Azure AD application. 825 | 826 | Author: Roberto Rodriguez (@Cyb3rWard0g) 827 | License: MIT 828 | Required Dependencies: None 829 | Optional Dependencies: None 830 | 831 | .DESCRIPTION 832 | Get-CKAccessToken is a simple PowerShell wrapper around the Microsoft Graph API to get an access token. 833 | 834 | .PARAMETER ClientId 835 | The Application (client) ID assigned to the Azure AD application. 836 | 837 | .PARAMETER TenantId 838 | Tenant ID. Can be /common, /consumers, or /organizations. It can also be the directory tenant that you want to request permission from in GUID or friendly name format. 839 | 840 | .PARAMETER ResourceUrl 841 | Resource url for what you're requesting token. This could be one of the Azure services that support Azure AD authentication or any other resource URI. Example: https://graph.microsoft.com/ 842 | 843 | .PARAMETER GrantType 844 | The type of token request. 845 | 846 | .PARAMETER Username 847 | Username used for Password grant type. 848 | 849 | .PARAMETER Password 850 | Password used for Password grant type. 851 | 852 | .PARAMETER SamlToken 853 | SAML token used for SAML token grant type. 854 | 855 | .PARAMETER DeviceCode 856 | The device_code returned in the device authorization request. 857 | 858 | .PARAMETER AppSecret 859 | if the application requires a client secret, then use this parameter. 860 | 861 | .LINK 862 | https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-overview 863 | 864 | #> 865 | 866 | [cmdletbinding()] 867 | Param( 868 | [Parameter(Mandatory = $true)] 869 | [String] $ClientId, 870 | 871 | [Parameter(Mandatory = $false)] 872 | [string] $TenantId, 873 | 874 | [Parameter(Mandatory = $false)] 875 | [string] $Resource = 'https://graph.microsoft.com/', 876 | 877 | [Parameter(Mandatory=$true)] 878 | [ValidateSet("client_credentials","password","saml_token","device_code","refresh_token")] 879 | [string] $GrantType, 880 | 881 | [Parameter(Mandatory=$false)] 882 | [AllowEmptyString()] 883 | [string] $AppSecret 884 | ) 885 | DynamicParam { 886 | if ($GrantType) { 887 | # Adding Dynamic parameters 888 | if ($GrantType -eq 'password') { 889 | $ParamOptions = @( 890 | @{ 891 | 'Name' = 'Username'; 892 | 'Mandatory' = $true 893 | }, 894 | @{ 895 | 'Name' = 'Password'; 896 | 'Mandatory' = $true 897 | } 898 | ) 899 | } 900 | elseif ($GrantType -eq 'saml_token') { 901 | $ParamOptions = @( 902 | @{ 903 | 'Name' = 'SamlToken'; 904 | 'Mandatory' = $true 905 | } 906 | ) 907 | } 908 | elseif ($GrantType -eq 'device_code') { 909 | $ParamOptions = @( 910 | @{ 911 | 'Name' = 'DeviceCode'; 912 | 'Mandatory' = $true 913 | } 914 | ) 915 | } 916 | elseif ($GrantType -eq 'refresh_token') { 917 | $ParamOptions = @( 918 | @{ 919 | 'Name' = 'RefreshToken'; 920 | 'Mandatory' = $true 921 | } 922 | ) 923 | } 924 | 925 | # Adding Dynamic parameter 926 | $RuntimeParamDic = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary 927 | foreach ($Param in $ParamOptions) { 928 | $RuntimeParam = New-DynamicParam @Param 929 | $RuntimeParamDic.Add($Param.Name, $RuntimeParam) 930 | } 931 | return $RuntimeParamDic 932 | } 933 | } 934 | begin { 935 | # Force TLS 1.2 936 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 937 | 938 | # Process Tenant ID 939 | if (!$TenantId) { 940 | $TenantId = 'common' 941 | } 942 | 943 | # Process Dynamic parameters 944 | $PsBoundParameters.GetEnumerator() | ForEach-Object { New-Variable -Name $_.Key -Value $_.Value -ea 'SilentlyContinue'} 945 | } 946 | process { 947 | # Initialize Headers dictionary 948 | $headers = @{} 949 | $headers.Add('Content-Type','application/x-www-form-urlencoded') 950 | 951 | # Initialize Body 952 | $body = @{} 953 | $body.Add('resource',$Resource) 954 | $body.Add('client_id',$ClientId) 955 | 956 | if ($GrantType -eq 'client_credentials') { 957 | $body.Add('grant_type','client_credentials') 958 | } 959 | elseif ($GrantType -eq 'password') { 960 | $body.Add('username',$Username) 961 | $body.Add('password',$Password) 962 | $body.Add('grant_type','password') 963 | } 964 | elseif ($GrantType -eq 'saml_token') { 965 | $encodedSamlToken= [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($SamlToken)) 966 | $body.Add('assertion',$encodedSamlToken) 967 | $body.Add('grant_type','urn:ietf:params:oauth:grant-type:saml1_1-bearer') 968 | $body.Add('scope','openid') 969 | } 970 | elseif ($GrantType -eq 'device_code') { 971 | $body.Add('grant_type','urn:ietf:params:oauth:grant-type:device_code') 972 | $body.Add('code',$DeviceCode) 973 | } 974 | elseif ($GrantType -eq 'refresh_token') { 975 | $body.Add('refresh_token',$RefreshToken) 976 | $body.Add('grant_type','refresh_token') 977 | $body.Add('scope','openid') 978 | } 979 | 980 | if ($AppSecret) 981 | { 982 | $body.Add('client_secret',$AppSecret) 983 | } 984 | 985 | $Params = @{ 986 | Headers = $headers 987 | uri = "https://login.microsoftonline.com/$TenantId/oauth2/token?api-version=1.0" 988 | Body = $body 989 | method = 'Post' 990 | } 991 | $request = Invoke-RestMethod @Params 992 | 993 | # Process authentication request 994 | if($null -eq $request) { 995 | throw "Token never received from AAD" 996 | } 997 | else { 998 | $request 999 | } 1000 | } 1001 | } 1002 | 1003 | function New-DynamicParam { 1004 | [CmdletBinding()] 1005 | [OutputType('System.Management.Automation.RuntimeDefinedParameter')] 1006 | param ( 1007 | [Parameter(Mandatory)] 1008 | [ValidateNotNullOrEmpty()] 1009 | [string]$Name, 1010 | [Parameter(Mandatory=$false)] 1011 | [array]$ValidateSetOptions, 1012 | [Parameter()] 1013 | [switch]$Mandatory = $false, 1014 | [Parameter()] 1015 | [switch]$ValueFromPipeline = $false, 1016 | [Parameter()] 1017 | [switch]$ValueFromPipelineByPropertyName = $false 1018 | ) 1019 | 1020 | $Attrib = New-Object System.Management.Automation.ParameterAttribute 1021 | $Attrib.Mandatory = $Mandatory.IsPresent 1022 | $Attrib.ValueFromPipeline = $ValueFromPipeline.IsPresent 1023 | $Attrib.ValueFromPipelineByPropertyName = $ValueFromPipelineByPropertyName.IsPresent 1024 | 1025 | # Create AttributeCollection object for the attribute 1026 | $Collection = new-object System.Collections.ObjectModel.Collection[System.Attribute] 1027 | # Add our custom attribute 1028 | $Collection.Add($Attrib) 1029 | # Add Validate Set 1030 | if ($ValidateSetOptions) 1031 | { 1032 | $ValidateSet= new-object System.Management.Automation.ValidateSetAttribute($Param.ValidateSetOptions) 1033 | $Collection.Add($ValidateSet) 1034 | } 1035 | 1036 | # Create Runtime Parameter 1037 | $DynParam = New-Object System.Management.Automation.RuntimeDefinedParameter($Param.Name, [string], $Collection) 1038 | $DynParam 1039 | } 1040 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BadZure 2 | [![BlackHat Arsenal 2024](https://raw.githubusercontent.com/toolswatch/badges/master/arsenal/usa/2024.svg)](https://www.blackhat.com/asia-23/arsenal/schedule/index.html#purplesharp-automated-adversary-simulation-31336) 3 | [![Open_Threat_Research Community](https://img.shields.io/badge/Open_Threat_Research-Community-brightgreen.svg)](https://twitter.com/OTR_Community) 4 | 5 |
6 | BadZure logo 7 |
8 |
9 | 10 | BadZure is a Python tool that utilizes Terraform to automate the setup of Azure Active Directory (now Entra ID) tenants, populating them with various entities and introducing common security misconfigurations to create vulnerable tenants with multiple attack paths. 11 | 12 | BadZure automates the creation of various entities, including users, groups, application registrations, service principals, and administrative units. To simulate common security misconfigurations in real environments, it randomly assigns Azure AD roles, Graph permissions, and application ownership privileges to selected security principals, enabling the creation of unique attack paths. Adhering to the 'Assume Breach' principle, BadZure offers users two methods of initial access to the vulnerable tenants it creates, thereby simulating account takeover scenarios. 13 | 14 | The key advantage of BadZure is its ability to quickly populate and purge Azure AD tenants with randomly generated vulnerable configurations and pre-configured initial access, facilitating continuous and iterative Azure AD adversary simulation and detection development experimentation. It is designed for security practitioners interested in exploring and understanding Azure AD security. 15 | 16 | ## Goals / Use Cases 17 | 18 | BadZure was initialy written to host the [Azure AD Battle School: Hands-on Attack and Defense](https://www.x33fcon.com/#!s/MauricioVelazco.md) workshop at X33fcon 2023. 19 | 20 | An Azure AD tenant populated with BadZure also enables red and blue teams to: 21 | 22 | * Experiment with common Azure AD attack vectors and tools 23 | * Quickly stand up misconfigured Azure AD lab tenants. 24 | * Obtain attack telemetry to build, test and enhance detection controls 25 | * Execute purple team exercises in a safe setting 26 | * Faciliate hands-on Azure AD security training 27 | * Host dynamic Azure AD Capture the Flag (CTF) events 28 | 29 | ## Attack Paths 30 | 31 | ### Initial Access 32 | 33 | BadZure simulates initial access by employing common account takeover techniques, including password attacks and token theft. By providing both passwords and tokens, BadZure enables security practitioners to effectively simulate initial access scenarios and explore various attack vectors against Azure AD tenants. 34 | 35 | #### Password-Based Access 36 | 37 | When configured to use passwords, BadZure assigns randomly generated passwords to key user accounts that are part of the attack paths. These passwords are provided to BadZure users to simulate password-based attacks, such as [credential stuffing](https://owasp.org/www-community/attacks/Credential_stuffing) or [password spraying](https://owasp.org/www-community/attacks/Password_Spraying_Attack). Additionally, BadZure automatically generates a users.txt file containing the usernames of all created accounts, facilitating testing strategies like password spraying. 38 | 39 | #### Token-Based Access 40 | 41 | For token-based access, BadZure generates JWT access tokens for specified principals. These tokens are provided in the output, simulating scenarios where an attacker has obtained valid tokens through [reverse proxy phishing](https://help.evilginx.com/), [endpoint malware](https://mrd0x.com/stealing-tokens-from-office-applications/) or [device code phishing](https://aadinternals.com/post/phishing/). Users can utilize these tokens to authenticate directly against Azure AD resources, gaining an understanding of potential attack vectors involving token theft. 42 | 43 | 44 | ### Privilege Escalation 45 | 46 | BadZure simulates privilege escalation by introducing misconfigurations within Azure AD roles, Graph API permissions, and application ownerships. These misconfigurations include assigning high-privilege roles to service principals or users, granting extensive Graph API permissions to applications, and configuring users as owners of privileged applications. These settings allow for the configuration of [service principal abuse](https://posts.specterops.io/azure-privilege-escalation-via-service-principal-abuse-210ae2be2a5) attack scenarios. 47 | 48 | A BloodHound-generated graph, showcasing the attack paths BadZure can create, is shown below. 49 | 50 | ![](img/attack_paths.png) 51 | 52 | ## Demo 53 | 54 | [![BadZure](https://img.youtube.com/vi/IzurUrOsvsQ/0.jpg)](https://www.youtube.com/watch?v=IzurUrOsvsQ&t=746s) 55 | 56 | ## Quick Start Guide 57 | 58 | ### Requirements 59 | 60 | - **Azure CLI**: Follow the instructions [here](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) to install Azure CLI. 61 | - **Terraform**: Follow the instructions [here](https://learn.hashicorp.com/tutorials/terraform/install-cli) to install Terraform. 62 | 63 | ### Create an Azure AD Tenant 64 | 65 | [Creating an Azure subscription](https://learn.microsoft.com/en-us/training/modules/create-an-azure-account/1-introduction) will also provide you an Azure AD tenant. 66 | 67 | **Note:** Utilizing BadZure within your Azure subscription won't lead to any additional costs as it only requires an [Azure AD Free license](https://azure.microsoft.com/en-us/free/). 68 | 69 | ### Clone Repository 70 | 71 | ````shell 72 | git clone https://github.com/mvelazc0/BadZure 73 | cd BadZure 74 | ```` 75 | 76 | ### Create virtual environment and install dependencies 77 | 78 | ```shell 79 | # Create a virtual environment 80 | python -m venv venv 81 | 82 | # Activate the virtual environment 83 | # On Windows 84 | venv\Scripts\activate 85 | 86 | # On Unix or MacOS 87 | source venv/bin/activate 88 | 89 | # Install dependencies 90 | pip install -r requirements.txt 91 | ``` 92 | ### Login to Azure as a Global Administrator 93 | 94 | ```shell 95 | az login 96 | ```` 97 | 98 | ### Create and destroy vulnerable tenants 99 | 100 | ````shell 101 | # Display the help menu 102 | python badzure.py --help 103 | 104 | # Populate a tenant and configure all attack paths using the default badzure.yml config file 105 | python badzure.py build 106 | 107 | # Populate a tenant and configure all attack paths with a different config file 108 | python badzure.py build --config config.yml 109 | 110 | # Show the created resources in Azure AD tenant 111 | python badzure.py show 112 | 113 | # Destroy all created identities with verbose logging 114 | python badzure.py destroy --verbose 115 | 116 | ```` 117 | 118 | ## YAML Configuration File 119 | 120 | BadZure leverages a configuration file used to configure the setup of the Azure AD tenant. This file allows users to specify details such as the number of users, groups, applications, administrative units, and attack paths to be created. 121 | 122 | ### Example Configuration 123 | 124 | ```yaml 125 | tenant: 126 | tenant_id: "your-tenant-id" 127 | domain: "your-domain.com" 128 | users: 30 129 | groups: 10 130 | applications: 10 131 | administrative_units: 10 132 | 133 | attack_paths: 134 | 135 | attack_path_1: 136 | enabled: true 137 | initial_access: password 138 | privilege_escalation: ServicePrincipalAbuse 139 | method: AzureADRole 140 | entra_role : random 141 | 142 | attack_path_2: 143 | enabled: true 144 | initial_access: token 145 | privilege_escalation: ServicePrincipalAbuse 146 | method: GraphAPIPermission 147 | app_role : random 148 | 149 | ``` 150 | 151 | For more details on the configuration options, please refer to the [Wiki](https://github.com/mvelazc0/BadZure/wiki/YAML-Configuration-Guide) 152 | 153 | ## Author 154 | 155 | * **Mauricio Velazco** - [@mvelazco](https://twitter.com/mvelazco) 156 | 157 | ## Contributors 158 | 159 | * [Chan Huan Jun](https://www.linkedin.com/in/chan-huan-jun-50a704115/) 160 | 161 | ## References 162 | 163 | * [Cloud Katana](https://github.com/Azure/Cloud-Katana) by [Roberto Rodriguez](https://twitter.com/Cyb3rWard0g) 164 | * [AADInternals](https://github.com/Gerenios/AADInternals) by [Nestori Syynimaa](https://twitter.com/DrAzureAD) 165 | * [Azure Attack Paths](https://cloudbrothers.info/en/azure-attack-paths/) by [Fabian Bader](https://twitter.com/fabian_bader) 166 | * [ROADtools](https://github.com/dirkjanm/ROADtools) by [Dirkjan Mollema](https://twitter.com/_dirkjan) 167 | * [PurpleCloud](https://github.com/iknowjason/PurpleCloud) by [Jason Ostrom](https://twitter.com/securitypuck) 168 | * [Azure AD - Attack and Defense Playbook](https://github.com/Cloud-Architekt/AzureAD-Attack-Defense) by [Sami Lamppu](https://twitter.com/samilamppu) and [Thomas Naunheim](https://twitter.com/Thomas_Live) 169 | * [BloodHound/AzureHound](https://github.com/BloodHoundAD/AzureHound) by [Andy Robbins](https://twitter.com/_wald0) 170 | * Blog posts, talks and tools by [@Haus3c](https://twitter.com/Haus3c), [@kfosaaen](https://twitter.com/kfosaaen), [@inversecos](https://twitter.com/inversecos) and others. 171 | 172 | ## License 173 | 174 | This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details 175 | -------------------------------------------------------------------------------- /badzure.yml: -------------------------------------------------------------------------------- 1 | tenant: 2 | 3 | tenant_id: 00000000-0000-0000-0000-000000000000 4 | domain: contoso.onmicrosoft.com 5 | 6 | users: 30 7 | applications: 15 8 | groups: 10 9 | administrative_units: 10 10 | 11 | attack_paths: 12 | 13 | attack_path_1: 14 | enabled: true 15 | initial_access: password 16 | scenario : direct 17 | privilege_escalation: ServicePrincipalAbuse 18 | method: AzureADRole 19 | entra_role : random 20 | 21 | attack_path_2: 22 | enabled: true 23 | initial_access: token 24 | scenario : helpdesk 25 | privilege_escalation: ServicePrincipalAbuse 26 | method: GraphAPIPermission 27 | app_role : random 28 | 29 | attack_path_3: 30 | enabled: false 31 | initial_access: token 32 | scenario : helpdesk 33 | privilege_escalation: ServicePrincipalAbuse 34 | method: GraphAPIPermission 35 | app_role : 810c84a8-4a9e-49e6-bf7d-12d183f40d01 # Mail.Read Application Permission -------------------------------------------------------------------------------- /entity_data/administrative-units.txt: -------------------------------------------------------------------------------- 1 | NorthAmerica 2 | SouthAmerica 3 | Europe 4 | Asia 5 | Africa 6 | Australia 7 | Headquarters 8 | SalesDepartment 9 | MarketingDepartment 10 | FinanceDepartment 11 | HumanResources 12 | ITDepartment 13 | ResearchAndDevelopment 14 | CustomerSupport 15 | Logistics 16 | Procurement 17 | Operations 18 | LegalDepartment 19 | Compliance 20 | RiskManagement 21 | QualityAssurance 22 | ProductDevelopment 23 | ProjectManagement 24 | EngineeringDepartment 25 | Manufacturing 26 | Production 27 | SupplyChain 28 | Distribution 29 | RetailOperations 30 | WholesaleOperations 31 | ECommerce 32 | BusinessDevelopment 33 | CorporateStrategy 34 | InnovationCenter 35 | TrainingAndDevelopment 36 | TalentManagement 37 | PublicRelations 38 | InvestorRelations 39 | CorporateCommunications 40 | EnvironmentalHealthAndSafety 41 | FacilitiesManagement 42 | SecurityOperations 43 | DataCenter 44 | TechnicalSupport 45 | NetworkOperations 46 | CloudOperations 47 | EnterpriseApplications 48 | ApplicationDevelopment 49 | WebDevelopment 50 | MobileDevelopment 51 | DevOps 52 | QualityEngineering 53 | TestAutomation 54 | ReleaseManagement 55 | ConfigurationManagement 56 | ServiceDesk 57 | ITOperations 58 | NetworkSecurity 59 | EndpointSecurity 60 | IdentityAndAccessManagement 61 | SecurityEngineering 62 | IncidentResponse 63 | ThreatIntelligence 64 | VulnerabilityManagement 65 | ComplianceAudits 66 | InternalAudit 67 | FinancialAudit 68 | TaxDepartment 69 | Treasury 70 | InvestorRelations 71 | FinancialPlanningAndAnalysis 72 | GeneralAccounting 73 | CostAccounting 74 | BudgetingAndForecasting 75 | AccountsPayable 76 | AccountsReceivable 77 | PayrollDepartment 78 | BenefitsAdministration 79 | EmployeeRelations 80 | EmployeeEngagement 81 | DiversityAndInclusion 82 | LearningAndDevelopment 83 | CompensationAndBenefits 84 | WorkforcePlanning 85 | SuccessionPlanning 86 | LaborRelations 87 | EmployeeHealthAndWellness 88 | RemoteWork 89 | FlexibleWorkArrangements 90 | Telecommuting 91 | VirtualTeams 92 | HybridWorkModels 93 | ExecutiveLeadership 94 | BoardOfDirectors 95 | CorporateDevelopment 96 | MergersAndAcquisitions 97 | BusinessUnitA 98 | BusinessUnitB 99 | BusinessUnitC 100 | BusinessUnitD 101 | RegionNorth 102 | RegionSouth 103 | RegionEast 104 | RegionWest 105 | RegionCentral 106 | RegionalOfficeA 107 | RegionalOfficeB 108 | RegionalOfficeC 109 | RegionalOfficeD 110 | RegionalOfficeE 111 | CountryUSA 112 | CountryCanada 113 | CountryMexico 114 | CountryBrazil 115 | CountryArgentina 116 | CountryUK 117 | CountryGermany 118 | CountryFrance 119 | CountrySpain 120 | CountryItaly 121 | CountryNetherlands 122 | CountrySweden 123 | CountryNorway 124 | CountryDenmark 125 | CountryFinland 126 | CountryRussia 127 | CountryChina 128 | CountryJapan 129 | CountrySouthKorea 130 | CountryIndia 131 | CountryAustralia 132 | CountryNewZealand 133 | CountrySouthAfrica 134 | CountryNigeria 135 | CountryKenya 136 | CountryEgypt 137 | CityNewYork 138 | CityLosAngeles 139 | CityChicago 140 | CityHouston 141 | CityPhoenix 142 | CityPhiladelphia 143 | CitySanAntonio 144 | CitySanDiego 145 | CityDallas 146 | CitySanJose 147 | CityToronto 148 | CityVancouver 149 | CityMontreal 150 | CityMexicoCity 151 | CitySaoPaulo 152 | CityBuenosAires 153 | CityLondon 154 | CityBerlin 155 | CityParis 156 | CityMadrid 157 | CityRome 158 | CityAmsterdam 159 | CityStockholm 160 | CityOslo 161 | CityCopenhagen 162 | CityHelsinki 163 | CityMoscow 164 | CityBeijing 165 | CityShanghai 166 | CityTokyo 167 | CitySeoul 168 | CityMumbai 169 | CitySydney 170 | CityMelbourne 171 | CityJohannesburg 172 | CityCapeTown 173 | DivisionA 174 | DivisionB 175 | DivisionC 176 | DivisionD 177 | SectorA 178 | SectorB 179 | SectorC 180 | SectorD 181 | FunctionalGroupA 182 | FunctionalGroupB 183 | FunctionalGroupC 184 | FunctionalGroupD 185 | SupportServices 186 | AdministrativeServices 187 | StrategicPlanning 188 | OperationalPlanning 189 | OrganizationalDevelopment 190 | ProcessImprovement 191 | BusinessAnalysis 192 | StakeholderManagement 193 | CustomerRelations 194 | PartnerManagement 195 | VendorManagement 196 | SupplierManagement 197 | ContractManagement 198 | AssetManagement 199 | InventoryManagement 200 | WarehouseManagement 201 | TransportationManagement 202 | FleetManagement 203 | MaterialManagement 204 | HealthAndSafety 205 | EnvironmentalSustainability 206 | CorporateSocialResponsibility 207 | CommunityRelations -------------------------------------------------------------------------------- /entity_data/app-core-names.txt: -------------------------------------------------------------------------------- 1 | Connector 2 | Syncer 3 | Launchpad 4 | Mesh 5 | Caster 6 | Master 7 | Compiler 8 | Forge 9 | Pipeline 10 | Flow 11 | Pilot 12 | Mover 13 | Bridge 14 | Stream 15 | Pulse 16 | Switch 17 | Mapper 18 | Sentry 19 | Scaffold 20 | Builder 21 | Integrator 22 | Manager 23 | Engine 24 | Solver 25 | Navigator 26 | Tracker 27 | Scheduler 28 | Analyzer 29 | Monitor 30 | Guardian 31 | Agent 32 | Driver 33 | Conductor 34 | Director 35 | Generator 36 | Orchestrator 37 | Planner 38 | Controller 39 | Coordinator 40 | Facilitator 41 | Enabler 42 | Innovator 43 | Provider 44 | Reactor 45 | Operator 46 | Catalyst 47 | Enhancer 48 | Booster 49 | Processor 50 | Transmitter 51 | Receiver 52 | Responder 53 | -------------------------------------------------------------------------------- /entity_data/app-prefixes.txt: -------------------------------------------------------------------------------- 1 | Cloud 2 | Data 3 | App 4 | Service 5 | Deploy 6 | Code 7 | Net 8 | Info 9 | Cyber 10 | Secure 11 | Tech 12 | Smart 13 | Soft 14 | Sys 15 | Web 16 | Ultra 17 | Hyper 18 | Mega 19 | Alpha 20 | Beta 21 | Gamma 22 | Omni 23 | Quantum 24 | Nano 25 | Micro 26 | Macro 27 | Globe 28 | Terra 29 | Geo 30 | Aero 31 | Fusion 32 | Core 33 | Prime 34 | Next 35 | Future 36 | Vision 37 | Insight 38 | Infinity 39 | Nova 40 | Penta 41 | Tetra 42 | Hexa 43 | Octa 44 | Meta 45 | Sonic 46 | Dynamic 47 | Vector 48 | Vertex 49 | Matrix 50 | Orion 51 | Zenith -------------------------------------------------------------------------------- /entity_data/app-sufixes.txt: -------------------------------------------------------------------------------- 1 | X 2 | Pro 3 | Max 4 | Plus 5 | Edge 6 | Hub 7 | Tech 8 | Net 9 | Link 10 | Wave 11 | Force 12 | Power 13 | Boost 14 | Shield 15 | Guard 16 | Zone 17 | Core 18 | Port 19 | Base 20 | Point 21 | Grid 22 | Node 23 | Ray 24 | Beam 25 | Storm 26 | Blaze 27 | Spark 28 | Pulse 29 | Surge 30 | Flash 31 | Glow 32 | Blitz 33 | Rush 34 | Rush 35 | Speed 36 | Swift 37 | Dash 38 | Blast 39 | Bolt 40 | Zip 41 | Jet 42 | Drive 43 | Fly 44 | Sky 45 | Space 46 | Star 47 | Comet 48 | Nova 49 | Eon 50 | Globe 51 | Orbit 52 | Sphere 53 | -------------------------------------------------------------------------------- /entity_data/first-names.txt: -------------------------------------------------------------------------------- 1 | JAMES 2 | JOHN 3 | ROBERT 4 | MICHAEL 5 | WILLIAM 6 | DAVID 7 | RICHARD 8 | CHARLES 9 | JOSEPH 10 | THOMAS 11 | CHRISTOPHER 12 | DANIEL 13 | PAUL 14 | MARK 15 | DONALD 16 | GEORGE 17 | KENNETH 18 | STEVEN 19 | EDWARD 20 | BRIAN 21 | RONALD 22 | ANTHONY 23 | KEVIN 24 | JASON 25 | MATTHEW 26 | GARY 27 | TIMOTHY 28 | JOSE 29 | LARRY 30 | JEFFREY 31 | FRANK 32 | SCOTT 33 | ERIC 34 | STEPHEN 35 | ANDREW 36 | RAYMOND 37 | GREGORY 38 | JOSHUA 39 | JERRY 40 | DENNIS 41 | WALTER 42 | PATRICK 43 | PETER 44 | HAROLD 45 | DOUGLAS 46 | HENRY 47 | CARL 48 | ARTHUR 49 | RYAN 50 | ROGER 51 | JOE 52 | JUAN 53 | JACK 54 | ALBERT 55 | JONATHAN 56 | JUSTIN 57 | TERRY 58 | GERALD 59 | KEITH 60 | SAMUEL 61 | WILLIE 62 | RALPH 63 | LAWRENCE 64 | NICHOLAS 65 | ROY 66 | BENJAMIN 67 | BRUCE 68 | BRANDON 69 | ADAM 70 | HARRY 71 | FRED 72 | WAYNE 73 | BILLY 74 | STEVE 75 | LOUIS 76 | JEREMY 77 | AARON 78 | RANDY 79 | HOWARD 80 | EUGENE 81 | CARLOS 82 | RUSSELL 83 | BOBBY 84 | VICTOR 85 | MARTIN 86 | ERNEST 87 | PHILLIP 88 | TODD 89 | JESSE 90 | CRAIG 91 | ALAN 92 | SHAWN 93 | CLARENCE 94 | SEAN 95 | PHILIP 96 | CHRIS 97 | JOHNNY 98 | EARL 99 | JIMMY 100 | ANTONIO 101 | DANNY 102 | BRYAN 103 | TONY 104 | LUIS 105 | MIKE 106 | STANLEY 107 | LEONARD 108 | NATHAN 109 | DALE 110 | MANUEL 111 | RODNEY 112 | CURTIS 113 | NORMAN 114 | ALLEN 115 | MARVIN 116 | VINCENT 117 | GLENN 118 | JEFFERY 119 | TRAVIS 120 | JEFF 121 | CHAD 122 | JACOB 123 | LEE 124 | MELVIN 125 | ALFRED 126 | KYLE 127 | FRANCIS 128 | BRADLEY 129 | JESUS 130 | HERBERT 131 | FREDERICK 132 | RAY 133 | JOEL 134 | EDWIN 135 | DON 136 | EDDIE 137 | RICKY 138 | TROY 139 | RANDALL 140 | BARRY 141 | ALEXANDER 142 | BERNARD 143 | MARIO 144 | LEROY 145 | FRANCISCO 146 | MARCUS 147 | MICHEAL 148 | THEODORE 149 | CLIFFORD 150 | MIGUEL 151 | OSCAR 152 | JAY 153 | JIM 154 | TOM 155 | CALVIN 156 | ALEX 157 | JON 158 | RONNIE 159 | BILL 160 | LLOYD 161 | TOMMY 162 | LEON 163 | DEREK 164 | WARREN 165 | DARRELL 166 | JEROME 167 | FLOYD 168 | LEO 169 | ALVIN 170 | TIM 171 | WESLEY 172 | GORDON 173 | DEAN 174 | GREG 175 | JORGE 176 | DUSTIN 177 | PEDRO 178 | DERRICK 179 | DAN 180 | LEWIS 181 | ZACHARY 182 | COREY 183 | HERMAN 184 | MAURICE 185 | VERNON 186 | ROBERTO 187 | CLYDE 188 | GLEN 189 | HECTOR 190 | SHANE 191 | RICARDO 192 | SAM 193 | RICK 194 | LESTER 195 | BRENT 196 | RAMON 197 | CHARLIE 198 | TYLER 199 | GILBERT 200 | GENE 201 | MARC 202 | REGINALD 203 | RUBEN 204 | BRETT 205 | ANGEL 206 | NATHANIEL 207 | RAFAEL 208 | LESLIE 209 | EDGAR 210 | MILTON 211 | RAUL 212 | BEN 213 | CHESTER 214 | CECIL 215 | DUANE 216 | FRANKLIN 217 | ANDRE 218 | ELMER 219 | BRAD 220 | GABRIEL 221 | RON 222 | MITCHELL 223 | ROLAND 224 | ARNOLD 225 | HARVEY 226 | JARED 227 | ADRIAN 228 | KARL 229 | CORY 230 | CLAUDE 231 | ERIK 232 | DARRYL 233 | JAMIE 234 | NEIL 235 | JESSIE 236 | CHRISTIAN 237 | JAVIER 238 | FERNANDO 239 | CLINTON 240 | TED 241 | MATHEW 242 | TYRONE 243 | DARREN 244 | LONNIE 245 | LANCE 246 | CODY 247 | JULIO 248 | KELLY 249 | KURT 250 | ALLAN 251 | NELSON 252 | GUY 253 | CLAYTON 254 | HUGH 255 | MAX 256 | DWAYNE 257 | DWIGHT 258 | ARMANDO 259 | FELIX 260 | JIMMIE 261 | EVERETT 262 | JORDAN 263 | IAN 264 | WALLACE 265 | KEN 266 | BOB 267 | JAIME 268 | CASEY 269 | ALFREDO 270 | ALBERTO 271 | DAVE 272 | IVAN 273 | JOHNNIE 274 | SIDNEY 275 | BYRON 276 | JULIAN 277 | ISAAC 278 | MORRIS 279 | CLIFTON 280 | WILLARD 281 | DARYL 282 | ROSS 283 | VIRGIL 284 | ANDY 285 | MARSHALL 286 | SALVADOR 287 | PERRY 288 | KIRK 289 | SERGIO 290 | MARION 291 | TRACY 292 | SETH 293 | KENT 294 | TERRANCE 295 | RENE 296 | EDUARDO 297 | TERRENCE 298 | ENRIQUE 299 | FREDDIE 300 | WADE 301 | AUSTIN 302 | STUART 303 | FREDRICK 304 | ARTURO 305 | ALEJANDRO 306 | JACKIE 307 | JOEY 308 | NICK 309 | LUTHER 310 | WENDELL 311 | JEREMIAH 312 | EVAN 313 | JULIUS 314 | DANA 315 | DONNIE 316 | OTIS 317 | SHANNON 318 | TREVOR 319 | OLIVER 320 | LUKE 321 | HOMER 322 | GERARD 323 | DOUG 324 | KENNY 325 | HUBERT 326 | ANGELO 327 | SHAUN 328 | LYLE 329 | MATT 330 | LYNN 331 | ALFONSO 332 | ORLANDO 333 | REX 334 | CARLTON 335 | ERNESTO 336 | CAMERON 337 | NEAL 338 | PABLO 339 | LORENZO 340 | OMAR 341 | WILBUR 342 | BLAKE 343 | GRANT 344 | HORACE 345 | RODERICK 346 | KERRY 347 | ABRAHAM 348 | WILLIS 349 | RICKEY 350 | JEAN 351 | IRA 352 | ANDRES 353 | CESAR 354 | JOHNATHAN 355 | MALCOLM 356 | RUDOLPH 357 | DAMON 358 | KELVIN 359 | RUDY 360 | PRESTON 361 | ALTON 362 | ARCHIE 363 | MARCO 364 | WM 365 | PETE 366 | RANDOLPH 367 | GARRY 368 | GEOFFREY 369 | JONATHON 370 | FELIPE 371 | BENNIE 372 | GERARDO 373 | ED 374 | DOMINIC 375 | ROBIN 376 | LOREN 377 | DELBERT 378 | COLIN 379 | GUILLERMO 380 | EARNEST 381 | LUCAS 382 | BENNY 383 | NOEL 384 | SPENCER 385 | RODOLFO 386 | MYRON 387 | EDMUND 388 | GARRETT 389 | SALVATORE 390 | CEDRIC 391 | LOWELL 392 | GREGG 393 | SHERMAN 394 | WILSON 395 | DEVIN 396 | SYLVESTER 397 | KIM 398 | ROOSEVELT 399 | ISRAEL 400 | JERMAINE 401 | FORREST 402 | WILBERT 403 | LELAND 404 | SIMON 405 | GUADALUPE 406 | CLARK 407 | IRVING 408 | CARROLL 409 | BRYANT 410 | OWEN 411 | RUFUS 412 | WOODROW 413 | SAMMY 414 | KRISTOPHER 415 | MACK 416 | LEVI 417 | MARCOS 418 | GUSTAVO 419 | JAKE 420 | LIONEL 421 | MARTY 422 | TAYLOR 423 | ELLIS 424 | DALLAS 425 | GILBERTO 426 | CLINT 427 | NICOLAS 428 | LAURENCE 429 | ISMAEL 430 | ORVILLE 431 | DREW 432 | JODY 433 | ERVIN 434 | DEWEY 435 | AL 436 | WILFRED 437 | JOSH 438 | HUGO 439 | IGNACIO 440 | CALEB 441 | TOMAS 442 | SHELDON 443 | ERICK 444 | FRANKIE 445 | STEWART 446 | DOYLE 447 | DARREL 448 | ROGELIO 449 | TERENCE 450 | SANTIAGO 451 | ALONZO 452 | ELIAS 453 | BERT 454 | ELBERT 455 | RAMIRO 456 | CONRAD 457 | PAT 458 | NOAH 459 | GRADY 460 | PHIL 461 | CORNELIUS 462 | LAMAR 463 | ROLANDO 464 | CLAY 465 | PERCY 466 | DEXTER 467 | BRADFORD 468 | MERLE 469 | DARIN 470 | AMOS 471 | TERRELL 472 | MOSES 473 | IRVIN 474 | SAUL 475 | ROMAN 476 | DARNELL 477 | RANDAL 478 | TOMMIE 479 | TIMMY 480 | DARRIN 481 | WINSTON 482 | BRENDAN 483 | TOBY 484 | VAN 485 | ABEL 486 | DOMINICK 487 | BOYD 488 | COURTNEY 489 | JAN 490 | EMILIO 491 | ELIJAH 492 | CARY 493 | DOMINGO 494 | SANTOS 495 | AUBREY 496 | EMMETT 497 | MARLON 498 | EMANUEL 499 | JERALD 500 | EDMOND 501 | EMIL 502 | DEWAYNE 503 | WILL 504 | OTTO 505 | TEDDY 506 | REYNALDO 507 | BRET 508 | MORGAN 509 | JESS 510 | TRENT 511 | HUMBERTO 512 | EMMANUEL 513 | STEPHAN 514 | LOUIE 515 | VICENTE 516 | LAMONT 517 | STACY 518 | GARLAND 519 | MILES 520 | MICAH 521 | EFRAIN 522 | BILLIE 523 | LOGAN 524 | HEATH 525 | RODGER 526 | HARLEY 527 | DEMETRIUS 528 | ETHAN 529 | ELDON 530 | ROCKY 531 | PIERRE 532 | JUNIOR 533 | FREDDY 534 | ELI 535 | BRYCE 536 | ANTOINE 537 | ROBBIE 538 | KENDALL 539 | ROYCE 540 | STERLING 541 | MICKEY 542 | CHASE 543 | GROVER 544 | ELTON 545 | CLEVELAND 546 | DYLAN 547 | CHUCK 548 | DAMIAN 549 | REUBEN 550 | STAN 551 | AUGUST 552 | LEONARDO 553 | JASPER 554 | RUSSEL 555 | ERWIN 556 | BENITO 557 | HANS 558 | MONTE 559 | BLAINE 560 | ERNIE 561 | CURT 562 | QUENTIN 563 | AGUSTIN 564 | MURRAY 565 | JAMAL 566 | DEVON 567 | ADOLFO 568 | HARRISON 569 | TYSON 570 | BURTON 571 | BRADY 572 | ELLIOTT 573 | WILFREDO 574 | BART 575 | JARROD 576 | VANCE 577 | DENIS 578 | DAMIEN 579 | JOAQUIN 580 | HARLAN 581 | DESMOND 582 | ELLIOT 583 | DARWIN 584 | ASHLEY 585 | GREGORIO 586 | BUDDY 587 | XAVIER 588 | KERMIT 589 | ROSCOE 590 | ESTEBAN 591 | ANTON 592 | SOLOMON 593 | SCOTTY 594 | NORBERT 595 | ELVIN 596 | WILLIAMS 597 | NOLAN 598 | CAREY 599 | ROD 600 | QUINTON 601 | HAL 602 | BRAIN 603 | ROB 604 | ELWOOD 605 | KENDRICK 606 | DARIUS 607 | MOISES 608 | SON 609 | MARLIN 610 | FIDEL 611 | THADDEUS 612 | CLIFF 613 | MARCEL 614 | ALI 615 | JACKSON 616 | RAPHAEL 617 | BRYON 618 | ARMAND 619 | ALVARO 620 | JEFFRY 621 | DANE 622 | JOESPH 623 | THURMAN 624 | NED 625 | SAMMIE 626 | RUSTY 627 | MICHEL 628 | MONTY 629 | RORY 630 | FABIAN 631 | REGGIE 632 | MASON 633 | GRAHAM 634 | KRIS 635 | ISAIAH 636 | VAUGHN 637 | GUS 638 | AVERY 639 | LOYD 640 | DIEGO 641 | ALEXIS 642 | ADOLPH 643 | NORRIS 644 | MILLARD 645 | ROCCO 646 | GONZALO 647 | DERICK 648 | RODRIGO 649 | GERRY 650 | STACEY 651 | CARMEN 652 | WILEY 653 | RIGOBERTO 654 | ALPHONSO 655 | TY 656 | SHELBY 657 | RICKIE 658 | NOE 659 | VERN 660 | BOBBIE 661 | REED 662 | JEFFERSON 663 | ELVIS 664 | BERNARDO 665 | MAURICIO 666 | HIRAM 667 | DONOVAN 668 | BASIL 669 | RILEY 670 | OLLIE 671 | NICKOLAS 672 | MAYNARD 673 | SCOT 674 | VINCE 675 | QUINCY 676 | EDDY 677 | SEBASTIAN 678 | FEDERICO 679 | ULYSSES 680 | HERIBERTO 681 | DONNELL 682 | COLE 683 | DENNY 684 | DAVIS 685 | GAVIN 686 | EMERY 687 | WARD 688 | ROMEO 689 | JAYSON 690 | DION 691 | DANTE 692 | CLEMENT 693 | COY 694 | ODELL 695 | MAXWELL 696 | JARVIS 697 | BRUNO 698 | ISSAC 699 | MARY 700 | DUDLEY 701 | BROCK 702 | SANFORD 703 | COLBY 704 | CARMELO 705 | BARNEY 706 | NESTOR 707 | HOLLIS 708 | STEFAN 709 | DONNY 710 | ART 711 | LINWOOD 712 | BEAU 713 | WELDON 714 | GALEN 715 | ISIDRO 716 | TRUMAN 717 | DELMAR 718 | JOHNATHON 719 | SILAS 720 | FREDERIC 721 | DICK 722 | KIRBY 723 | IRWIN 724 | CRUZ 725 | MERLIN 726 | MERRILL 727 | CHARLEY 728 | MARCELINO 729 | LANE 730 | HARRIS 731 | CLEO 732 | CARLO 733 | TRENTON 734 | KURTIS 735 | HUNTER 736 | AURELIO 737 | WINFRED 738 | VITO 739 | COLLIN 740 | DENVER 741 | CARTER 742 | LEONEL 743 | EMORY 744 | PASQUALE 745 | MOHAMMAD 746 | MARIANO 747 | DANIAL 748 | BLAIR 749 | LANDON 750 | DIRK 751 | BRANDEN 752 | ADAN 753 | NUMBERS 754 | CLAIR 755 | BUFORD 756 | GERMAN 757 | BERNIE 758 | WILMER 759 | JOAN 760 | EMERSON 761 | ZACHERY 762 | FLETCHER 763 | JACQUES 764 | ERROL 765 | DALTON 766 | MONROE 767 | JOSUE 768 | DOMINIQUE 769 | EDWARDO 770 | BOOKER 771 | WILFORD 772 | SONNY 773 | SHELTON 774 | CARSON 775 | THERON 776 | RAYMUNDO 777 | DAREN 778 | TRISTAN 779 | HOUSTON 780 | ROBBY 781 | LINCOLN 782 | JAME 783 | GENARO 784 | GALE 785 | BENNETT 786 | OCTAVIO 787 | CORNELL 788 | LAVERNE 789 | HUNG 790 | ARRON 791 | ANTONY 792 | HERSCHEL 793 | ALVA 794 | GIOVANNI 795 | GARTH 796 | CYRUS 797 | CYRIL 798 | RONNY 799 | STEVIE 800 | LON 801 | FREEMAN 802 | ERIN 803 | DUNCAN 804 | KENNITH 805 | CARMINE 806 | AUGUSTINE 807 | YOUNG 808 | ERICH 809 | CHADWICK 810 | WILBURN 811 | RUSS 812 | REID 813 | MYLES 814 | ANDERSON 815 | MORTON 816 | JONAS 817 | FOREST 818 | MITCHEL 819 | MERVIN 820 | ZANE 821 | RICH 822 | JAMEL 823 | LAZARO 824 | ALPHONSE 825 | RANDELL 826 | MAJOR 827 | JOHNIE 828 | JARRETT 829 | BROOKS 830 | ARIEL 831 | ABDUL 832 | DUSTY 833 | LUCIANO 834 | LINDSEY 835 | TRACEY 836 | SEYMOUR 837 | SCOTTIE 838 | EUGENIO 839 | MOHAMMED 840 | SANDY 841 | VALENTIN 842 | CHANCE 843 | ARNULFO 844 | LUCIEN 845 | FERDINAND 846 | THAD 847 | EZRA 848 | SYDNEY 849 | ALDO 850 | RUBIN 851 | ROYAL 852 | MITCH 853 | EARLE 854 | ABE 855 | WYATT 856 | MARQUIS 857 | LANNY 858 | KAREEM 859 | JAMAR 860 | BORIS 861 | ISIAH 862 | EMILE 863 | ELMO 864 | ARON 865 | LEOPOLDO 866 | EVERETTE 867 | JOSEF 868 | GAIL 869 | ELOY 870 | DORIAN 871 | RODRICK 872 | REINALDO 873 | LUCIO 874 | JERROD 875 | WESTON 876 | HERSHEL 877 | BARTON 878 | PARKER 879 | LEMUEL 880 | LAVERN 881 | BURT 882 | JULES 883 | GIL 884 | ELISEO 885 | AHMAD 886 | NIGEL 887 | EFREN 888 | ANTWAN 889 | ALDEN 890 | MARGARITO 891 | COLEMAN 892 | REFUGIO 893 | DINO 894 | OSVALDO 895 | LES 896 | DEANDRE 897 | NORMAND 898 | KIETH 899 | IVORY 900 | ANDREA 901 | TREY 902 | NORBERTO 903 | NAPOLEON 904 | JEROLD 905 | FRITZ 906 | ROSENDO 907 | MILFORD 908 | SANG 909 | DEON 910 | CHRISTOPER 911 | ALFONZO 912 | LYMAN 913 | JOSIAH 914 | BRANT 915 | WILTON 916 | RICO 917 | JAMAAL 918 | DEWITT 919 | CAROL 920 | BRENTON 921 | YONG 922 | OLIN 923 | FOSTER 924 | FAUSTINO 925 | CLAUDIO 926 | JUDSON 927 | GINO 928 | EDGARDO 929 | BERRY 930 | ALEC 931 | TANNER 932 | JARRED 933 | DONN 934 | TRINIDAD 935 | TAD 936 | SHIRLEY 937 | PRINCE 938 | PORFIRIO 939 | ODIS 940 | MARIA 941 | LENARD 942 | CHAUNCEY 943 | CHANG 944 | TOD 945 | MEL 946 | MARCELO 947 | KORY 948 | AUGUSTUS 949 | KEVEN 950 | HILARIO 951 | BUD 952 | SAL 953 | ROSARIO 954 | ORVAL 955 | MAURO 956 | DANNIE 957 | ZACHARIAH 958 | OLEN 959 | ANIBAL 960 | MILO 961 | JED 962 | FRANCES 963 | THANH 964 | DILLON 965 | AMADO 966 | NEWTON 967 | CONNIE 968 | LENNY 969 | TORY 970 | RICHIE 971 | LUPE 972 | HORACIO 973 | BRICE 974 | MOHAMED 975 | DELMER 976 | DARIO 977 | REYES 978 | DEE 979 | MAC 980 | JONAH 981 | JERROLD 982 | ROBT 983 | HANK 984 | SUNG 985 | RUPERT 986 | ROLLAND 987 | KENTON 988 | DAMION 989 | CHI 990 | ANTONE 991 | WALDO 992 | FREDRIC 993 | BRADLY 994 | QUINN 995 | KIP 996 | BURL 997 | WALKER 998 | TYREE 999 | JEFFEREY 1000 | AHMED 1001 | WILLY 1002 | STANFORD 1003 | OREN 1004 | NOBLE 1005 | MOSHE 1006 | MIKEL 1007 | ENOCH 1008 | BRENDON 1009 | QUINTIN 1010 | JAMISON 1011 | FLORENCIO 1012 | DARRICK 1013 | TOBIAS 1014 | MINH 1015 | HASSAN 1016 | GIUSEPPE 1017 | DEMARCUS 1018 | CLETUS 1019 | TYRELL 1020 | LYNDON 1021 | KEENAN 1022 | WERNER 1023 | THEO 1024 | GERALDO 1025 | LOU 1026 | COLUMBUS 1027 | CHET 1028 | BERTRAM 1029 | MARKUS 1030 | HUEY 1031 | HILTON 1032 | DWAIN 1033 | DONTE 1034 | TYRON 1035 | OMER 1036 | ISAIAS 1037 | HIPOLITO 1038 | FERMIN 1039 | CHUNG 1040 | ADALBERTO 1041 | VALENTINE 1042 | JAMEY 1043 | BO 1044 | BARRETT 1045 | WHITNEY 1046 | TEODORO 1047 | MCKINLEY 1048 | MAXIMO 1049 | GARFIELD 1050 | SOL 1051 | RALEIGH 1052 | LAWERENCE 1053 | ABRAM 1054 | RASHAD 1055 | KING 1056 | EMMITT 1057 | DARON 1058 | CHONG 1059 | SAMUAL 1060 | PARIS 1061 | OTHA 1062 | MIQUEL 1063 | LACY 1064 | EUSEBIO 1065 | DONG 1066 | DOMENIC 1067 | DARRON 1068 | BUSTER 1069 | ANTONIA 1070 | WILBER 1071 | RENATO 1072 | JC 1073 | HOYT 1074 | HAYWOOD 1075 | EZEKIEL 1076 | CHAS 1077 | FLORENTINO 1078 | ELROY 1079 | CLEMENTE 1080 | ARDEN 1081 | NEVILLE 1082 | KELLEY 1083 | EDISON 1084 | DESHAWN 1085 | CARROL 1086 | SHAYNE 1087 | NATHANIAL 1088 | JORDON 1089 | DANILO 1090 | CLAUD 1091 | VAL 1092 | SHERWOOD 1093 | RAYMON 1094 | RAYFORD 1095 | CRISTOBAL 1096 | AMBROSE 1097 | TITUS 1098 | HYMAN 1099 | FELTON 1100 | EZEQUIEL 1101 | ERASMO 1102 | STANTON 1103 | LONNY 1104 | LEN 1105 | IKE 1106 | MILAN 1107 | LINO 1108 | JAROD 1109 | HERB 1110 | ANDREAS 1111 | WALTON 1112 | RHETT 1113 | PALMER 1114 | JUDE 1115 | DOUGLASS 1116 | CORDELL 1117 | OSWALDO 1118 | ELLSWORTH 1119 | VIRGILIO 1120 | TONEY 1121 | NATHANAEL 1122 | DEL 1123 | BRITT 1124 | BENEDICT 1125 | MOSE 1126 | HONG 1127 | LEIGH 1128 | JOHNSON 1129 | ISREAL 1130 | GAYLE 1131 | GARRET 1132 | FAUSTO 1133 | ASA 1134 | ARLEN 1135 | ZACK 1136 | WARNER 1137 | MODESTO 1138 | FRANCESCO 1139 | MANUAL 1140 | JAE 1141 | GAYLORD 1142 | GASTON 1143 | FILIBERTO 1144 | DEANGELO 1145 | MICHALE 1146 | GRANVILLE 1147 | WES 1148 | MALIK 1149 | ZACKARY 1150 | TUAN 1151 | NICKY 1152 | ELDRIDGE 1153 | CRISTOPHER 1154 | CORTEZ 1155 | ANTIONE 1156 | MALCOM 1157 | LONG 1158 | KOREY 1159 | JOSPEH 1160 | COLTON 1161 | WAYLON 1162 | VON 1163 | HOSEA 1164 | SHAD 1165 | SANTO 1166 | RUDOLF 1167 | ROLF 1168 | REY 1169 | RENALDO 1170 | MARCELLUS 1171 | LUCIUS 1172 | LESLEY 1173 | KRISTOFER 1174 | BOYCE 1175 | BENTON 1176 | MAN 1177 | KASEY 1178 | JEWELL 1179 | HAYDEN 1180 | HARLAND 1181 | ARNOLDO 1182 | RUEBEN 1183 | LEANDRO 1184 | KRAIG 1185 | JERRELL 1186 | JEROMY 1187 | HOBERT 1188 | CEDRICK 1189 | ARLIE 1190 | WINFORD 1191 | WALLY 1192 | PATRICIA 1193 | LUIGI 1194 | KENETH 1195 | JACINTO 1196 | GRAIG 1197 | FRANKLYN 1198 | EDMUNDO 1199 | SID 1200 | PORTER 1201 | LEIF 1202 | LAUREN 1203 | JERAMY 1204 | ELISHA 1205 | BUCK 1206 | WILLIAN 1207 | VINCENZO 1208 | SHON 1209 | MICHAL 1210 | LYNWOOD 1211 | LINDSAY 1212 | JEWEL 1213 | JERE 1214 | HAI 1215 | ELDEN 1216 | DORSEY 1217 | DARELL 1218 | BRODERICK 1219 | ALONSO 1220 | MARY 1221 | PATRICIA 1222 | LINDA 1223 | BARBARA 1224 | ELIZABETH 1225 | JENNIFER 1226 | MARIA 1227 | SUSAN 1228 | MARGARET 1229 | DOROTHY 1230 | LISA 1231 | NANCY 1232 | KAREN 1233 | BETTY 1234 | HELEN 1235 | SANDRA 1236 | DONNA 1237 | CAROL 1238 | RUTH 1239 | SHARON 1240 | MICHELLE 1241 | LAURA 1242 | SARAH 1243 | KIMBERLY 1244 | DEBORAH 1245 | JESSICA 1246 | SHIRLEY 1247 | CYNTHIA 1248 | ANGELA 1249 | MELISSA 1250 | BRENDA 1251 | AMY 1252 | ANNA 1253 | REBECCA 1254 | VIRGINIA 1255 | KATHLEEN 1256 | PAMELA 1257 | MARTHA 1258 | DEBRA 1259 | AMANDA 1260 | STEPHANIE 1261 | CAROLYN 1262 | CHRISTINE 1263 | MARIE 1264 | JANET 1265 | CATHERINE 1266 | FRANCES 1267 | ANN 1268 | JOYCE 1269 | DIANE 1270 | ALICE 1271 | JULIE 1272 | HEATHER 1273 | TERESA 1274 | DORIS 1275 | GLORIA 1276 | EVELYN 1277 | JEAN 1278 | CHERYL 1279 | MILDRED 1280 | KATHERINE 1281 | JOAN 1282 | ASHLEY 1283 | JUDITH 1284 | ROSE 1285 | JANICE 1286 | KELLY 1287 | NICOLE 1288 | JUDY 1289 | CHRISTINA 1290 | KATHY 1291 | THERESA 1292 | BEVERLY 1293 | DENISE 1294 | TAMMY 1295 | IRENE 1296 | JANE 1297 | LORI 1298 | RACHEL 1299 | MARILYN 1300 | ANDREA 1301 | KATHRYN 1302 | LOUISE 1303 | SARA 1304 | ANNE 1305 | JACQUELINE 1306 | WANDA 1307 | BONNIE 1308 | JULIA 1309 | RUBY 1310 | LOIS 1311 | TINA 1312 | PHYLLIS 1313 | NORMA 1314 | PAULA 1315 | DIANA 1316 | ANNIE 1317 | LILLIAN 1318 | EMILY 1319 | ROBIN 1320 | PEGGY 1321 | CRYSTAL 1322 | GLADYS 1323 | RITA 1324 | DAWN 1325 | CONNIE 1326 | FLORENCE 1327 | TRACY 1328 | EDNA 1329 | TIFFANY 1330 | CARMEN 1331 | ROSA 1332 | CINDY 1333 | GRACE 1334 | WENDY 1335 | VICTORIA 1336 | EDITH 1337 | KIM 1338 | SHERRY 1339 | SYLVIA 1340 | JOSEPHINE 1341 | THELMA 1342 | SHANNON 1343 | SHEILA 1344 | ETHEL 1345 | ELLEN 1346 | ELAINE 1347 | MARJORIE 1348 | CARRIE 1349 | CHARLOTTE 1350 | MONICA 1351 | ESTHER 1352 | PAULINE 1353 | EMMA 1354 | JUANITA 1355 | ANITA 1356 | RHONDA 1357 | HAZEL 1358 | AMBER 1359 | EVA 1360 | DEBBIE 1361 | APRIL 1362 | LESLIE 1363 | CLARA 1364 | LUCILLE 1365 | JAMIE 1366 | JOANNE 1367 | ELEANOR 1368 | VALERIE 1369 | DANIELLE 1370 | MEGAN 1371 | ALICIA 1372 | SUZANNE 1373 | MICHELE 1374 | GAIL 1375 | BERTHA 1376 | DARLENE 1377 | VERONICA 1378 | JILL 1379 | ERIN 1380 | GERALDINE 1381 | LAUREN 1382 | CATHY 1383 | JOANN 1384 | LORRAINE 1385 | LYNN 1386 | SALLY 1387 | REGINA 1388 | ERICA 1389 | BEATRICE 1390 | DOLORES 1391 | BERNICE 1392 | AUDREY 1393 | YVONNE 1394 | ANNETTE 1395 | JUNE 1396 | SAMANTHA 1397 | MARION 1398 | DANA 1399 | STACY 1400 | ANA 1401 | RENEE 1402 | IDA 1403 | VIVIAN 1404 | ROBERTA 1405 | HOLLY 1406 | BRITTANY 1407 | MELANIE 1408 | LORETTA 1409 | YOLANDA 1410 | JEANETTE 1411 | LAURIE 1412 | KATIE 1413 | KRISTEN 1414 | VANESSA 1415 | ALMA 1416 | SUE 1417 | ELSIE 1418 | BETH 1419 | JEANNE 1420 | VICKI 1421 | CARLA 1422 | TARA 1423 | ROSEMARY 1424 | EILEEN 1425 | TERRI 1426 | GERTRUDE 1427 | LUCY 1428 | TONYA 1429 | ELLA 1430 | STACEY 1431 | WILMA 1432 | GINA 1433 | KRISTIN 1434 | JESSIE 1435 | NATALIE 1436 | AGNES 1437 | VERA 1438 | WILLIE 1439 | CHARLENE 1440 | BESSIE 1441 | DELORES 1442 | MELINDA 1443 | PEARL 1444 | ARLENE 1445 | MAUREEN 1446 | COLLEEN 1447 | ALLISON 1448 | TAMARA 1449 | JOY 1450 | GEORGIA 1451 | CONSTANCE 1452 | LILLIE 1453 | CLAUDIA 1454 | JACKIE 1455 | MARCIA 1456 | TANYA 1457 | NELLIE 1458 | MINNIE 1459 | MARLENE 1460 | HEIDI 1461 | GLENDA 1462 | LYDIA 1463 | VIOLA 1464 | COURTNEY 1465 | MARIAN 1466 | STELLA 1467 | CAROLINE 1468 | DORA 1469 | JO 1470 | VICKIE 1471 | MATTIE 1472 | TERRY 1473 | MAXINE 1474 | IRMA 1475 | MABEL 1476 | MARSHA 1477 | MYRTLE 1478 | LENA 1479 | CHRISTY 1480 | DEANNA 1481 | PATSY 1482 | HILDA 1483 | GWENDOLYN 1484 | JENNIE 1485 | NORA 1486 | MARGIE 1487 | NINA 1488 | CASSANDRA 1489 | LEAH 1490 | PENNY 1491 | KAY 1492 | PRISCILLA 1493 | NAOMI 1494 | CAROLE 1495 | BRANDY 1496 | OLGA 1497 | BILLIE 1498 | DIANNE 1499 | TRACEY 1500 | LEONA 1501 | JENNY 1502 | FELICIA 1503 | SONIA 1504 | MIRIAM 1505 | VELMA 1506 | BECKY 1507 | BOBBIE 1508 | VIOLET 1509 | KRISTINA 1510 | TONI 1511 | MISTY 1512 | MAE 1513 | SHELLY 1514 | DAISY 1515 | RAMONA 1516 | SHERRI 1517 | ERIKA 1518 | KATRINA 1519 | CLAIRE 1520 | LINDSEY 1521 | LINDSAY 1522 | GENEVA 1523 | GUADALUPE 1524 | BELINDA 1525 | MARGARITA 1526 | SHERYL 1527 | CORA 1528 | FAYE 1529 | ADA 1530 | NATASHA 1531 | SABRINA 1532 | ISABEL 1533 | MARGUERITE 1534 | HATTIE 1535 | HARRIET 1536 | MOLLY 1537 | CECILIA 1538 | KRISTI 1539 | BRANDI 1540 | BLANCHE 1541 | SANDY 1542 | ROSIE 1543 | JOANNA 1544 | IRIS 1545 | EUNICE 1546 | ANGIE 1547 | INEZ 1548 | LYNDA 1549 | MADELINE 1550 | AMELIA 1551 | ALBERTA 1552 | GENEVIEVE 1553 | MONIQUE 1554 | JODI 1555 | JANIE 1556 | MAGGIE 1557 | KAYLA 1558 | SONYA 1559 | JAN 1560 | LEE 1561 | KRISTINE 1562 | CANDACE 1563 | FANNIE 1564 | MARYANN 1565 | OPAL 1566 | ALISON 1567 | YVETTE 1568 | MELODY 1569 | LUZ 1570 | SUSIE 1571 | OLIVIA 1572 | FLORA 1573 | SHELLEY 1574 | KRISTY 1575 | MAMIE 1576 | LULA 1577 | LOLA 1578 | VERNA 1579 | BEULAH 1580 | ANTOINETTE 1581 | CANDICE 1582 | JUANA 1583 | JEANNETTE 1584 | PAM 1585 | KELLI 1586 | HANNAH 1587 | WHITNEY 1588 | BRIDGET 1589 | KARLA 1590 | CELIA 1591 | LATOYA 1592 | PATTY 1593 | SHELIA 1594 | GAYLE 1595 | DELLA 1596 | VICKY 1597 | LYNNE 1598 | SHERI 1599 | MARIANNE 1600 | KARA 1601 | JACQUELYN 1602 | ERMA 1603 | BLANCA 1604 | MYRA 1605 | LETICIA 1606 | PAT 1607 | KRISTA 1608 | ROXANNE 1609 | ANGELICA 1610 | JOHNNIE 1611 | ROBYN 1612 | FRANCIS 1613 | ADRIENNE 1614 | ROSALIE 1615 | ALEXANDRA 1616 | BROOKE 1617 | BETHANY 1618 | SADIE 1619 | BERNADETTE 1620 | TRACI 1621 | JODY 1622 | KENDRA 1623 | JASMINE 1624 | NICHOLE 1625 | RACHAEL 1626 | CHELSEA 1627 | MABLE 1628 | ERNESTINE 1629 | MURIEL 1630 | MARCELLA 1631 | ELENA 1632 | KRYSTAL 1633 | ANGELINA 1634 | NADINE 1635 | KARI 1636 | ESTELLE 1637 | DIANNA 1638 | PAULETTE 1639 | LORA 1640 | MONA 1641 | DOREEN 1642 | ROSEMARIE 1643 | ANGEL 1644 | DESIREE 1645 | ANTONIA 1646 | HOPE 1647 | GINGER 1648 | JANIS 1649 | BETSY 1650 | CHRISTIE 1651 | FREDA 1652 | MERCEDES 1653 | MEREDITH 1654 | LYNETTE 1655 | TERI 1656 | CRISTINA 1657 | EULA 1658 | LEIGH 1659 | MEGHAN 1660 | SOPHIA 1661 | ELOISE 1662 | ROCHELLE 1663 | GRETCHEN 1664 | CECELIA 1665 | RAQUEL 1666 | HENRIETTA 1667 | ALYSSA 1668 | JANA 1669 | KELLEY 1670 | GWEN 1671 | KERRY 1672 | JENNA 1673 | TRICIA 1674 | LAVERNE 1675 | OLIVE 1676 | ALEXIS 1677 | TASHA 1678 | SILVIA 1679 | ELVIRA 1680 | CASEY 1681 | DELIA 1682 | SOPHIE 1683 | KATE 1684 | PATTI 1685 | LORENA 1686 | KELLIE 1687 | SONJA 1688 | LILA 1689 | LANA 1690 | DARLA 1691 | MAY 1692 | MINDY 1693 | ESSIE 1694 | MANDY 1695 | LORENE 1696 | ELSA 1697 | JOSEFINA 1698 | JEANNIE 1699 | MIRANDA 1700 | DIXIE 1701 | LUCIA 1702 | MARTA 1703 | FAITH 1704 | LELA 1705 | JOHANNA 1706 | SHARI 1707 | CAMILLE 1708 | TAMI 1709 | SHAWNA 1710 | ELISA 1711 | EBONY 1712 | MELBA 1713 | ORA 1714 | NETTIE 1715 | TABITHA 1716 | OLLIE 1717 | JAIME 1718 | WINIFRED 1719 | KRISTIE 1720 | MARINA 1721 | ALISHA 1722 | AIMEE 1723 | RENA 1724 | MYRNA 1725 | MARLA 1726 | TAMMIE 1727 | LATASHA 1728 | BONITA 1729 | PATRICE 1730 | RONDA 1731 | SHERRIE 1732 | ADDIE 1733 | FRANCINE 1734 | DELORIS 1735 | STACIE 1736 | ADRIANA 1737 | CHERI 1738 | SHELBY 1739 | ABIGAIL 1740 | CELESTE 1741 | JEWEL 1742 | CARA 1743 | ADELE 1744 | REBEKAH 1745 | LUCINDA 1746 | DORTHY 1747 | CHRIS 1748 | EFFIE 1749 | TRINA 1750 | REBA 1751 | SHAWN 1752 | SALLIE 1753 | AURORA 1754 | LENORA 1755 | ETTA 1756 | LOTTIE 1757 | KERRI 1758 | TRISHA 1759 | NIKKI 1760 | ESTELLA 1761 | FRANCISCA 1762 | JOSIE 1763 | TRACIE 1764 | MARISSA 1765 | KARIN 1766 | BRITTNEY 1767 | JANELLE 1768 | LOURDES 1769 | LAUREL 1770 | HELENE 1771 | FERN 1772 | ELVA 1773 | CORINNE 1774 | KELSEY 1775 | INA 1776 | BETTIE 1777 | ELISABETH 1778 | AIDA 1779 | CAITLIN 1780 | INGRID 1781 | IVA 1782 | EUGENIA 1783 | CHRISTA 1784 | GOLDIE 1785 | CASSIE 1786 | MAUDE 1787 | JENIFER 1788 | THERESE 1789 | FRANKIE 1790 | DENA 1791 | LORNA 1792 | JANETTE 1793 | LATONYA 1794 | CANDY 1795 | MORGAN 1796 | CONSUELO 1797 | TAMIKA 1798 | ROSETTA 1799 | DEBORA 1800 | CHERIE 1801 | POLLY 1802 | DINA 1803 | JEWELL 1804 | FAY 1805 | JILLIAN 1806 | DOROTHEA 1807 | NELL 1808 | TRUDY 1809 | ESPERANZA 1810 | PATRICA 1811 | KIMBERLEY 1812 | SHANNA 1813 | HELENA 1814 | CAROLINA 1815 | CLEO 1816 | STEFANIE 1817 | ROSARIO 1818 | OLA 1819 | JANINE 1820 | MOLLIE 1821 | LUPE 1822 | ALISA 1823 | LOU 1824 | MARIBEL 1825 | SUSANNE 1826 | BETTE 1827 | SUSANA 1828 | ELISE 1829 | CECILE 1830 | ISABELLE 1831 | LESLEY 1832 | JOCELYN 1833 | PAIGE 1834 | JONI 1835 | RACHELLE 1836 | LEOLA 1837 | DAPHNE 1838 | ALTA 1839 | ESTER 1840 | PETRA 1841 | GRACIELA 1842 | IMOGENE 1843 | JOLENE 1844 | KEISHA 1845 | LACEY 1846 | GLENNA 1847 | GABRIELA 1848 | KERI 1849 | URSULA 1850 | LIZZIE 1851 | KIRSTEN 1852 | SHANA 1853 | ADELINE 1854 | MAYRA 1855 | JAYNE 1856 | JACLYN 1857 | GRACIE 1858 | SONDRA 1859 | CARMELA 1860 | MARISA 1861 | ROSALIND 1862 | CHARITY 1863 | TONIA 1864 | BEATRIZ 1865 | MARISOL 1866 | CLARICE 1867 | JEANINE 1868 | SHEENA 1869 | ANGELINE 1870 | FRIEDA 1871 | LILY 1872 | ROBBIE 1873 | SHAUNA 1874 | MILLIE 1875 | CLAUDETTE 1876 | CATHLEEN 1877 | ANGELIA 1878 | GABRIELLE 1879 | AUTUMN 1880 | KATHARINE 1881 | SUMMER 1882 | JODIE 1883 | STACI 1884 | LEA 1885 | CHRISTI 1886 | JIMMIE 1887 | JUSTINE 1888 | ELMA 1889 | LUELLA 1890 | MARGRET 1891 | DOMINIQUE 1892 | SOCORRO 1893 | RENE 1894 | MARTINA 1895 | MARGO 1896 | MAVIS 1897 | CALLIE 1898 | BOBBI 1899 | MARITZA 1900 | LUCILE 1901 | LEANNE 1902 | JEANNINE 1903 | DEANA 1904 | AILEEN 1905 | LORIE 1906 | LADONNA 1907 | WILLA 1908 | MANUELA 1909 | GALE 1910 | SELMA 1911 | DOLLY 1912 | SYBIL 1913 | ABBY 1914 | LARA 1915 | DALE 1916 | IVY 1917 | DEE 1918 | WINNIE 1919 | MARCY 1920 | LUISA 1921 | JERI 1922 | MAGDALENA 1923 | OFELIA 1924 | MEAGAN 1925 | AUDRA 1926 | MATILDA 1927 | LEILA 1928 | CORNELIA 1929 | BIANCA 1930 | SIMONE 1931 | BETTYE 1932 | RANDI 1933 | VIRGIE 1934 | LATISHA 1935 | BARBRA 1936 | GEORGINA 1937 | ELIZA 1938 | LEANN 1939 | BRIDGETTE 1940 | RHODA 1941 | HALEY 1942 | ADELA 1943 | NOLA 1944 | BERNADINE 1945 | FLOSSIE 1946 | ILA 1947 | GRETA 1948 | RUTHIE 1949 | NELDA 1950 | MINERVA 1951 | LILLY 1952 | TERRIE 1953 | LETHA 1954 | HILARY 1955 | ESTELA 1956 | VALARIE 1957 | BRIANNA 1958 | ROSALYN 1959 | EARLINE 1960 | CATALINA 1961 | AVA 1962 | MIA 1963 | CLARISSA 1964 | LIDIA 1965 | CORRINE 1966 | ALEXANDRIA 1967 | CONCEPCION 1968 | TIA 1969 | SHARRON 1970 | RAE 1971 | DONA 1972 | ERICKA 1973 | JAMI 1974 | ELNORA 1975 | CHANDRA 1976 | LENORE 1977 | NEVA 1978 | MARYLOU 1979 | MELISA 1980 | TABATHA 1981 | SERENA 1982 | AVIS 1983 | ALLIE 1984 | SOFIA 1985 | JEANIE 1986 | ODESSA 1987 | NANNIE 1988 | HARRIETT 1989 | LORAINE 1990 | PENELOPE 1991 | MILAGROS 1992 | EMILIA 1993 | BENITA 1994 | ALLYSON 1995 | ASHLEE 1996 | TANIA 1997 | TOMMIE 1998 | ESMERALDA 1999 | KARINA 2000 | EVE 2001 | PEARLIE 2002 | ZELMA 2003 | MALINDA 2004 | NOREEN 2005 | TAMEKA 2006 | SAUNDRA 2007 | HILLARY 2008 | AMIE 2009 | ALTHEA 2010 | ROSALINDA 2011 | JORDAN 2012 | LILIA 2013 | ALANA 2014 | GAY 2015 | CLARE 2016 | ALEJANDRA 2017 | ELINOR 2018 | MICHAEL 2019 | LORRIE 2020 | JERRI 2021 | DARCY 2022 | EARNESTINE 2023 | CARMELLA 2024 | TAYLOR 2025 | NOEMI 2026 | MARCIE 2027 | LIZA 2028 | ANNABELLE 2029 | LOUISA 2030 | EARLENE 2031 | MALLORY 2032 | CARLENE 2033 | NITA 2034 | SELENA 2035 | TANISHA 2036 | KATY 2037 | JULIANNE 2038 | JOHN 2039 | LAKISHA 2040 | EDWINA 2041 | MARICELA 2042 | MARGERY 2043 | KENYA 2044 | DOLLIE 2045 | ROXIE 2046 | ROSLYN 2047 | KATHRINE 2048 | NANETTE 2049 | CHARMAINE 2050 | LAVONNE 2051 | ILENE 2052 | KRIS 2053 | TAMMI 2054 | SUZETTE 2055 | CORINE 2056 | KAYE 2057 | JERRY 2058 | MERLE 2059 | CHRYSTAL 2060 | LINA 2061 | DEANNE 2062 | LILIAN 2063 | JULIANA 2064 | ALINE 2065 | LUANN 2066 | KASEY 2067 | MARYANNE 2068 | EVANGELINE 2069 | COLETTE 2070 | MELVA 2071 | LAWANDA 2072 | YESENIA 2073 | NADIA 2074 | MADGE 2075 | KATHIE 2076 | EDDIE 2077 | OPHELIA 2078 | VALERIA 2079 | NONA 2080 | MITZI 2081 | MARI 2082 | GEORGETTE 2083 | CLAUDINE 2084 | FRAN 2085 | ALISSA 2086 | ROSEANN 2087 | LAKEISHA 2088 | SUSANNA 2089 | REVA 2090 | DEIDRE 2091 | CHASITY 2092 | SHEREE 2093 | CARLY 2094 | JAMES 2095 | ELVIA 2096 | ALYCE 2097 | DEIRDRE 2098 | GENA 2099 | BRIANA 2100 | ARACELI 2101 | KATELYN 2102 | ROSANNE 2103 | WENDI 2104 | TESSA 2105 | BERTA 2106 | MARVA 2107 | IMELDA 2108 | MARIETTA 2109 | MARCI 2110 | LEONOR 2111 | ARLINE 2112 | SASHA 2113 | MADELYN 2114 | JANNA 2115 | JULIETTE 2116 | DEENA 2117 | AURELIA 2118 | JOSEFA 2119 | AUGUSTA 2120 | LILIANA 2121 | YOUNG 2122 | CHRISTIAN 2123 | LESSIE 2124 | AMALIA 2125 | SAVANNAH 2126 | ANASTASIA 2127 | VILMA 2128 | NATALIA 2129 | ROSELLA 2130 | LYNNETTE 2131 | CORINA 2132 | ALFREDA 2133 | LEANNA 2134 | CAREY 2135 | AMPARO 2136 | COLEEN 2137 | TAMRA 2138 | AISHA 2139 | WILDA 2140 | KARYN 2141 | CHERRY 2142 | QUEEN 2143 | MAURA 2144 | MAI 2145 | EVANGELINA 2146 | ROSANNA 2147 | HALLIE 2148 | ERNA 2149 | ENID 2150 | MARIANA 2151 | LACY 2152 | JULIET 2153 | JACKLYN 2154 | FREIDA 2155 | MADELEINE 2156 | MARA 2157 | HESTER 2158 | CATHRYN 2159 | LELIA 2160 | CASANDRA 2161 | BRIDGETT 2162 | ANGELITA 2163 | JANNIE 2164 | DIONNE 2165 | ANNMARIE 2166 | KATINA 2167 | BERYL 2168 | PHOEBE 2169 | MILLICENT 2170 | KATHERYN 2171 | DIANN 2172 | CARISSA 2173 | MARYELLEN 2174 | LIZ 2175 | LAURI 2176 | HELGA 2177 | GILDA 2178 | ADRIAN 2179 | RHEA 2180 | MARQUITA 2181 | HOLLIE 2182 | TISHA 2183 | TAMERA 2184 | ANGELIQUE 2185 | FRANCESCA 2186 | BRITNEY 2187 | KAITLIN 2188 | LOLITA 2189 | FLORINE 2190 | ROWENA 2191 | REYNA 2192 | TWILA 2193 | FANNY 2194 | JANELL 2195 | INES 2196 | CONCETTA 2197 | BERTIE 2198 | ALBA 2199 | BRIGITTE 2200 | ALYSON 2201 | VONDA 2202 | PANSY 2203 | ELBA 2204 | NOELLE 2205 | LETITIA 2206 | KITTY 2207 | DEANN 2208 | BRANDIE 2209 | LOUELLA 2210 | LETA 2211 | FELECIA 2212 | SHARLENE 2213 | LESA 2214 | BEVERLEY 2215 | ROBERT 2216 | ISABELLA 2217 | HERMINIA 2218 | TERRA 2219 | CELINA 2220 | TORI 2221 | OCTAVIA 2222 | JADE 2223 | DENICE 2224 | GERMAINE 2225 | SIERRA 2226 | MICHELL 2227 | CORTNEY 2228 | NELLY 2229 | DORETHA 2230 | SYDNEY 2231 | DEIDRA 2232 | MONIKA 2233 | LASHONDA 2234 | JUDI 2235 | CHELSEY 2236 | ANTIONETTE 2237 | MARGOT 2238 | BOBBY 2239 | ADELAIDE 2240 | NAN 2241 | LEEANN 2242 | ELISHA 2243 | DESSIE 2244 | LIBBY 2245 | KATHI 2246 | GAYLA 2247 | LATANYA 2248 | MINA 2249 | MELLISA 2250 | KIMBERLEE 2251 | JASMIN 2252 | RENAE 2253 | ZELDA 2254 | ELDA 2255 | MA 2256 | JUSTINA 2257 | GUSSIE 2258 | EMILIE 2259 | CAMILLA 2260 | ABBIE 2261 | ROCIO 2262 | KAITLYN 2263 | JESSE 2264 | EDYTHE 2265 | ASHLEIGH 2266 | SELINA 2267 | LAKESHA 2268 | GERI 2269 | ALLENE 2270 | PAMALA 2271 | MICHAELA 2272 | DAYNA 2273 | CARYN 2274 | ROSALIA 2275 | SUN 2276 | JACQULINE 2277 | REBECA 2278 | MARYBETH 2279 | KRYSTLE 2280 | IOLA 2281 | DOTTIE 2282 | BENNIE 2283 | BELLE 2284 | AUBREY 2285 | GRISELDA 2286 | ERNESTINA 2287 | ELIDA 2288 | ADRIANNE 2289 | DEMETRIA 2290 | DELMA 2291 | CHONG 2292 | JAQUELINE 2293 | DESTINY 2294 | ARLEEN 2295 | VIRGINA 2296 | RETHA 2297 | FATIMA 2298 | TILLIE 2299 | ELEANORE 2300 | CARI 2301 | TREVA 2302 | BIRDIE 2303 | WILHELMINA 2304 | ROSALEE 2305 | MAURINE 2306 | LATRICE 2307 | YONG 2308 | JENA 2309 | TARYN 2310 | ELIA 2311 | DEBBY 2312 | MAUDIE 2313 | JEANNA 2314 | DELILAH 2315 | CATRINA 2316 | SHONDA 2317 | HORTENCIA 2318 | THEODORA 2319 | TERESITA 2320 | ROBBIN 2321 | DANETTE 2322 | MARYJANE 2323 | FREDDIE 2324 | DELPHINE 2325 | BRIANNE 2326 | NILDA 2327 | DANNA 2328 | CINDI 2329 | BESS 2330 | IONA 2331 | HANNA 2332 | ARIEL 2333 | WINONA 2334 | VIDA 2335 | ROSITA 2336 | MARIANNA 2337 | WILLIAM 2338 | RACHEAL 2339 | GUILLERMINA 2340 | ELOISA 2341 | CELESTINE 2342 | CAREN 2343 | MALISSA 2344 | LONA 2345 | CHANTEL 2346 | SHELLIE 2347 | MARISELA 2348 | LEORA 2349 | AGATHA 2350 | SOLEDAD 2351 | MIGDALIA 2352 | IVETTE 2353 | CHRISTEN 2354 | ATHENA 2355 | JANEL 2356 | CHLOE 2357 | VEDA 2358 | PATTIE 2359 | TESSIE 2360 | TERA 2361 | MARILYNN 2362 | LUCRETIA 2363 | KARRIE 2364 | DINAH 2365 | DANIELA 2366 | ALECIA 2367 | ADELINA 2368 | VERNICE 2369 | SHIELA 2370 | PORTIA 2371 | MERRY 2372 | LASHAWN 2373 | DEVON 2374 | DARA 2375 | TAWANA 2376 | OMA 2377 | VERDA 2378 | CHRISTIN 2379 | ALENE 2380 | ZELLA 2381 | SANDI 2382 | RAFAELA 2383 | MAYA 2384 | KIRA 2385 | CANDIDA 2386 | ALVINA 2387 | SUZAN 2388 | SHAYLA 2389 | LYN 2390 | LETTIE 2391 | ALVA 2392 | SAMATHA 2393 | ORALIA 2394 | MATILDE 2395 | MADONNA 2396 | LARISSA 2397 | VESTA 2398 | RENITA 2399 | INDIA 2400 | DELOIS 2401 | SHANDA 2402 | PHILLIS 2403 | LORRI 2404 | ERLINDA 2405 | CRUZ 2406 | CATHRINE 2407 | BARB 2408 | ZOE 2409 | ISABELL 2410 | IONE 2411 | GISELA 2412 | CHARLIE 2413 | VALENCIA 2414 | ROXANNA 2415 | MAYME 2416 | KISHA 2417 | ELLIE 2418 | MELLISSA 2419 | DORRIS 2420 | DALIA 2421 | BELLA 2422 | ANNETTA 2423 | ZOILA 2424 | RETA 2425 | REINA 2426 | LAURETTA 2427 | KYLIE 2428 | CHRISTAL 2429 | PILAR 2430 | CHARLA 2431 | ELISSA 2432 | TIFFANI 2433 | TANA 2434 | PAULINA 2435 | LEOTA 2436 | BREANNA 2437 | JAYME 2438 | CARMEL 2439 | VERNELL -------------------------------------------------------------------------------- /entity_data/group-names.txt: -------------------------------------------------------------------------------- 1 | Marketing 2 | Finance 3 | IT 4 | HR Team 5 | Sales Team 6 | Product Development 7 | Project Alpha 8 | Project Beta 9 | Admin Users 10 | Vpn Users 11 | Engineering 12 | Customer Support 13 | Legal 14 | Operations 15 | Compliance 16 | Data Science 17 | Business Intelligence 18 | Executive Team 19 | Corporate Strategy 20 | Procurement 21 | Quality Assurance 22 | Research and Development 23 | Field Operations 24 | Internal Audit 25 | Public Relations 26 | Investor Relations 27 | Event Management 28 | Training and Development 29 | Recruiting Team 30 | Payroll 31 | Benefits Administration 32 | Employee Relations 33 | Risk Management 34 | Sustainability 35 | Facilities Management 36 | Manufacturing 37 | Distribution 38 | Logistics 39 | Warehousing 40 | Supply Chain 41 | Production 42 | Product Management 43 | UX Design 44 | Software Development 45 | Network Security 46 | Systems Administration 47 | Cloud Infrastructure 48 | DevOps 49 | Technical Support 50 | Application Development 51 | Database Management 52 | IT Helpdesk 53 | IT Operations 54 | IT Security 55 | IT Governance 56 | Enterprise Architecture 57 | Business Analysis 58 | Project Management 59 | Product Marketing 60 | Field Marketing 61 | Digital Marketing 62 | Content Marketing 63 | Brand Management 64 | Corporate Communications 65 | Social Media 66 | Market Research 67 | Customer Experience 68 | Customer Success 69 | Partner Management 70 | Channel Sales 71 | Direct Sales 72 | Sales Operations 73 | Account Management 74 | Sales Engineering 75 | Pre-Sales 76 | Post-Sales 77 | Inside Sales 78 | Outside Sales 79 | Sales Enablement 80 | Sales Training 81 | Solution Engineering 82 | Solution Architecture 83 | Professional Services 84 | Customer Training 85 | Customer Onboarding 86 | Customer Implementation 87 | Customer Retention 88 | Customer Feedback 89 | Customer Advocacy 90 | Product Support 91 | Field Support 92 | Technical Account Management 93 | User Experience 94 | User Research 95 | User Testing 96 | Quality Engineering 97 | Test Automation 98 | Release Management 99 | Configuration Management 100 | Service Delivery 101 | Service Management 102 | Incident Management 103 | Problem Management 104 | Change Management 105 | Asset Management 106 | Configuration Management 107 | Vendor Management 108 | Contract Management 109 | Procurement Operations 110 | Strategic Sourcing 111 | Supplier Relationship Management 112 | Purchasing 113 | Inventory Management 114 | Materials Management 115 | Fleet Management 116 | Warehouse Operations 117 | Transportation Management 118 | Order Fulfillment 119 | Demand Planning 120 | Production Planning 121 | Production Control 122 | Quality Control 123 | Process Improvement 124 | Lean Manufacturing 125 | Six Sigma 126 | Continuous Improvement 127 | Safety Management 128 | Environmental Health and Safety 129 | Regulatory Compliance 130 | Corporate Governance 131 | Ethics and Compliance 132 | Internal Controls 133 | Financial Reporting 134 | Financial Planning 135 | Financial Analysis 136 | Treasury 137 | Tax 138 | Audit 139 | Controllership 140 | General Accounting 141 | Cost Accounting 142 | Budgeting 143 | Forecasting 144 | Mergers and Acquisitions 145 | Investor Relations 146 | Corporate Development 147 | Strategy and Planning 148 | Business Development 149 | Corporate Social Responsibility 150 | Community Relations 151 | Employee Engagement 152 | Talent Management 153 | Leadership Development 154 | Organizational Development 155 | Learning and Development 156 | Compensation and Benefits 157 | Employee Health and Wellness 158 | Diversity and Inclusion 159 | Workforce Planning 160 | Succession Planning 161 | Performance Management 162 | Employee Recognition 163 | Employee Training 164 | Talent Acquisition 165 | Talent Retention 166 | Labor Relations 167 | Industrial Relations 168 | Union Relations 169 | Employee Safety 170 | Employee Wellbeing 171 | HR Operations 172 | HR Compliance 173 | HR Information Systems 174 | HR Analytics 175 | HR Strategy 176 | Executive Search 177 | Campus Recruitment 178 | Employer Branding 179 | Employee Surveys 180 | Exit Interviews 181 | Retention Strategies 182 | Employee Referral Programs 183 | Remote Work 184 | Flexible Work Arrangements 185 | Work-Life Balance 186 | Telecommuting 187 | Virtual Teams 188 | Remote Collaboration 189 | Hybrid Work Models 190 | IT Service Management 191 | Enterprise Applications 192 | Collaboration Tools 193 | Messaging and Communication 194 | Unified Communications 195 | Telephony 196 | Video Conferencing 197 | Web Conferencing 198 | Mobile Device Management 199 | Endpoint Security 200 | Identity and Access Management 201 | Single Sign-On 202 | Multi-Factor Authentication 203 | Directory Services 204 | User Provisioning 205 | Access Control 206 | Security Operations 207 | Security Engineering 208 | Cybersecurity 209 | Information Security 210 | Data Protection 211 | Data Privacy 212 | Security Awareness 213 | Threat Intelligence 214 | Incident Response 215 | Forensics 216 | Vulnerability Management 217 | Penetration Testing 218 | Security Compliance 219 | Security Architecture 220 | Application Security 221 | Network Security 222 | Cloud Security 223 | Endpoint Protection 224 | Mobile Security 225 | Security Analytics 226 | Security Information and Event Management 227 | Threat Hunting 228 | Security Monitoring 229 | Security Operations Center 230 | Security Orchestration and Automation 231 | Security Incident Management 232 | Data Governance 233 | Data Quality 234 | Data Integration 235 | Data Warehousing 236 | Data Lakes 237 | Big Data 238 | Data Analytics 239 | Data Visualization 240 | Business Intelligence 241 | Data Science 242 | Machine Learning 243 | Artificial Intelligence 244 | Data Engineering 245 | Data Architecture 246 | Data Modeling 247 | Data Operations 248 | Data Management 249 | Master Data Management 250 | Metadata Management 251 | Data Stewardship 252 | Data Strategy 253 | Data Transformation 254 | Data Migration 255 | Data Consolidation 256 | Data Enrichment 257 | Data Lineage 258 | Data Cataloging 259 | Data Privacy 260 | Data Compliance 261 | Data Security 262 | Data Access 263 | Data Sharing 264 | Data Governance Council 265 | Data Governance Office 266 | Data Governance Framework 267 | Data Governance Policy 268 | Data Governance Strategy 269 | Data Governance Implementation 270 | Data Governance Maturity 271 | Data Governance Metrics 272 | Data Governance Standards 273 | Data Governance Processes 274 | Data Governance Roles 275 | Data Governance Responsibilities 276 | Data Governance Communication 277 | Data Governance Education 278 | Data Governance Training 279 | Data Governance GitHub Status 280 | Data Governance GitHub API 281 | Data Governance GitHub CLI 282 | Data Governance GitHub Desktop 283 | Data Governance GitHub Actions 284 | Data Governance GitHub Discussions 285 | Data Governance GitHub Codespaces 286 | Data Governance GitHub Packages 287 | Data Governance GitHub Insights 288 | Data Governance GitHub Security 289 | Data Governance GitHub Copilot 290 | Data Governance GitHub Classroom 291 | Data Governance GitHub Enterprise 292 | Data Governance GitHub Education 293 | Data Governance GitHub Marketplace 294 | Data Governance GitHub Learning Lab 295 | Data Governance GitHub Explore 296 | Data Governance GitHub Docs 297 | Data Governance GitHub Blog 298 | Data Governance GitHub Community 299 | Data Governance GitHub Support 300 | Data Governance GitHub Status 301 | Data Governance GitHub API 302 | Data Governance GitHub CLI -------------------------------------------------------------------------------- /img/BadZure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mvelazc0/BadZure/0c9d78a49c453192a6f9decd9d05e4c11656879c/img/BadZure.png -------------------------------------------------------------------------------- /img/attack_paths.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mvelazc0/BadZure/0c9d78a49c453192a6f9decd9d05e4c11656879c/img/attack_paths.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | pyyaml 3 | click 4 | python-terraform -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mvelazc0/BadZure/0c9d78a49c453192a6f9decd9d05e4c11656879c/src/__init__.py -------------------------------------------------------------------------------- /terraform/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azuread = { 4 | source = "hashicorp/azuread" 5 | version = "2.50.0" 6 | } 7 | } 8 | } 9 | 10 | provider "azuread" { 11 | tenant_id = var.tenant_id 12 | } 13 | 14 | data "azuread_domains" "example" { 15 | only_initial = true 16 | } 17 | 18 | 19 | data "azuread_service_principal" "microsoft_graph" { 20 | display_name = "Microsoft Graph" 21 | } 22 | 23 | resource "azuread_user" "users" { 24 | for_each = var.users 25 | 26 | user_principal_name = "${each.value.user_principal_name}@${var.domain}" 27 | display_name = each.value.display_name 28 | mail_nickname = each.value.mail_nickname 29 | password = each.value.password 30 | 31 | } 32 | 33 | resource "azuread_group" "groups" { 34 | for_each = var.groups 35 | 36 | display_name = each.value.display_name 37 | mail_enabled = false 38 | security_enabled = true 39 | } 40 | 41 | resource "azuread_application_registration" "spns" { 42 | for_each = var.applications 43 | 44 | display_name = each.value.display_name 45 | } 46 | 47 | resource "azuread_service_principal" "spns" { 48 | for_each = var.applications 49 | 50 | client_id = azuread_application_registration.spns[each.key].client_id 51 | } 52 | 53 | 54 | resource "azuread_administrative_unit" "aunits" { 55 | for_each = var.administrative_units 56 | 57 | display_name = each.value.display_name 58 | } 59 | 60 | resource "azuread_group_member" "group_memberships" { 61 | for_each = var.user_group_assignments 62 | 63 | group_object_id = azuread_group.groups[each.value.group_name].id 64 | member_object_id = azuread_user.users[each.value.user_name].id 65 | } 66 | 67 | resource "azuread_administrative_unit_member" "au_memberships" { 68 | for_each = var.user_au_assignments 69 | 70 | administrative_unit_object_id = azuread_administrative_unit.aunits[each.value.administrative_unit_name].id 71 | member_object_id = azuread_user.users[each.value.user_name].id 72 | } 73 | 74 | resource "azuread_directory_role_assignment" "user_role_assignments" { 75 | for_each = var.user_role_assignments 76 | 77 | principal_object_id = azuread_user.users[each.value.user_name].id 78 | role_id = each.value.role_definition_id 79 | } 80 | 81 | resource "azuread_directory_role_assignment" "app_role_assignments" { 82 | for_each = var.app_role_assignments 83 | 84 | principal_object_id = azuread_service_principal.spns[each.value.app_name].object_id 85 | role_id = each.value.role_id 86 | } 87 | 88 | resource "azuread_app_role_assignment" "app_api_permission_assignments" { 89 | for_each = var.app_api_permission_assignments 90 | 91 | app_role_id = each.value.api_permission_id 92 | principal_object_id = azuread_service_principal.spns[each.value.app_name].id 93 | resource_object_id = data.azuread_service_principal.microsoft_graph.id 94 | } 95 | 96 | resource "azuread_directory_role_assignment" "attack_path_user_role_assignments" { 97 | for_each = var.attack_path_user_role_assignments 98 | 99 | principal_object_id = azuread_user.users[each.value.user_name].id 100 | role_id = each.value.role_definition_id 101 | } 102 | 103 | resource "azuread_directory_role_assignment" "attack_path_application_role_assignments" { 104 | for_each = var.attack_path_application_role_assignments 105 | 106 | principal_object_id = azuread_service_principal.spns[each.value.app_name].id 107 | role_id = each.value.role_id 108 | } 109 | 110 | resource "azuread_app_role_assignment" "attack_path_application_api_permission_assignments" { 111 | for_each = var.attack_path_application_api_permission_assignments 112 | 113 | app_role_id = each.value.api_permission_id 114 | principal_object_id = azuread_service_principal.spns[each.value.app_name].id 115 | resource_object_id = data.azuread_service_principal.microsoft_graph.id 116 | } 117 | 118 | resource "azuread_application_owner" "attack_path_application_owner_assignments" { 119 | for_each = var.attack_path_application_owner_assignments 120 | 121 | application_id = "/applications/${azuread_application_registration.spns[each.value.app_name].object_id}" 122 | owner_object_id = azuread_user.users[replace(each.value.user_principal_name, "@${var.domain}", "")].object_id 123 | } -------------------------------------------------------------------------------- /terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "user_ids" { 2 | description = "The IDs of the created users" 3 | value = [for user in azuread_user.users : user.id] 4 | } 5 | 6 | output "group_ids" { 7 | description = "The IDs of the created groups" 8 | value = [for group in azuread_group.groups : group.id] 9 | } 10 | 11 | output "application_ids" { 12 | description = "The IDs of the created applications" 13 | value = [for app in azuread_application_registration.spns : app.object_id] 14 | } 15 | 16 | output "service_principal_ids" { 17 | description = "The IDs of the created service principals" 18 | value = [for sp in azuread_service_principal.spns : sp.id] 19 | } 20 | 21 | output "administrative_unit_ids" { 22 | description = "The IDs of the created administrative units" 23 | value = [for au in azuread_administrative_unit.aunits : au.id] 24 | } 25 | -------------------------------------------------------------------------------- /terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "azure_config_dir" { 2 | description = "Path to the Azure CLI configuration directory" 3 | type = string 4 | } 5 | 6 | variable "tenant_id" { 7 | description = "The tenant ID for Azure AD" 8 | type = string 9 | } 10 | 11 | variable "domain" { 12 | description = "The domain for Azure AD users" 13 | type = string 14 | } 15 | 16 | variable "users" { 17 | type = map(object({ 18 | user_principal_name = string 19 | display_name = string 20 | mail_nickname = string 21 | password = string 22 | })) 23 | } 24 | 25 | variable "groups" { 26 | description = "A map of groups to create" 27 | type = map(object({ 28 | display_name = string 29 | })) 30 | } 31 | 32 | variable "applications" { 33 | description = "A map of applications to create" 34 | type = map(object({ 35 | display_name = string 36 | })) 37 | } 38 | 39 | variable "administrative_units" { 40 | description = "A map of administrative units to create" 41 | type = map(object({ 42 | display_name = string 43 | })) 44 | } 45 | 46 | variable "user_group_assignments" { 47 | description = "A map of user-to-group assignments" 48 | type = map(object({ 49 | user_name = string 50 | group_name = string 51 | })) 52 | } 53 | 54 | variable "user_au_assignments" { 55 | description = "A map of user-to-administrative unit assignments" 56 | type = map(object({ 57 | user_name = string 58 | administrative_unit_name = string 59 | })) 60 | } 61 | 62 | variable "user_role_assignments" { 63 | description = "A map of user-to-role assignments" 64 | type = map(object({ 65 | user_name = string 66 | role_definition_id = string 67 | })) 68 | } 69 | 70 | variable "app_role_assignments" { 71 | description = "A map of app-to-role assignments" 72 | type = map(object({ 73 | app_name = string 74 | role_id = string 75 | })) 76 | } 77 | 78 | variable "app_api_permission_assignments" { 79 | description = "A map of application to API permission assingments" 80 | default = {} 81 | type = map(object({ 82 | app_name = string 83 | api_permission_id = string 84 | })) 85 | } 86 | 87 | variable "attack_path_user_role_assignments" { 88 | description = "A map of user role assignments in an attack path" 89 | type = map(object({ 90 | user_name = string 91 | role_definition_id = string 92 | })) 93 | } 94 | 95 | variable "attack_path_application_role_assignments" { 96 | description = "A map of application role assignments used in an attack path" 97 | default = {} 98 | type = map(object({ 99 | app_name = string 100 | role_id = string 101 | })) 102 | } 103 | 104 | variable "attack_path_application_api_permission_assignments" { 105 | description = "A map of application to API permission assingments in an attack path" 106 | default = {} 107 | type = map(object({ 108 | app_name = string 109 | api_permission_id = string 110 | })) 111 | } 112 | 113 | 114 | variable "attack_path_application_owner_assignments" { 115 | description = "A map of application owner assignments used in an attack path" 116 | default = {} 117 | type = map(object({ 118 | app_name = string 119 | user_principal_name = string 120 | })) 121 | } --------------------------------------------------------------------------------