Home > Net >  Bash :: SU command removes Variables from SCP Command?
Bash :: SU command removes Variables from SCP Command?

Time:09-06

I have a Bash (ver 4.4.20(1)) script running on Ubuntu (ver 18.04.6 LTS) that generates an SCP error. Yet, when I run the offending command on the command line, the same line runs fine.

The script is designed to SCP a file from a remote machine and copy it to /tmp on the local machine. One caveat is that the script must be run as root (yes, I know that's bad, this is a proof-of-concept thing), but root can't do passwordless SCP in my enviroment. User me can so passwordless SCP, so when root runs the script, it must "borrow" me's public SSH key.

Here's my script, slightly abridged for SO:

#!/bin/bash

writeCmd() { printf '%q ' "$@"; printf '\n'; }

printf -v date '%(%Y%m%d)T' -1

user=me
host=10.10.10.100
file=myfile
target_dir=/path/to/dir/$date

# print command to screen so I can see what is being submitted to OS:
writeCmd su - me -c 'scp -C me@$host:/$target_dir/$file.txt /tmp/.'
su - me -c 'scp -C me@$host:/$target_dir/$file.txt /tmp/.'

Output is:

su - me -c [email protected]://.txt/tmp/.

It looks like the ' ' character are not being printed, but for the moment, I'll assume that is a display thing and not the root of the problem. What's more serious is that I don't see my variables in the actual SCP command.

What gives? Why would the variables be ignored? Does the su part of the command interfere somehow? Thank you.

(NOTE: This post has been reedited from its earlier form, if you wondering why the below comments seem off-topic.)

CodePudding user response:

When you run:

writeCmd su - me -c 'scp -C me@$host:/$target_dir/$file.txt /tmp/.'

you'll see that its output is (something equivalent to -- may change version-to-version):

su - me -c scp\ -C\ me@\$host:/\$target_dir/\$file.txt\ /tmp/. 

Importantly, none of the variables have been substituted yet (and they're emitted escaped to show that they won't be substituted until after su runs).

This is important, because only variables that have been exported -- becoming environment variables instead of shell variables -- survive a process boundary, such as that caused by the shell starting the external su command, or the one caused by su starting a new and separate shell interpreter as the target user account. Consequently, the new shell started by su doesn't have access to the variables, so it substitutes them with empty values.


Sometimes, you can solve this by exporting your variables: export host target_dir file, and if su passes the environment through that'll suffice. However, that's a pretty big "if": there are compelling security reasons not to pass arbitrary environment variables across a privilege boundary.

The safer way to do this is to build a correctly-escaped command with the variables already substituted:

#!/usr/bin/env bash
#              ^^^^- needs to be bash, not sh, to work reliably

cmd=( scp -C "me@$host:/$target_dir/$file.txt" /tmp/. )
printf -v cmd_v '%q ' "${cmd[@]}"
su - me -c "$cmd_v"

Using printf %q is protection against shell injection attacks -- ensuring that a target_dir named /tmp/evil/$(rm -rf ~) doesn't delete your home directory.

  • Related