Tag Archive: Exposure Management

Unquoted – Combining vuln data with processes

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:

  • C:\Program.exe
  • C:\Program Files\Vendor.exe
  • C:\Program Files\Vendor Corp\Service.exe
  • C:\Program Files\Vendor Corp\Service App\appservice.exe

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:

Get-WmiObject Win32_Service | Where-Object { $_.PathName -match ' ' -and $_.PathName -notmatch '"'} | Select Name, PathName, StartName

( There are other powershell variants as well)

You’re looking for:

  • Paths with spaces
  • No surrounding quotes (“)
  • 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 Vendorervice
sc 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

https://security.microsoft.com/exposure-recommendations?tabId=Devices

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:

mattiasborg82/Talks: Demo code and other information from my talks

Happy Hunting

Microsoft announces Security Exposure Management

Microsoft Security Exposure Management is a security solution that provides a unified view of security posture across company assets and workloads. Security Exposure Management enriches asset information with security context that helps you to manage attack surfaces, protect critical assets, and explore and mitigate exposure risk.

From a personal perspective this is going to change a lot in the security business!

It is enabled in the Microsoft Defender XDR portal (https://security.microsoft.com)

Security Exposure Management is currently in public preview.

View attack surface map, this is bloodhound on steroids!

Microsoft is leading the next chapter of attack surface management so organizations can proactively improve their posture and reduce their exposure, faster than attackers are able to exploit them.

Microsoft Security Exposure Management is in Public preview and empowers organizations to:

  • Build an effective exposure management program with a continuous threat exposure management (CTEM) process.
  • Reduce risk with a clear view of every asset and real-time assessment of potential exposures both inside-out and outside-in.
  • Identify and classify critical assets, ensuring they are protected against a wide variety of threats.
  • Discover and visualize potential adversary intrusion paths, including lateral movement, to proactively identify and stop attacker activity.
  • Communicate exposure risk to business leaders and stakeholders with clear KPIs and actionable insights.
  • Enhance exposure analysis and remediation by integrating with third-party data sources and tools

The new foundational capabilities for a exposure management program is

  • Attack Surface Management: Provides a comprehensive view of the entire attack surface, allowing the exploration of assets and their relationships.
  • Attack Path Analysis: Assists security teams in visualizing and prioritizing attack paths and risks across environments, enabling focused remediation efforts to reduce exposure and breach likelihood.
  • Unified Exposure Insights: Provides decision-makers with a consolidated, clear view of an organization’s threat exposure, facilitating security teams in addressing critical questions about security posture.

Current seamless integrations are

  • Vulnerability Management (VRM)
    • Microsoft Defender Vulnerability Management (MDVM)
    • Qualys Vulnerability Management (Preview)
    • Rapid7 Vulnerability Management (Preview)
  • External Attack Surface Management (EASM)
    • Microsoft Defender External Attack Surface Management
  • Cloud Security (CSPM)
    • Microsoft Defender Cloud Security Posture Management (CSPM)
  • Endpoint Security (Device Security Posture)
    • Microsoft Defender for Endpoint (MDE) 
  • Identity Security (ISPM)
    • Microsoft Defender for Identity (MDI) 
    • Microsoft Entra ID (Free, P1, P2)
  • SaaS Security Posture (SSPM)
  • Email Security
    • Microsoft Defender for Office (MDO)
  • OT/IOT Security
    • Microsoft Defender for IOT
  • Asset & Configuration Management
    • ServiceNow CMDB (Preview)

Identifying and resolving attack paths

Who uses Security exposure management?

  • Security and compliance admins responsible for maintaining and improving organizational security posture.
  • Security operations (SecOps) and partner teams who need visibility into data and workloads across organizational silos to effectively detect, investigate, and mitigate security threats.
  • Security architects responsible for solving systematic issues in overall security posture.
  • Chief Security Information Officers (CISOs) and security decision makers who need insights into organizational attack surfaces and exposure in order to understand security risk within organizational risk frameworks.

As always, provide feedback

Happy Hunting!