Ashish Gupta

This user hasn't shared any biographical information

Homepage: http://ashishrocks.net

Azure Policy – Deny creation of virtual machines without IP restriction across all Azure subscriptions

TLDR;

Public Azure virtual machines without any IP restriction is always an attack vector which may result in compromise of the VM and further lateral movement in Azure infrastructure.
Azure policy may be used to deny any attempt to even create the virtual machines without IP restriction.
This blog post has step-by-step process on how to implement an Azure policy on ALL your subscriptions covering IP restriction for ALL your future virtual machines.

What is Azure policy:

Azure policy is a service inside Azure that allows configuration management.It executes every time a new resource is added or an existing resource is changed. It has a set of rules, and set of actions. The Azure policy could report the event as non-compliant or even deny the action altogether if the rules are not matched.

Azure policy is an excellent way to enforce and bake-in security and compliance in the Azure infrastructure.

Summary:

If the Azure virtual machines need to be accessible over internet, Its important to restrict access its access ONLY from your corporate public IP addresses.
This will help in couple of situations :
a) Limit external access from an attacker.
b) Limit Insider threat or misuse from an employee.
The IP address restriction could be created while creating the virtual machine using network security groups.
However, enforcing this on the policy level by the administrator would ensure we are not dependent on individual team’s best judgment.

Process:

As a best practice, always test the policy in audit mode before switching to deny mode. In this walkthrough, we will follow below steps :

1) Create the policy definition.
2) Apply the policy (Policy Assignment) in audit mode
3) Test with Audit mode
3) Apply the policy (Policy Assignment) in deny mode
4) Test with Deny mode

Create the policy definition

On the search bar, search for “policy” and click on it.

image

Click Definitions and then click Policy Definitionimage

Click the … button under “Definition Location” to select the management group. If you want to apply this policy to all subscriptions, don’t select any subscription.
To apply this policy to a specific subscription, select the desired subscription under the subscription dropdown.

image

Policy Details:

Name:
Deny creation of virtual machine without access restricted only from company’s public IP addresses
(on-prem/VPN)

Description (Change the IP address list below):
Deny creation of virtual machine which does not have external company IP addresses restriction in the network security group.
One or more of the below corporate IP addresses must be specified in the network security group when creating the virtual machine. Otherwise, the validation will fail and the virtual machine will not be created.
Below is the valid public corporate IP addresses list :
208.114.51.253
104.104.51.253
108.104.51.253

Category : Network
image 
Policy Rule:

{
  "mode": "All",
  "policyRule": {
    "if": {
      "allOf": [
        {
          "field": "type",
          "equals": "Microsoft.Network/networkSecurityGroups"
        },
        {
          "count": {
            "field": "Microsoft.Network/networkSecurityGroups/securityRules[*]",
            "where": {
              "allOf": [
                {
                  "anyof": [
                    {
                      "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].sourceAddressPrefix",
                      "notIn": [
                        "208.114.51.253",
                        "104.104.51.253",
                        "108.104.51.253"
                      ]
                    }
                  ]
                }
              ]
            }
          },
          "greater": 0
        }
      ]
    },
    "then": {
      "effect": "[parameters('effect')]"
    }
  },
  "parameters": {
    "effect": {
      "type": "String",
      "metadata": {
        "displayName": "Effect",
        "description": "The effect determines what happens when the policy rule is evaluated to match"
      },
      "allowedValues": [
        "audit",
        "deny"
      ],
      "defaultValue": "audit"
    }
  }
}

Policy Assignment

Under policy > definition, go to the newly created policy definition.
image

Click Assign.

image

Provide an assignment name and description
Name:
Deny creation of virtual machine without access restricted only from company’s public IP addresses
(on-prem/VPN)

Description (Change the IP address list below):
Deny creation of virtual machine which does not have external company IP addresses restriction in the network
security group.
One or more of the below corporate IP addresses must be specified in the network security group when creating the virtual machine. Otherwise, the validation will fail and the virtual machine will not be created.
Below is the valid public corporate IP addresses list :
208.114.51.253
104.104.51.253
108.104.51.253

image

Under “Parameters” tab, select “audit” in the Effect dropdown and click “Review+Create”

image

On the review page, click “Create” .

image

The policy assignment is created. Please note It takes about 30 minutes to take effect.

image

 

Test 1 – Audit mode :
Create virtual machine with RDP allowed from any external IP Address

With the policy in Audit mode, let us create a new virtual machine with RDP open to any external IP address.

image

image

image
When the policy is in the audit mode, the virtual machine creation is successful but Azure policy adds a Microsoft.Authorization/policies/audit/action operation to the activity log and marks the resource as non-compliant.

Activity Logsimage

Compliance State:
Policy > Compliance
image

Test 2 – Deny mode
Create virtual machine with RDP allowed from any external IP Address

We need to change the effect mode to “deny” in our policy assignment.
Head over to Policy > Assignments > Click on the policy we created

image
Click “Parameters” tab. Select “deny” from the dropdown and continue to save the policy assignment.

image

Attempt to create a virtual machine with the same settings as we did before.
image

When you proceed to create the virtual machine, the final validation will fail with an error message (left side) which when clicked will show which policy disallowed this action.

image

Clicking on the policy would show the policy assignment with details showing why the policy disallowed this action.

image

When the policy is in the deny mode, the virtual machine creation is successful but Azure policy adds a Microsoft.Authorization/policies/deny/action operation to the activity log and marks the resource as non-compliant.

Under activity logs, you can see the deny action.:
image

image

Summary :

Azure policy is an excellent way of enforcing compliance in Azure infrastructure. In this blog post we saw how we can apply Azure policy to deny creation of virtual machines without any IP restriction.
For further readings :
Azure policy docs : https://docs.microsoft.com/en-us/azure/governance/policy/overview
Azure policy Github : https://github.com/Azure/azure-policy

, , , ,

Leave a comment

Detection of identity-based risks using Azure AD Identity Protection and Graph API

Github repository : https://github.com/ashishmgupta/AzureADIdentityProtection

image

What is Azure AD Identity Protection?
Identity Protection is a tool in Azure AD that allows organizations to accomplish three key tasks:

  • Automate the detection and remediation of identity-based risks.
  • Investigate risks using data in the portal.
  • Export risk detection data to third-party utilities for further analysis.

Identity Protection identifies risks in the following classifications:

Risk Detection Type Description
Atypical travel Sign in from an atypical location based on the user’s recent sign-ins.
Anonymous IP address Sign in from an anonymous IP address (for example: Tor browser, anonymizer VPNs).
Unfamiliar sign-in properties Sign in with properties we‘ve not seen recently for the given user.
Malware linked IP address Sign in from a malware linked IP address
Leaked Credentials This risk detection indicates that the user’s valid credentials have been leaked
Azure AD threat intelligence Microsoft’s internal and external threat intelligence sources have identified a known attack pattern

Source :https://docs.microsoft.com/en-us/azure/active-directory/identity-protection/overview-identity-protection

Note : Azure AD Identity Protection is fully available in the Azure AD Premium P2 only.

In this blog post, we will focus on detection of the above identity based risks.

There are two steps :
1) From Azure Portal, setup the Application (with a token) with configured permissions to read data from Identity Protection.
2) Python script to use Graph API with the above OAuth token to access the Identity protection data to ingest in your SIEM tool.

Setting up the application in the Azure portal
Azure Active Directory > App Registrations > New Registration

image

Give the application a name.

image

Add API permissions.
API Permission > Add a permission

image

Click Microsoft Graph

image

Since we want the logs from an application instead for a user, select Application permission.

image

Now, there are three API and we need specific permissions for them to access data via Graph API.

API Name Details Permission Needed
Sign Ins Allows query Graph API for information on Azure AD sign-ins with specific properties related to risk state, details and level AuditLog.ReadAll
Directory.ReadAll
Risky Users Gets users identified by identity protection as risky users. IdentityRiskyUser.ReadAll
Risk Detections Gets both risky users and sign-in linked risk detections and associated information identityRiskEvents.ReadAll

Above API permissions need to be set under Microsoft Graph as shown below.

image image
image image

The Global Administrator of your tenant needs to grant admin consent for the permissions you added.
You should contact them for this and get the consent granted.

image

Create a new client secret.

Certificate and Secrets > New Client Secret

image

image

A secret is automatically generated and can be copied.

image

Python script to use Graph API to pull Identity Protection data
Below is the screenshot of of a section of the python code which uses the ClientId, Client Secret and the tenant domain to get the OAuth token and then uses the OAuth token to query the Microsoft Graph API to get the identity protection data in the JSON format for both risky users and risky detection.

Full source code is located here :
https://github.com/ashishmgupta/AzureADIdentityProtection

The code also retries in case of the number of requests crosses the threshold (HTTP 429 Too many requests).

image

Hope this post helps you implementing and querying the Azure Identity Protection data in your organization.
Please feel free to ask questions in the comments sections below.

, , ,

Leave a comment

Azure Sentinel – Detecting brute force RDP attempts

Azure Sentinel is a cloud based SIEM* and SOAR** solution.
As it’s still in preview, I wanted to test out few of Its capabilities.
In this post we will see how we can detect RDP brute-force attempts and respond using automated playbooks in Azure Sentinel.
[*SIEM : Security Incident Event Management]
[**SOAR : Security Orchestration Automated Response]

image
https://docs.microsoft.com/en-us/azure/sentinel/overview

The infrastructure:

I have couple of virtual machines in Azure which have RDP opened (sure, I am the first one to keep that opened) 🙂 Below is one of the Win 2012 machine.

image

The Attack:

Attackers always the scan the whole CIDR to find the services running on the machines in the range. In this example, simulating the scan, I will use only one machine ( the above one) from the Kali VM looking if the RDP (port 3389) is opened.

nmap -p 3389 IPAddress –Pn

image

For brute-force, we will use crowbar.
Clone the repository:
git clone https://github.com/galkan/crowbar.git
image

I have separate files for usernames(userlist) and passwords(passwordlist) which will be used by Crowbar in combination to attempt to login to the above machine via RDP.

python crowbar.py -b rdp -s ipaddress -U userlist -C passwordlist –v
-b indicates target service. In this case Its rdp but crowbar also supports openvpn, sskkey and vnckey.
-v indicates verbose output

You see the combination which has “RDP-SUCCESS” is the right combination of user name and password which was brute-forced for successful login via RDP. Other attempts failed. Of course, I have the right user name and password in the file. 🙂
 
image

Azure Sentinel

Now lets get to Azure Sentinel. As noted above, Its a cloud based SIEM.
You can quickly locate “Azure Sentinel” from the search bar.
image

Sentinel manages all Its data in a log analytics workspace. If you already have one, you can reuse or create a new one.

image

One of the first thing you notice in Azure Sentinel is a number of in-built Data Connectors available to collect data from different sources. Not only that includes Azure native data sources such as Azure AD, Office 365, Security center to name a few but also third parties like Palo Alto, Cisco ASA, Checkpoint, Fortinet and F5.
Pretty sure the list will only get longer.

For the purpose of this blog post, we will focus on the “Security Events” by clicking on “Configure”.

image

Select “All events”.
Click on “Download install Agent for Windows Virtual machines”.
Select the Virtual machine where the agent will be installed.
Click “Connect”.
The “Connect” process takes few minutes to complete.

image

image

image

When the machine is shows “Connected” in Azure portal, you will see the Microsoft Monitoring Agent (MMA) service running on the machine which will upload the logs to the Azure sentinel workspace for the subscription.

image

Start writing some queries

Azure Sentinel uses Kusto Query Language for read-only requests to process data and return results.
In the sentinel workspace, click on “Logs” and use the below query which is basically looking for security events with successful login event (EventId 4624) and unsuccessful login event (EventId 4625) originating from a workstation named “kali”.
Note the highlighted event was the only successful attempt(EventId 4624) and rest were failures (4525).

SecurityEvent
| where (EventID == 4625 or EventID== 4624) and WorkstationName == “kali”
| project TimeGenerated, EventID , WorkstationName,Computer, Account , LogonTypeName , IpAddress
| order by TimeGenerated desc

image

image

Creating Alerts

Create an alert for the above use case by clicking “Analytics” > Add

image

Give a name to the alert, provide a description. and set the severity.

image

Set the alert query to detect any RDP login failure:

SecurityEvent
| where EventID == 4625
| project TimeGenerated, WorkstationName,Computer, Account , LogonTypeName , IpAddress
| order by TimeGenerated desc

image

Set the entity mapping. These properties will be populated from the projected fields in the query above.
Will be very useful information when we build playbooks. As you can see, there are only three properties which could be mapped at this point but more to come.

In this example, Account Name used for the attempted login, the host where It is being tried on and the workstation where It is tried from will be populated.

image

Playbook

Playbooks in Azure Sentinel are basically Logic apps which is really powerful not only because of the inbuilt templates but also because they can be heavily customized.

image

Sorry, I just wanted to remind myself again and you, dear reader that logic apps are really powerful. 🙂

image

Create the logic app:

image

In the designer, click on “Blank Logic App”

image

We first need to define the trigger. In this case It would be when the response to an alert is triggered in Azure Sentinel.
Search “Sentinel” in the textbox and you will find the trigger. Click on the trigger and the trigger will be added as a first step.

image

We will send an email to respective team (e.g. Security Operations) when this event happens. In this case I am sending the email to my Office 365 email address.

image

You will need an Office 365 tenant(sign up for free trial here) to send email.
In the below example, I already one and connected. If I didn’t, all I had to do is to sign-in with my admin office-365 account and connection would be available to send emails.

As you click through the subject and body, you will be prompted to select the Dynamic contents which will have relevant data in this case.

image

image

Cases

When an alert fires, I creates a case and you can execute the relevant playbook for the case.
In this example, we have a alert configured named “rdp-bruce-force attempt-alert”.
Every time that alert fires, I will create a new case with the same name as the alert with a case Id.
We can then execute the relevant playbook on the case. In this example, we will execute the playbook we created before “rdp-bruce-force attempt-alert-playbook”.

In the Sentinel workspace, click on “Cases” to review all the cases and click on the case which got created for the brute-force attempt.

image

At the bottom the details pane of the case, click on the “View full details”.image

Click “View Playbooks”

image

Click on “Run” for the playbook we want to execute.

image

Below is the email as a part of the playbook I got with the account names in the security event logs.

image

Hope this helps! 🙂

, ,

1 Comment

Identify Critical Assets in your environment using F5 Load balancer

Identifying the servers hosting critical applications in your environment is crucial so that alerts for unusual events on those servers are put on higher priority for your security operations team.

One of the approaches we can take to identify the critical assets is by leveraging the load balancer. This could be a head start to build a mini-CMDB (Configuration Management Database) for assets for your sec ops team.

Below is an over-simplified example of network architecture showing a critical web app named “example.com” web app hosted by 5 servers which are load balanced on F5 (VIP : 172.22.23.11). Out of the 5 servers, only 3 servers are active.

In this example, the goal is to get the active servers behind the VIP.

image

I wrote a PowerShell script to get all the active servers behind all the active VIPs on the a given load balancer.

Why this approach?

  1. If the server is hosting an app which is critical, It has to be load balanced.
  2. If a new server is added to an existing VIP, this script will get it.
  3. If a server is decommissioned, It would be inactive to the load balancer and therefore the script will ignore it.

If the script is scheduled to run periodically, we will have an up-to-date list of servers which are running critical applications. That list can be integrated with SIEM to prioritize alerts from the those servers.

The script 

The PowerShell script makes use of PowerShell cmdlet for F5 which can be downloaded from the below location.
https://devcentral.f5.com/d/microsoft-powershell-with-icontrol?download=true
The downloaded file is a .zip file. You copy to your local folder, unzip it and run .\setupSnapin.ps1 which is unzipped file. You may get an error :

Could not load file or assembly iControlSnapin.dll or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x8013515
image

Make sure the “Unblock” is checked from all the files in the unzipped file including “setupSnapin.ps1”

image

.\setupSnapin.ps1 should work fine now.

image

Below is the script. You will need to change the F5 IP address and the partition name where the virtual servers reside. The output of the script is saved to a file named “ServerList.csv”

Add-PSSnapIn iControlSnapIn

function GetPoolMembers($poolname,$virtual_server_name_only,$virtual_ip)
{

	$poolmembers = $ic.LocalLBPool.get_member_v2(@($poolname))
	
	$test = $poolmembers[0]
	write-output($poolname)
	write-output ('Backend servers and ports :')
	$member_status = $ic.LocalLBPool.get_member_object_status($poolname,$poolmembers)
	
	$node_index = 0
	foreach($poolmember in $test)
	{
		$availability_status = $member_status[0][$node_index].availability_status
		if($availability_status -eq "AVAILABILITY_STATUS_GREEN")
		{
			$ip_address = $poolmember[0].address.replace($active_folder,"").replace("/","")
			write-output($ip_address )
			$global:server_node_details += $ip_address+","+$virtual_server_name_only + "," + $virtual_ip +"`n"
			write-output($global:server_node_details)
			
		}
		$node_index = $node_index + 1
	}
	
	write-output $global:server_node_details
}

$global:server_node_details = "sep=,"+"`n"
$global:server_node_details += "Server IP,F5 Virtual IP,F5 Virtual Server Name" +"`n"

$connection = Initialize-F5.iControl -Hostname <Your F5 IP Address> -Credentials (Get-Credential)
$ic = Get-F5.iControl


# Set the active folder aka partitions where you know the virtul servers exist
$active_folder = "/YourPartitionName/"
$ic.SystemSession.set_active_folder($active_folder)

# get list of all the virtual servers 
$virtual_server_list = $ic.LocalLBVirtualServer.get_list()
$virtual_server_with_server_side_profile = @()
foreach($virtualserver in $virtual_server_list)
{
	
	$object_status = $ic.LocalLBVirtualServer.get_object_status($virtualserver).availability_status
	
	if($object_status -eq "AVAILABILITY_STATUS_GREEN")
	{
		$virtual_server_name_only = $virtualserver.replace($active_folder,"")
		write-output ('Virtual server name ' + $virtualserver)
		write-output ($object_status)
		$addresses = $ic.LocalLBVirtualServer.get_destination_v2($virtualserver)
		write-output($addresses.Length)
		$virtual_ip = $addresses[0].address.replace($active_folder,"")
		write-output('Virtual IP address : ' + $virtual_ip )
		$pool_name = $ic.LocalLBVirtualServer.get_default_pool_name($virtualserver)
		GetPoolMembers $pool_name $virtual_ip $virtual_server_name_only
	}
	
}

$global:server_node_details | Out-File -FilePath .\ServerList.csv

The output
The script output has 3 columns for each server in with Its Virtual IP and Virtual server name.
Virtual server name is an identifier for the Virtual IP address on the load balancer. That name provides an indication of what that server is used for. This approach groups servers by their Virtual server name and saves identifying each server for what it is used for.

In the below hypothetical example for the output, servers 172.22.1.1, 172.22.1.2 and 172.22.1.3 running example.com web app.

image

, , ,

Leave a comment

Compromising a Azure Windows 2008 R2 SP1 VM

TL;DR (Too long Didn’t Read)
If you stand up a windows 2008 R2 VM in Azure with a random user name and password, Its very easy to know that user name and depending on the complexity of the chosen password, It may be feasible to brute-force the VM using RDP.

Below is the process we will follow :

1) As a victim, set up the Azure VM and RDP to it
2) As an attacker, determine the user name for the Azure VM.
3) As an attacker, determine the password for the Azure VM.
4) Simple steps to defend against the attack.

As a victim, set up the Azure VM and RDP to it

Log in to the Azure portal and create the VM

Choose Windows 2008 R2 SP1

image

image

Machine Name : Test-Machine-1
Username : SomeRandomName
Password: Choose a password.
Resource Group : Select an existing or just create a new one.

image

Be cheap and select the lowest cost disk from the list.

image

Select the RDP from the public inbound port

image

Finally click on the “Create” button to create the VM

image

Once the VM is created and running (you will see a notification in the portal), you should see the VM in the list with “Running” status.

image

Click on the “Test-Machine-1” VM entry in the above list and then click on the “Connect” button.

image

Click on the “Download RDP File” to download the RDP file for the VM.

image

Open the RDP file for the VM and enter the credential you used while setting up the VM.

image

and now we are in the VM

image

As an attacker, determine the user name for the Azure VM

There is a easier way to determine the user name rather than brute-forcing the VM.
If the victim is logged in to the VM, the attacker can use something like rdpy-rdpscreenshot.py which is one of the fantastic binaries in rdppy by Sylvain Peyrefitte (@citronneur) which allows to take the screenshot of the login screen.

See in the below screenshot. It connected to the IP address 40.76.88.172 and saved the screenshot to c:\temp\test_40.76.88.172.jpg.
python rdpy-rdpscreenshot.py -o c:\temp\test_ 40.76.88.172:3389

image

Here is the file with screenshot of the login screens showing the user name for the machine.

image

This was possible because Windows 2008 R2 supports Network Level Authentication – which completes the user authentication before you establish a RDP connection and the logon screen appears.

As an attacker, determine the password for the Azure VM

You can use a RDP brute-forcing utility such as Hydra [https://github.com/maaaaz/thc-hydra-windows] to determine the password for the user from a password list.

hydra -C “C:\temp\login-password-list.txt” 40.76.88.172 rdp

image

 

Simple steps to defend against the attack

When you set up the VM and setting up the RDP for it, It warns you about RDP will be exposed to the internet.
So, you should limit access to the RDP from your/your organization’s public IP address/CIDR only through the network security group changes for RDP.

VM Settings > Networking> RDP > Source IP addresses/CIDR ranges

image

image

Now with the source IP restriction, rdpy-rdpscreenshot.py from a different IP wont be able to reach the VM to take the screenshot of the login screen.

image

, ,

Leave a comment

Won the challenge coin! SANS FOR500 – Windows Forensics Analysis

Feel very privileged to be part of that group!

https://twitter.com/robtlee/status/959902237668540416

image

ext

ext (1)

,

Leave a comment

GCIH Certification

Passed GCIH (GIAC Certified Incident Handler).This was a tough one.

Leave a comment

F5 iRules : Setting the HttpOnly flag on a HTTP cookie

HttpOnly flag on a cookie prevents the client side code to access the cookie. More details here.

If you set this flag on the cookie in the HTTP_RESPONSE in the iRule, you get the below error :

when HTTP_RESPONSE {
HTTP::cookie insert name “UserName” value “john.doe” path “/” domain “xyz.com”
HTTP::cookie httponly “UserName” enable
}

Improper version invoked from within “HTTP::cookie httponly “UserName” enable”

If you create a cookie using the insert method the cookie is created with version 0. If you try to change Its version to 1 to avoid the above error, you will get another error.

when HTTP_RESPONSE {
HTTP::cookie insert name “UserName” value “john.doe” path “/” domain “xyz.com”
HTTP::cookie version “UserName” 1
HTTP::cookie httponly “UserName” enable
}

Illegal argument (line x) invoked from within “HTTP::cookie version “UserName” “1”

The right way to add the httponly flag to a cookie is to specify the version while you are inserting it and then set the httponly flag.

when HTTP_RESPONSE {
HTTP::cookie insert name “UserName” value “john.doe” path “/” domain “xyz.com” version 1
HTTP::cookie version “UserName” 1
HTTP::cookie httponly “UserName” enable
}

Testing

If you have a chrome extension like EditThisCookie which can let you view all the cookies for the web app, you can notice the HttpOnly flag checked for the cookie.

image

 

With httponly not enabled on the cookie, the cookie can be accessed via the client side script document.cookie

image

With httponly not enabled on the cookie, the cookie can NOT be accessed via the client side script document.cookie.

image

, ,

Leave a comment

PowerShell Transcription and script bulk logging

PowerShell is indeed command prompt on steroids. The dev and IT productivity can be multifolded using PowerShell. Having said that, PowerShell can be used to execute malicious commands on the host machine.

Fortunately, the group policy allows to not only transcribe every PowerShell command on the host machine but also log the WHOLE PowerShell script (every line of it) executed as such or using by other means e.g. using System.Management.Automation to invoke PowerShell commands. The logs then can be ingested into a SIEM for monitoring and alerting.

PowerShell Transcription
Below screenshot shows that If you enable the PowerShell Transcription and specify a log location, any PowerShell command you execute will be transcribed in a file in the specified location.

clip_image001

Script bulk logging
Below screenshot shows If you enable PowerShell script bulk logging, any script you execute will be logged in the event viewer [Application and Services Logs > Microsoft > Windows > PowerShell > Operational]

clip_image002

Logging for the Powershell script executed using C# (System.Management.Automation)
The below C# code is using System.Management.Automation to execute the PowerShell script. The script was compiled and executed on the host machine. The PowerShell script which got executed from this C# app was logged in the event viewer [Application and Services Logs > Microsoft > Windows > PowerShell > Operational].

image

,

Leave a comment

SP-Initiated Single Sign On using SAML 2.0–Architecture and a simple implementation

Identity Provider (idP) : Party which authenticates the user
Service Provider(SP/RP) : Party which provide a resource/service to the user. Also called a relying party.

Below are the high level steps and I will expand on them later in this article :-

  1. User tries to access a resource on the SP website
  2. SP saves the requested resource URL, sees there user does not have a session with them. It then sends the user (with a SAMLRequest) back to the idP it trusts to authenticate.
  3. idP checks if the user has a session with them and If not challenges the user to log in.
  4. Upon successful login, idP sends the user to the SP with a SAML assertion.
  5. SP verifies the SAML assertion, creates a session for the user and lets the user access the resource.

[Click on the below image to enlarge]

image

1. User tries to access a resource on the SP website
This step is simple. User access a link on the SP.
GET http://serviceprovider.com/serviceproviderweb/Folder1/Folder2/Folder3/DeeplyPlacedFile.asp?id=1 HTTP/1.1
Typically each SP website page user accesses directly (e.g. DeeplyPlacedFile.asp) would check If user has an existing session (e.g. has the required valid SP auth cookie), It will let us user stay in the page the SSO flow ends here. If the user does not have an active session next steps (starting from 2) ) below will follow.

Typically, SP website have cookie check in each page via shared lib. In the example below Its an ASP include file which can be used in all the ASP pages within the SP website.

Note the line :-
Response.Redirect “https://federationengine.serviceprovider.com/sp/startSSO.ping?partnerIdpId=https://federationengine.identityprovider.com&TargetResource=”&url
The above line indicates there is an PingFederate idP connection (with the name ‘https://federationengine.identityprovider.com’) on the service provider federation engine.
This will create a AuthNRequest (SAMLRequest) and will port to the SSO URL on the idp side.
Also note that the the requested Url is also tagged along with the TargetResource querystring. This will make sure the idP Connection would create a relaystate token and will map the same to the requested URL. (more details on this later)

<%
Dim protocol
Dim domainName
Dim fileName
Dim queryString
Dim url

protocol = “http”
If lcase(request.ServerVariables(“HTTPS”))<> “off” Then
protocol = “https”
End If

domainName= Request.ServerVariables(“SERVER_NAME”)
fileName= Request.ServerVariables(“SCRIPT_NAME”)
queryString= Request.ServerVariables(“QUERY_STRING”)

url = protocol & “://” & domainName & fileName & “?” & queryString

cookieValue = Request.Cookies(“ServiceProviderAuthCookie”)
if len(cookieValue) < 1  Then

Response.Redirect https://federationengine.serviceprovider.com/sp/startSSO.ping?partnerIdpId=https://federationengine.identityprovider.com&TargetResource=”&url

end if

%>
2. SP asks the idP to authenticate the user
If the SP’s federation server determines there is no active session for the user and user is coming for the first time, the federation server constructs an SAML Authentication request also called “SAMLRequest” and sends the base64 version of it to the idP federation server SSO URL.
It also creates an identifier (as a RelayState), saves along with the originally requested URL as a key-value pair. This is done so that when the idP comes with the same identifier, It can retrieve the originally requested URL and can just redirect the user. This way, there is no need of passing the URL around.

If the authentication request is sent via POST, there are the SAMLRequest and RelayState are sent in hidden fields,
SAMLRequest: PHNhbWxwOkF1dGhuUmVxdWVzdCBWZXJzaW9uPSIyLjAiIElEPSJXcWhtdmZVUDQwOXl6
Sm10cGxDbjdSUkVOX3oiIElzc3VlSW5zdGFudD0iMjAxNi0wMy0wNFQwMTowNjoyNy44MDhaIiB4bWxuczpzY
W1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIj48c2FtbDpJc3N1ZXIgeG1sbnM6c2Ft
bD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+c2FtbDIwLmxwbC5jb208L3NhbWw6SXN
zdWVyPjxzYW1scDpOYW1lSURQb2xpY3kgQWxsb3dDcmVhdGU9InRydWUiLz48L3NhbWxwOkF1dGhuUmVxdWVzdD4=
RelayState: FMCbXM9vrgFI0jT5ZvqC4hieR3JVT4

Below is the Xml representation of the SAMLRequest :-
<samlp:AuthnRequest Version=”2.0″
ID=”WqhmvfUP409yzJmtplCn7RREN_z”
IssueInstant=”2016-03-04T01:06:27.808Z”
xmlns:samlp=”urn:oasis:names:tc:SAML:2.0:protocol”>
<saml:Issuer xmlns:saml=”urn:oasis:names:tc:SAML:2.0:assertion”>http://www.serviceprovider.com</saml:Issuer>
<samlp:NameIDPolicy AllowCreate=”true” />
</samlp:AuthnRequest>

Notice the highlighted issuer id. This is the entityid of the service provider. This is needed by the identity provider to determine which party It needs to issue the SAMLResponse to.

3. idP checks if the user has a session with them and If not challenges the user to log in
idP Federation server, upon getting the authentication request, initializes the appropriate connection configuration for the service provider (identified by the SP issuer id sent in the previous step). The configured authentication adapter in teh connection is invoked and user is challenged.

4. idP sends SAML assertion to the SP Federation engine
Upon successful user login, the server issues the SAMLResponse. This SAMLResponse is signed with the idP’s private certificate and other attributes needed for the SP to create the session for the user.
If the authentication request is sent via POST, there are the SAMLRequest and RelayState are sent in hidden fields. Notice, Its the RelayState hidden field has the same value SP passed to the idp in step 2).
SAMLResponse: PHNhbWxwOlJlc3BvbnNlIFZlcnNpb249IjIuMCIgSUQ9IldJcHlvTnVrRkdZQ3Q3b0FuQ1JnOHpFUG9V
QiIgSXNzdWVJbnN0YW50PSIyMDE2LTAzLTA0VDAxOjA2OjE2LjczOFoiIEluUmVzcG9uc2VUbz0iV3FobXZ
mVVA0MDl5ekptdHBsQ243UlJFTl96IiB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6M
RelayState: FMCbXM9vrgFI0jT5ZvqC4hieR3JVT4

XML respresentation of SAMLResponse below :-
Key points here are :-
a) The issuer id “www.identityprovider.com” is the identity provider’s entity id.
b) The SAML is issued to the entity id “www.serviceprovider.com” of the service provider.
c) SAML is posted to the Assertion Consumer Service (ACS) URL https://www.serviceprovider.com/sp/ACS.saml2 of the service provider.
<samlp:Response Version=”2.0″
ID=”WIpyoNukFGYCt7oAnCRg8zEPoUB”
IssueInstant=”2016-03-04T01:06:16.738Z”
InResponseTo=”WqhmvfUP409yzJmtplCn7RREN_z”
xmlns:samlp=”urn:oasis:names:tc:SAML:2.0:protocol”
>
<saml:Issuer xmlns:saml=”urn:oasis:names:tc:SAML:2.0:assertion”>www.identityprovider.com</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value=”urn:oasis:names:tc:SAML:2.0:status:Success” />
</samlp:Status>
<saml:Assertion ID=”fciE1SgZ2WX-duiqbwBiuvZj9XT”
IssueInstant=”2016-03-04T01:06:17.050Z”
Version=”2.0″
xmlns:saml=”urn:oasis:names:tc:SAML:2.0:assertion”
>
<saml:Issuer>www.identityprovider.com</saml:Issuer>
<ds:Signature xmlns:ds=”http://www.w3.org/2000/09/xmldsig#”&gt;
<ds:SignatureValue>
SM2NhtM3jExOy/RDnCMWc/RKnOfsOs2nr3o9JQMjgdT/g16mcxEA6c+1jRPl8imlhNFk48t6obmN
</ds:SignatureValue>
</ds:Signature>
<saml:Subject>
<saml:NameID Format=”urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified”>48DEDA35890E4746A134920E70844E6A</saml:NameID>
<saml:SubjectConfirmation Method=”urn:oasis:names:tc:SAML:2.0:cm:bearer”>
<saml:SubjectConfirmationData Recipient=”https://www.serviceprovider.com/sp/ACS.saml2&#8243;
NotOnOrAfter=”2016-03-04T01:11:17.050Z”
InResponseTo=”WqhmvfUP409yzJmtplCn7RREN_z”
/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore=”2016-03-04T01:01:17.050Z”
NotOnOrAfter=”2016-03-04T01:11:17.050Z”
>
<saml:AudienceRestriction>
<saml:Audience>www.serviceprovider.com</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement SessionIndex=”fciE1SgZ2WX-duiqbwBiuvZj9XT”
AuthnInstant=”2016-03-04T01:06:17.050Z”
>
</saml:AttributeStatement>
</saml:Assertion>
</samlp:Response>

5. SP federation engine validates the SAML assertion and redirects to SP portal
SP federation engine verifies the signature on the SAML with the publice cert (previously sent offline) and if the verification is successful, creates the session (e.g. via a cookie).
The RelayState token also comes as part of the POST request. It also pulls up the originally requested URL mapped with that token and redirects the user to the the URL.

, ,

4 Comments

Random Thoughts

The World as I see it

Simple Programmer

Making The Complex Simple

Ionic Solutions

Random thoughts on software construction, design patterns and optimization.

Long (Way) Off

A tragic's view from the cricket hinterlands