Skip to content

Instantly share code, notes, and snippets.

@Parahexen
Created July 20, 2017 01:23
Show Gist options
  • Save Parahexen/c8a2e8d553eb3ac5d15d0a2e0687f05e to your computer and use it in GitHub Desktop.
Save Parahexen/c8a2e8d553eb3ac5d15d0a2e0687f05e to your computer and use it in GitHub Desktop.
Powershell script to solve all windows '10016' event error.
# ================================================================
# by K
# Thanks to:
# https://gist.github.com/kitmenke/3213d58ffd60ae9873ca466f143945f4
# https://hinchley.net/articles/changing-dcom-security-permissions-with-powershell/
# ================================================================
# To take ownership of a registry key:
# https://social.technet.microsoft.com/Forums/windowsserver/en-US/e718a560-2908-4b91-ad42-d392e7f8f1ad/take-ownership-of-a-registry-key-and-change-permissions?forum=winserverpowershell
# Originally from here maybe?
# http://www.leeholmes.com/blog/2010/09/24/adjusting-token-privileges-in-powershell/
# ************************* START enable-privilege
function enable-privilege {
param(
## The privilege to adjust. This set is taken from
## http://msdn.microsoft.com/en-us/library/bb530716(VS.85).aspx
[ValidateSet(
'SeAssignPrimaryTokenPrivilege','SeAuditPrivilege','SeBackupPrivilege',
'SeChangeNotifyPrivilege','SeCreateGlobalPrivilege','SeCreatePagefilePrivilege',
'SeCreatePermanentPrivilege','SeCreateSymbolicLinkPrivilege','SeCreateTokenPrivilege',
'SeDebugPrivilege','SeEnableDelegationPrivilege','SeImpersonatePrivilege','SeIncreaseBasePriorityPrivilege',
'SeIncreaseQuotaPrivilege','SeIncreaseWorkingSetPrivilege','SeLoadDriverPrivilege',
'SeLockMemoryPrivilege','SeMachineAccountPrivilege','SeManageVolumePrivilege',
'SeProfileSingleProcessPrivilege','SeRelabelPrivilege','SeRemoteShutdownPrivilege',
'SeRestorePrivilege','SeSecurityPrivilege','SeShutdownPrivilege','SeSyncAgentPrivilege',
'SeSystemEnvironmentPrivilege','SeSystemProfilePrivilege','SeSystemtimePrivilege',
'SeTakeOwnershipPrivilege','SeTcbPrivilege','SeTimeZonePrivilege','SeTrustedCredManAccessPrivilege',
'SeUndockPrivilege','SeUnsolicitedInputPrivilege')]
$Privilege,
## The process on which to adjust the privilege. Defaults to the current process.
$ProcessId = $pid,
## Switch to disable the privilege, rather than enable it.
[switch]$Disable
)
## Taken from P/Invoke.NET with minor adjustments.
$definition = @'
using System;
using System.Runtime.InteropServices;
public class AdjPriv { [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct TokPriv1Luid {
public int Count;
public long Luid;
public int Attr;
}
internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
internal const int TOKEN_QUERY = 0x00000008;
internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
public static bool EnablePrivilege(long processHandle, string privilege, bool disable) {
bool retVal;
TokPriv1Luid tp;
IntPtr hproc = new IntPtr(processHandle);
IntPtr htok = IntPtr.Zero;
retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
tp.Count = 1;
tp.Luid = 0;
if (disable) {
tp.Attr = SE_PRIVILEGE_DISABLED;
}
else {
tp.Attr = SE_PRIVILEGE_ENABLED;
}
retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
return retVal;
}
}
'@
$processHandle = (Get-Process -Id $ProcessId).Handle
$type = Add-Type $definition -PassThru
$type[0]::EnablePrivilege($processHandle,$Privilege,$Disable)
}
# ************************* END enable-privilege
function take-over ($path) {
$owner = [Security.Principal.NTAccount]'Administrators'
enable-privilege SeTakeOwnershipPrivilege | Out-Null
$key = [Microsoft.Win32.Registry]::ClassesRoot.OpenSubKey($path,'ReadWriteSubTree','TakeOwnership')
$acl = $key.GetAccessControl()
$acl.SetOwner($owner)
$key.SetAccessControl($acl)
$acl = $key.GetAccessControl()
$rule = New-Object System.Security.AccessControl.RegistryAccessRule 'Administrators','FullControl','ContainerInherit','None','Allow'
$acl.SetAccessRule($rule)
$key.SetAccessControl($acl)
enable-privilege SeTakeOwnershipPrivilege -Disable | Out-Null
}
function take-back ($path) {
$owner = [Security.Principal.NTAccount]'NT SERVICE\TrustedInstaller'
enable-privilege SeTakeOwnershipPrivilege | Out-Null
$key = [Microsoft.Win32.Registry]::ClassesRoot.OpenSubKey($path,'ReadWriteSubTree','TakeOwnership')
$acl = $key.GetAccessControl()
$rule = New-Object System.Security.AccessControl.RegistryAccessRule 'Administrators','ReadKey','ContainerInherit','None','Allow'
$acl.SetAccessRule($rule)
$key.SetAccessControl($acl)
$acl = $key.GetAccessControl()
$acl.SetOwner($owner)
enable-privilege SeRestorePrivilege | Out-Null
$key.SetAccessControl($acl)
enable-privilege SeTakeOwnershipPrivilege -Disable | Out-Null
enable-privilege SeRestorePrivilege -Disable | Out-Null
}
function fix ($path,$user) {
$name = 'LaunchPermission'
$key = [Microsoft.Win32.Registry]::ClassesRoot.OpenSubKey($path,'ReadWriteSubTree','ReadKey, SetValue')
$value = $key.GetValue($name)
$sddl = ([wmiclass]'Win32_SecurityDescriptorHelper').BinarySDToSDDL($value).SDDL
Write-Host "SDDL:`t$sddl"
$sd = (New-Object System.Security.AccessControl.RawSecurityDescriptor ("O:SYG:SYD:(A;;CCDCSW;;;$user)S:")).GetSddlForm('Access')
$sid = $sd.Substring($sd.LastIndexOf(';') + 1).TrimEnd(')')
$ace = "(A;;CCDCSW;;;$sid)"
Write-Host "ACE:`t$ace"
if ($sddl.IndexOf($ace) -eq -1) {
if ($sddl.LastIndexOf('S:') -eq -1) {
$sddl += $ace
} else {
$sddl = $sddl.Insert($sddl.LastIndexOf('S:'),$ace)
}
Write-Host "New:`t$sddl"
$value = ([wmiclass]'Win32_SecurityDescriptorHelper').SDDLToBinarySD($sddl).BinarySD
$key.SetValue($name,$value)
} else {
Write-Host 'Already fixed, we do nothing.'
}
}
# fix all 10016 events occurred in past 10 minutes.
$events = Get-WinEvent -FilterHashTable @{ LogName = 'System'; Level = 2; Id = 10016 } | Where-Object { $_.TimeCreated -ge (Get-Date).AddMinutes(-10) }
foreach ($e in $events) {
$path = 'AppID\' + $e.Properties[4].Value
$user = $e.Properties[7].Value
take-over $path
fix $path $user
take-back $path
}
@Simbiat
Copy link

Simbiat commented Aug 2, 2021

For language independent administrators group replace

$owner = [Security.Principal.NTAccount]'Administrators'

with

$owner= New-Object System.Security.Principal.NTAccount(Get-LocalGroup -SID S-1-5-32-544)

Also, it looks like you need to use Level = 3 for the filter

You also are not covering registry entry for AppID, only CLSID (while calling it AppID) and I believe, there is requirement to give permissions to SYSTEM for the COM object

@Simbiat
Copy link

Simbiat commented Aug 3, 2021

A proper working automated script https://github.com/Simbiat/Anti10016

@Parahexen
Copy link
Author

Thanks bro, I almost forget this snippet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment