4

I have a script that must be run as an elevated user. I'm using the code from the question "Running a PowerShell script as administrator without typing in passwords" but my 2nd script is not being called. The first script gets triggered by a system process (my ticketing system getting an email, then it calls my elevation script with the subject line as a parameter), and simply using a scheduled task is not an option.

The calling script:

param(
    [Parameter(Mandatory=$true)]
    [String]$MyParam
)

$LogFile = "c:\temp\log.txt"

$encpwd = Get-Content c:\temp\password.bin
$passwd = ConvertTo-SecureString $encpwd
$cred = new-object System.Management.Automation.PSCredential 'domain\LocalAdminAccount',$passwd
Add-Content $LogFile "I am running as $env:userdomain\$env:username"
Add-Content $LogFile "Trying to call the script with the parameter: $MyParam"
try {
    Add-Content $LogFile "Calling script"
    Start-Process PowerShell -WorkingDirectory 'C:\Windows\System32' -Cred $cred -ArgumentList '-File', "c:\temp\TargetScript.ps1 $MyParam"
    Add-Content $LogFile "Script called"
} catch {
    $msg = $Error[0].Exception.Message
    Add-Content $LogFile "Error caught: $msg"
}
Add-Content $LogFile "Error caught: $msg"

The called script:

param(
    [Parameter(Mandatory=$true)]
    [String]$PassedParam
)
$LogFile = "c:\temp\log.txt"

Add-Content $LogFile "I am running as $env:userdomain\$env:username"
if ($PassedParam) {
    try {
        #stuff
        if ($?) {
            Add-Content $LogFile "$PassedParam worked"
        } else {
            Add-Content $LogFile "Failed"
        }
    } catch {
        $msg = $Error[0].Exception.Message
        Add-Content $LogFile "Error caught: $msg"
    }
}
Add-Content $LogFile "Error caught: $msg"

And this is what gets put in the log file:

I am running as DOMAIN\COMPUTER$
Trying to call the script with the parameter: Tim
Calling script

It never seems to actually start the 2nd powershell process, or at least if it does, the 2nd powershell process isn't writing to the log file. I specifically granted the LocalAdminAccount full rights to the log file and password.bin file, and the LocalAdminAccount is in the administrators group on the computer.

And in case it matters, my powershell version is:

PS C:\> $PSVersionTable.PSVersion
Major  Minor  Build  Revision
-----  -----  -----  --------
4      0      -1     -1

Update: If I log in to the computer and run the script as myself, Here's is what the log file shows:

I am running as DOMAIN\TIM
Trying to call the script with the parameter: Tim
Calling script
Script called
I am running as DOMAIN\LocalAdminAccount
Tim worked
Error caught: 

Update: I did find this article: https://www.pdq.com/blog/secure-password-with-powershell-encrypting-credentials-part-2/ which shows I need to provide a decryption key for the password since I made the password file under my account but it is being decrypted by NT SYSTEM. That didn't fix my issue though.

I did more testing by simply trying to call notepad. It fails if I try to open it as a different user, but if I just try to open it I can see it in Task Manager running under the SYSTEM user name.

My issue really seems to be that SYSTEM (DOMAIN\COMPUTER$) does not have the ability to run a process as a different user?

Tim
  • 2,701
  • 3
  • 26
  • 47
  • Try to execute the `Start-Process PowerShell...` line from powershell... when I test it, it is not elevated... (Not even with `get-credential`) – T-Me Jul 24 '19 at 15:18
  • @T-Me If I run the lines to get password.bin and create $cred, then run the start-process line, it works. This is expected since manually running the script also works. It is only when the script is running as `DOMAIN\COMPUTER$` that the 2nd script is not called. – Tim Jul 24 '19 at 15:45
  • Anything useful in Event Viewer? I would think any call involving credentials would at least appear in the Security log. – John Jones Jul 26 '19 at 18:12
  • Instead of trying to find the cause and a possible solution for this (which likely has to do with the impersonation, but is hard to simulate by somebody else), I rather tell you how troubleshoot PowerShell scripts under the system account. For this, see: [Run PowerShell as SYSTEM](https://stackoverflow.com/a/51612478/1701026) (Give a man a fish and you feed him for a day. Teach a man to fish and you feed him for a lifetime.) – iRon Jul 28 '19 at 18:38
  • You don't need elevation if you are already running as the `SYSTEM` account. – Bill_Stewart Jul 29 '19 at 18:47
  • @JohnJones Sadly no. I see some events in the Application log about folder redirection, but there is nothing in the Security log. – Tim Jul 30 '19 at 18:20
  • @iRon Thanks, yeah, I got tired of triggering the script from my ticketing system so I installed PSExec so I could start a PS session as SYSTEM. I'm still getting `Error caught: This command cannot be run due to the error: Access is denied.` This leads me to believe that it is not the credentials for the local admin that are the problem, but the SYSTEM account lacking the rights to start an instance of PS with more authority. – Tim Jul 30 '19 at 18:24
  • @Bill_Stewart The actions that need to be performed need to be run as a domain account. – Tim Jul 30 '19 at 18:27
  • Was going to suggest psexec. How are you calling it? I vaguely remember having issues when I didn't specify a session for the target user. – John Jones Jul 30 '19 at 18:29
  • `Start-Process -FilePath cmd.exe -Verb Runas -ArgumentList '/k C:\SysinternalsSuite\PsExec.exe -i -s %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe'` – Tim Jul 30 '19 at 18:34
  • Yes, there is a security policy for this. See: [Powershell Start-Process : This command cannot be executed due to the error: Access is denied](https://stackoverflow.com/a/30933225/1701026): [`"Impersonate a client after authentication" in the Local Security Policy under Local Policies -> User Rights Assignment`](https://serverfault.com/a/193717) – iRon Jul 30 '19 at 18:41
  • @Tim "The actions that need to be performed need to be run as a domain account" - why? – Bill_Stewart Aug 02 '19 at 13:28
  • @Bill_Stewart because Microsoft requires it? 'm not sure exactly how to answer your question without knowing your level of Active Directory knowledge. Some commands in Active Directory (especially those that do more than read an object) require the user running the command to have the appropriate permissions relegated to them. Its a security thing. Here's a good link on AD: https://serverfault.com/questions/18339/active-directory-explained (Especially paragraphs 5 and 6) – Tim Aug 02 '19 at 14:54
  • @Tim It seems I did not express my question correctly (I understand Active Directory security). What is your process doing that requires permissions on objects in the directory? (Permission on AD objects is a separate issue from UAC.) – Bill_Stewart Aug 02 '19 at 15:47
  • @Bill_Stewart I'm hesitant to say exactly what it will be doing because I fear it will instantly trigger all kinds of discussions about "you shouldn't automate this or that or anything", but it will be doing things that require delegated permissions. Eg Moving objects between OUs, or immediately disabling a user's account upon termination. Trying to go from SYSTEM to an account that has permission to perform certain tasks is triggering UAC. I will not always be present to type a password into a UAC prompt. – Tim Aug 02 '19 at 18:44
  • @Tim So you are saying you need to modify objects using an account that's a member of some group that has permissions over those objects (`Domain Admins` is the usual example)? If that's the case, you should be able to add the computer account to that group (which means SYSTEM on that computer can modify those objects). – Bill_Stewart Aug 02 '19 at 21:58

4 Answers4

2

Run the main script commands from interactive PowerShell window as System (see: Run PowerShell as System.

This will reveal the error:

This command cannot be run due to the error: Access is denied.

This issue has been described at: Powershell Start-Process : This command cannot be executed due to the error: Access is denied and is defined by the local policy:

Security Settings
    Local Policies
        User Rights Assignment
            Impersonate a client after authentication
iRon
  • 20,463
  • 10
  • 53
  • 79
  • This looks promising, but maybe I'm doing something wrong. I updated secpol and added SYSTEM, DOMAIN\COMPUTER$, and DOMAIN\AdminUser for good measure. I tried both `gpupdate /force` and rebooting the server, but I still get access denied. – Tim Jul 30 '19 at 20:23
0

To run a script in another process, remember PowerShell always runs in the context for the user who started it, thus you need to set your script to auto-elevate / at minimu use the RunAs verb.

Start-Process PowerShell -verb RunAs -Cred $cred -ArgumentList '-noexit','-File','path-to-script' 

or say in a notepad or other script use case...

Start-Process powershell -Credential mydomain\mydomainAdmin -ArgumentList '-noprofile -command &{Start-Process notepad -verb runas}'

… as the pointer you are using shows. You are not using that anywhere in the code you posted. You must start another process separate to run as another user.

postanote
  • 15,138
  • 2
  • 14
  • 25
  • Except RunAs will require me to type in my the password of the user I want to impersonate. See the 2nd part of Keith's answer: https://stackoverflow.com/questions/12693327/running-a-powershell-script-as-administrator-without-typing-in-passwords – Tim Jul 30 '19 at 18:29
0

As a work around, I've created a scheduled task that runs as my serviceadmin. Now when the ticketing system triggers a script that calls the scheduled task, and the scheduled task runs the second script which requires elevation. This is not ideal, but it does work as desired.

The Scheduled Task (named Step 2)

<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Date>2019-08-01T09:12:24.7058452</Date>
    <Author>Tim</Author>
    <Description></Description>
  </RegistrationInfo>
  <Triggers />
  <Principals>
    <Principal id="Author">
      <UserId>Domain\ServiceAccount</UserId>
      <LogonType>Password</LogonType>
      <RunLevel>HighestAvailable</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    all defaults. Removed for brevity.
  </Settings>
  <Actions Context="Author">
    <Exec>
      <Command>%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe</Command>
      <Arguments>-file "C:\Temp\Step2.ps1"</Arguments>
    </Exec>
  </Actions>
</Task>

Step1.ps1 (The calling script)

param(
    [Parameter(Mandatory=$true)]
    [String]$MyParam
)

$LogFile = "c:\Temp\Actions.log"
$ParamFile = "c:\Temp\Params.txt"
Remove-Item $ParamFile
$MyParam | Out-File -FilePath $ParamFile
Add-Content $LogFile "$MyParam written to file."
Add-Content $LogFile "Calling Step 2 task."
Start-ScheduledTask -TaskName "Step 2"

Step2.ps1 (the called script)

$LogFile = "c:\temp\log.txt"
$ParamFile = "c:\Temp\Params.txt"
$PassedParam = Get-Content $ParamFile
Add-Content $LogFile "I am running as $env:userdomain\$env:username"
if ($PassedParam) {
    try {
        #stuff
        if ($?) {
            Add-Content $LogFile "$PassedParam worked"
        } else {
            Add-Content $LogFile "Failed"
        }
    } catch {
        $msg = $Error[0].Exception.Message
        Add-Content $LogFile "Error caught: $msg"
    }
}
Add-Content $LogFile "Step 2 complete"

I am not going to mark this as the answer because it solves my problem using a work around, but it does not answer my question.

Tim
  • 2,701
  • 3
  • 26
  • 47
0

My suggestion is to grant the computer account access to the directory objects in question (the most obvious way being to add the computer's account to a group that has the necessary permissions in the directory) and run the script on that computer using the SYSTEM account. This seems to me to be the most straightforward and least complex solution for your scenario.

Bill_Stewart
  • 22,916
  • 4
  • 51
  • 62