help-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: sed with Variable Substitution in the command


From: Greg Wooledge
Subject: Re: sed with Variable Substitution in the command
Date: Sat, 21 Sep 2024 16:08:28 -0400

On Sat, Sep 21, 2024 at 15:41:15 -0400, Alan D. Salewski wrote:
> On 2024-09-21 13:54:41, "Alan D. Salewski" <salewski@att.net> spake thus:
> [...]
> > Sometimes using the braces would "look odd" to experienced shell
> > folks, and are omitted by convention:
> > 
> >     echo $1 $2 $3              # looks "odd"
>                                           ^^^
> s/odd/normal/
> 
> >     echo "$1" "$2" "$3"        # same here, and safer in general

The first one may "look normal" if you're used to broken shell scripts,
which, to be fair, the majority of shell users in the world seem to be,
but it's not reliable.

The second one is better, but still not completely reliable.

If the goal is to write out the first three positional parameters, with
no modifications, with spaces between them, and a newline at the end,
then the only way to do this reliably is:

    printf '%s %s %s\n' "$1" "$2" "$3"

or, if you prefer:

    printf '%s\n' "$1 $2 $3"

Anything else runs the risk of having the shell or the echo command do
something you don't want.  Take a look at this command again:

    echo $1 $2 $3

If $1 is unquoted, then it will be subject to word splitting and pathname
expansion by the shell.  If IFS hasn't been changed, this means you'll
potentially have altered whitespace in the output, or characters like *
might be replaced by a whole bunch of filenames.

If echo is used, backslashes may have a special meaning, and may cause
alterations of the output.  Or if the first word of the $1 expansion
looks like an option (e.g. -e or -n), it may be consumed by echo and not
printed.  Or both, or neither -- you can't predict how the echo command
in any given execution environment will work.

If we return to the original question, we had:

    sed 's/old/$new/'

As I pointed out, the *simplistic* answer would be to use double quotes:

    sed "s/old/$new/"

which "works" (or appears to work) as long as the contents of the $new
variable are safe.  As soon as the contents are provided by a malicious
user, you have a code injection concern.

GNU sed allows the execution of its result text as a shell command:

    hobbit:~$ echo foo | sed 's/foo/date/e'
    Sat Sep 21 15:58:45 EDT 2024

This means we can trick GNU sed into arbitrary code execution:

    hobbit:~$ new='new/;s/.*/date/e;s/z/z'
    hobbit:~$ echo foo | sed "s/old/$new/"
    Sat Sep 21 16:02:46 EDT 2024

That took me 4 minutes to figure out, most of which was spent dealing
with the final slash.  I'm sure there are more clever examples out
there somewhere.

See <https://mywiki.wooledge.org/CodeInjection> for more of this type
of thing.  Sed isn't covered there specifically, but other commands are.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]