locked
PowerShell PSCredential temporarily corrupted after using BITS cmdlets RRS feed

  • Question

  • I have a PowerShell script that uses the *-BitsTransfer cmdlets.  They work fine if I don't use the Credential parameter, but if I do use it, the specified PSCredential object becomes corrupted.  The UserName property gets a string value of the proper length, but all characters have a numeric value of zero.  The password appears to be valid (i.e., GetNetworkCredential().Password returns the correct password).  When I call Start-BitsTransfer for several files, the first transfers successfully, but subsequent files fail with "The format of the supplied security credentials is invalid."  It appears the first transfer corrupts the credential before the next starts.

    Also interesting (and even more frustrating), creating a new PSCredential object with the same username, even at the command prompt or from the next execution of the script, returns the same (or identically) corrupted PSCredential object.  After several minutes, however, I can usually create another valid one.  Calling Get-Credential also seems to work, but requires user input, of course.

    This makes the BITS virtually cmdlets useless as I can never count on being able to create a valid credential.  Any ideas how to fix or get around this?

    Thursday, August 11, 2016 11:58 PM

Answers

  • This is a bug in PowerShell, or possibly the underlying .NET classes (I haven't verified which), but I've only duplicated it on Windows 7 machines.  I cannot duplicate it on Windows 10 (and haven't tried any other platforms).

    For anyone else having such a problem, my workaround was twofold:

    1. For each BITS transfer job, a new PSCredential object must be created using a new instance of the username string.  Since the original string used to create a PSCredential will also be corrupted by this bug, store the username in a [System.Text.StringBuilder] object, then create a new [PSCredential] object for each transfer job, calling the [StringBuilder] object's ToString() method for the username parameter (thereby creating a new String object each time).

    2. Do not upload more than one file in a single BITS job.  [Technically, this isn't allowed, but by piping a list of files to transfer to Start-BitsTransfer, one job is created that contains multiple child jobs, thus giving the appearance of such.]  The first child job will succeed, but subsequent jobs will fail.  Step 1 must be performed prior to each transfer.

    Example:

    [System.Text.StringBuilder] $un = $UserName
    
    $jobs = $sourceDestPairs | foreach { 
        $cred = New-Object -TypeName PSCredential -ArgumentList @( $un.ToString(), $secpass ) 
        Start-BitsTransfer -Credential $cred -TransferType Upload -Source $_.Source -Destination $_.Destination
    }
    


    It would be great if Microsoft could fix this bug, but since I haven't been able to replicate it on Windows 10, that seems unlikely.  If anyone hears of such a hotfix, please let me know.

    • Marked as answer by RickAndersen Thursday, September 1, 2016 4:46 PM
    Thursday, September 1, 2016 4:45 PM

All replies

  • Hi Rick,

    Thanks for posting here.

    >>  Any ideas how to fix or get around this?

    I have test this issue on my machines, found no such issues, see figure below:

    I used this command to transfer:

    Start-BitsTransfer c:\share\123.txt,c:\share\adamsync.log \\dc\share,\\dc\share -Credential corp\administrator 
    Only input once password, the two machines both in the same domain.

    This is for your reference, if you have more information and need more assistances, welcome to post here.

    Best regards,

    Andy_Pan


    Please remember to mark the replies as an answers if they help and unmark them if they provide no help. If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft..com.

    Friday, August 12, 2016 8:26 AM
  • Thanks for the quick reply, Andy.  I'm passing a credential variable to Start-BitsTransfer, and it's the variable that becomes corrupted.  Could you repeat your test but with a PSCredential variable in place of "corp\administrator"?  Then display the variable to see if the username is still valid, and if so, execute it again to see if the second execution works.

    Also, I'm using PowerShell 4.0 and 5.0.  Not sure if that matters.

    Friday, August 12, 2016 6:21 PM
  • Hi Rick,

    I follow your suggestion, trying this:

    $username='corp\administrator'
    $password='PWD' | ConvertTo-SecureString -AsPlainText -Force
    $mycreds = New-Object System.Management.Automation.PSCredential($username,$password)
    Start-BitsTransfer c:\share\123.txt,c:\share\adamsync.log \\dc\share,\\dc\share -Credential $mycreds

    Has the same result.

    Note:My testing machines were in same domain.

    Best regards,

    Andy_Pan


    Please remember to mark the replies as an answers if they help and unmark them if they provide no help. If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft..com.

    Tuesday, August 16, 2016 6:07 AM
  • I'm wondering if the problem may be related to my use of HTTPS as the protocol.  I've put together the following fairly small script that duplicates the problem.  If possible, can you setup a small HTTPS site and give this a try?  Step through and watch the values of the various credential variables and see when/if the UserName value becomes corrupted.

    Thanks in advance.

    #Requires -Version 4.0
    
    Set-StrictMode -Version Latest
    $ErrorActionPreference = 'Stop' 
    
    
    function TestCredential( [PSCredential] $Credential )
    {
        try
        {
            # Does not corrupt $Credential
            [System.Text.StringBuilder] $un = $Credential.UserName
            $cred1 = New-Object -TypeName PSCredential -ArgumentList @( $un.ToString(), $Credential.Password ) 
            Start-BitsTransfer -Credential $cred1 -Authentication Ntlm -TransferType Download -Source https://<ipaddress>:<port>/data/data.txt -Destination F:\httpsTest\download
    
            # Corrupts $cred2 and $Credential (included the value passed in as $Credential)
            $cred2 = New-Object -TypeName PSCredential -ArgumentList @( $Credential.UserName, $Credential.Password )
            Start-BitsTransfer -Credential $cred2 -Authentication Ntlm -TransferType Download -Source https://<ipaddress>:<port>/data/data.txt -Destination F:\httpsTest\download
        }
        finally
        {
            'Place breakpoint here if you want to view the various credential variables.'
        }
    }
    
    $cred = (Get-Credential) 
    "Credential username now looks like: [$($cred.UserName)]"
    TestCredential -Credential $cred
    "Credential username now looks like: [$($cred.UserName)]"

    Tuesday, August 16, 2016 5:20 PM
  • First this:

    $cred2 = New-Object -TypeName PSCredential -ArgumentList @($Credential.UserName, $Credential.Password)

    Should be

    $cred2 = New-Object -TypeName PSCredential($Credential.UserName, $Credential.Password)

    Most of what you are doing doesn't seem to make much sense. Just use the credential object directly.


    \_(ツ)_/

    Tuesday, August 16, 2016 5:27 PM
  • I would love to, but it doesn't work.  :-)  When I use the credential object directly, it becomes corrupted. That's what this topic is all about: why do the BitsTransfer cmdlets corrupt the credential object so that it can't be used again? 

    The example above is not what my script is doing; it is a test script to illustrate the problem.  The first section ("Does not corrupt $Credential") shows the workaround that gets me around the problem because it only corrupts $cred1, not $Credential.  The second section ("Corrupts $cred2 and $Credential") shows how using a credential object ($cred2) in the Start-BitsTransfer cmdlet corrupts not only $cred2, but also the credential it was created from, $Credential, thereby making both of them useless henceforth.

    It appears there is a bug in the BitsTransfer cmdlets or in .NET itself that is corrupting the UserName property of the PSCredential object (even though that property is supposed to be invariant).  It also appears that this corrupted value is also used when a new PSCredential is created (via New-Object) using the same username unless Get-Credential is called (which apparently works).  This is not necessarily a bug, but probably an optimization of sorts.

    I'm hoping someone can verify that this corruption takes place for them, as opposed to it being some strange environment-specific behavior (e.g., could my company's antivirus or spyware be corrupting this value?).  Any verification would be helpful.

    Thanks.

    Tuesday, August 16, 2016 7:26 PM
  • I cannot reproduce this.  Maybe it is an odd configuration.

    Try this

    $cred=Get-Credential
    New-Variable cred2 -Value $cred -Option ReadOnly

    Now use $cred2 and see what happens.  This will test your theory that the credential is getting changed.


    \_(ツ)_/

    Tuesday, August 16, 2016 8:05 PM
  • No luck.  Marking the variable as ReadOnly did not prevent it the object it references from being modified.  This makes as expected because making the variable ReadOnly does not prevent the object from being modified, only from pointing the reference variable at another object.

    When you ran the script, did you step through and verify the value of the UserName property?  The script as written won't give an error, it only shows how the $cred variable is corrupted and modified by the Start-BitsTransfer cmdlet. 

    Thanks again for your help.

    Tuesday, August 16, 2016 9:43 PM
  • I recommend opening a case with MS support.  You likely have a  corrupted net framework or other system issue.

    \_(ツ)_/

    • Proposed as answer by Hello_2018 Monday, August 29, 2016 7:56 AM
    • Unproposed as answer by RickAndersen Thursday, September 1, 2016 4:11 PM
    Tuesday, August 16, 2016 9:54 PM
  • This is a bug in PowerShell, or possibly the underlying .NET classes (I haven't verified which), but I've only duplicated it on Windows 7 machines.  I cannot duplicate it on Windows 10 (and haven't tried any other platforms).

    For anyone else having such a problem, my workaround was twofold:

    1. For each BITS transfer job, a new PSCredential object must be created using a new instance of the username string.  Since the original string used to create a PSCredential will also be corrupted by this bug, store the username in a [System.Text.StringBuilder] object, then create a new [PSCredential] object for each transfer job, calling the [StringBuilder] object's ToString() method for the username parameter (thereby creating a new String object each time).

    2. Do not upload more than one file in a single BITS job.  [Technically, this isn't allowed, but by piping a list of files to transfer to Start-BitsTransfer, one job is created that contains multiple child jobs, thus giving the appearance of such.]  The first child job will succeed, but subsequent jobs will fail.  Step 1 must be performed prior to each transfer.

    Example:

    [System.Text.StringBuilder] $un = $UserName
    
    $jobs = $sourceDestPairs | foreach { 
        $cred = New-Object -TypeName PSCredential -ArgumentList @( $un.ToString(), $secpass ) 
        Start-BitsTransfer -Credential $cred -TransferType Upload -Source $_.Source -Destination $_.Destination
    }
    


    It would be great if Microsoft could fix this bug, but since I haven't been able to replicate it on Windows 10, that seems unlikely.  If anyone hears of such a hotfix, please let me know.

    • Marked as answer by RickAndersen Thursday, September 1, 2016 4:46 PM
    Thursday, September 1, 2016 4:45 PM
  • Thursday, September 1, 2016 6:23 PM
  • I'm seeing the exact same problem - even a System.Net.NetworkCredential object I create via the PSCredential GetNetworkCredential method gets the username blanked out as well. Rick's work-around works fine though and this problem doesn't occur on Windows 10.

    Monday, January 23, 2017 8:00 PM