Securing password for use with .RegisterTaskDefinition()

Tagged: ,

This topic contains 6 replies, has 3 voices, and was last updated by Profile photo of Trevor Freedland Trevor Freedland 2 months, 1 week ago.

Viewing 7 posts - 1 through 7 (of 7 total)
  • Author
    Posts
  • #47820
    Profile photo of Trevor Freedland
    Trevor Freedland
    Participant

    Securing password for use with .RegisterTaskDefinition()

    I'm working on a helper function that registers a new task in Task Scheduler. The task is triggered by Event id 400, in the Windows PowerShell event log. The function is working as desired, with one exception- I am unable to secure the password. When passing in a SecureString I get a Type mismatch error. Is there any way I can use a secure credential here? Using a plain text password is not a valid option for me. I was unable to use the PSScheduledJob module, because New-JobTrigger does not seem to support 'When a specific system event occurs' as a trigger yet.

    I've tried:

    $taskRunasUserPwd = Read-Host 'Enter Your Password: ' –AsSecureString
    

    and

    $creds = Get-Credential
    

    Then for the password value passed in:

    $creds.Password
    

    Both of these attempts yield this error message:

    ERROR: Type mismatch. (Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))
    qpo.ps1 (28, 1): ERROR: At Line: 28 char: 1
    ERROR: + $rootFolder.RegisterTaskDefinition($name, $TaskDefinition, 6, $taskRu ...
    ERROR: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ERROR:     + CategoryInfo          : OperationStopped: (:) [], COMException
    ERROR:     + FullyQualifiedErrorId : System.Runtime.InteropServices.COMException
    ERROR:
    

    Here is the full function:

    #helper function
    function Register-CustomTask
    {
        param (
            [parameter(Mandatory = $true)]
            [ValidateNotNullOrEmpty()]
            [string]$TaskName,
            
            [int]$EventId = 400,
            
            [string]$Subscription = "<QueryList><Query Id='0'><Select Path='Windows PowerShell'>*[System[(EventID='400')]]</Select></Query></QueryList>"
        )
        Set-StrictMode -Version latest
        try
        {
            $Hostname = $Env:computername
            $Service = new-object -ComObject ("Schedule.Service")
            $Service.Connect($Hostname)
            $RootFolder = $Service.GetFolder("\")
            $TaskDefinition = $Service.NewTask(0)
            
            $regInfo = $TaskDefinition.RegistrationInfo
            $regInfo.Description = "$TaskName"
            $regInfo.Author = "$env:USERNAME"
            
            $settings = $taskDefinition.Settings
            $settings.Enabled = $true
            $settings.StartWhenAvailable = $true
            $settings.Hidden = $false
            
            $Triggers = $TaskDefinition.Triggers
            $Trigger = $Triggers.Create(0)
            $Trigger.Id = $EventId
            $Trigger.Subscription = $Subscription
            $Trigger.Enabled = $true
            
            $Action = $TaskDefinition.Actions.Create(0)
            $Action.Path = 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe'
            $Action.Arguments = "Import-Module Foo; New-Foo"
            
            $taskRunAsUser = $env:USERNAME
            #TODO secure password
            $taskRunAsUserPwd = Read-Host 'Enter Your Password: '
            $rootFolder.RegisterTaskDefinition($TaskName, $TaskDefinition, 6, $taskRunAsUser, $taskRunAsUserPwd, 1)
            Clear-Variable -Name taskRunAsUserPwd
        }
        catch { Write-Warning "Could not create task..." }
    }
    
    Register-CustomTask -TaskName 'foo'
    

    edit: escaped angle brackets in $Subscription parameter

    #47824
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    After calling Get-Credential, if you need the plain-text password, you can do $creds.GetNetworkCredential().Password

    #47828
    Profile photo of Trevor Freedland
    Trevor Freedland
    Participant

    @dave Wyatt Thank you! That worked like a charm.

    #47842
    Profile photo of Trevor Freedland
    Trevor Freedland
    Participant

    At first I was shocked that the secured password field could be so accessed so easily. I found a great article by Ed Wilson explaining why it was designed this way and why it is not seen as a security issue.

    I was also pleased to learn that PSCredential objects created with Import-Clixml do not appear to be subject to this extraction method.

    $creds = Import-Clixml -Path C:\temp\MyCredential.xml
    $creds.GetNetworkCredential() | fl *
    
    
    #returns
    UserName       : myUserName
    Password       : System.Security.SecureString
    SecurePassword : System.Security.SecureString
    Domain         : 
    
    • This reply was modified 2 months, 1 week ago by Profile photo of Trevor Freedland Trevor Freedland. Reason: spelling
    #47845
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    SecureStrings aren't there to keep it secret from the user running the script. 🙂 They're there to encrypt the password in memory, so it doesn't show up in dumps if an attacker manages to obtain one.

    That said, an encrypted password is useless. You need to be able to convert it back to plain text in some way before you can use it, and if you need to use it in managed code, then it'll just wind up being a string in memory anyway. (Which makes the use of the SecureString sort of pointless, except in PowerShell where you can do things like mask input, encrypt the credential in DSC configs or files, etc.)

    #47901
    Profile photo of Graham Beer
    Graham Beer
    Participant

    Trevor,

    You can store credentials in an XML file, which is quite cool. Plus the password is encrypted.

    $CredPath = "$home\Desktop\mycred.xml"
    Get-Credential | Export-Clixml -Path $CredPath 
    
    $CredPath = "$home\Desktop\mycred.xml"
    $cred = Import-Clixml -Path $CredPath 
    
    #47945
    Profile photo of Trevor Freedland
    Trevor Freedland
    Participant

    Thanks Dave for the additional info, this structure makes sense to me now 🙂

    Thanks Graham for the climxl note. I have an upcoming project that needs to handle 3rd party credentials in a manner that nobody (including me) has ability to view the password in plain text. I've been investigating clixml as a potential solution to meet that requirement.

Viewing 7 posts - 1 through 7 (of 7 total)

You must be logged in to reply to this topic.