PowerShell –EncodedCommand and Round-Trips
PowerShell.exe accepts the –EncodedCommand parameter, which is a way to ‘wrap’ DOS-unfriendly command strings in such a way as to be safely passed into PSH for execution. It’s a great feature. However, it has a huge documentation hole. Let’s see what PowerShell.exe /? has to say about it:
-EncodedCommand
Accepts a base-64-encoded string version of a command. Use this parameter
to submit commands to Windows PowerShell that require complex quotation
marks or curly braces.
And, it has a helpful example:
# To use the -EncodedCommand parameter:
$command = 'dir "c:\program files" '
$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$encodedCommand = [Convert]::ToBase64String($bytes)
powershell.exe -encodedCommand $encodedCommand
That’s pretty useful, right? Do you see the documentation hole?
It’s missing a way to convert it back.
“Why would I need a way to convert it back? It’s encoded, and it’s good, right?”
I’m sure it is. However, from my SDET (Software Development Engineer in Test) background, I’ve learned not to trust software. There’s the concept of “round trip” where the data is transformed, then transformed back, and the two instances of data had better be identical.
For purposes of discussion, here’s the reverse process.
$decodedCommand = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($base64));
“But, PSH hasn’t given us any reason to distrust it, so why go through this trouble?”
Actually, they have. Any guesses as to which one?
Export-Clixml
When you round-trip an object through:
$object | Export-CliXml –Path $path;
$object = Import-CliXml -Path $path;
…you end up with a feature drop: methods. CliXml persists only properties. Now, I have no idea how to actually persist code functionality, but that’s not my point. PSH, for all its crunchy goodness, does have areas where round-trip testing shows differences.
However, even if we couldn’t find a difference, software test best practices indicates we should at least evaluate the worthiness of round-trip testing, even if we don’t implement it.
Comments
Anonymous
November 02, 2015
Hi Tim, sound advice - though I'm fairly confident in the fidelity of base64string conversion, taking it for granted might just bite you in the ass when you least expect it. Regarding serializing and deserializing objects with Export-/Import-Clixml: You can do this with a PSTypeConverter and adding some Powershell properties using type-extension. Here's an example extension XML: <?xml version="1.0" encoding="utf-8"?> <Types> <Type> <Name>Deserialized.<FullTypeName></Name> <Members> <MemberSet> <Name>PSStandardMembers</Name> <Members> <NoteProperty> <Name> TargetTypeForDeserialization </Name> <Value> <FullTypeName> </Value> </NoteProperty> </Members> </MemberSet> </Members> </Type> <Type> <Name><FullTypeName></Name> <Members> <CodeProperty IsHidden="true"> <Name>SerializationData</Name> <GetCodeReference> <TypeName><PSTypeConverter FullTypeName></TypeName> <MethodName>GetSerializationData</MethodName> </GetCodeReference> </CodeProperty> </Members> <TypeConverter> <TypeName><FullTypeName></TypeName> </TypeConverter> </Type> </Types> That's how the ExchangeShell does things also. It comes with some limitations on how your object may look (Private fields/properties will not be preserved, public properties may not throw an error when set (though the set needs not do anything)). Other than those limitations though it's fairly easy to use. Cheers FredAnonymous
November 03, 2015
Re: Export-CliXml - Much thanks! Re: Deserialization - Recently, I found a use for this aside from round-trip testing.. Running a PSH as a scheduled task isn't very secure if the task is "powershell.exe pathtoscript.ps1" because someone can overwrite script.ps1 (or add stuff like send-mailmessage -subject $cred.Username -body $cred.GetNetworkCredential().Password...) A more secure way is to create it as powershell.exe { (do-this; do-that) -and $soForth }. However, there's only so much punctuation before cmd.exe, which is tasked with interpreting the scheduled task, will throw up. So, we use -EncodedCommand. That's great, but how do we debug it when it fails a few months later? Deserialization.Anonymous
November 03, 2015
Hi Tim, you can secure the path to make it harder. We store our task-scripts in a subfolder of program files. To change these you need admin privileges, and if you have local admin, you can take apart the task anyway (or read the credentials from the local system credentials cache). This is our compromise between security and maintainability. Because passing a script as encoded command comes with an issue: For lengthy scripts this can fail due to too long an application parameter. Not so much an issue on modern systems, it can be fairly crippling on Vista / 2008 / XP / 2003.Anonymous
November 04, 2015
Security vs. Convenience, the classic tradeoff. The risk increases with the scope and ability. We're applying SDL mindset to this, so this means PSH has a lot of scope and ability. Which we already know. :)Anonymous
February 06, 2016
The comment has been removedAnonymous
February 19, 2016
The comment has been removedAnonymous
February 19, 2016
The comment has been removed