Calling sh with script on stdin and passing command line arguments
Solution 1
This script will ask for a password, decrypt the encrypted script and run it with the first command line option passed to the main script:
#!/bin/bash
bash <(
openssl enc -base64 -d -aes-256-cbc <<<'U2FsdGVkX1/m01Eg0CvZ7DiBi5Nf81+JrCWfKIDI9WtbkTZIVLhZskkKIioVfbSe'
) "$1"
This does not use the #!
-line of the decrypted script but assumes it will run okay under bash
(and that it is always decrypted successfully). The decrypt script is passed to a bash
interpreter by means of a process substitution on the openssl
invocation, along with the first command line argument.
To write the decrypted script to disk, run it only if it was decrypted properly (using its intended interpreter), and then delete it:
#!/bin/bash
tmpscript=$(mktemp)
trap 'rm -f "$tmpscript"' EXIT
if ! openssl enc -base64 -d -aes-256-cbc <<<'U2FsdGVkX1/m01Eg0CvZ7DiBi5Nf81+JrCWfKIDI9WtbkTZIVLhZskkKIioVfbSe' >$tmpscript
then
exit 1
fi
chmod +x "$tmpscript"
"$tmpscript" "$1"
Would you want to pass all the command line parameters of the main script to the decrypted script, you should change "$1"
in the above examples to "$@"
. The double quotes are important as they ensure that the individual arguments are passed without getting split on whitespaces and without undergoing filename globbing.
Solution 2
You had it in an earlier version of the question — pass a -s
option to sh
:
echo "U2FsdGVkX1/m01Eg0CvZ7DiBi5Nf81+JrCWfKIDI9WtbkTZIVLhZskkKIioVfbSe" | openssl enc -base64 -d -aes-256-cbc | sh -s "$1"
This tells the shell to read commands from the standard input,
while accepting arguments on its command line (after the -s
)
as positional parameters for the script code coming from the standard input.
Of course shell variables should always be quoted,
unless you have a good reason not to,
and it's probably more useful in the long run
to pass "@"
(all the arguments) to the shell.
So it would also be wise to quote all references
to shell variables in test1.sh
; e.g.,
ls -l "$1"
The first solution (presented above) seems to work well
for "normal" arguments.
But, to handle the case of arguments that begin with -
,
it's better to do sh -s -- "$1"
(or "$@"
).
E.g.,
#!/bin/sh
code="U2FsdGVkX1/m01Eg0CvZ7DiBi5Nf81+JrCWfKIDI9WtbkTZIVLhZskkKIioVfbSe"
echo "$code" | openssl enc -base64 -d -aes-256-cbc | sh -s -- "$@"
(where I put the encrypted script into a variable just to make the command line shorter, so it would fit into the Super User display column).
Jack Brown
Updated on September 18, 2022Comments
-
Jack Brown over 1 year
This works of course. I can run
./test1.sh cats
and the argumentcats
is passed correctly tols
.$ cat test1.sh #!/bin/sh ls -l $1
Can you tell me how do I make this second example — the same content as the first one, but encrypted — work the same way? The password is "password".
openssl
outputs the above script, andls -l
runs, but how do I pass the command line argument to it?$ cat test2.sh #!/bin/sh echo "U2FsdGVkX1/m01Eg0CvZ7DiBi5Nf81+JrCWfKIDI9WtbkTZIVLhZskkKIioVfbSe"|openssl enc -base64 -d -aes-256-cbc|sh $1
To clarify,
test2.sh
is equivalent to$ cat test3.sh #!/bin/sh printf '%s\n' '#!/bin/sh' 'ls -l $1' | sh $1
-
Scott - Слава Україні over 5 yearsWhat do you want the script to do with the argument?
-
Jack Brown over 5 yearsSame as in test1.sh, pass it to ls -l.
-
muru over 5 yearsThere's no
ls -l
intest2.sh
-
Kusalananda over 5 years@muru There is, inside the encrypted script in
test2.sh
.
-
-
Jack Brown over 5 yearsThanks, the 1st works also with my mysql script where command line arguments are in the middle.
-
Kusalananda over 5 yearsUsing
bash -s
orsh -s
may confuse things in the script that usesread
. Also, it would ignore the interpreter of the embedded script (which would matter if it was later switched out for something written in another language). -
G-Man Says 'Reinstate Monica' over 5 years@Kusalananda: (1) Interesting. Does your answer do better? (2) Your answer does the same.
-
Kusalananda over 5 yearsThe first part of my answer ignores the hashbang, but the second one doesn't. I never pass the decrypted script as data on stdin, so standard input will behave as expected.
-
Jack Brown over 5 yearsWorks. Thank you. I realised that I need to provide wildcard using \* even to my original script. Example test1.sh cat* would show only one file, while test1.sh cat\* shows all cat* files.
-
Jack Brown over 5 yearsYes, I had -s in my original question. Somehow I edited it many times, it didn't work anyway. It is indeed difficult to figure out that missing part -- "$@" at the end, still is.