Mastering PowerShell Incompatibilities

PowerShell does aim to be downwards compatible to the classic shell cmd.exe but compatibility is poor. Today, I'd like to look at some of the issues that prevent classic console commands from running correctly within PowerShell and ways to work around it.

Launching Files and Programs

Many users wonder why some programs can be launched in PowerShell while others fail to start. For example, to ping a computer, you can use the good old ping command:

PS> ping.exe 127.0.0.1

Pinging 127.0.0.1 with 32 bytes of data:
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128

Ping statistics for 127.0.0.1:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms

PS>

You can also launch windows applications like notepad:

PS> notepad
PS>

However, trying to launch Internet Explorer fails:

PS> iexplore.exe
The term 'iexplore.exe' is not recognized as a cmdlet, function, operable program, or script
 file. Verify the term and try again.
At line:1 char:12
+ iexplore.exe <<<<
PS>

Whenever you specify a file name, PowerShell needs to know where the file is located, so it is somewhat understandable that PowerShell cannot find iexplore. But even when you change the current directory to the folder iexplore is located in, you still cannot launch it:

PS> cd "$env:programfiles\Internet Explorer"
C:\Program Files\Internet Explorer
PS> dir iexplore.exe


    Directory: Microsoft.PowerShell.Core\FileSystem::C:\Program Files\Internet Explorer


Mode           LastWriteTime       Length Name
----           -------------       ------ ----
-a---     1/18/2008 10:33 PM       625664 iexplore.exe


PS> iexplore.exe
The term 'iexplore.exe' is not recognized as a cmdlet, function, operable program, or script
 file. Verify the term and try again.
At line:1 char:12
+ iexplore.exe <<<<
PS>

To launch it, you have to specify a complete absolute or relative path name. The following calls both work:

PS> .\iexplore.exe
PS> & "$env:programfiles\Internet Explorer\iexplore.exe"
PS>

As it turns out, PowerShell keeps a list of trustworthy folders. Any file located in one of the trustworthy folders can be launched simply by entering its file name (like ping, ipconfig or notepad). Any file located outside a trustworthy folder must be specified with an absolute or relative path name. Why?

Security. In the past, it was easy for an attacker to trick you. The reason is the way how Windows locates files when you do not specify an absolute or relative path name: it looks in your current folder first. Here is how an attacker would exploit this vulnerability:

First, the attacker smuggles a batch file into your current folder and assigns a name of a common command to it. Let's create a batch file in your home folder called ping.bat:

PS> cd $home
C:\Users\Tobias
PS> set-content ping.bat "@echo off
>> echo I could format your drive now or do whatever I want using your privileges!
>> ping.exe %1
>> echo Note how the ping command still works so you won't notice the hijacking!"
>>
PS>

When you try and ping a computer from within PowerShell, nothing bad happens because PowerShell by default will only look at files in one of the trustworthy folders so it ignores the batch file:

PS> ping 127.0.0.1

Pinging 127.0.0.1 with 32 bytes of data:
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128

Ping statistics for 127.0.0.1:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
PS>

Once you switch to the old console and try the same, the attack succeeds:

PS> cmd.exe
Microsoft Windows [Version 6.0.6001]
Copyright (c) 2006 Microsoft Corporation.  All rights reserved.

C:\Users\Tobias>ping 127.0.0.1
I could format your drive now or do whatever I want using your privileges!

Pinging 127.0.0.1 with 32 bytes of data:
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128

Ping statistics for 127.0.0.1:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
Note how the ping command still works so you won't notice the hijacking!

C:\Users\Tobias>exit
PS>

So it is actually a very well designed feature that PowerShell distinguishes between files located just anywhere and files located in safe places. Remember: files located in a safe place like the windows folder can be launched without specifying a path name because PowerShell assumes that an attacker will have a much harder time trying to smuggle malware to such a place.

It is a compromise to raise the security bar yet enable you to still continue to use commands without the hassle of specifying a complete path.

You can add additional folders to the "safe list". Actually, there is no such list. PowerShell considers all folders listed in the PATH environment variable to be safe. You could easily add the Internet Explorer home folder to that environment variable and then be able to launch iexplore without a path name:

PS> $env:path += ";$env:programfiles\Internet Explorer"
PS> iexplore
PS>

Note that all changes to your environment variables only apply to your current PowerShell session (and all programs you launch from there). They are not persistent. Add your changes to one of your profile scripts to make persistent changes.

Alias Names

Some commands seem to work in PowerShell but once you play with them you notice they are not really compatible. While you can use the old-fashioned Dir command to list the current folder content alright, the command does not accept the usual parameters. For example, to recursively list subfolders, the old /S switch does not work:

PS> dir /s
Get-ChildItem : Cannot find path 'C:\s' because it does not exist.
At line:1 char:4
+ dir  <<<< /s
PS>

Other commands seem to still exist but work completely different. Trying to list all environment variables using Set fails. Instead, PowerShell starts to prompt for strange input. Press CTRL+C to bail out.

PS> Set

cmdlet Set at command pipeline position 1
Supply values for the following parameters:
Name[0]:
PS>

Here, the reason is different. The old console uses its own set of internal commands like Dir or Set. Since PowerShell has its own command line processor and its own commands, many of the old internal commands no longer exist.

To make transition to the new PowerShell world easier, PowerShell has a built-in alias system. Dir is just an alias for the new internal PowerShell command ("Cmdlet") Get-Childitem. While Aliases can help you find and access the new commands, still the new commands work different and support different parameters or switches than the old commands. For example, to list all subfolders, in PowerShell you no longer use the /S switch but instead the -recurse parameter.

To find out what PowerShell "thinks" when you enter a command, use Get-Command:

PS> Get-Command Dir

CommandType     Name                                   Definition
-----------     ----                                   ----------
Alias           dir                                    Get-ChildItem


PS> Get-Command Set

CommandType     Name                                   Definition
-----------     ----                                   ----------
Alias           set                                    Set-Variable


PS>

As it turns out, Set is an alias pointing to the internal Cmdlet Set-Variable and has nothing to do with the old console Set command. This is why PowerShell prompted you for missing mandatory parameters when you entered Set.

There is no other way but learn about the new internal PowerShell commands. One way is to use the Cmdlet Get-Help which will provide you with text-based help pages: Get-Help Dir -detailed.

If you must launch a classic command, you can also temporarily invoke the old console shell like this:

PS> cmd.exe /c set
ALLUSERSPROFILE=C:\ProgramData
APPDATA=C:\Users\Tobias\AppData\Roaming
CommonProgramFiles=C:\Program Files\Common Files
...
USERDOMAIN=PCNEU01
USERNAME=Tobias
USERPROFILE=C:\Users\Tobias
windir=C:\Windows
__COMPAT_LAYER=ElevateCreateProcess
PS>

With a little experience, you will find PowerShell replacements for most old approaches though like this one:

PS> dir env:

Name                           Value
----                           -----
ProgramData                    C:\ProgramData
COMPUTERNAME                   PCNEU01
...
OS                             Windows_NT
ComSpec                        C:\Windows\system32\cmd.exe
SystemDrive                    C:
ALLUSERSPROFILE                C:\ProgramData


PS>

Special Characters

PowerShell uses a number of special characters which influence the way PowerShell interprets your input. These special characters can lead to very surprising and unexpected results like this one:

PS> echo Hello World (and Universe!)
The term 'and' is not recognized as a cmdlet, function, operable program, or script file. Ve
rify the term and try again.
At line:1 char:22
+ echo Hello World (and  <<<< Universe!)
PS>

PowerShell uses parenthesis in pretty much the same way as you would in mathematical expressions: it evaluates the expression inside the parenthesis first and then continues with the result.

In this example, thus, PowerShell tried to launch the command "and Universe!" and was unable to find a command like "and". One thing you can do here is to use the special PowerShell escape character (the backtick) to escape the parenthesis and make sure they are interpreted as plain text:

PS> echo Hello World `(and Universe!`)
Hello
World
(and
Universe!)
PS>

You no longer receive an error message. The result is not exactly what you expected though because PowerShell still interprets a special character: the space is used as delimiter and causes the result to be separated in multiple lines. While it is perfectly possible to escape spaces also, the result looks a bit ridiculous:

PS> echo Hello` World` `(and` Universe!`)
Hello World (and Universe!)
PS>

In fact, you should not try too hard to make old commands backwards compatible but instead transition to PowerShell. Outputting a string is as simple as writing it to the console, no echo required:

PS> 'Hello World (and Universe!)'
Hello World (and Universe!)
PS>

Not always can you do that, though. Take a look at the next command which uses the good old find.exe utility.

In the old console, it does a good job finding all files that contain a certain word. The following command looks at all ps1-Scripts in your current folder and identifies those that contain the word 'Get-Process'. However, the call fails in PowerShell:

PS> find.exe *.ps1 'Get-Process'
FIND: Parameter format not correct
PS>

This time, the cause is less obvious. Find.exe expects both arguments to be separated by a space. Since PowerShell interprets the space as separator also, it is removed from the arguments, and Find.exe receives the arguments without the separator.

There are two workarounds that illustrate the problem. You could either add an escaped space, or you could add a space to the second parameter:

PS> find.exe *.ps1 ` 'Get-Process'

---------- AKKUCHECK.PS1



---------- WMIEVENTLOGDATE.PS1
PS> find.exe *.ps1 ' Get-Process'

---------- AKKUCHECK.PS1


---------- WMIEVENTLOGDATE.PS1
PS>

To dig a bit deeper into this kind of problems, you could create a little test function which dumps the arguments an external application receives from PowerShell. Here is the function:

function CheckArgs {
$Args | % {$c=0} {$c++; "Argument $c : '$_'"}
}

Simply call CheckArgs and provide the arguments you would normally want to provide to an external tool. Immediately, you see how your input was processed by PowerShell and what the external tool really receives:

PS> CheckArgs *.ps1 'Get-Process'
Argument 1 : '*.ps1'
Argument 2 : 'Get-Process'
PS> CheckArgs *.ps1 ` 'Get-Process'
Argument 1 : '*.ps1'
Argument 2 : ' Get-Process'
PS> CheckArgs *.ps1 ' Get-Process'
Argument 1 : '*.ps1'
Argument 2 : ' Get-Process'
PS>

Ambiguous Commands

Finally, you may run  into compatibility issues because of ambiguous commands. PowerShell can run a number of different command types like Cmdlets, Aliases, Functions, external Applications and files.

Whenever you launch a command, there may be more than one command with that name. For example, when you ping a computer you may be surprised that the ping result looks different from what you are used to. Maybe you have installed a PowerShell extension like PSCX which comes with its own ping command.

To find out whether there are any ambiguities, use Get-Command:

PS> Get-Command ping

CommandType     Name                                   Definition
-----------     ----                                   ----------
Alias           ping                                   Ping-Host
Application     PING.EXE                               C:\Windows\system32\PING.EXE


PS>

In this example, there is a ping.exe external application and also a ping alias. Since aliases have precedence over applications, PowerShell picks the alias which points to a Ping-Host cmdlet installed by the PSCX extension.

To avoid these ambiguities, make sure to be more specific. To explicitly run ping.exe, call it ping.exe and not just ping.

I am sure there are tons of additional compatibility issues. If you run into something not covered here, please add a comment so we can include it in this list.

May the PowerShell be with you,

-Tobias

 


Posted Nov 26 2008, 05:22 AM by Tobias Weltner

Comments

Aleksandar wrote re: Mastering PowerShell Incompatibilities
on 11-26-2008 2:40 PM

If you are a user of the PowerShellPlus, you can run the commands that you've used in cmd.exe environment, simply by pressing SHIFT+ENTER instead of ENTER.

repair your credit wrote repair your credit
on 10-05-2009 11:44 AM

wow - nice site! This place is great!

repair credit wrote repair credit
on 12-17-2009 7:30 AM

Kudo\'s to the webmaster for running a great site!

all blogs wrote all blogs
on 04-02-2010 6:05 AM

No trackbacks yet. Art & Crafts Art Auctions Marketing & Promotion Article Marketing Generating Website Traffic Writing & Publishing Writing Articles Free Content Free Articles and Content for Your Websites and E- Zine Newsletters.

Concentrated Tech NSoftware Dell Compellent Sponsored by Idera and Concentrated Tech and NSoftware and Dell Compellent
Copyright 2011 PowerShell.com. All rights reserved.