Why Defender Boys decided to build Invoke-DBDefenderPerformance powershell tool
When you run into performance issues on a Windows endpoint, Microsoft Defender is sometimes part of the conversation.
So why does this happen?
It usually falls back to the fact that modern environments are messy:
legacy applications
developer tooling
self-updating binaries
temporary execution paths
unsigned components
The Reality of Modern Endpoint Protection
Microsoft Defender today is far more than traditional antivirus.
It includes:
real-time protection
behavioral monitoring
Endpoint Detection & Response (EDR, and if you are looking at all workloads such as identity, data etc we have XDR)
script and memory inspection
integration with cloud intelligence
All of this is designed to handle both:
modern threats
and older, less predictable software
That combination is important. Because in many environments, the biggest performance impact doesn’t come from malware – it comes from:
complex applications
heavy file churn
development workloads
installers and updaters
or software that doesn’t follow modern best practices
Where Things Get Difficult
From a troubleshooting perspective, this creates a challenge.
You might see:
CPU spikes in MsMpEng.exe
activity from MsSense.exe
delays during file operations
But that doesn’t tell you:
which files are being scanned
why they are being scanned
which process triggered the activity
or how expensive the scans actually are
Without that visibility, it’s easy to fall into guesswork.
What Microsoft Provides
Microsoft provides built-in tooling for this:
New-MpPerformanceRecording
Get-MpPerformanceReport
Which allow you to capture:
detailed scan activity
durations
top files and processes
scan reasons (like EDRSensor)
The data is there but the format is not very easy to interpret for everyone
timestamps are not human-readable
durations are in ticks
output is deeply nested
there’s no prioritization
So while everything you need is technically there, it still takes effort to interpret.
Bridging the Gap
That’s where these functions come in:
Invoke-DBDefenderPerformance
Convert-DBDefenderPerformanceReport
The idea is simple, take Defender’s detailed trace data and make it usable for real-world analysis. Instead of manually parsing JSON, you get:
normalized timings (milliseconds instead of ticks)
clear overview of scan activity
grouped insights by:
scan reason
file type
path
visibility into what’s actually consuming time
This makes it easier to answer questions like:
Is this driven by AV scanning or EDR activity?
Are specific file types more expensive?
Are certain paths generating excessive activity?
Is this expected behavior for the workload?
Why This Matters
In many cases, Defender is working exactly as intended but it has to operate in environments where:
software may be outdated
files change frequently
large numbers of small files are accessed
or applications behave in ways that trigger deeper inspection
Without proper analysis, it’s easy to jump straight to exclusions. That approach can work – but could introduce risks.
A better approach is:
Understand what’s happening
Identify the actual cost drivers
Decide whether tuning is necessary
A typical trace might show:
OnDemandScan activity
Reason: EDRSensor
Files under:
AppData\Local\Programs\Microsoft VS Code
node_modules
Process-based scans (pid:xxxxx)
Defender is simply doing its job – inspecting what could be risky.
The Goal
The purpose of these functions isn’t to reduce protection. It’s to provide clarity. Because once you can see:
what is being scanned
why it’s being scanned
and how much it costs
The tool is published in the following Github repo:
https://github.com/mattiasborg82/PublicTools/blob/main/Defender%20Boys/Defender/Performance/Invoke-DBDefenderPerformance.ps1
Example usage to Create a recording:
Invoke-DBDefenderPerformance -Seconds 900 -Top 50 -Raw -ExportJson
What can be more exciting than new Advance Hunting tables. Since it’s Preview it could change
The DisruptionAndResponseEvents table in Microsoft Defender XDR is your visibility layer into automatic attack disruption – Microsoft’s built-in capability to actively stop attacks in progress.
It records what Defender did, not just what the attacker did.
Telemetry
Most telemetry tells you what happened while this table tells you how an attack was disrupted in real time and it helps you understand attack scope, attack impact and Defender’s automated response decisions.
What kind of events you’ll see?
The table focuses on defensive actions
Containment actions
Examples:
User logon blocked
SMB access blocked
RPC access denied
RDP sessions disconnected
Hardening & protection policies
Safe Boot blocked
Safe Boot Guard applied
Group Policy hardening applied
SYSVOL access blocked
These indicate environment hardening triggered automatically
Session / activity termination
SMB session killed
Remote session logged off
These show active attacker sessions being cut off
Important nuance (most people miss this) the table does NOT log the action itself (like “Contain User”) instead, it logs the resulting effects, such as:
Blocked logons
Denied access
Policy enforcement
Data sources & dependencies
Populated from multiple Defender services (Endpoint, Identity, etc.) which requires proper onboarding – otherwise you’ll see little or no data
No onboarding = no visibility.
This table shines when you want to answer:
What did Defender automatically stop?
Why did this attack fail?
Which users/devices were actively contained?
What hardening actions were triggered during an incident?
If you want to have the table extended with the description you can use the following User-defined function (which runs in query time in Defender)
let GetResponseDescription = (ActionType:string)
{
case(
ActionType == "ContainedRestrictedUserSmbFileOpenBlocked", "Logs an event when a user who is a member of a restricted user group attempts to open a specific Server Message Block (SMB) shared file and the action is blocked.",
ActionType == "ContainedUserLogonBlocked", "Logs an event when a contained user's logon attempt is blocked.",
ActionType == "ContainedUserLogonBlockedByDomainController", "Logs an event when a user's logon attempt to a device in the domain is blocked by the Domain Controller due to containment policies.",
ActionType == "ContainedUserRemoteDesktopSessionDisconnected", "Logs an event when a contained user's remote desktop session is forcibly disconnected using WTSDisconnectSession.",
ActionType == "ContainedUserRemoteDesktopSessionStopped", "Logs an event when a contained user's remote desktop session is stopped using WTSLogoffSession.",
ActionType == "ContainedUserRpcAccessBlocked", "Logs an event when a contained user's attempt to access a resource via RPC is blocked.",
ActionType == "ContainedUserSmbFileOpenBlocked", "Logs an event when a contained user attempts to open an SMB shared file and the attempt is blocked.",
ActionType == "ContainedUserSmbFileOpenBlockedAggregation", "Same as ContainedUserSmbFileOpenBlocked, but aggregated for cases where the same contained user accesses more than 10 files within a one-minute window.",
ActionType == "ContainedUserSmbSessionStopped", "Logs an event when an SMB session initiated by a contained user is forcibly ended.",
ActionType == "GroupPolicyAccessBlocked", "Blocks access to the SYSVOL directory, preventing the device from pulling group policy updates.",
ActionType == "SafeBootBlocked", "Prevents the device from being rebooted into safe mode.",
ActionType == "SafeBootGuardPolicyApplied", "Applies the Safe Boot Guard policy to the device.",
ActionType == "SafeBootGuardPolicyRemoved", "Removes the Safe Boot Guard policy from the device.",
ActionType == "GroupPolicyHardeningPolicyApplied", "Applies the Group Policy Hardening policy to the device.",
ActionType == "GroupPolicyHardeningPolicyRemoved", "Removes the Group Policy Hardening policy from the device.",
"Unknown ActionType"
)
};
DisruptionAndResponseEvents
| extend ResponseDescription = GetResponseDescription(ActionType)
| summarize arg_max(TimeGenerated,*) by ActionType
Something detection engineers (working with Defender XDR) has been waiting for is being able to use other static data to enrich queries. In Sentinel we have been able to use watchlists for a long time but on the Defender side this has been missing.
Some short updates (this give value):
_GetWatchlist('ExcludedIPs')
So what happens when you configure a new detection?
The detection wizard is showing and one of the new things here is the Frequenzy “Custom” which is familiar from a Sentinel perspective.
When it comes to lookback time, there are a few things to know:
In the next step, the Alert settings we have the opportunity to dynamically set the Provider name and other custom details (key value pairs)
We can also do detailed entity mapping as well as “Related Evidence” mapping. This means Entities that are related to the alert, but are not impacted assets.
Depending on data source we can also configure automated actions (as before).
We recommend you to AVOID doing isolate device as an automated action, things can go wrong fast.
The background is not new in anyway, this is more on how to combine information about existing vulnerability and find exploitation of the vulnerability.
Let’s do a refresh on unquoted service paths and why it’s dangerous…
If you’ve done any amount of Windows privilege escalation, you’ve probably come across unquoted service paths. It’s one of those vulnerabilities that feels almost too simple-but it still shows up in real environments far more often than it should.
//Query to list all services vulnerable to unquoted service path
DeviceTvmSecureConfigurationAssessment
| where ConfigurationId == "scid-3001"
| where IsApplicable == 1 and IsCompliant == 0
| extend ParsedContext = parse_json(Context)
| mv-expand ServiceEntry = ParsedContext
| extend ServiceName = tostring(ServiceEntry[0]), ServicePathRaw = tostring(ServiceEntry[1])
| extend ServicePathRawClean = tolower(replace_string(ServicePathRaw, "/", "\\"))
| where ServicePathRawClean contains " "
| where not(ServicePathRawClean startswith "\"")
| extend ServicePath = trim(@" """, ServicePathRawClean)
| where ServicePath matches regex @"^[a-zA-Z]:\\.*\.exe$"
| summarize count() by ServicePath
So how does this work?
Let’s break down how it works, why it’s exploitable, and how to actually abuse it in a real scenario.
The Root Cause In Windows, services are defined with a binary path (Image Path) that tells the Service Control Manager (SCM) what executable to run.
A typical service path might look like this: C:\Program Files\Example App\service.exe
If this path is not enclosed in quotes, Windows does something really dangerous. Instead of treating the full string as a the single path, Windows parses it by splitting on spaces and tries to execute each possibility in order. So this: C:\Program Files\Example App\service.exe
Gets interpreted like this: C:\Program.exe C:\Program Files\Example.exe C:\Program Files\Example App\service.exe
Windows will execute the first match it finds. That behavior is the entire vulnerability.
Why does this become a Privilege Escalation? Many services run as LocalSystem or another highly privileged account. If you can influence what binary gets executed, you effectively get code execution at that privilege level.
So the attack becomes very straightforward:
Find a service with an unquoted path
Identify writable directories in the path chain
Drop a malicious executable with the right name
Restart the service (or wait for reboot)
If Windows hits your binary first, it executes it as SYSTEM. Game over.
You might think that no user has write access to a subfolder in Program Files… Normally no, but some apps sets ACLs that allow any user to write there (this is in you application management process to check if this happens)
Real Example
Let’s say a service is configured like this: PathName: C:\Program Files\Vendor Corp\Service App\appservice.exe – No quotes. Windows will try:
Now imagine you have write permissions to: C:\Program Files\Vendor Corp\ You can drop: Service.exe And when the service starts, Windows executes your payload instead of the legitimate binary.
Finding Vulnerable Services
You don’t need fancy tools to identify these. A simple command works:
Services running as SYSTEM (or at least higher privileges, or as a user you want to impersonate)
Services that are restartable
Exploitation in Practice Once you’ve found a candidate, the next step is checking permissions. You need write access to one of the directories in the path chain. Tools like icacls or accesschk (from Sysinternals) are useful here. Example:
icacls "C:\Program Files\Vendor Corp"
If you can write there, you’re good to go
Create a payload (e.g., reverse shell, add admin user, etc.)
Name it according to the parsed path (Service.exe, Vendor.exe, etc.)
Drop it in the directory
Restart the service:
sc stop Vendorervicesc start AcmeService
If you don’t have permission to restart it, you wait for System reboot, Scheduled restart etc
Detection and Defense
From a defensive perspective, this is one of the easiest issues to fix-and one of the easiest to miss. The fix is trivial: “C:\Program Files\Venfdor Corp\Service App\appservice.exe” Just add quotes.
Using Defender to find the vulnerabilities
In Defender portal and Vulnerability management there are recommendations related to configurations that should be fixed
The configuration ID for unquoted service paths is: scid-3001 (This ID can be used in advanced hunting
But only finding the vulnerable devices is not enough. It would be even better to also find exploitation attempts which can be done by enumerating all possibilities for each service path and then check for file writes using DeviceFileEvents, Process executions from DeviceProcessEvents.
Below is a query to detect if someone tries to exploit unquoted service paths, but don’t forget, you need to adapt your process to identify these issues when introducing a new software in your environment
// Detect Unquoted Service Path Abuse
// Identifies vulnerable unquoted service paths(scid-3001) and correlates them with executions from possible abuse paths.
// Generates all executable candidates from space paths and detects when services executes those.
// Added enrichment with file activity when available.
//
// DefenderBoys
let Lookback = 30d;
let UnquotedServicePathService =
DeviceTvmSecureConfigurationAssessment
| where ConfigurationId == "scid-3001"
| where Timestamp >= ago(Lookback)
| where IsApplicable == 1 and IsCompliant == 0
| extend ParsedContext = parse_json(Context)
| mv-expand ServiceEntry = ParsedContext
| extend ServiceName = tostring(ServiceEntry[0]), ServicePathRaw = tostring(ServiceEntry[1])
| extend ServicePathRawClean = tolower(replace_string(ServicePathRaw, "/", "\\"))
| where ServicePathRawClean contains " "
| where not(ServicePathRawClean startswith "\"")
| extend ServicePath = trim(@" """, ServicePathRawClean)
| where ServicePath matches regex @"^[a-zA-Z]:\\.*\.exe$"
| extend CandidateIndexes = range(0, strlen(ServicePath) - 1, 1)
| mv-apply CandidateIndex = CandidateIndexes on (
extend SpaceIndex = toint(CandidateIndex)
| where substring(ServicePath, SpaceIndex, 1) == " "
| extend CandidatePrefix = substring(ServicePath, 0, SpaceIndex)
| extend HijackFolderPath = strcat(CandidatePrefix, ".exe")
| where HijackFolderPath matches regex @"^[a-zA-Z]:\\.*\.exe$"
| where HijackFolderPath != ServicePath
| project DeviceId, DeviceName, ServiceName, ServicePath, HijackFolderPath
)
| summarize by DeviceId, DeviceName, ServiceName, ServicePath, HijackFolderPath;
let ServiceHijackExecutions =
DeviceProcessEvents
| where Timestamp >= ago(Lookback)
| where InitiatingProcessFileName =~ "services.exe"
| extend ExecutedPath = tolower(FolderPath)
| project ExecutionTime = Timestamp, DeviceId, DeviceName, ExecutedPath, FileName, FolderPath, ProcessCommandLine, SHA1, SHA256, MD5, FileSize, ProcessVersionInfoCompanyName, ProcessVersionInfoProductName, ProcessVersionInfoOriginalFileName, AccountDomain, AccountName, AccountUpn, ProcessIntegrityLevel, ProcessTokenElevation, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessIntegrityLevel, InitiatingProcessTokenElevation, ReportId;
let FileActivityOnHijackFolderPath =
// Adding File activities to provide more data into an eventual incident
DeviceFileEvents
| where Timestamp >= ago(Lookback)
| where FolderPath matches regex @"^[a-zA-Z]:\\.*\.exe$"
| extend CurrentFolderPath = tolower(FolderPath)
| extend PreviousPath = tolower(iff(ActionType =~ "FileRenamed" and isnotempty(PreviousFolderPath) and isnotempty(PreviousFileName), strcat(PreviousFolderPath, "\\", PreviousFileName), ""))
| project Timestamp, DeviceId, DeviceName, FileActionType=ActionType, CurrentFolderPath, PreviousPath, FileName, SHA1, SHA256, MD5, FileSize, FileOriginUrl, FileOriginReferrerUrl, FileOriginIP, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessAccountUpn, InitiatingProcessFolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessIntegrityLevel, InitiatingProcessTokenElevation, InitiatingProcessParentFileName, RequestProtocol, RequestSourceIP, RequestSourcePort, RequestAccountName, RequestAccountDomain, ShareName, ReportId;
UnquotedServicePathService
| join kind=inner ServiceHijackExecutions on DeviceId
| where ExecutedPath == HijackFolderPath
| join kind=leftouter FileActivityOnHijackFolderPath on DeviceId // File events is just extra not mandatory
| where CurrentFolderPath == HijackFolderPath or isempty(CurrentFolderPath)
| extend FileActivityBeforeExecution = iff(isnotempty(Timestamp) and Timestamp <= ExecutionTime, true, false)
| extend FileActivityType = case(
CurrentFolderPath == HijackFolderPath and FileActionType =~ "FileCreated", "Hijack file created",
CurrentFolderPath == HijackFolderPath and FileActionType =~ "FileModified", "Hijack file modified",
CurrentFolderPath == HijackFolderPath and FileActionType =~ "FileDeleted", "Hijack file deleted",
CurrentFolderPath == HijackFolderPath and FileActionType =~ "FileRenamed", "Hijack file renamed into place",
isempty(FileActionType), "No matching file event",
"N/A"
)
| project ExecutionTime, DeviceName, ServiceName, VulnerableServicePath = ServicePath, ExpectedHijackFolderPath = HijackFolderPath, ExecutedPath, FileActivityTimestamp = Timestamp, FileActivityBeforeExecution, FileActivityType, CurrentFolderPath, PreviousPath, FileName, FolderPath, ProcessCommandLine, SHA1, SHA256, MD5, FileSize, ProcessVersionInfoCompanyName, ProcessVersionInfoProductName, ProcessVersionInfoOriginalFileName, AccountDomain, AccountName, AccountUpn, ProcessIntegrityLevel, ProcessTokenElevation, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessIntegrityLevel, InitiatingProcessTokenElevation, InitiatingProcessParentFileName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessAccountUpn, InitiatingProcessFolderPath, FileOriginUrl, FileOriginReferrerUrl, FileOriginIP, RequestProtocol, RequestSourceIP, RequestSourcePort, RequestAccountName, RequestAccountDomain, ShareName, ReportId
| order by ExecutionTime desc
Over and out – Defender Boys
Update – Some queries are uploaded to this github repository:
Microsoft has published a set of new security functions (user-defined) in KQL. These are focused on the graph mindset, meaning relationships between nodes (objects). A previous blogpost explains graph semantics a bit more:
Mattias from the duo DefenderBoys (DefenderBoys.com) (Stefan Schörling & Mattias Borg) was asked by Microsoft to join Heike Ritters Ninja Show to talk about, and demo Attack Disruption and Evilginx and MFA bypass
When working with both Threat Hunting and Detection Engineering it has been a challenge to dynamically get domain administrators and domain controllers. Sometimes, a valid guess was the best approach.
With the Exposure Graph data, we can dynamically retrieve these entities.
Another thing which will simplify your queries is to save this as a function
It uses Defender XDR’s ability to correlate signals from many different sources into a single, high-confidence incident through insights from endpoints, identities, email and collaboration tools, and SaaS apps.
It identifies assets controlled by the attacker and used to spread the attack.
It automatically takes response actions across relevant Microsoft Defender products to contain the attack in real-time by containing and disabling affected assets.
With all the capabilities provided within Attack Disruption, it’s important that Defender XDR is configured correctly. For instance, if you have configured a gMSA account for the MDI actions, and it does not work properly, it will affect disruption negatively.
MDE Prereqs
Minimum Sense version for contain user:v10.8470
Auto IR: Full – remediate threats automatically (recommended for full coverage, no automated response turns off automatic disruption for device group)
Endpoint discovery set to Standard (not basic)
In MDE the most common configuration improvements seen are:
Sample submission is not configured to send files automatically
Some time ago, Microsoft announced and released Automatic Attack Disruption which is a feature within the Microsoft Defender XDR suite. Since then it has growth and from working for a service provider of Managed Detection and Response and running 100% Microsoft XDR, this is an amazing feature.
This blogpost will explain different aspects of disruption and how certain things works and the requirements in form of configurations to make it work.
Please note: All screenshots from Defender is from a Demo and Lab tenants and not customer tenants
The feature has to many parts possible to write a blog post about on its own, but let’s start somewhere:
Automatic Attack Disruption is not a new Auto IR (MDE) that acts on a single detection and tries to remediate according to defined playbook. Attack Disruption is chain of events, lower signals, such as RDP login to next event to next etc. until a certain level of confidence is reached. When the confidence reaches 99%+ SNR (Signal to Noise Ratio) then it kicks in. But it doesn’t simply stops processes or uses other classical Antimalware techniques, it goes back to the initial event and blocks everything used on the way.
Example scenario:
Internet to exposed RDP > Ask for service tickets for all SPNs (Kerberoast) > [Offline crack] > Login interactively to a server from the compromised RDP using one of the credentials from the Kerberoast attack > Enumerate ADCS templates > enroll certificate with new SAN > Domain admin
What Attack disruption will do is take a note of the initial RDP (similar to a human like: “Ok, someone used RDP, it’s not malicious but I will keep an eye on it”).
Next step is the Kerberoast
Still not malicious activities depending on how the threat actor executes that it could be an SPN enumeration alert. However, asking DC for a ticket is how Active Directory works and let’s play this as no incidents are generated in this case.
Threat Actor’s next step, after the tickets where cracked and the SPN account password is now in clear text (using hashcat, john to crack the password). Now it’s time for Threat Actor to login using these credentials and they user RDP to login interactively on the remote server.
The server they end up on has access as system to enroll a vulnerable certificate template (ESC1) and becomes domain admin using a sensitive account as the SAN name.
When Attack Disruption is over 99% certain that this is malicious activities, it kicks in. Remember that just RDP is very common still by both users and admins and Attack Disruption does not kick in until it is certain.
What is awesome with the feature is that when it kicks in, it takes care of entities involved in the chain, to completely block the Threat Actor. Such as containing device (If not onboarded to MDE, it will tell all onboarded endpoints that they are not allowed to communicate with the mentioned device). The involved users will be contained as well, blocking them from logons and disabling accounts.
Another example I would like to explain is the Business Email Compromise, BEC
A user clicks on a phishing link (even if we, as a community pushes security awareness, it still happens frequently)
The user who clicked ends up on the link lands on a phishing website, which act as a proxy and therefor can handle MFA requests as well since the proxy will try authenticate to, in this case, Office 365
MFA
From the Threat Actor side and the Evilginx, the authentication is captured
The cookies from the authentication can then be injected to the browser and the threat actors are logged in.
The Threat Actor then takes further steps, such as configure rules to exfiltrate all incoming emails using a forward rule to an external email address, or send a email to finance department to pay an invoice, and configure a rule to move all replies to such email to another folder to avoid the compromised user to notice this.
Back to Attack Disruption…
So what happens in the last scenario is that the service detects the events which by themselves might not be that bad, but when chained it’s certain that it’s something bad going on and disruption kicks in to stop the attack.
Another example
Attack Disruption is definitely a game-changer. When it detects something bad it will take actions on all assets used by the Threat actor automatically which will happen faster than a human can react and click. The humans are still needed, but this is a great feature the back the human SecOps.
Building Disruption Scenarios
When Microsoft develop new scenarios, they start, as in proactive Threat Hunting, with a hypothesis.
Research creates hypothesis
Research validates hypothesis in telemetry
Detector(s) development for events worth noticing
Detectors trigger as non-customer facing detections (establish 99% confidence)
Non customer facing alerts are manually investigated (establishing 99% confidence)
Release alerts to customers (not taking action)
Alerts are released but throttled – Disrupt engages (Triggered alerts are manually investigated)
Alerts are released through release rings (Customer grading is monitored)
Customers with Disrupt incidents are being contacted and interviewed (Validating response efficacy)
Scenario gets released to all customers
Detector are being reviewed and updated (based on new TTPs and customer feedback/ researcher grading)
From correlated signals to disruption
Scenarios and requirements
Endpoints
On-Prem Identity
Office 365
Cloud Apps
Sentinel
Cloud Identity
Human Operated Ransomware
**
**
Hands on Keyboard Attack
**
Business Email Compromise
**
**
Adversary-in-the-Middle
**
**
**
**
**
Compomised Credentials by Known Threat Actor
**
**
Credential Stuffing
**
**
Compromised IaaS Cloud Resource
**
SAP Financial Process Manipulation (3rd Party Signals required)
We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept”, you consent to the use of ALL the cookies.
This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.
Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.