Suppose that some shell function foo is defined in the current shell session. Then the command

typeset -f foo

prints out the source code of this function. This means that, at least in principle, one can execute this function on a remote host via ssh as shown below:

ssh <REMOTE-HOST> "$(typeset -f foo); foo"

(For the purposes of this question, assume that all the commands and file-system paths mentioned by foo are available in <REMOTE-HOST>. Also, assume that bash is the shell on both ends.)

My instinct is that blindly sourcing some automatically generated source code like this would be extremely fragile, but I have not been able to come up with an example of a function foo where this idiom fails.

My question is: How safe is this idiom? If it is not safe, can someone give me a concrete example that illustrates how it fails? (The question of safety includes the following: does bash guarantee that such uses of typeset -f are safe?)

NB: In this post I am not looking for alternatives; I just want to understand the properties of this particular idiom.

EDIT: clarified that bash is the shell on both ends.

  • 2
    Do you know you're running the same shell on both ends? Jan 10 '18 at 21:49
  • @MichaelHomer: yes, I should have made that clear. I just added that clarification to my post, but in short: yes, bash at both ends.
    – kjo
    Jan 11 '18 at 12:40
  • It seems quite weird and unconventional, though, IMO Jan 11 '18 at 13:05
  • @MukeshSaiKumar: I agree, but I'm looking for a more objective rationale for opposing it
    – kjo
    Jan 11 '18 at 13:45

It is not safe if the code is not interpreted in the same locale as the one where it was generated (or if the locale name is the same but the definition of that locale differs between the ssh client host and server host).

Particularly important is the encoding of characters and membership of characters in the blank, alpha and alnum categories.

For instance, the α character (Greek letter lower case alpha) in the BIG5 charset is encoded as 0xa3 0x5c, 0x5c being \ in ASCII (and all charsets including BIG5).

So if you have a foo function defined in that charset as:

foo() {
  echo α

typeset -f will output it as such, but if interpreted in a different locale like C, that 0xa3 0x5c would not be taken as a α but as an unknown 0xa3 character followed by a backslash. That can be exploited like with:

$ env LC_ALL=zh_TW.big5 $'BASH_FUNC_foo%%=() { echo \xa3\\\\;}; echo gotcha; }' bash -c 'typeset -f foo' | bash
bash: line 5: syntax error near unexpected token `}'
bash: line 5: `}'

The foo() { echo α\;}; echo gotcha; } became foo() { echo <0xa3>\\; }; echo gotcha; } when interpreted in the different locale.

Other problems:

The à character in UTF-8 is encoded as 0xc3 0xa0. In iso8859-1 and several other iso8859 character sets, 0xa0 is the non-breaking space character. On some systems, that character is included in the blank character class, so honoured by bash as a token delimiter in its syntax.

Solaris is one such system where U+00A0 is considered a blank:

$ env $'BASH_FUNC_foo%%=() { nawk -v x=àBEGIN\'{system("echo gotcha")}\' 1;}' bash -c 'typeset -f foo; echo foo'  | ssh solaris LC_ALL=en_GB.ISO8859-1 bash

See how the:

nawk -v x=àBEGIN... 1

was interpreted as:

nawk -v x=<0xc3> 'BEGIN{system("...")}' 1

Note that if the function was defined in a locale where 0xa0 was not a blank or where 0xa3 0x5c was alpha, typeset -f will still output it the same even if called in a locale where it's a blank (producing a different output when interpreted)

$ LC_ALL=zh_TW.big5 bash -c $'f() { echo \xa3\x5c$(uname); }; export LC_ALL=C; typeset -f f | bash'
bash: line 3: syntax error near unexpected token `('
bash: line 3: `    echo �\$(uname)'

More generally, the output of typeset, alias, export -p, set, locale are all meant to be suitable for reinput to a shell, but besides those locale issues, various implementations have been known to have several issues, and I wouldn't be surprised if there were still many more.

So, yes, I'd agree you're right to consider it dangerous, and it's a good idea to only use them in context where you know where the data that is output comes from. For instance, in the case of typeset -f foo, only use it on a foo function that you have defined (and avoid using non-ASCII characters in it).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.