Wednesday 22 August 2007

Checking Service Permissions with PowerShell

This interesting little security article that shows some poor Windows Security model understanding on the part of someone in Cisco got me thinking about how would one go about taking a look at the permissions associated with each service’s exe. There’s the simple but boring approach of stepping through every service and manually checking the files but I wanted to see if Powershell could do the trick.

There is a potentially useful built in cmdlet called Get-Service that should help us dig into this but a quick scan of the output from:

Get-Service | get-member

shows that a number of important Service properties are missing, not least of which is the Service’s startup “Path to Executable”. That is a bit of an oversight but it’s not a huge problem as this is one of very many cases where the Get-WMIObject cmdlet comes to the rescue. A simple WMI query later and we have an object collection that includes all the details we need on our services.

Once we have the collection we need to do some additional work. Let’s give it a roll.

$services = get-wmiobject -query 'select * from win32_service'
foreach ($service in $services) {

The Service.Pathname object contains the startup command line for the service. This is what we need but it is (unfortunately) quite messy. It sometimes includes switches and parameters and sometimes (but not always) it’s escaped within quotes in order to handle paths with spaces. To figure out which it is we first test whether the full Pathname value a valid file using Test-Path. Like many PowerShell cmdlets this one complains quite loudly when it’s unhappy with its parameters by throwing an error exception and printing the error out in nasty red text to get your attention. You can mitigate this a bit by enclosing the path to be tested in quotes however in this case our best approach is to temporarily suppress all errors using the common cmdlet parameter –ErrorAction ( or its alias “-ea” ) with a value of “silentlycontinue”. If the test fails to find a file then we need to do some parsing of the string to find the actual path to the service executable. Inspecting some sample values shows that we have the following possibilities:
  • If there are matching double quotes then return what’s in between the First pair of matching double quotes
  • Otherwise if there is a space anywhere in the string then return everything to the left of the first space
  • Otherwise return the entire string.
And then test the resulting path again (using Test-Path $path –ea silentlycontinue ) to be certain. You can jump through some hoops to do this if you want but I prefer regular expressions.

if ($Service.Pathname -match "(\""([^\""]+)\"")|((^[^\s]+)\s)|(^[^\s]+$)") {
$path = $matches[0] – replace “”””,””

Powershell has a reasonably powerful cmdlet called Get-ACL which returns a windows security descriptor object for an object (a file usually but it will work for inspecting a registry key too). Inspecting a sample file shows that the object returned by Get-ACL provides us with a lot of properties and methods. The ACL object’s “Access” method is the most useful one as it is a collection of objects that correspond to the entries in the service executable file’s access ACL. Each of these objects has the following properties that are of interest.
  • IdentityReference (The name of the object that this Access Control applies to – this will generally be a user account or group name)
  • AccessControlType (Allow or Deny)
  • FileSystemRights (Read, Execute, Change, Full Control etc)

In general there’s not going to be a problem with Administrator accounts, the System or Network accounts having high privileges levels on services so we can safely ignore any entries for the following:
  • NT AUTHORITY\Service
  • BUILTIN\Administrators
  • BUILTIN\Power Users

A quick check also shows that many other users can safely have Read, Execute and Synchronize rights without any problem. We only want to see entries where users other than those listed above have Modify\Change or Full Control Permissions.

Putting it all together we now have.

$services = get-wmiobject -query 'select * from win32_service'
foreach ($service in $services) {
if (-not( test-path $path -ea silentlycontinue)) {
if ($Service.Pathname -match "(\""([^\""]+)\"")|((^[^\s]+)\s)|(^[^\s]+$)") {
$path = $matches[0] –replace """",""
if (test-path "$path") {
$ServiceName = $service.Displayname
$secure=get-acl $path
foreach ($item in $secure.Access) {
if ( ($item.IdentityReference -match "NT AUTHORITY\\SYSTEM" ) -or
($item.IdentityReference -match "NT AUTHORITY\\NETWORK" ) -or
($item.IdentityReference -match "BUILTIN\\Administrators") -or
($item.IdentityReference -match "BUILTIN\\Power Users" ) ) {
} else {
if ($item.FileSystemRights.tostring() -match "Modify|Full|Change") {
Write "$ServiceName : Potentially Elevated Service Permission(s)"
Write (" "+$item.IdentityReference.value + " : "+$item.AccessControlType.tostring() + " : "+$item.FileSystemRights.tostring())
} else {
Write ("Service Path Not Found: "+$service.Displayname)
Write (" "+$Path)

This produces output like this:

Ati HotKey Poller : Potentially Elevated Service Permission(s)
JTMANSFI-MOBL\Administrator : Allow : FullControl
Service Path Not Found: SMS Agent Assistant
Service Path Not Found: DCOM Server Process Launcher
Intel(R) PROSet/Wireless Event Log : Potentially Elevated Service Permission(s)
GER\jtmansfi : Allow : FullControl

This shows that we still have some issues. The command line for calling DCOM doesn’t specify the full name for SVCHOST.EXE above so we generate a not found error even though we could find it if we added some more smarts to the code that parses the path. The error displayed for the SMS agent is valid as that file did not exist on the system being tested.

Apart from those minor quibbles the script does now pretty much do what I intended. We can quickly see if there are any glaring problems like that referenced in the original article (where NT AUTHORITY\INTERACTIVE users had modify rights to the Cisco VPN Service exe). In my case (thankfully) there are none.

As a footnote it is worth pointing out that I deliberately excluded Power Users from my scan as the default permissions for a Power User on an XP system allow them to carry out a number of Privilege Escalation attacks that can bring their account up to Administrator level or to avail of System level access if they want to. Mark Russinovitch posted a very thorough blog article on this last year.

No comments: