3

Bear with me, as I have a somehow tricky setup in a custom AWS EC2 AMI based on Windows Server 2012.

I orchestrate a machine using CloudFormation. Once the machine is up and provisioned through the UserData part of the provisioning script, I need to launch a daemon on the machine. I do so using PowerShell from a remote machine:

Invoke-Command \
    -ComputerName '<IP>' \
    -Credential $cred \
    -ScriptBlock { \
        $res = C:\bootstrap.ps1 C:\daemonlauncher.ps1;
        Start-Sleep -Seconds 5;
        return $res;
    };

The contents of bootstrap.ps1 are:

Invoke-WmiMethod -Class "Win32_Process" -Name "Create" -ArgumentList "powershell -File $($args[0])";

And the contents of daemonlauncher.ps1 are simple:

# Some PATH environment setup...
Start-Process -FilePath daemon.exe -ArgumentList 8080;

This way, I end up having a daemon.exe instance running on port 8080. For now, what happened is:

  1. Invoke-Command executes in the remote machine bootstrap.ps1.
  2. bootstrap.ps1 creates a new instance of PowerShell, passing to it daemonlauncher.ps1.
  3. daemonlauncher.ps1 starts the daemon.exe process and exits. The daemon remains running.

Now, the tricky part. daemon.exe is used to launch a second process, let's call it server.exe. Now, server.exe listens on an SSL port using a self-signed certificate. However, it fails to load the self-signed certificate, although it is able to create it. server.exe is written in C#, and the error it throws is (check the access denied part):

Error importing certificate 'c:\server\ssl-certificate.pfx': Access denied

However, if instead of using Invoke-Command from a remote computer to execute bootstrap.ps1 (step 1) I do so directly from within the target computer, the server.exe process can load the certificate with no issues at all.

C:\bootstrap.ps1 C:\daemonlauncher.ps1

Examining the Security tab of daemon.exe with ProcessExplorer, I can see that the Privileges are different depending on how I launched it:

  • Launching using Invoke-Command from the remote machine:
    • All privileges are Enabled
  • Launching bootstrap.ps1 manually from the target machine:
    • All privileges are Disabled except for:
      • SeChangeNotifyPrivilege > Default Enabled
      • SeCreateGlobalPrivilege > Default Enabled
      • SeDebugPrivilege > Enabled
      • SeImpersonatePrivilege > Default Enabled

I'm at a loss here. Any help will be much appreciated.


Further information

After messing with Process Explorer / Process Monitor I could get this additional info:

When started from the remote machine (using Invoke-Command), daemon.exe has the correct user ({MACHINE-NAME}\Administrator) and correct SID under the Security tab.

Checking with Process Monitor, I can see that server.exe tries to access %APPDATA%\Microsoft\Crypto\RSA\{SID}, but it doesn't try to create any file inside it (I think the CREATE FILE on ed0352f... with NAME NOT FOUND result is just the process trying to read it).

12:01:42.0687133 PM server.exe  1228    CreateFile  C:\Users\Administrator\AppData\Roaming\Microsoft\Crypto NAME COLLISION  Desired Access: Read Data/List Directory, Synchronize, Disposition: Create, Options: Directory, Synchronous IO Non-Alert, Attributes: S, ShareMode: Read, Write, AllocationSize: 0
12:01:42.0687908 PM server.exe  1228    CreateFile  C:\Users\Administrator\AppData\Roaming\Microsoft\Crypto\RSA NAME COLLISION  Desired Access: Read Data/List Directory, Synchronize, Disposition: Create, Options: Directory, Synchronous IO Non-Alert, Attributes: S, ShareMode: Read, Write, AllocationSize: 0
12:01:42.0689283 PM server.exe  1228    CreateFile  C:\Users\Administrator\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-928426534-1735938674-1186316988-500    NAME COLLISION  Desired Access: Read Data/List Directory, Synchronize, Disposition: Create, Options: Directory, Synchronous IO Non-Alert, Attributes: S, ShareMode: Read, Write, AllocationSize: 0
12:01:42.0691444 PM server.exe  1228    CreateFile  C:\Users\Administrator\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-928426534-1735938674-1186316988-500\ed0352ff16ca244170b661836cbecde9_63d47ad1-1696-4af6-bfda-1963bc1d25d0  NAME NOT FOUND  Desired Access: Generic Read, Disposition: Open, Options: Sequential Access, Synchronous IO Non-Alert, Non-Directory File, Attributes: n/a, ShareMode: Read, AllocationSize: n/a
12:01:42.0692365 PM server.exe  1228    CreateFile  C:\Users\Administrator\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-928426534-1735938674-1186316988-500    SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
12:01:42.0692590 PM server.exe  1228    QueryDirectory  C:\Users\Administrator\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-928426534-1735938674-1186316988-500\ed0352ff16ca244170b661836cbecde9_* NO SUCH FILE    Filter: ed0352ff16ca244170b661836cbecde9_*
12:01:42.0692752 PM server.exe  1228    CloseFile   C:\Users\Administrator\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-928426534-1735938674-1186316988-500    SUCCESS 

When I execute the following line from the remote computer I get that the Administrator user profile is loaded (property Loaded is True). Worth noting everything I'm running is with -Credential for Administrator:

Invoke-Command \
    -ComputerName '<IP>' \
    -Credential $cred` \
    -ScriptBlock {
        Get-WmiObject -Class "Win32_UserProfile";
    };

Then, once I run at least once the daemon.exe from the target machine (just running step 2 from above directly on the target machine), server.exe DOES CREATE a file under %APPDATA%\Microsoft\Crypto\RSA\{SID} and writes to it (9a470a... in this example):

12:06:39.3433354 PM server.exe  3420    CreateFile  C:\Users\Administrator\AppData\Roaming\Microsoft\Crypto NAME COLLISION  Desired Access: Read Data/List Directory, Synchronize, Disposition: Create, Options: Directory, Synchronous IO Non-Alert, Open For Backup, Attributes: S, ShareMode: Read, Write, AllocationSize: 0
12:06:39.3434039 PM server.exe  3420    CreateFile  C:\Users\Administrator\AppData\Roaming\Microsoft\Crypto\RSA NAME COLLISION  Desired Access: Read Data/List Directory, Synchronize, Disposition: Create, Options: Directory, Synchronous IO Non-Alert, Open For Backup, Attributes: S, ShareMode: Read, Write, AllocationSize: 0
12:06:39.3434697 PM server.exe  3420    CreateFile  C:\Users\Administrator\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-928426534-1735938674-1186316988-500    NAME COLLISION  Desired Access: Read Data/List Directory, Synchronize, Disposition: Create, Options: Directory, Synchronous IO Non-Alert, Open For Backup, Attributes: S, ShareMode: Read, Write, AllocationSize: 0
12:06:39.3435435 PM server.exe  3420    CreateFile  C:\Users\Administrator\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-928426534-1735938674-1186316988-500\9a470a37b16b84cf43ed29a419dfbb0b_63d47ad1-1696-4af6-bfda-1963bc1d25d0  SUCCESS Desired Access: Generic Write, Read Attributes, Disposition: OpenIf, Options: Sequential Access, Synchronous IO Non-Alert, Non-Directory File, Attributes: S, ShareMode: None, AllocationSize: 0, OpenResult: Created
12:06:39.3436466 PM server.exe  3420    WriteFile   C:\Users\Administrator\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-928426534-1735938674-1186316988-500\9a470a37b16b84cf43ed29a419dfbb0b_63d47ad1-1696-4af6-bfda-1963bc1d25d0  SUCCESS Offset: 0, Length: 79, Priority: Normal
12:06:39.3436929 PM server.exe  3420    CloseFile   C:\Users\Administrator\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-928426534-1735938674-1186316988-500\9a470a37b16b84cf43ed29a419dfbb0b_63d47ad1-1696-4af6-bfda-1963bc1d25d0  SUCCESS 

Once I have run the program locally once, if I execute all from the beginning from the remote computer server.exe can correctly load the certificate until a machine restart. Then we are back at the beginning of the issue.

Every time it creates a different file under ...\Crypto\RSA\S-1-5-21...\.

S_Luis
  • 502
  • 5
  • 13
  • Generally "access denied" when opening a PFX means either: it wants to open user keys, but there isn't a user profile loaded; or it wants to open machine keys and the user doesn't have access to write to the [appropriate directory](https://learn.microsoft.com/en-us/windows/win32/seccng/key-storage-and-retrieval#key-directories-and-files). I don't personally know enough about PS execution to know which seems more likely, but you could force it to one or the other with the MachineKeySet or UserKeySet import flags. – bartonjs Jan 05 '21 at 23:14
  • Thank you for your insights @bartonjs. I have added more information to the question based on what I could find out regarding private key directory permissions and user profile load status! – S_Luis Jan 07 '21 at 12:28
  • For your new variant of the problem see https://stackoverflow.com/questions/36867243/what-is-the-impact-of-the-persistkeyset-storageflag-when-importing-a-certifica – bartonjs Jan 07 '21 at 17:43

0 Answers0