5

I ran into a bizarre issue when I upgraded some machines to Windows 10 where incorrect permissions on RuntimeBroker caused problems. I found a solution online that recommended changing permissions (first in the registry, then in DCOM configuration), and I'm trying to write a small .NET application to automate the process.

Presently the owner of the relevant registry keys is NT SERVICE\TrustedInstaller and I'm trying to change it to COMPUTER\Administrators. I have a simple WPF application with the requestedExecutionLevel set to "requireAdministrator," but I'm still running into problems. Here's a snippet of code to illustrate the problem:

using System.Security.AccessControl;
using System.Security.Principal;
using Microsoft.Win32;

namespace PermissionFixer
{
    public class Fixer
    {
        public void Fix()
        {
            var subKey = Registry.ClassesRoot.OpenSubKey(@"AppID\{9CA88EE3-ACB7-47c8-AFC4-AB702511C276}", true);
            if (subKey != null)
            {
                var admins = new NTAccount("Administrators");
                var ac = subKey.GetAccessControl();
                ac.SetOwner(admins);
                ac.AddAccessRule(new RegistryAccessRule(admins, RegistryRights.FullControl, AccessControlType.Allow));
                subKey.SetAccessControl(ac);
            }
        }
    }
}

The trouble is that it doesn't even get past the call to OpenSubKey() before hitting a SecurityException that says "Requested registry access is not allowed." I think that's because Administrators doesn't yet have the access (remember it belongs to TrustedInstaller), but it becomes a bit of a chicken and egg problem. The strange thing is that when I use regedit by hand I am allowed to change the owner to Administrators, and I'm pretty sure my instance of regedit is running as Administrators.

How can I get this working in .NET?

soapergem
  • 9,263
  • 18
  • 96
  • 152
  • In native code, you'd do this by carefully choosing which access permissions to request when opening the key. You'd also need to open it twice, once to set the owner and then again to change the permissions. I don't know offhand whether you can do it this way using the .NET classes. As a simple workaround, try enabling backup and restore privilege. – Harry Johnston Jul 19 '16 at 03:32
  • Forgive my ignorance here... when you say native code, I assume that means C++, and that makes me wonder whether there are any P/invoke methods I could call to achieve this? – soapergem Jul 19 '16 at 03:34
  • Sure, you could do it with P/Invoke. It would just be significantly easier if you can use the .NET classes, and easier still to use backup/restore privilege. (If you want to go the P/Invoke route, start by looking up RegCreateKeyEx and SetSecurityInfo.) – Harry Johnston Jul 19 '16 at 04:05
  • Could you point me in the direction of an article or post on how to programmatically go the backup/restore privilege route? – soapergem Jul 19 '16 at 14:19
  • I found a code example online of a class called TokenManipulator with a void method called "AddPrivilege" that hooks into advapi32.dll. I tried calling AddPrivilege with SeRestorePrivilege, SeBackupPrivilege, and SeTakeOwnershipPrivilege, which all returned true, but I'm still not able to open the registry key. – soapergem Jul 19 '16 at 19:11
  • Bog. Sorry, I thought enabling privileges in .NET was straightforward, must have been misremembering. But I suppose you'd have needed SeTakeOwnershipPrivilege in any case so it wasn't wasted time. Unfortunately it doesn't look like there is a supported way to pass the parameter that says "use backup privilege" when opening the key. – Harry Johnston Jul 19 '16 at 22:44
  • You could just pass the relevant value in anyway (it is probably passed directly to Win32) but if you want to do things right I guess we do it the long way. To open the subkey to take ownership, use a version of `.OpenSubKey` that has a `RegistryRights` argument and specify nothing but `TakeOwnership`. Having taken ownership, you can open the subkey to set permissions, by specifying nothing but `ChangePermissions`. – Harry Johnston Jul 19 '16 at 22:51
  • I tried this but still got a SecurityException – soapergem Jul 19 '16 at 23:06

1 Answers1

9

I figured it out, and fortunately it is possible to achieve with the .NET classes. Here is how you have to call OpenSubKey:

var subKey = Registry.ClassesRoot.OpenSubKey(@"AppID\{9CA88EE3-ACB7-47c8-AFC4-AB702511C276}", RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.TakeOwnership);

Then you have to nix the call to AddAccessRule()... you can't modify that until you have ownership; and you have to do those two operations in serial. So take ownership first, then re-open the key with different access rights to add the access rule.

EDIT: I discovered today that you must first manipulate the token with which your application is running, by hooking into P/Invoke calls. I found a class called TokenManipulator referenced in another Stack Overflow question. Include that class in your project, and then grant Backup, Restore, and TakeOwnership privileges to your token before calling OpenSubKey. So your method will end up looking something like this:

try
{
    TokenManipulator.AddPrivilege("SeRestorePrivilege");
    TokenManipulator.AddPrivilege("SeBackupPrivilege");
    TokenManipulator.AddPrivilege("SeTakeOwnershipPrivilege");

    var subKey = Registry.ClassesRoot.OpenSubKey(@"AppID\{9CA88EE3-ACB7-47c8-AFC4-AB702511C276}", RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.TakeOwnership);
    // code to change owner...
}
finally
{
    TokenManipulator.RemovePrivilege("SeRestorePrivilege");
    TokenManipulator.RemovePrivilege("SeBackupPrivilege");
    TokenManipulator.RemovePrivilege("SeTakeOwnershipPrivilege");
}
Community
  • 1
  • 1
soapergem
  • 9,263
  • 18
  • 96
  • 152
  • I'd hazard a guess that you only really need the "take ownership" privilege. But it doesn't hurt to claim the other two as well. – Harry Johnston Aug 05 '16 at 03:40
  • For me this worked with SeTakeOwnershipPrivilege and SeRestorePrivilege. Leaving out SeRestorePrivilege caused an error. – WeekendHacker Mar 22 '21 at 08:23