diff --git a/Playbooks/Get-CompromisedPasswords/azuredeploy.json b/Playbooks/Get-CompromisedPasswords/azuredeploy.json new file mode 100644 index 0000000000..12351ed0fc --- /dev/null +++ b/Playbooks/Get-CompromisedPasswords/azuredeploy.json @@ -0,0 +1,941 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "PlaybookName": { + "defaultValue": "", + "type": "string" + }, + "UserName": { + "defaultValue": "@", + "type": "string" + }, + "ClientID": { + "defaultValue": "****REPLACE WITH CLIENT ID ****", + "type": "string" + }, + "TenantID": { + "defaultValue": "****REPLACE WITH TENANT ID ****", + "type": "string" + }, + "DeHashed-Username": { + "defaultValue": "****REPLACE WITH DEHASHED USERNAME ****", + "type": "string" + }, + "KeyVaultClientCredentialsURL-DeHashed-Password": { + "defaultValue": "****REPLACE WITH KEY VAULT URI FOR DEHASHED PASSWORD ****", + "type": "string" + }, + "KeyVaultClientCredentialsURL-GraphAPI": { + "defaultValue": "****REPLACE WITH KEY VAULT URI FOR GRAPH API KEY ****", + "type": "string" + } + }, + "variables": { + "AzureSentinelConnectionName": "[concat('azuresentinel-', parameters('PlaybookName'))]" + }, + "resources": [ + { + "type": "Microsoft.Web/connections", + "apiVersion": "2016-06-01", + "name": "[variables('AzureSentinelConnectionName')]", + "location": "[resourceGroup().location]", + "properties": { + "displayName": "[parameters('UserName')]", + "customParameterValues": {}, + "api": { + "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/azuresentinel')]" + } + } + }, + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2017-07-01", + "name": "[parameters('PlaybookName')]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[resourceId('Microsoft.Web/connections', variables('AzureSentinelConnectionName'))]" + ], + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "ClientID": { + "defaultValue": "[parameters('ClientID')]", + "type": "String" + }, + "DeHashed-Username": { + "defaultValue": "[parameters('DeHashed-Username')]", + "type": "String" + }, + "KeyVaultClientCredentialsURL-DeHashed-Password": { + "defaultValue": "[parameters('KeyVaultClientCredentialsURL-DeHashed-Password')]", + "type": "String" + }, + "KeyVaultClientCredentialsURL-GraphAPI": { + "defaultValue": "[parameters('KeyVaultClientCredentialsURL-GraphAPI')]", + "type": "String" + }, + "TenantId": { + "defaultValue": "[parameters('TenantID')]", + "type": "String" + }, + "$connections": { + "defaultValue": { + }, + "type": "Object" + } + }, + "triggers": { + "When_a_response_to_an_Azure_Sentinel_alert_is_triggered": { + "type": "ApiConnectionWebhook", + "inputs": { + "body": { + "callback_url": "@{listCallbackUrl()}" + }, + "host": { + "connection": { + "name": "@parameters('$connections')['azuresentinel']['connectionId']" + } + }, + "path": "/subscribe" + } + } + }, + "actions": { + "Alert_-_Get_incident": { + "runAfter": {}, + "type": "ApiConnection", + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['azuresentinel']['connectionId']" + } + }, + "method": "get", + "path": "/Incidents/subscriptions/@{encodeURIComponent(triggerBody()?['WorkspaceSubscriptionId'])}/resourceGroups/@{encodeURIComponent(triggerBody()?['WorkspaceResourceGroup'])}/workspaces/@{encodeURIComponent(triggerBody()?['WorkspaceId'])}/alerts/@{encodeURIComponent(triggerBody()?['SystemAlertId'])}" + } + }, + "Entities_-_Get_Accounts": { + "runAfter": { + "Alert_-_Get_incident": [ + "Succeeded" + ] + }, + "type": "ApiConnection", + "inputs": { + "body": "@triggerBody()?['Entities']", + "host": { + "connection": { + "name": "@parameters('$connections')['azuresentinel']['connectionId']" + } + }, + "method": "post", + "path": "/entities/account" + } + }, + "Extract_Password_List": { + "runAfter": { + "Parse_Password_Settings": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@body('Parse_Password_Settings')?['values']", + "where": "@equals(item()?['name'], 'BannedPasswordList')" + } + }, + "For_Each_Account": { + "foreach": "@body('Entities_-_Get_Accounts')?['Accounts']", + "actions": { + "If_AAD_User_ID_is_Not_Null": { + "actions": { + "Get_Passwords": { + "runAfter": { + "Parse_AAD_User_Info": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "authentication": { + "password": "@body('Parse_Response_from_Key_Vault_-_Dehashed_Password')?['value']", + "type": "Basic", + "username": "@parameters('DeHashed-Username')" + }, + "headers": { + "Accept": "application/json" + }, + "method": "GET", + "uri": "https://api.dehashed.com/search?query=@{body('Parse_AAD_User_Info')?['mail']}" + } + }, + "Graph_-_Get_AAD_User_Info": { + "runAfter": {}, + "type": "Http", + "inputs": { + "authentication": { + "audience": "https://graph.microsoft.com", + "clientId": "@parameters('ClientID')", + "secret": "@body('Parse_Response_from_Key_Vault_-_Graph_API')?['value']", + "tenant": "@parameters('TenantId')", + "type": "ActiveDirectoryOAuth" + }, + "method": "GET", + "uri": "https://graph.microsoft.com/v1.0/users/@{items('For_Each_Account')?['AadUserId']}" + } + }, + "If_Passwords_Found_Greater_Then_0_-_Update_Banned_Password_List": { + "actions": { + "Add_comment_to_incident_(V3)_2": { + "runAfter": { + "Update_Password_Settings": [ + "Succeeded" + ] + }, + "type": "ApiConnection", + "inputs": { + "body": { + "incidentArmId": "@body('Alert_-_Get_incident')?['id']", + "message": "

The following compromised passwords @{variables('foundPasswords')} were found for @{body('Parse_AAD_User_Info')?['mail']}. These passwords have been succesfully added to the Azure AD Banned Password list.

" + }, + "host": { + "connection": { + "name": "@parameters('$connections')['azuresentinel']['connectionId']" + } + }, + "method": "post", + "path": "/Incidents/Comment" + } + }, + "Compose_Password_Array": { + "runAfter": { + "For_Each_Result": [ + "Succeeded" + ] + }, + "type": "Compose", + "inputs": "@intersection(variables('dehashedPasswordArray'),variables('dehashedPasswordArray'))" + }, + "For_Each_Result": { + "foreach": "@body('Parse_Dehashed_Data')?['entries']", + "actions": { + "Condition": { + "actions": { + "Append_to_array_variable": { + "runAfter": {}, + "type": "AppendToArrayVariable", + "inputs": { + "name": "dehashedPasswordArray", + "value": "@items('For_Each_Result')?['password']" + } + } + }, + "runAfter": { + "Set_Password": [ + "Succeeded" + ] + }, + "expression": { + "and": [ + { + "not": { + "equals": [ + "@items('For_Each_Result')?['password']", + "@null" + ] + } + }, + { + "greaterOrEquals": [ + "@length(variables('password'))", + 4 + ] + }, + { + "lessOrEquals": [ + "@length(variables('password'))", + 16 + ] + } + ] + }, + "type": "If" + }, + "Set_Password": { + "runAfter": {}, + "type": "SetVariable", + "inputs": { + "name": "password", + "value": "@{items('For_Each_Result')?['password']}" + } + } + }, + "runAfter": { + "Parse_Dehashed_Data": [ + "Succeeded" + ] + }, + "type": "Foreach" + }, + "For_Each_Unique_Password": { + "foreach": "@outputs('Compose_Password_Array')", + "actions": { + "Check_for_Null_password": { + "actions": { + "Append_to_string_variable": { + "runAfter": {}, + "type": "AppendToStringVariable", + "inputs": { + "name": "bannedPasswordList", + "value": "@{items('For_Each_Unique_Password')}\t" + } + } + }, + "runAfter": {}, + "expression": { + "and": [ + { + "not": { + "equals": [ + "@items('For_Each_Unique_Password')", + "" + ] + } + } + ] + }, + "type": "If" + } + }, + "runAfter": { + "Set_Bad_Passwords_String": [ + "Succeeded" + ] + }, + "type": "Foreach" + }, + "Join_Bad_Passwords_String": { + "runAfter": { + "Compose_Password_Array": [ + "Succeeded" + ] + }, + "type": "Join", + "inputs": { + "from": "@outputs('Compose_Password_Array')", + "joinWith": ":" + } + }, + "Parse_Dehashed_Data": { + "runAfter": {}, + "type": "ParseJson", + "inputs": { + "content": "@body('Get_Passwords')", + "schema": { + "properties": { + "balance": { + "type": "integer" + }, + "entries": { + "items": { + "properties": { + "address": {}, + "email": {}, + "hashed_password": {}, + "id": {}, + "ip_address": {}, + "name": {}, + "obtained_from": {}, + "password": {}, + "phone": {}, + "username": {}, + "vin": {} + }, + "required": [], + "type": "object" + }, + "type": "array" + }, + "success": { + "type": "boolean" + }, + "took": { + "type": "string" + }, + "total": { + "type": "integer" + } + }, + "type": "object" + } + } + }, + "Set_Bad_Passwords_String": { + "runAfter": { + "Join_Bad_Passwords_String": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "foundPasswords", + "value": "@body('Join_Bad_Passwords_String')" + } + }, + "Update_Password_Settings": { + "runAfter": { + "For_Each_Unique_Password": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "authentication": { + "audience": "https://graph.microsoft.com", + "clientId": "@parameters('ClientID')", + "secret": "@body('Parse_Response_from_Key_Vault_-_Graph_API')?['value']", + "tenant": "@parameters('TenantId')", + "type": "ActiveDirectoryOAuth" + }, + "body": { + "templateId": "5cf42378-d67d-4f36-ba46-e8b86229381d", + "values": [ + { + "name": "BannedPasswordCheckOnPremisesMode", + "value": "@{variables('BannedPasswordCheckOnPremisesMode')}" + }, + { + "name": "EnableBannedPasswordCheckOnPremises", + "value": "@{variables('EnableBannedPasswordCheckOnPremises')}" + }, + { + "name": "EnableBannedPasswordCheck", + "value": "@{variables('EnableBannedPasswordCheck')}" + }, + { + "name": "LockoutDurationInSeconds", + "value": "@{variables('LockoutDurationInSeconds')}" + }, + { + "name": "LockoutThreshold", + "value": "@{variables('LockoutThreshold')}" + }, + { + "name": "BannedPasswordList", + "value": "@{substring(variables('bannedPasswordList'),0,sub(length(variables('bannedPasswordList')),1))}" + } + ] + }, + "method": "PATCH", + "uri": "https://graph.microsoft.com/beta/settings//5bfc75a9-c4cb-42d5-9abc-56d458ab560d" + } + } + }, + "runAfter": { + "Set_Number_of_Passwords_found": [ + "Succeeded" + ] + }, + "else": { + "actions": { + "Add_comment_to_incident_(V3)": { + "runAfter": {}, + "type": "ApiConnection", + "inputs": { + "body": { + "incidentArmId": "@body('Alert_-_Get_incident')?['id']", + "message": "

No passwords were found for @{body('Parse_AAD_User_Info')?['mail']}.  The Azure AD Banned Password list remains unchanged.

" + }, + "host": { + "connection": { + "name": "@parameters('$connections')['azuresentinel']['connectionId']" + } + }, + "method": "post", + "path": "/Incidents/Comment" + } + } + } + }, + "expression": { + "and": [ + { + "greater": [ + "@variables('numPasswords')", + 0 + ] + } + ] + }, + "type": "If" + }, + "Parse_AAD_User_Info": { + "runAfter": { + "Graph_-_Get_AAD_User_Info": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Graph_-_Get_AAD_User_Info')", + "schema": { + "properties": { + "@@odata.context": { + "type": "string" + }, + "businessPhones": {}, + "displayName": {}, + "givenName": {}, + "id": {}, + "jobTitle": {}, + "mail": {}, + "mobilePhone": {}, + "officeLocation": {}, + "preferredLanguage": {}, + "surname": {}, + "userPrincipalName": {} + }, + "type": "object" + } + } + }, + "Set_Number_of_Passwords_found": { + "runAfter": { + "Get_Passwords": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "numPasswords", + "value": "@body('Get_Passwords')['total']" + } + } + }, + "runAfter": {}, + "expression": { + "and": [ + { + "not": { + "equals": [ + "@items('For_Each_Account')?['AadUserId']", + "@null" + ] + } + } + ] + }, + "type": "If" + } + }, + "runAfter": { + "Initialize_Passwords": [ + "Succeeded" + ] + }, + "type": "Foreach" + }, + "Get_Client_Secret_from_Vault_-_Dehashed": { + "runAfter": { + "Parse_Response_from_Key_Vault_-_Graph_API": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "authentication": { + "audience": "https://vault.azure.net", + "identity": "Managed Identity", + "type": "ManagedServiceIdentity" + }, + "method": "GET", + "queries": { + "api-version": "2016-10-01" + }, + "uri": "@parameters('KeyVaultClientCredentialsURL-DeHashed-Password')" + } + }, + "Get_Client_Secret_from_Vault_-_Graph_API": { + "runAfter": { + "Entities_-_Get_Accounts": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "authentication": { + "audience": "https://vault.azure.net", + "identity": "Managed Identity", + "type": "ManagedServiceIdentity" + }, + "method": "GET", + "queries": { + "api-version": "2016-10-01" + }, + "uri": "@parameters('KeyVaultClientCredentialsURL-GraphAPI')" + } + }, + "Get_Password_Settings": { + "runAfter": { + "Parse_Response_from_Key_Vault_-_Dehashed_Password": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "authentication": { + "audience": "https://graph.microsoft.com", + "clientId": "@parameters('ClientID')", + "secret": "@body('Parse_Response_from_Key_Vault_-_Graph_API')?['value']", + "tenant": "@parameters('TenantId')", + "type": "ActiveDirectoryOAuth" + }, + "method": "GET", + "uri": "https://graph.microsoft.com/beta/settings/5bfc75a9-c4cb-42d5-9abc-56d458ab560d" + } + }, + "Initialize_BannedPasswordCheckOnPremisesMode": { + "runAfter": { + "Initialize_deHashedPasswordArray": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "BannedPasswordCheckOnPremisesMode", + "type": "string", + "value": "@{body('Parse_Password_Settings')['values'][0]['value']}" + } + ] + } + }, + "Initialize_Banned_Password_List": { + "runAfter": { + "Initialize_LockoutThreshold": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "bannedPasswordList", + "type": "string", + "value": "@{body('Parse_Password_Settings')['values'][5]['value']}\t" + } + ] + } + }, + "Initialize_EnableBannedPasswordCheck": { + "runAfter": { + "Initialize_EnableBannedPasswordCheckOnPremises": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "EnableBannedPasswordCheck", + "type": "string", + "value": "@{body('Parse_Password_Settings')['values'][2]['value']}" + } + ] + } + }, + "Initialize_EnableBannedPasswordCheckOnPremises": { + "runAfter": { + "Initialize_BannedPasswordCheckOnPremisesMode": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "EnableBannedPasswordCheckOnPremises", + "type": "string", + "value": "@body('Parse_Password_Settings')['values'][1]['value']" + } + ] + } + }, + "Initialize_Found_Password": { + "runAfter": { + "Initialize_Banned_Password_List": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "foundPasswords", + "type": "string" + } + ] + } + }, + "Initialize_JSON_Sample": { + "runAfter": { + "Extract_Password_List": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "dehashedJSONOutput", + "type": "string", + "value": "{\n \"balance\": 997,\n \"entries\": [\n {\n \"id\": \"6KZt3HhQ9U1SzdowCnwMjrx3Owk88bqBFiA=\",\n \"email\": \"officechris@gmail.com\",\n \"username\": \"d41049959f5978944462a0ea0\",\n \"password\": null,\n \"hashed_password\": \"\",\n \"name\": \"officechris@gmail.com\",\n \"vin\": null,\n \"address\": null,\n \"ip_address\": null,\n \"phone\": null,\n \"obtained_from\": \"ShareThis\"\n },\n {\n \"id\": \"wz61JTRosVazC-sk0OegPq1XTKvF3ZC6q94=\",\n \"email\": \"officechris@gmail.com\",\n \"username\": \"\",\n \"password\": \"sp937215\",\n \"hashed_password\": \"\",\n \"name\": \"\",\n \"vin\": \"\",\n \"address\": \"\",\n \"ip_address\": \"\",\n \"phone\": \"\",\n \"obtained_from\": \"BreachCompilation\"\n },\n {\n \"id\": \"CUEf2whaDI437NljaHO2DunouWsSa33D86c=\",\n \"email\": \"officechris@gmail.com\",\n \"username\": \"\",\n \"password\": \"sp937215\",\n \"hashed_password\": \"\",\n \"name\": \"\",\n \"vin\": \"\",\n \"address\": \"\",\n \"ip_address\": \"\",\n \"phone\": \"\",\n \"obtained_from\": \"UnkownCombo\"\n },\n {\n \"id\": \"4sO57I4-lQ85z0lAQsj8IFF6y1N-4Z_KAs4=\",\n \"email\": \"officechris@gmail.com\",\n \"username\": \"\",\n \"password\": \"\",\n \"hashed_password\": \"\",\n \"name\": \"sp937215\",\n \"vin\": \"\",\n \"address\": \"\",\n \"ip_address\": \"\",\n \"phone\": \"\",\n \"obtained_from\": \"Gmail\"\n },\n {\n \"id\": \"yQfoKoUy8u1jQeFt68OfuTWT14bCRcsjCSw=\",\n \"email\": \"officechris@gmail.com\",\n \"username\": null,\n \"password\": \"sp937215\",\n \"hashed_password\": null,\n \"name\": null,\n \"vin\": null,\n \"address\": null,\n \"ip_address\": null,\n \"phone\": null,\n \"obtained_from\": \"Pemiblanc\"\n },\n {\n \"id\": \"1uk1LFfa7o--G5zO0CdxK6hslB_StjXN4RLy\",\n \"email\": \"officechris@gmail.com\",\n \"username\": null,\n \"password\": \"aa123456\",\n \"hashed_password\": null,\n \"name\": null,\n \"vin\": null,\n \"address\": null,\n \"ip_address\": null,\n \"phone\": null,\n \"obtained_from\": \"Collections\"\n },\n {\n \"id\": \"0EFTjGdiwGmzCtYrjfkEA-DleYQY6FRnRcU=\",\n \"email\": \"officechris@gmail.com\",\n \"username\": null,\n \"password\": \"sp937215\",\n \"hashed_password\": null,\n \"name\": null,\n \"vin\": null,\n \"address\": null,\n \"ip_address\": null,\n \"phone\": null,\n \"obtained_from\": \"Plex.tv\"\n },\n {\n \"id\": \"A4zLoUW6OioZqbnQWqDpU3N1bpvRVdo_1i_Q\",\n \"email\": \"officechris@gmail.com\",\n \"username\": null,\n \"password\": \"sp937215\",\n \"hashed_password\": null,\n \"name\": null,\n \"vin\": null,\n \"address\": null,\n \"ip_address\": null,\n \"phone\": null,\n \"obtained_from\": \"Collections\"\n },\n {\n \"id\": \"to8M1ilcmX-GreUdDIVY550H8yfrATdm6et1\",\n \"email\": \"officechris@gmail.com\",\n \"username\": null,\n \"password\": \"sp937215\",\n \"hashed_password\": null,\n \"name\": null,\n \"vin\": null,\n \"address\": null,\n \"ip_address\": null,\n \"phone\": null,\n \"obtained_from\": \"Collections\"\n },\n {\n \"id\": \"xfH14-GqLuOMvoo5zvqCB7HSOjq4iyWQu9w=\",\n \"email\": \"officechris@gmail.com\",\n \"username\": \"\",\n \"password\": \"enferm@69\",\n \"hashed_password\": \"\",\n \"name\": \"\",\n \"vin\": \"\",\n \"address\": \"\",\n \"ip_address\": \"\",\n \"phone\": \"\",\n \"obtained_from\": \"BreachCompilation\"\n },\n {\n \"id\": \"WWaVWYskc3tYQg-2BKGKNRG6vZDoXFin_nE=\",\n \"email\": \"officechris@gmail.com\",\n \"username\": null,\n \"password\": null,\n \"hashed_password\": \"1aa165ed11a5f735e90cf4db440276f4f48e1853\",\n \"name\": null,\n \"vin\": null,\n \"address\": null,\n \"ip_address\": null,\n \"phone\": null,\n \"obtained_from\": \"Dropbox\"\n },\n {\n \"id\": \"4E--WTW39vSHY6YVtOx71PiN0laxgpeL0Wbo\",\n \"email\": \"officechris@gmail.com\",\n \"username\": null,\n \"password\": \"enferm@69\",\n \"hashed_password\": null,\n \"name\": null,\n \"vin\": null,\n \"address\": null,\n \"ip_address\": null,\n \"phone\": null,\n \"obtained_from\": \"Collections\"\n },\n {\n \"id\": \"0m4wjEcA1EfXypoJ35cEgKndPQRaY3l43wU=\",\n \"email\": \"officechris@gmail.com\",\n \"username\": \"106710246\",\n \"password\": null,\n \"hashed_password\": \"0x1B36321292BA83462C9BFAD700D2473115CA8412\",\n \"name\": null,\n \"vin\": null,\n \"address\": null,\n \"ip_address\": null,\n \"phone\": null,\n \"obtained_from\": \"MySpace\"\n },\n {\n \"id\": \"Z7CaqJToX_eQ_wsvHWFhbiPKWxdxOPirj0w=\",\n \"email\": \"officechris@gmail.com\",\n \"username\": \"\",\n \"password\": \"\",\n \"hashed_password\": \"61865fe0c1f6207323add65470a18760:jrNuz\",\n \"name\": \"officechris\",\n \"vin\": \"\",\n \"address\": \"\",\n \"ip_address\": \"199.60.239.10\",\n \"phone\": \"\",\n \"obtained_from\": \"PlexTV\"\n }\n ],\n \"success\": true,\n \"took\": \"8µs\",\n \"total\": 14\n}" + } + ] + } + }, + "Initialize_LockoutDurationInSeconds": { + "runAfter": { + "Initialize_EnableBannedPasswordCheck": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "LockoutDurationInSeconds", + "type": "string", + "value": "@body('Parse_Password_Settings')['values'][3]['value']" + } + ] + } + }, + "Initialize_LockoutThreshold": { + "runAfter": { + "Initialize_LockoutDurationInSeconds": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "LockoutThreshold", + "type": "string", + "value": "@body('Parse_Password_Settings')['values'][4]['value']" + } + ] + } + }, + "Initialize_Number_of_Passwords": { + "runAfter": { + "Initialize_Found_Password": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "numPasswords", + "type": "integer" + } + ] + } + }, + "Initialize_Passwords": { + "runAfter": { + "Initialize_Number_of_Passwords": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "password", + "type": "string" + } + ] + } + }, + "Initialize_deHashedPasswordArray": { + "runAfter": { + "Initialize_emailAddress": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "dehashedPasswordArray", + "type": "array" + } + ] + } + }, + "Initialize_emailAddress": { + "runAfter": { + "Initialize_JSON_Sample": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "emailAddress", + "type": "string" + } + ] + } + }, + "Parse_Password_Settings": { + "runAfter": { + "Get_Password_Settings": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Get_Password_Settings')", + "schema": { + "properties": { + "@@odata.context": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "templateId": { + "type": "string" + }, + "values": { + "items": { + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "name", + "value" + ], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + } + } + }, + "Parse_Response_from_Key_Vault_-_Dehashed_Password": { + "runAfter": { + "Get_Client_Secret_from_Vault_-_Dehashed": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Get_Client_Secret_from_Vault_-_Dehashed')", + "schema": { + "properties": { + "attributes": { + "properties": { + "created": { + "type": "integer" + }, + "enabled": { + "type": "boolean" + }, + "recoveryLevel": { + "type": "string" + }, + "updated": { + "type": "integer" + } + }, + "type": "object" + }, + "id": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "Parse_Response_from_Key_Vault_-_Graph_API": { + "runAfter": { + "Get_Client_Secret_from_Vault_-_Graph_API": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('Get_Client_Secret_from_Vault_-_Graph_API')", + "schema": { + "properties": { + "attributes": { + "properties": { + "created": { + "type": "integer" + }, + "enabled": { + "type": "boolean" + }, + "recoveryLevel": { + "type": "string" + }, + "updated": { + "type": "integer" + } + }, + "type": "object" + }, + "id": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + } + } + } + }, + "outputs": {} + }, + "parameters": { + "$connections": { + "value": { + "azuresentinel": { + "connectionId": "[resourceId('Microsoft.Web/connections', variables('AzureSentinelConnectionName'))]", + "connectionName": "[variables('AzureSentinelConnectionName')]", + "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/azuresentinel')]" + } + } + } + } + } + } + ] +} diff --git a/Playbooks/Get-CompromisedPasswords/media/bannedpasswords.png b/Playbooks/Get-CompromisedPasswords/media/bannedpasswords.png new file mode 100644 index 0000000000..897ad364ec Binary files /dev/null and b/Playbooks/Get-CompromisedPasswords/media/bannedpasswords.png differ diff --git a/Playbooks/Get-CompromisedPasswords/media/blueprint-mi-edit.png b/Playbooks/Get-CompromisedPasswords/media/blueprint-mi-edit.png new file mode 100644 index 0000000000..93b601738d Binary files /dev/null and b/Playbooks/Get-CompromisedPasswords/media/blueprint-mi-edit.png differ diff --git a/Playbooks/Get-CompromisedPasswords/media/dehashed.png b/Playbooks/Get-CompromisedPasswords/media/dehashed.png new file mode 100644 index 0000000000..76094a43df Binary files /dev/null and b/Playbooks/Get-CompromisedPasswords/media/dehashed.png differ diff --git a/Playbooks/Get-CompromisedPasswords/media/mi-new.png b/Playbooks/Get-CompromisedPasswords/media/mi-new.png new file mode 100644 index 0000000000..8bcea1f19f Binary files /dev/null and b/Playbooks/Get-CompromisedPasswords/media/mi-new.png differ diff --git a/Playbooks/Get-CompromisedPasswords/media/note.png b/Playbooks/Get-CompromisedPasswords/media/note.png new file mode 100644 index 0000000000..9c93c85280 Binary files /dev/null and b/Playbooks/Get-CompromisedPasswords/media/note.png differ diff --git a/Playbooks/Get-CompromisedPasswords/media/parameters.png b/Playbooks/Get-CompromisedPasswords/media/parameters.png new file mode 100644 index 0000000000..6e9f4c9d69 Binary files /dev/null and b/Playbooks/Get-CompromisedPasswords/media/parameters.png differ diff --git a/Playbooks/Get-CompromisedPasswords/media/postman1.png b/Playbooks/Get-CompromisedPasswords/media/postman1.png new file mode 100644 index 0000000000..8545a9e5e3 Binary files /dev/null and b/Playbooks/Get-CompromisedPasswords/media/postman1.png differ diff --git a/Playbooks/Get-CompromisedPasswords/media/postman2.png b/Playbooks/Get-CompromisedPasswords/media/postman2.png new file mode 100644 index 0000000000..42f40dac86 Binary files /dev/null and b/Playbooks/Get-CompromisedPasswords/media/postman2.png differ diff --git a/Playbooks/Get-CompromisedPasswords/readme.md b/Playbooks/Get-CompromisedPasswords/readme.md new file mode 100644 index 0000000000..6e50561dd5 --- /dev/null +++ b/Playbooks/Get-CompromisedPasswords/readme.md @@ -0,0 +1,92 @@ +# Tutorial: Synchronize Compromised Passwords to the Azure AD Banned Password List using the paid DeHashed API + +Intent: As an IT admin, I want to be know which users have publicly posted compromised passwords and I want to ensure these passwords and variations of those passwords are not used in my environment. + +The [Azure AD Banned Password List](https://docs.microsoft.com/azure/active-directory/authentication/concept-password-ban-bad) is designed to support your own business and security needs, you can define entries in a custom banned password list. When users change or reset their passwords, these banned password lists are checked to enforce the use of strong passwords. + +Utilizing the publicly available [DeHashed API](https://www.dehashed.com/pricing) we can query for a specific e-mail address, and if breached passwords are found have them automatically added to the Azure AD Banned Password list to prevent those passwords (and all variations) from being used in the environment. + +[![Note](./media/note.png)](https://docs.microsoft.com/azure/active-directory/authentication/concept-password-ban-bad#custom-banned-password-list) + +## Example: +`The following compromised passwords 7seas:okaydokay:table36072:kachra:p4ssword:apple05:Steve:chipmunk89:Stevencity:Godye were found for steve.jobs@apple.com. These passwords have been succesfully added to the Azure AD Banned Password list.` + + ![Dehashed](./media/dehashed.png) + ![BannedPasswords](./media/bannedpasswords.png) + +## Prerequisites + +If you don't have an Azure subscription, create a [free Azure account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you start. + +This Logic App retrieves API keys from Key Vault and as such you should review and complete the steps in the article [Secure authentication for Conditional Access automation](https://github.com/Azure-Samples/azure-ad-conditional-access-apis/blob/main/00-prereq/readme.md) to create a Key Vault and connect to Managed Identity. + +This Logic App will require Graph API Permissions to [Update a directory setting](https://docs.microsoft.com/graph/api/directorysetting-update?view=graph-rest-beta&tabs=http) - `Directory.ReadWrite.All`in order to update the Azure AD Banned Password list. + +This Logic App will require a paid subscription to the [DeHashed API](https://www.dehashed.com/pricing) which will use your username and password within the Logic App. Store your DeHashed password in an Azure Key Vault that your Managed Identity has access to and note the URI for secret retrieval. + +## Step 1: Deploy this logic app to your organization + +If your Azure environment meets the prerequisites, and you're familiar with using Azure Resource Manager templates, these steps help you sign in directly to Azure and open the Azure Resource Manager template in the Azure portal. For more information, see the article, [Deploy resources with Azure Resource Manager templates and Azure portal](https://docs.microsoft.com/azure/azure-resource-manager/templates/overview). + +Select the following image to sign in with your Azure account and open the logic app in the Azure portal: + + [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FSCStelz%2FAzure-Sentinel%2Fmaster%2FPlaybooks%2FGet-CompromisedPasswords%2Fazuredeploy.json) [![Deploy to Azure Gov](https://aka.ms/deploytoazuregovbutton)](https%3A%2F%2Fraw.githubusercontent.com%2FSCStelz%2FAzure-Sentinel%2Fmaster%2FPlaybooks%2FGet-CompromisedPasswords%2Fazuredeploy.json) + +1. In the portal, on the **Custom deployment** page, enter or select these values: + + | Property | Value | Description | + |----------|-------|-------------| + | **Subscription** | <*Azure-subscription-name*> | The name for the Azure subscription to use | + | **Resource group** | <*Azure-resource-group-name*> | The name for a new or existing Azure resource group | + | **Region** | <*Azure-region-for-all-resources*> | The Azure region to use for all resources, if different from the default value. This example uses the default value which is the resource group location. | + | **Playbook Name**| <*PlaybookName*> | This is the name for the deployed Logic App. | + | **Client ID** | <*ClientID*> | The Client ID of your App Registration with Graph API Permissions.| + | **Tenant ID** | <*TenantID*> | The Azure AD Tenant ID where your App Registration Resides.| + | **Dehashed Username** | <*DeHashed-Username*> | The Username for your Dehashed Subscription.| + | **Key Vault Client Credentials URL-DeHashed-Password** | <*KeyVaultClientCredentialsURL-DeHashed-Password*> | The URL for your DeHashed Password secret from Azure Key Vault| + | **Key Vault Client Credentials URL-GraphAPI** | <*KeyVaultClientCredentialsURL-GraphAPI*> | The URL for your Graph API Key Secret from Azure Key Vault| + +1. When you're done, select **Review + Create** and finally **Create**. + +## Step 2: Authenticate your logic app to Azure AD with the right permissions + +This logic app uses Managed Identity to access secrets from Key Vault to call the Graph API. As a prerequisite you must have completed the steps in the article [Secure authentication for Conditional Access automation](https://github.com/Azure-Samples/azure-ad-conditional-access-apis/blob/main/00-prereq/readme.md) to create a Key Vault and connect to Managed Identity. To learn more about how to use managed identities within Logic Apps, see the article [**Logic Apps and Managed Identities**](https://docs.microsoft.com/azure/logic-apps/create-managed-service-identity). + +1. In the left-hand navigation pane, select Identity > User Assigned > Select Add. + +1. Select the User-assigned managed identity from the context pane that appears on the right, select Add. + + ![Select managed identity](./media/blueprint-mi-edit.png) + +## Step 3: Update parameters + +1. In the left-hand navigation pane, select Logic App designer > Parameters > Ensure all the default values are updated with Key Vault URL's (storing Client Secrets), Client ID and Tenant ID. + + ![Replace defaults with tenant specific values](./media/parameters.png) + +## Step 4: Select appropriate managed identity + +1. On the Logic App Designer, in the HTTP connection box, click `GET client secret from key vault - Graph API`. This example uses HTTP connector. + +2. Specify the Managed Identity to use. + + ![Select "Managed Identity"](./media/mi-new.png) + +Repeat the above for the step `GET client secret from key vault - DeHashed` + +This will give the Managed Identity the ability to retrieve both the Graph API secret and DeHashed password from Azure Key Vault. + +> [!WARNING] +> Ensure you follow best practices regarding managing secrets within Logic apps by using secure inputs and outputs as documented in the article, [Secure access and data in Azure Logic Apps]](https://docs.microsoft.com/azure/logic-apps/logic-apps-securing-a-logic-app). + +## Next steps + +This Logic App can be triggered in response to an Azure Sentinel alert. It will grab all the Account Entities associated with the Alert, retrieve their mail attribute from Azure AD using Graph API, it will check each account against the DeHashed API, if passwords have been found, it will update the Azure AD Banned Password list and will also add a comment to the Azure Sentinel Incident. + +1. Launch Playbook in response to an Azure Sentinel Alert with an associated Azure AD Entity. +2. Wait for the Playbook to complete and check the Incident comments. + +![Postman2](./media/postman2.png) + +7. Review the updates to the Azure AD Banned Password List + ![BannedPasswords](./media/bannedpasswords.png)