Getting name of service from files

I’m trying to write a custom property that’ll help us identify which computers have services with incorrect permissions that need to be fixed. Tenable reports “Insecure Windows Service Permissions” for some of these.

I’m able to identify the services by permissions but can’t figure out how to get the actual service name.

What I have is:

q: files (unique values of pathnames of files of services) whose ((it = TRUE) of effective write permissions 
for "Authenticated Users" of dacls of security descriptors of it)
A: "BESClient.exe" "10.0.8.37" "BigFix Agent" "10.0.8.37" "HCL Technologies Limited"

What I’d like to get instead is Service Name from Services. But I can’t figure out how to do that in this instance. Anybody know how?

1 Like

on Windows you should be able to do service names of <service>

Something like this?

service names of services whose ((effective write permission for "Authenticated Users" of dacl of security descriptor of file of it) = true)

There’s an important thing to note here that file of <service> will not work if the service command line has any arguments on it:

Q: (service name of it, pathname of file of it | "none") of services
A: AJRouter, none
A: ALG, C:\Windows\System32\alg.exe
A: AppIDSvc, none
A: Appinfo, none
A: AppMgmt, none
A: AppReadiness, none
A: AppVClient, C:\Windows\system32\AppVClient.exe
A: AppXSvc, none
A: AssignedAccessManagerSvc, none
A: AudioEndpointBuilder, none
A: Audiosrv, none
A: autotimesvc, none
A: AxInstSV, none
A: BDESVC, none
A: BFE, none
A: BITS, none
A: BrokerInfrastructure, none
A: BTAGService, none
A: BthAvctpSvc, none
1 Like

on Windows you should be able to do service names of
Something like this?

service names of services whose ((effective write permission for "Authenticated Users" of dacl of security descriptor of file of it) = true)

That’s just what I was looking for, thanks!

There’s an important thing to note here that file of will not work if the service command line has any arguments on it:

Well this gives me more than what I had before, which was nothing. I’ll have to find a way to work around that limitation somehow.

1 Like

The easiest way to work around it is to use parse the command line to pull out the executable, maybe using image path.

If you give it a shot and run into trouble let me know and I can take a stab at it

If you give it a shot and run into trouble let me know and I can take a stab at it

I’d appreciate it if you could as I’m not making any headway with it.

Try this:
(display name of it, state of it, start type of it, (it, (if (it contains "%22") then (preceding text of first "%22" of following text of first "%22" of it) else (if (it contains " ") then (preceding text of first " " of it) else (it))), exist native file ((if (it contains "%22") then (preceding text of first "%22" of following text of first "%22" of it) else (if (it contains " ") then (preceding text of first " " of it) else (it))))) of image path of it) of services

It should give you the data for all services in the following tuple format
Display-Name, State, Start-Type, (Original-Image-Path, Parsed-Image-Path-to-Exe-only, Check-if-EXE-exists)

if this is not erroring out, you can trim it down to the file only:
(display name of it, (if (it contains "%22") then (preceding text of first "%22" of following text of first "%22" of it) else (if (it contains " ") then (preceding text of first " " of it) else (it))) of image path of it) of services

I had come up with something similar:

(((unique value of (substring between "%22" of it) of image path of it) | (unique value of (preceding text of first " " of it) of image path of it)) | image path of it) of services

What I’m having an issue with (and could use help on) is integrating that with the original query that checks the permissions on the file:

service names of services whose ((effective write permission for "Authenticated Users" of dacl of security descriptor of file of it) = true)

I should be able to look at this later today, but I wanted to give a word of caution up front.

The “effective permission” inspectors have to resolve all of the files SIDs, resolve group memberships, nested group memberships, etc. - which means traffic to your Domain Controllers. I’ve seen at least one support ticket where Domain Controllers were overwhelmed by the resolution traffic generated by every client repeatedly looking up ‘effective permission’ results on files, constantly, as part of their evaluation cycle.

I’d recommend a couple of approaches to resolve this -

  1. Don’t use this in Fixlet/Task Relevance. Instead, make it an Analysis Property, and set the property to evaluate only once a day / once a week instead of “every report”. Then you can target any remediation actions based on the property result, not relying on the constantly-evaluating Fixlet Relevance to perform the lookups, or

  2. Use the “Breadcrumb method”, where you create a task to Poll clients, evaluate this logic as part of an ActionScript, save the results to a file on the client, and parse the file content as part of your remediation Fixlet’s relevance.

1 Like

Don’t use this in Fixlet/Task Relevance. Instead, make it an Analysis Property, and set the property to evaluate only once a day / once a week instead of “every report”. Then you can target any remediation actions based on the property result, not relying on the constantly-evaluating Fixlet Relevance to perform the lookups, or

That is exactly my plan - I’ll create an analysis to gather the initial data, fix what needs to be fixed and then have it evaluate weekly or even longer afterwards.

combining these is relatively straightforward:

(((unique value of (substring between "%22" of it) of image path of it) | (unique value of (preceding text of first " " of it) of image path of it)) | image path of it) of services

This relevance returns a list of paths of services that we can then use with file to check the security descriptors

service names of services whose ((effective write permission for "Authenticated Users" of dacl of security descriptor of file of it) = true)

Now for combining them. Basically we are just going to replace the reference to of it in this clause with everything but the final of services from our service path relevance from above. Let’s expand out the relevance to make this more clear:

service names of services whose ((effective write permission for "Authenticated Users" of dacl of security descriptor of file

of it

) = true)

We are just going to replace the reference to of it in this clause with everything but the final of services from our service path relevance from above.

service names of services whose ((effective write permission for "Authenticated Users" of dacl of security descriptor of file

(((unique value of (substring between "%22" of it) of image path of it) | (unique value of (preceding text of first " " of it) of image path of it)) | image path of it)

) = true)

Give that a shot!

Other method

If I was writing this in my preferred method it would end up looking like this (test using single clause tab of qna):

items 0 of
it
	whose (
		(effective write permission for "Authenticated Users" of dacl of security descriptor of file (item 1 of it)) = true
	)
of
(
	service name of it,
	((unique value of (substring between "%22" of it) of image path of it) | (unique value of (preceding text of first " " of it) of image path of it)) | image path of it
)
of services

I think good relevance is most easily read backwards so this is in three parts and we will start from the services reference at the end of the relevance and work our way back to the start.

The later part of the relevance

(
	service name of it,
	((unique value of (substring between "%22" of it) of image path of it) | (unique value of (preceding text of first " " of it) of image path of it)) | image path of it
)
of services

returns a list of tuples where the first part is the service name and the second part is our parsed path:

Current Selection Evaluates To:

AJRouter, C:\Windows\system32\svchost.exe
ALG, C:\Windows\System32\alg.exe
AppIDSvc, C:\Windows\system32\svchost.exe
Appinfo, C:\Windows\system32\svchost.exe
AppMgmt, C:\Windows\system32\svchost.exe

The middle part of the relevance:

it
	whose (
		(effective write permission for "Authenticated Users" of dacl of security descriptor of file (item 1 of it)) = true
	)
of <first part>

Reading backwards we start with (from the first section of relevance) a list of tuples where the first part is the service name and the second part is our parsed path. We then use it whose () of to filter the first part, filtering on our permissions check. From this we return a list of tuples where the first part is the service name and the second part is our parsed path but, as previously mentioned filtered using our whose clause that does our permissions check.

Current Selection Evaluates To:
jhi_service, C:\Windows\System32\DriverStore\FileRepository\dal.inf_amd64_b5484efd38adbe8d\jhi_service.exe
WMIRegistrationService, C:\Windows\System32\DriverStore\FileRepository\mewmiprov.inf_amd64_cad1db73e8c782a6\WMIRegistrationService.exe

The first part of the relevance:

items 0 of

simply returns the service name from the tuple, dropping the path.

Current Selection Evaluates To:
jhi_service
WMIRegistrationService

I like this format for a couple of reasons. I like the clear separation of parsing logic from filtering logic. I like that I could really easily modify my parsing logic and not feel like im playing with a firey pile of spaghetti. I like that I could easily locate and change my security permission filtering. I like that I could easily return a different field from the service or multiple fields from the service. I just wish the console would let you keep the formatting in every circumstance instead of just some of them.

3 Likes

Excellent explanation there, thank you very much!

Give that a shot!

Almost! I found that if there were more than 2 quotes in the image path (including the command line arguments), it wouldn’t pick it up. This one was fine:

“C:\Program Files (x86)\BigFix Enterprise\BES Client\BESClient.exe”

But this wasn’t:

“C:\Zabbix\bin\win64\zabbix_agentd.exe” --config “C:\Zabbix\conf\zabbix_agentd.win.conf”

What I did was remove the code I had come up with myself and replaced it with what @ageorgiev suggested. This works great:

items 0 of it whose ((effective write permission for "Authenticated Users" of dacl of security descriptor of file (item 1 of it)) = true) of (service name of it, ((((if (it contains "%22") then (preceding text of first "%22" of following text of first "%22" of it) else (if (it contains " ") then (preceding text of first " " of it) else (it)))) of image path of it) of it)) of services
1 Like

Solving this with Regex also appears to work well –

first matches (regex ".:\\[^:%22]*\.exe") of <command line>

This should do a good job with quoted, unquoted, and command line arguments – even ones which reference an executable like this hypthetical test case:

C:\Zabbix\bin\win64\zabbix_agentd.exe --agentExe C:\Zabbix\conf\zabbix_agentd.win.exe

And here’s how ChatGPT explains what the regex does:

This regular expression matches a Windows file path that ends in “.exe”. Here’s a breakdown of each component:

  • “.” matches any character except a newline.
  • “:” matches a colon character.
  • "" matches a backslash character.
  • “[^:%22]*” matches zero or more characters that are not a colon or a double-quote character.
  • “.exe” matches the “.exe” string.

Putting it all together, the regex matches a Windows file path that starts with a drive letter followed by a colon and a backslash, then has zero or more characters that are not a colon or a double-quote, and ends with the string “.exe”. Here are some examples of paths that would match this regex:

  • C:\Program Files\Example.exe
  • D:\Folder\MyProgram.exe
  • E:\myapp.exe

And here are some examples of paths that would not match:

  • C:\Program Files\Example.doc (wrong file extension)

I asked ChatGPT to provide more invalid examples and it started spitting out very wrong answers so we’ll leave that to the operator :slight_smile:

One thing Chat GPT didn’t catch is that this would not work for a service with an image path that points to a UNC directory.

2 Likes