3
votes

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?

1
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
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
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
Could you point me in the direction of an article or post on how to programmatically go the backup/restore privilege route? - soapergem
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

1 Answers

5
votes

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");
}