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]
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.
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
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. 🙂
Sentinel manages all Its data in a log analytics workspace. If you already have one, you can reuse or create a new one.
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”.
Select “All events”.
Click on “Download install Agent for Windows Virtual machines”.
Select the Virtual machine where the agent will be installed.
The “Connect” process takes few minutes to complete.
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.
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).
| where (EventID == 4625 or EventID== 4624) and WorkstationName == “kali”
| project TimeGenerated, EventID , WorkstationName,Computer, Account , LogonTypeName , IpAddress
| order by TimeGenerated desc
Create an alert for the above use case by clicking “Analytics” > Add
Give a name to the alert, provide a description. and set the severity.
Set the alert query to detect any RDP login failure:
| where EventID == 4625
| project TimeGenerated, WorkstationName,Computer, Account , LogonTypeName , IpAddress
| order by TimeGenerated desc
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.
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.
Sorry, I just wanted to remind myself again and you, dear reader that logic apps are really powerful. 🙂
Create the logic app:
In the designer, click on “Blank Logic App”
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.
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.
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.
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.
At the bottom the details pane of the case, click on the “View full details”.
Click “View Playbooks”
Click on “Run” for the playbook we want to execute.
Below is the email as a part of the playbook I got with the account names in the security event logs.
Hope this helps! 🙂
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
Machine Name : Test-Machine-1
Username : SomeRandomName
Password: Choose a password.
Resource Group : Select an existing or just create a new one.
Be cheap and select the lowest cost disk from the list.
Select the RDP from the public inbound port
Finally click on the “Create” button to create the VM
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.
Click on the “Test-Machine-1” VM entry in the above list and then click on the “Connect” button.
Click on the “Download RDP File” to download the RDP file for the VM.
Open the RDP file for the VM and enter the credential you used while setting up the VM.
and now we are in the VM
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 188.8.131.52 and saved the screenshot to c:\temp\test_184.108.40.206.jpg.
python rdpy-rdpscreenshot.py -o c:\temp\test_ 220.127.116.11:3389
Here is the file with screenshot of the login screens showing the user name for the machine.
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” 18.104.22.168 rdp
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
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.
Feel very privileged to be part of that group!
Passed GCIH (GIAC Certified Incident Handler).This was a tough one.
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.
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.
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]
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].
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 :-
- User tries to access a resource on the SP website
- 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.
- idP checks if the user has a session with them and If not challenges the user to log in.
- Upon successful login, idP sends the user to the SP with a SAML assertion.
- 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]
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 :-
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)
protocol = “http”
If lcase(request.ServerVariables(“HTTPS”))<> “off” Then
protocol = “https”
url = protocol & “://” & domainName & fileName & “?” & queryString
cookieValue = Request.Cookies(“ServiceProviderAuthCookie”)
if len(cookieValue) < 1 Then
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,
Below is the Xml representation of the SAMLRequest :-
<samlp:NameIDPolicy AllowCreate=”true” />
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).
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:StatusCode Value=”urn:oasis:names:tc:SAML:2.0:status:Success” />
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.
Apparently if you have a Java 7 app, and if the app connects to a HTTPS endpoint, TLS 1.0 is used by default with a weak cipher suite ECDHE-RSA-DES-CBC3-SHA.
Interestingly, Java 7 does support TLS 1.2 but not enabled by default. So now, If the company managing the HTTPS endpoint decide to disable TLS 1.0 for better security, the client java7 app wont be able to connect to it, because It will use TLS v1.0.
Fortunately, there is a very simple way to make a change from TLS v1.0 to v1.2. Changes below are for a Windows 2012 Tomcat hosted Java 7 app.
1) Stop the Tomcat windows service.
2) Open Tomcat configuration panel (should be listed as “Configure Tomcat” in the start menu).
3) Go to Java tab > Java Options > Add the below lines at the end (screenshot below). Note you may add as many as ciphers as supported by the HTTPS endpoint you are connecting to.
4) Start the Tomcat windows service.
By the way, there are a number of supported cipher suites by Java 7 on TLS v1.2.
The supported ciphers can be added as a comma separated list in the cipher suites options.