Why does Windows rearrange the arguments when executing external process?

Most people, including myself, would expect java.lang.Runtime.exec(String[]) to pass the arguments array to the command without any modification. This is indeed true, but only on UNIX, not on Windows.

 

Take a look at the following code snippets, where a Java program calls a C++ program via Runtime.exec(String[]). Keep in mind that the call is made with a string array and not a single string. Then ask yourself why does it look like the command line arguments have been rearranged?

 

command_img1.png

 

Expected Output

Actual Output

[1] apple

[2] banana “orange

[3] pear “watermelon

[1] apple

[2] banana orange pear

[3] watermelon

 

It turns out this is because Windows does not support multiple command line arguments -- it only supports one. If you look at the Windows CreateProcess() API, you can see the function accepts only one single string as the command line argument. The illusion that both Java and C++ can call and receive multiple command line arguments is created because both libraries perform some extra work to achieve the trick. In our example, the Java library concatenates the string array into a single string before calling CreateProcess(). On the C++ side, the C++ library splits the single string back into an array before executing the main() entry point. However, when Java concatenates strings, it quotes them if they contain one or more spaces, without escaping any existing quote characters ("). The Java API is not the only API that does it this way: if you use C/C++ execl() to create a new process, it concatenates command line arguments in the same way.

 

command_img2.png

 

Since it is the responsibility of the newly started application to parse a single string into an array, different programs (or languages/libraries/etc…) can behave differently. Luckily, most programs are either compiled with Visual Studio or follow Visual Studio’s parsing rules. Unfortunately, Visual Studio changed its implementation in 2008, so there are at least two sets of popular parsing rules.

 

Post-2008

Command Line Input

Argv[1]

Argv[2]

Argv[3]

Argv[4]

Argv[5]

"""Call Me Ishmael""" 

"Call Me Ishmael" 

 

 

 

 

""""Call Me Ishmael"" b c 

"Call 

Me

Ishmael

b

C

 

Pre-2008

Command Line Input

Argv[1]

Argv[2]

Argv[3]

Argv[4]

Argv[5]

"""Call Me Ishmael""" 

"Call 

Me

Ishmael"

 

 

""""Call Me Ishmael"" b c 

"Call Me Ishmael" 

b

c

 

 

(From How Command Line Parameters Are Parsed by David Deley)

 

Even though I agree with Alex Smolen (and OWASP’s footnote) that it is unlikely that one would be able to inject an extra command unless executing with “cmd.exe /c xxx”, if we take a broader definition of command injection, we still need to guard against command “argument” injection – and the problem we discussed above is obviously one of the possible risks.

 

References:
1. How Command Line Parameters Are Parsed by David Deley
2. Everyone quotes command line arguments the wrong way 

 

 

 

Leave a Comment

We encourage you to share your comments on this post. Comments are moderated and will be reviewed
and posted as promptly as possible during regular business hours

To ensure your comment is published, be sure to follow the Community Guidelines.

Be sure to enter a unique name. You can't reuse a name that's already in use.
Be sure to enter a unique email address. You can't reuse an email address that's already in use.
Type the characters you see in the picture above.Type the words you hear.
Search
Showing results for 
Search instead for 
Do you mean 
About the Author


Follow Us
The opinions expressed above are the personal opinions of the authors, not of HP. By using this site, you accept the Terms of Use and Rules of Participation