Now Reading
SSH may shock you — in all of the unsuitable methods | by Martin Kjellstrand | Jul, 2023

SSH may shock you — in all of the unsuitable methods | by Martin Kjellstrand | Jul, 2023

2023-07-14 06:57:08

OpenSSH has some very peculiar dealing with round command line arguments.

It was solely after practically 23 years of utilizing OpenSSH on an nearly each day foundation that I encountered this situation.


When you simply need the quick abstract, I recommend you skip to “A few simple examples where this really bites you” part.


Some time again I used to be helping a mentee with a script to automate some duties and we had been utilizing ssh to execute instructions on the distant server.

I used to be simply feeding him instructions off the highest of my head with out testing them myself, and he acquired again to me and mentioned that they didn’t work.

Certainly he was mistaken? Seems, he wasn’t.

Intrigued? I actually was; Preserve studying.

Earlier than we dig into the gory particulars, let’s study how processes are literally executed and the way command line arguments are handed to the respective program.

Let’s speak about how a program will get executed

If you sort a command within the shell, the next description gives a simplified rationalization of the underlying course of:

  1. The shell breaks what you wrote into distinct arguments, observing well-defined guidelines round what’s a singular argument and what isn’t.
  2. The shell searches the directories specified within the $PATH surroundings variable to find the requested program.
  3. The shell employs the exec household of POSIX system calls (comparable to execl(), execv(), and others) to execute the specified program. In essence, a system name is a mechanism by which an utility solicits the kernel to carry out an motion on its behalf, and the exec household of system calls are used to execute one other program.


$ figlet foobar ‘bar baz’

You shell will search the $PATH surroundings variable for a program named figlet, after which calls fork() adopted by execve() with the arguments /usr/bin/figlet, figlet, foobar and bar baz as present on the beneath strace output (strace is a Linux-specific utility that exhibits which system calls an utility performs, however comparable instruments exist for different unices):

execve(“/usr/bin/figlet”, [“figlet”, “foobar”, “bar baz”], …

This output from strace exhibits us that our shell did a system name to the execve routine within the kernel (In the remainder of this text, I’ll interleave output from invocations of execve() calls with the command line illustrations. If you wish to do this out your self you need to use the strace -f -e hint=execve command to watch which calls to execve are literally made, and which arguments are handed to them)

As you’ll be able to see, the quotes round “bar baz” usually are not handed to execve() as a result of they’re a part of the shell’s parsing course of.

We might have expressed this in numerous functionally equal methods utilizing varied quoting and enlargement primitives, e.g:

$ figlet foobar bar baz
execve("/usr/bin/figlet", ["figlet", "foobar", "bar baz"], …
$ figlet foobar "bar baz"
execve("/usr/bin/figlet", ["figlet", "foobar", "bar baz"], …
$ BAZ='bar baz' figlet foobar "${BAZ}"
execve("/usr/bin/figlet", ["figlet", "foobar", "bar baz"], …

So long as we keep correct quoting, we will nest invocations of sub-shells and so forth as a lot as we would like, and nonetheless get the identical outcome. For instance:

$ sh -c 'sh -c "figlet foobar bar baz"'
execve("/usr/bin/sh", ["sh", "-c", "sh -c "figlet foobar bar baz""], …
execve("/usr/bin/sh", ["sh", "-c", "figlet foobar bar baz"], …
execve("/usr/bin/figlet", ["figlet", "foobar", "bar baz"], …

In every step the quoting/separating into particular person arguments works precisely as anticipated.

After a while, the nesting of quotes and escape characters turns into second nature to us, and we seldom dedicate a lot thought to it.

Which is why the following half was so complicated.

Enter OpenSSH

The ssh man web page says the next within the SYNOPSIS: (I’ve eliminated a few of the choices for brevity)


ssh — OpenSSH distant login shopper


ssh [-…] vacation spot [command [argument …]]

This seems awfully just like the person web page of for instance the sprint shell:


sprint — command interpreter (shell)


sprint [-aCefnuvxIimqVEbp] [+aCefnuvxIimqVEbp] [-o option_name][+o option_name] [command_file [argument …]]

At this level, I’d argue that most individuals anticipate ssh to behave in a clear style (i.e. like an everyday sub-shell or another command), and that the command line arguments are handed to the distant shell in response to common shell enlargement guidelines. Very similar to rsh does; In any case — the idea behind the SSH shopper command line utility was to create a safe drop-in alternative for utilities like rsh, providing enhanced safety features. rsh in flip, was an extension to run sh instructions on a distant host.

In different phrases, we’d anticipate the next instructions to be functionally equal:

See Also

$ figlet foobar bar baz
execve("/usr/bin/figlet", ["figlet", "foobar", "bar baz"], …
$ ssh localhost figlet foobar bar baz
execve("/usr/bin/ssh", ["ssh", "localhost", "figlet", "foobar", "bar baz"], …
execve("/usr/bin/figlet", ["figlet", "foobar", "bar", "baz"], …

WAT? Apparently our single argument “bar baz” which was handed to ssh as a single argument, was damaged up by ssh into two distinct arguments, particularly “bar” and “baz”.

Let’s nest it one stage deeper, utilizing customary shell enlargement guidelines:

$ sh -c 'figlet foobar bar baz'
execve("/usr/bin/sh", ["sh", "-c", "figlet foobar bar baz"], …
execve("/usr/bin/figlet", ["figlet", "foobar", "bar baz"], …
$ ssh localhost sh -c 'figlet foobar bar baz'
execve("/usr/bin/ssh", ["ssh", "localhost", "sh", "-c", "figlet foobar bar baz"], …
execve("/usr/bin/sh", ["sh", "-c", "figlet", "foobar", "bar baz"], …
execve("/usr/bin/figlet", ["figlet"], …

Now certainly, we’ve even embedded the “bar baz” argument inside single quotes — no means is ssh messing with that, proper?


You’ll discover that /usr/bin/sh within the ssh case out of the blue was invoked with [“sh”, “-c”, “figlet”, “foobar”, “bar baz”] and never [“sh”, “-c”, “figlet foobar bar baz”] as we’d anticipate.

Since we will see on the next line, ssh is invoked with correctly quoted arguments:

execve("/usr/bin/ssh", ["ssh", "localhost", "sh", "-c", "figlet foobar bar baz"], …

however we nonetheless find yourself with the unsuitable arguments executed on the distant server. (On this case, figlet will get referred to as with none arguments, making it seemingly “dangle” because it’s anticipating enter on stdin).

The ssh manpage vaguely alludes to this behaviour with the next sentence:

If a command is specified, it will likely be executed on the distant host as a substitute
of a login shell. A whole command line could also be specified as command,
or it could have further arguments. If provided, the arguments shall be
appended to the command, separated by areas, earlier than it’s despatched to the
server to be executed.

The sentence I’m referring to is If provided, the arguments shall be appended to the command, separated by areas, however to my thoughts it is extremely unclear and fails to convey the truth that ssh does its personal enlargement of command line arguments containing areas themselves.

A couple of easy examples the place this actually bites you

$ ssh localhost 'cd /tmp && pwd'
$ ssh localhost sh -c 'cd /tmp && pwd'

$ ls documentation listing
$ ssh localhost ls documentation listing
ls: can not entry 'documentation': No such file or listing
ls: can not entry 'listing': No such file or listing

In conclusion:


How anybody might assume this was a good suggestion is past me, however at this level I’m going to imagine that it was a both unintentional or intentional design determination taken way back and at this level openssh can’t repair/change it with the chance of breaking present code and installations.

Do you’ve got any insights into this? Know the historical past behind this habits? Really feel like spending a number of hours digging by means of historic openssh releases to uncover the again story behind this?

I’d love to listen to about it!

Source Link

What's Your Reaction?
In Love
Not Sure
View Comments (0)

Leave a Reply

Your email address will not be published.

2022 Blinking Robots.
WordPress by Doejo

Scroll To Top