1

I have a daemon daemon running in Server A.

There, there's an argument based script to control the daemon daemon_adm.py (Server A). Through this script I can insert "messages" to daemon coming from user input. Free text, whatever you like it to be.

Then, there's a web interface in Server B for daemon_adm.py in PHP using the phpseclib's SSH2 class.

I know that is strongly discouraged to pass user input to the command line, but well, there's must be a way to pass the text from the web Server B to daemon_adm.py in Server A.

How can I securely pass a text as argument to a command line utility?

Even if I echo the arguments and pipe them to daemon_adm.py like this:

<?php 
$command = '/path/to/daemon_adm.py "'.$text.'"';
ssh->exec($command);
// or whatever other library or programming language
?>

since this command is executed by a ssh interface with a formatted string, code could be injected

<?php 
$text = 'safetext"; echo "hazard"';
$command = '/path/to/daemon_adm.py "'.$text.'"';
ssh->exec($command);
// command sent: /path/to/daemon_adm.py "safetext"; echo "hazard"
?>

My current option in mind is encoding every user input to base64 (which as far as I know doesn't use quotes and spaces in its character set) and decode it inside daemon_adm.py like this:

<?php 
$text = 'safetext"; echo "hazard"';

// Enconding it to base64

$command = '/path/to/daemon_adm.py '.$encoded_text;
ssh->exec($command);

// command sent: /path/to/daemon_adm.py c2FmZXRleHQiOyBlY2hvICJoYXphcmQi
?>

Is this safe enough or convoluted?

-- EDIT --

One indirect solution as indicated by Barmar would be to made daemon_adm.py accept the text data from stdin, and not as a shell parsable argument.

1
  • Why not use "$*"? It's just your flattened shell arg array delimited with double quotes - it can be passed as a single argument to another process. You might avoid ssh doing anything with it if you put in a heredoc on <&[3-9] - the shell will expand it to fill the heredoc and pass it off as a file descriptor - but its contents arent interpreted. Your target process can then read it in at leisure. You might do the same w/ env as well, but that requires more care. Anyway, it can also be passed as a single arg of course.
    – mikeserv
    Oct 10, 2014 at 6:57

3 Answers 3

2

ssh2::exec() returns a stream, which is connected to the stdin, stdout, and stderr of the remote command. So you can do:

$command = '/path/to/daemon_adm.py';
$stream = $ssh->exec($command);
fwrite($stream, "$text\n");

If you don't want to pass the parameters via stdin, you can use escapeshellarg():

$command = '/path/to/daemon_adm.py ' . escapeshellarg($text);
$ssh->exec($command);
9
  • That would be with php's ssh2 library and ssh2_shell(). Still using your example, let $command="/bin/ls " and $text="-l; echo 'hazard';" then reading $stream will show me the ls -l ouput and the word "hazard". $text contents should never be executed by the shell.
    – Sdlion
    Oct 8, 2014 at 20:44
  • In my answer $text is sent to the command's standard input, it's not a command line argument.
    – Barmar
    Oct 8, 2014 at 20:52
  • Since /bin/ls doesn't do anything with its standard input, it will ignore $text.
    – Barmar
    Oct 8, 2014 at 20:53
  • Then daemon_adm.py should not accept any user input as an argument and instead use stdin to accept any user data? That's the preferred method to accept user input?
    – Sdlion
    Oct 8, 2014 at 20:57
  • That's how you have to do it if you want to hide the input. And it looks like what you're doing in the question, since you use echo ... | daemon_adm.py.
    – Barmar
    Oct 8, 2014 at 21:01
1

To insert a string in a shell snippet and arrange for the shell to interpret the string literally, there are two relatively simple approaches:

  • Surround the string with single quotes, and replace each single quote ' by the 4-character string '\''.
  • Prefix each ASCII punctuation character with \ (you may prefix other characters as well), and replace newlines with '␤' or "␤" (newline between single or double quotes).

When invoking a remote command over SSH, keep in mind that the remote shell will expand the command, and in addition, if you're invoking SSH via a local shell, the local shell will also perform expansion, so you need to quote twice.

PHP provides the escapeshellarg function to escape shell special characters. Snce exec performs expansion, call it twice on the string you want to protect.

Note that this is fine for text strings, but not for byte strings. Most shells won't let null bytes through.

Another approach which is less error-prone and allows arbitrary byte strings through, but requires changing what runs at the other end, is to pass the string on the remote command's standard input.

1
  • He's using phpseclib - not a local shell - to perform the commands - so he only needs one round of expansion.
    – neubert
    Oct 12, 2014 at 15:20
0

You could do something like...

$ssh->enablePTY();
$ssh->exec('/path/to/daemon_adm.py');
$ssh->write('...');
echo $ssh->read();

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .