GuidesManaging Windows Registry with Scripting (Part 3)

Managing Windows Registry with Scripting (Part 3)





by Marcin Policht

In the third article of this series, I will look closer into managing
permissions on the Windows Registry. Unfortunately, WMI functionality in
this area is fairly limited. The CheckAccess method of the Standard Registry
Provider allows only checking the level of permissinons for the current
account. The type of permission is determined by hexadecimal values, which I
have declared as the following constants:

In his latest article, Marcin Policht takes an in-depth look at managing permissions in the Windows Registry using customized scripting methods.

Const KEY_QUERY_VALUE 		= &H0001
Const KEY_SET_VALUE 		= &H0002
Const KEY_CREATE_SUB_KEY 	= &H0004
Const KEY_ENUMERATE_SUB_KEYS 	= &H0008
Const KEY_NOTIFY 		= &H0010
Const KEY_CREATE_LINK 		= &H0020
Const DELETE 			= &H00010000
Const READ_CONTROL 		= &H00020000
Const WRITE_DAC 		= &H00040000
Const WRITE_OWNER 		= &H00080000

As before (in all of the scripts in the previous two articles about
registry management), I will also define five constants determining the
target registry tree.

Const HKEY_CLASSES_ROOT 	= &H80000000
Const HKEY_CURRENT_USER 	= &H80000001
Const HKEY_LOCAL_MACHINE 	= &H80000002
Const HKEY_USERS 		= &H80000003
Const HKEY_CURRENT_CONFIG 	= &H80000005

The remaining portion of the script is fairly standard. First, we create
the oRegistry object, then provide the method name and its parameters. One of
them is the type of registry access that is being checked for the current
user, which can take one of the following values:

  • query – ability to query registry value
  • set – ability to set registry value
  • createsub – ability to create subkey
  • enumsub – ability to enumerate subkey
  • notify – ability to audit changes to the key
  • createlink – ability to create a symbolic link to the key (example of
    such a link is the one that exists between HKEY_CLASSES_ROOT and
    HKEY_LOCAL_MACHINESOFTWAREClasses)
  • delete – ability to delete current key
  • readcontrol – ability to read permissions on the current key
  • writedac – ability to modify permissions on the current key
  • writeowner – ability to take ownership of the current key

The method returns -1 if the current user does not have appropriate
permissions and 0 otherwise.

For example, if you save the script as CheckAccess.vbs, then you can find
out whether the current user has permissions to set registry values by
running the following:

cscript /nologo CheckAccess.vbs set

The returned value (non-zero or zero) indicates the permissions (granted
or not granted, respectively).


sComputer	= "USLT-NYPZ0010"
sMethod		= "CheckAccess"
hTree		= HKEY_LOCAL_MACHINE
sKey		= "SOFTWAREMicrosoftWindows NTCurrentVersionWinlogontest"

Set oRegistry	= GetObject("winmgmts:{impersonationLevel=impersonate}//" & _
		sComputer & "/root/default:StdRegProv")

Set oMethod	= oRegistry.Methods_(sMethod)
Set oInParam	= oMethod.inParameters.SpawnInstance_()

oInParam.hDefKey = hTree
oInParam.sSubKeyName = sKey

sAccess = WScript.Arguments(0)

Select Case sAccess
	Case "query"		hAccess = KEY_QUERY_VALUE
	Case "set"		hAccess = KEY_SET_VALUE
	Case "createsub"	hAccess = KEY_CREATE_SUB_KEY
	Case "enumsub"		hAccess = KEY_ENUMERATE_SUB_KEYS
	Case "notify"		hAccess = KEY_NOTIFY
	Case "createlink"	hAccess = KEY_CREATE_LINK
	Case "delete"		hAccess = DELETE
	Case "readcontrol"	hAccess = READ_CONTROL
	Case "writedac"		hAccess = WRITE_DAC
	Case "writeowner"	hAccess = WRITE_OWNER
End Select

oInParam.uRequired = hAccess
Set oOutParam = oRegistry.ExecMethod_(sMethod, oInParam)

If oOutParam.Properties_("bGranted") = 0 Then
	WScript.Echo "Current user does not have " & UCase(sAccess) & " registry 
permissions"
Else
	WScript.Echo "Current user has " & UCase(sAccess) & " registry permissions"
End If

In order to fully manage registry permissions we will need to resort to
use of custom Dynamic Link Libraries. I have demonstrated the
functionality of the first of them — ADsSecurity.DLL — in the series of
articles on Managing Permissions of the File System. It is part of ADSI 2.5
Software Development Kit and can be downloaded from the Microsoft Web site.

The second one, RegObj.DLL is also available for download at http://msdn.microsoft.com/vbasic/downloads/addins.asp.
Each of them needs to be registered — you can copy them to the system32
subfolder of your Windows folder and run the following at the Command
Prompt:

REGSVR32 ADsSecurity.DLL

followed by

REGSVR32 RegObj.DLL

You should receive the confirmation that both components have been
successfully registered.

The script below allows you to display contents of the Access Control
List for a particular registry key (I strongly recommend you refer to
another series of articles I published, which describe
Managing Permissions on the File System. This should get you familiar with
all the necessary terminology used here). If you save the script as
ShowRegACLs.vbs, then in order to display permissions on the
HKEY_LOCAL_MACHINESOFTWAREAdobe key, you would run the following at the
command prompt from the folder where the script resides:

cscript /nologo ShowRegACLs.vbs 
"HKEY_LOCAL_MACHINESOFTWAREAdobe"

Note the leading backslash character (“”) in the registry path. It is
required.

The script might look a bit intimidating, but note that roughly one third
of it contains definition of constants. Since different characteristics of
registry permissions are stored as numeric values, we need to know how to
translate these values into more meaningful format. This is the purpose of
the constants.

You are already familiar (from the script above) with the constants
indicating the level of permissions (read, write, create key, etc.). Two other
groups of constants indicate access type (whether access is allowed or
denied) and inheritance properties (e.g. whether permissions will propagate
deeper into key hierarchy or whether permission is inherited, etc.).

If the level of permissions matches one of the “generic” types (read, write,
full control), then appropriate information is displayed. Otherwise, each
individual permission is listed.

The results also contain the name of the trustee (the account for which the
permissions are set), access type (allowed or denied), and inheritance
flags.

Unfortunately, Microsoft documentation on the subject is rather limited,
so some of the permission level values do not match any of the defined
variables. In such cases, these values are displayed in the numeric
format.


Option Explicit
On Error Resume Next

'**************************************************
'*** Constant Declarations

'**************************************************
'*** Access Control Entry Inheritance Flags
'*** Possible values for the IADsAccessControlEntry::AceFlags property.

const ADS_ACEFLAG_UNKNOWN                  	= &h1

'*** child objects will inherit ACE of current object
const ADS_ACEFLAG_INHERIT_ACE 			= &h2
'*** prevents ACE inherited by the object from further propagation
const ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE 	= &h4
'*** indicates ACE used only for inheritance (it does not affect permissions 
on object itself)
const ADS_ACEFLAG_INHERIT_ONLY_ACE 		= &h8
'*** indicates that ACE was inherited
const ADS_ACEFLAG_INHERITED_ACE 		= &h10
'*** indicates that inherit flags are valid (provides confirmation of valid 
settings)
const ADS_ACEFLAG_VALID_INHERIT_FLAGS 		= &h1f
'*** for auditing success in system audit ACE
const ADS_ACEFLAG_SUCCESSFUL_ACCESS 		= &h40
'*** for auditing failure in system audit ACE
const ADS_ACEFLAG_FAILED_ACCESS 		= &h80

'**************************************************
'*** Access Control Entry Type Values
'*** Possible values for the IADsAccessContronEntry::AceType property.

const ADS_ACETYPE_ACCESS_ALLOWED           	= 0
const ADS_ACETYPE_ACCESS_DENIED            	= &h1
const ADS_ACETYPE_SYSTEM_AUDIT             	= &h2
const ADS_ACETYPE_ACCESS_ALLOWED_OBJECT    	= &h5
const ADS_ACETYPE_ACCESS_DENIED_OBJECT    	= &h6
const ADS_ACETYPE_SYSTEM_AUDIT_OBJECT     	= &h7

'**************************************************
'*** Registry Permission Type Values

Const KEY_QUERY_VALUE 		= &H0001
Const KEY_SET_VALUE 		= &H0002
Const KEY_CREATE_SUB_KEY 	= &H0004
Const KEY_ENUMERATE_SUB_KEYS 	= &H0008
Const KEY_NOTIFY 		= &H0010
Const KEY_CREATE_LINK 		= &H0020
Const DELETE 			= &H00010000
Const READ_CONTROL 		= &H00020000
Const WRITE_DAC 		= &H00040000
Const WRITE_OWNER 		= &H00080000

Dim KEY_READ		'represents access mask designating read access to registry 
key
Dim KEY_WRITE		'represents access mask designating write access to registry 
key
Dim KEY_ALL_ACCESS	'represents access mask designating full access to 
registry key

Dim iOffset		'value used for display only (left justifying displayed values)

Dim oADSSecurity	'object representing ADsSecurity class
Dim oReg		'object representing RegObj class
Dim oRegKey		'object representing target registry key

Dim sRegPath		'string representing path to target registry key

'*** hRights_KEY_READ is a combination of KEY_QUERY_VALUE,
'***	KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, and READ_CONTROL access.
KEY_READ 		= KEY_QUERY_VALUE + KEY_ENUMERATE_SUB_KEYS + KEY_NOTIFY + 
READ_CONTROL

'*** hRights_KEY_WRITE is a combination of KEY_SET_VALUE and 
KEY_CREATE_SUB_KEY access.
KEY_WRITE 		= KEY_SET_VALUE + KEY_CREATE_SUB_KEY + READ_CONTROL

'*** hRights_KEY_FULL_ACCESS is a combination of KEY_QUERY_VALUE, 
KEY_SET_VALUE, KEY_CREATE_SUB_KEY,
'	KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY and KEY_CREATE_LINK, DELETE,
'	READ_CONTROL, WRITE_DAC, and WRITE_OWNER access.
KEY_ALL_ACCESS		= KEY_QUERY_VALUE + KEY_SET_VALUE + KEY_CREATE_SUB_KEY + _
			KEY_ENUMERATE_SUB_KEYS + KEY_NOTIFY + KEY_CREATE_LINK + _
			DELETE + READ_CONTROL + WRITE_DAC + WRITE_OWNER

iOffset 		= 20

sRegPath 		= WScript.Arguments(0)

Set oADSSecurity 	= CreateObject("ADsSecurity")
Set oReg 		= CreateObject("RegObj.Registry")
Set oRegKey 		= oReg.RegKeyFromString(sRegPath)

Call DisplayACLs(oRegKey)

Sub DisplayACLs(oRegKey)

Dim oTargetSD		'object representing security descriptor of target file or 
folder
Dim oDACL		'object representing Discretionary Access Control List
Dim oACE		'object representing individual ACE
Dim sMsg, sAccessMask	'strings containing message to be displayed
Dim hAccessMask		'number representing Access Mask value

	Set oTargetSD 	= oADsSecurity.GetSecurityDescriptor("RGY://" & 
oRegKey.FullName)
	Set oDACL 	= oTargetSD.DiscretionaryACL

	WScript.Echo "Permissions on " & oRegKey.FullName

	For Each oACE in oDACL
		sMsg = vbCrLf & "Trustee:" & String(iOffset - Len("Trustee:"), Chr(32)) & 
oACE.Trustee & vbCrLf
		sMsg = sMsg & "ACE Type:" & String(iOffset - Len("ACE Type:"), Chr(32))
		Select Case oACE.AceType
				Case ADS_ACETYPE_ACCESS_ALLOWED
		'Implicit Allow ACE
		sMsg = sMsg & "ACCESS_ALLOWED"
				Case ADS_ACETYPE_ACCESS_DENIED
		'Implicit Deny ACE
		sMsg = sMsg & "ACCESS_DENIED"
				Case ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
		'Object Allowed ACE
		sMsg = sMsg & "ACCESS_ALLOWED_OBJECT"
				Case ADS_ACETYPE_ACCESS_DENIED_OBJECT
		'Object Deny ACE
		sMsg = sMsg & "ACCESS_DENIED_OBJECT"
		End Select
		Wscript.Echo sMsg

		sAccessMask = ""
		hAccessMask = 0

		If (oACE.AccessMask AND KEY_QUERY_VALUE) Then
			sAccessMask = String(iOffset, Chr(32)) & "KEY_QUERY_VALUE" & vbCrLf
			hAccessMask = hAccessMask + KEY_QUERY_VALUE
		End If
		If (oACE.AccessMask AND KEY_SET_VALUE) Then
			sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "KEY_SET_VALUE" & 
vbCrLf
			hAccessMask = hAccessMask + KEY_SET_VALUE
		End If
		If (oACE.AccessMask AND KEY_CREATE_SUB_KEY) Then
			sAccessMask = sAccessMask & String(iOffset, Chr(32)) & 
"KEY_CREATE_SUB_KEY" & vbCrLf
			hAccessMask = hAccessMask + KEY_CREATE_SUB_KEY
		End If
		If (oACE.AccessMask AND KEY_ENUMERATE_SUB_KEYS) Then
			sAccessMask = sAccessMask & String(iOffset, Chr(32)) & 
"KEY_ENUMERATE_SUB_KEYS" & vbCrLf
			hAccessMask = hAccessMask + KEY_ENUMERATE_SUB_KEYS
		End If
		If (oACE.AccessMask AND KEY_NOTIFY) Then
			sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_WRITE_EA" & 
vbCrLf
			hAccessMask = hAccessMask + KEY_NOTIFY
		End If
		If (oACE.AccessMask AND KEY_CREATE_LINK) Then
			sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "KEY_CREATE_LINK" 
& vbCrLf
			hAccessMask = hAccessMask + KEY_CREATE_LINK
		End If
		If (oACE.AccessMask AND DELETE) Then
			sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "DELETE" & vbCrLf
			hAccessMask = hAccessMask + DELETE
		End If
		If (oACE.AccessMask AND READ_CONTROL) Then
			sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "READ_CONTROL" & 
vbCrLf
			hAccessMask = hAccessMask + READ_CONTROL
		End If
		If (oACE.AccessMask AND WRITE_DAC) Then
			sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "WRITE_DAC" & 
vbCrLf
			hAccessMask = hAccessMask + WRITE_DAC
		End If
		If (oACE.AccessMask AND WRITE_OWNER) Then
			sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "WRITE_OWNER" & 
vbCrLf
			hAccessMask = hAccessMask + WRITE_OWNER
		End If

		sMsg = "ACE Permissions:" & String(iOffset - Len("ACE Permissions:"), 
Chr(32))
		Select Case hAccessMask
			Case KEY_ALL_ACCESS 	Wscript.Echo sMsg & "FULL CONTROL"
			Case KEY_WRITE 	Wscript.Echo sMsg & "WRITE"
			Case KEY_READ 	Wscript.Echo sMsg & "READ"
			Case Else	WScript.Echo sMsg & oACE.AccessMask
					WScript.Echo sAccessMask
		End Select

		sMsg = "ACE Flags:" & String(iOffset - Len("ACE Flags:"), Chr(32))
		If (oACE.AceFlags AND ADS_ACEFLAG_INHERIT_ACE) Then
			WScript.Echo sMsg & "ADS_ACEFLAG_INHERIT_ACE"
		End If
		If (oACE.AceFlags AND ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE) Then
			WScript.Echo sMsg & "ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE"
		End If
		If (oACE.AceFlags AND ADS_ACEFLAG_INHERIT_ONLY_ACE) Then
			WScript.Echo sMsg & "ADS_ACEFLAG_INHERIT_ONLY_ACE"
		End If
		If (oACE.AceFlags AND ADS_ACEFLAG_INHERITED_ACE) Then
			WScript.Echo sMsg & "ADS_ACEFLAG_INHERITED_ACE"
		End If
		If (oACE.AceFlags AND ADS_ACEFLAG_VALID_INHERIT_FLAGS) Then
			WScript.Echo sMsg & "ADS_ACEFLAG_VALID_INHERIT_FLAGS"
		End If
		If (oACE.AceFlags AND ADS_ACEFLAG_SUCCESSFUL_ACCESS) Then
			WScript.Echo sMsg & "ADS_ACEFLAG_SUCCESSFUL_ACCESS"
		End If
		If (oACE.AceFlags AND ADS_ACEFLAG_FAILED_ACCESS) Then
			WScript.Echo sMsg & "ADS_ACEFLAG_FAILED_ACCESS"
		End If
		If (oACE.AceFlags AND ADS_ACEFLAG_UNKNOWN) Then
			WScript.Echo sMsg & "ADS_ACEFLAG_UNKNOWN"
		End If
	Next
End Sub

Latest Posts

Related Stories