Password hiding in shell script - shell

I have created a shell script as given below.And I need to hide the password, which i will be given when the script prompt for that. If I run the below script written inside the single quotes in my local machine, it will hide the password. But if I add the SSH part it wont.
What should I do for hiding password ?
ssh root#10.3.2.0 'echo -n Password:;
read -s password;
echo;
echo $password;
'

Try using the -t option to ssh to force tty allocation.
ssh -t root#10.3.2.0 'echo -n Password:;
read -s password;
echo;
echo $password;'
EDIT: explanation of suggested solution
If not running a login-session but just a command instead by default SSH won't allocate a pseudo terminal device for the running process.
This behavior is not a bug a all, but delibarately chosen by design.
This way it is possible to pass binary data uninterpreted between different machines.
Maybe something like:
ssh whoever#wherever 'cat remote_file' | local_program
Passing data between two machines this way would almost be impossible if the data was filtered by a terminal driver in between - Think of the tons of escape sequences you would have to care about !
Without any terminal allocated there is no way of hiding user input. Any attempts to stty something will fail, as there isn't any terminal at all!
To make ssh allocate a pseudo terminal even if not running in a login-session you have to pass the -t parameter to force tty allocation.

Better use ssh keys, that way, no need to pass a password with the help of a ssh-agent. see a good how-to : https://wiki.archlinux.org/index.php/SSH_Keys

I am new to scripts, though I guess this might can help you :
#!/bin/sh
echo -n "Enter Password : "
stty -echo
read password < /dev/tty
stty echo
echo
echo -n "Enter Password again : "
stty -echo
read password1 < /dev/tty
stty echo
echo
echo "Password Entered are : $password and $password1"

Related

How to prevent text from being echoed over netcat (nc) when reading it with bash's read -s?

I have an authentication implemented in a bash script.
A user enters his password through this line of code:
read -s password
When entering the password over nc the password gets echoed like the -s option would not have been used.
This doesn't happen when running the script normally.
Is there an alternative to nc or an option for it that supports the -s option of bash's read function?
I tried this:
stty -echo
read -s password
stty echo
but the password still gets displayed.

how dangerous is to echo passwords via pipe to passwd

I've seen in more than one discussion that using echo to pipe to passwd is dangerous because one can get arguments provided to echo from process list.
I'm currently working on a tool, that changes passwords remotely via ssh. I do use echo method because I do not have root access to use usermod or chpasswd.
cmd:
echo -e 'old\nnew\nnew' | passwd
To check how easy it is - I tried. But could not get them.
this was my method:
#!/bin/bash
filename=$1
while true
do
echo "$(pgrep -af passwd)" >> "$filename"
sleep 0.1
done
I few times changed password via echo but could not seen anything. I think that sleep 0.1 may be a problem.
How easy it is to get it from process list and therefore how insecure it is to use it this way.
It depends on whether the shell you're using has echo as a builtin, or uses an external binary (/bin/echo). If it's an external binary, it'll be run as a subprocess with the password plainly visible in its argument list (via ps, pgrep, etc). If it's a builtin, an echo command in a pipeline will run as a subprocess, but it'll be a subshell with the same visible argument list as the parent shell (i.e. it's safe).
So it's probably safe, but there are several complications to worry about. First, if you're running this on a remote computer via ssh, you don't necessarily know what its default shell is. If it's bash, you're ok. If it's dash, I think you have a problem. Second, you don't just have to worry about the remote shell and/or echo command, you have to worry about every step along the path from your local script to that remote echo command. For example, if you use:
ssh user#computer "echo -e 'old\nnew\nnew' | passwd"
...then the remote echo is probably safe (depending on the remote shell), but as #thatotherguy pointed out the password will be visible in both the remote shell's (bash -c echo -e 'foo\nbar\nbar' | passwd) and in the local ssh process's argument list.
There's another complication, BTW: echo isn't consistent about how it handles options (like -e) and escapes in the output string (see this question for an example). You're much better off using printf (e.g. printf '%s\n' "$oldpass" "$newpass" "$newpass"), which is also a builtin in bash. Or, if you're sure you're using bash, you can use here-string (<<<string) instead. It doesn't interpret escapes at all, but you can use bash's $' ' construct to interpret them:
passwd <<<"$old"$'\n'"$new"$'\n'"$new"
This doesn't involve a subprocess at all (either /bin/echo or a subshell), so no worries about the process table.
Another possibility to avoid both the problems of uncertain remote shells and the password showing up in ssh's argument list is to pass the passwords via ssh's stdin:
ssh user#computer passwd <<<"$old"$'\n'"$new"$'\n'"$new"
If you were to invoke your echo|passwd command via ssh, e.g.:
client$ ssh example.com "echo -e 'foo\nbar\nbar' | passwd"
then the process you need to look at is not passwd but the user's login shell invoked by sshd on the remote side:
server$ until pgrep -af bash | grep passwd; do true; done
8387 bash -c echo -e 'foo\nbar\nbar' | passwd

Want to find status of sudo su - in multiple servers

I have a bash script, hostlist file and an expect script:
Below is the bash script to take inputs from hostlist file and keep looping ssh for multiple servers.
for x in $(cat hostlist); do
./sudoscript.exp $x
done
Below is the expect cum bash script I want to tun and collect outputs of sudo su - command. I just need to get outputs as '0 or non zero values in a file for successful run/execution of 'sudo su - '. I just need to simulate the execution and check if the command runs successfully or not with out actually changing user to admin by doing sudo su -.
#!/bin/bash
#!/usr/bin/expect
spawn ssh [lindex $argv 0]
expect "$"
send "sudo su -\r" exit ; echo $server:$? >> output
Can someone please suggest to complete the script above.
What exactly are you trying to do?
Maybe you're trying to see if it is possible to become root without a password? If that's the case, try:
for x in $(cat hostlist); do
echo $x
ssh $x sudo -l |egrep -w 'NOPASSWD:.*(ALL|/su)'
echo
done
sudo -l will list what you can run. It requires your password unless you have one or more commands that do not require your password (ssh won't run interactively when called with a command and without the -t flag. This is intentional since we don't want that).
The egrep command limits the results to just what can be done without a password as well as either ALL commands or else su itself. (Note, this won't find su if it's in an alias.)

A script command asks for username. Can I have it stored inside the script?

I have this command inside a script.
sudo openvpn --config ....
When it is executed it asks for a username, and then for a password.
Is it possible to store inside the script the username?
In other words to avoid typing it each time this script is being executed?
(I am using Linux Ubuntu)
Use the configuration directive
auth-user-pass filename
...where filename is a file with username on the first line, and password on the second. (If you don't want the password to ever touch disk, this password can be a socket on which your script passes through user input).
For instance:
#!/bin/bash
# ^- IMPORTANT: use bash, not /bin/sh
# clearing IFS and using read -r makes trailing whitespace, literal backslashes, etc. work.
username="hardcoded value"
IFS= read -r -p "Password: " password
openvpn \
--config file.ovpn \
--auth-user-pass <(printf '%s\n' "$username" "$password")
The use of printf -- a builtin -- is important here: When calling only builtins, arguments aren't placed on the argv (and thus made accessible to anything on the system inspecting the process list).
Alternately, you can use the management-query-passwords directive [or the --management-query-passwords command-line option] to allow username and password to be requested and entered via the management socket (this protocol has its own extensive documentation).
I believe it is possible. You have use the pipe | and pipe the username to the scripts beginning if it is possible. I use a command in C++, which if I remember right changes the password for the user. It looks like:
sprintf(command, "echo -e \"%s\n%s\" | passwd %s",password,password,user);
So, since this is a shell, I would guess you could do something like:
echo -e '<username>\n<password>\n' | YourScript
In your case this may work:
echo -e `<username>\n<password>\n | sudo openvpn --config...
Of course, this assumes that there are no other things it will ask for. This is also untested. Read more on piping here.
Edit:
As mentioned by Charles Duffy the above will only work with an XSI-extended system and with programs that do not rely on TTY. I'm still not 100% certain but, I ready printf was cross compatible and script -c can be used to pipe tty. Here is the information on the script command. However, trying it in CentOS7, it looks like this would work:
printf '%s\n' "username" "password" | script -c "sudo openvpn --config..."
NOTE: I tried only piping to su and it worked after POSIXLY_CORRECT was set to 1.
Also, I think I may have misunderstood exactly what you wanted to do. If you wanted to store the username and password for the duration of the script you could do something like this:
printf "Input username:"
read username
printf "Input password:"
read -s password
printf '%s\n' "$username" "$password" | script -c "sudo openvpn --config..."

Temporarily remove the ssh private key password in a shell scriptI

I am required to deploy some files from server A to server B. I connect to server A via SSH and from there, connect via ssh to server B, using a private key stored on server A, the public key of which resides in server B's authorized_keys file. The connection from A to B happens within a Bash shell script that resides on server A.
This all works fine, nice and simple, until a security-conscious admin pointed out that my SSH private key stored on server A is not passphrase protected, so that anyone who might conceivably hack into my account on server A would also have access to server B, as well as C, D, E, F, and G. He has a point, I guess.
He suggests a complicated scenario under which I would add a passphrase, then modify my shell script to add a a line at the beginning in which I would call
ssh-keygen -p -f {private key file}
answer the prompt for my old passphrase with the passphrase and the (two) prompts for my new passphrasw with just return which gets rid of the passphrase, and then at the end, after my scp command
calling
ssh-keygen -p -f {private key file}
again, to put the passphrase back
To which I say "Yecch!".
Well I can improve that a little by first reading the passphrase ONCE in the script with
read -s PASS_PHRASE
then supplying it as needed using the -N and -P parameters of ssh-keygen.
It's almost usable, but I hate interactive prompts in shell scripts. I'd like to get this down to one interactive prompt, but the part that's killing me is the part where I have to press enter twice to get rid of the passphrase
This works from the command line:
ssh-keygen -p -f {private key file} -P {pass phrase} -N ''
but not from the shell script. There, it seems I must remove the -N parameter and accept the need to type two returns.
That is the best I am able to do. Can anyone improve this? Or is there a better way to handle this? I can't believe there isn't.
Best would be some way of handling this securely without ever having to type in the passphrase but that may be asking too much. I would settle for once per script invocation.
Here is a simplified version the whole script in skeleton form
#! /bin/sh
KEYFILE=$HOME/.ssh/id_dsa
PASSPHRASE=''
unset_passphrase() {
# params
# oldpassword keyfile
echo "unset_key_password()"
cmd="ssh-keygen -p -P $1 -N '' -f $2"
echo "$cmd"
$cmd
echo
}
reset_passphrase() {
# params
# oldpassword keyfile
echo "reset_key_password()"
cmd="ssh-keygen -p -N '$1' -f $2"
echo "$cmd"
$cmd
echo
}
echo "Enter passphrase:"
read -s PASSPHRASE
unset_passphrase $PASSPHRASE $KEYFILE
# do something with ssh
reset_passphrase $PASSPHRASE $KEYFILE
Check out ssh-agent. It caches the passphrase so you can use the keyfile during a certain period regardless of how many sessions you have.
Here are more details about ssh-agent.
OpenSSH supports what's called a "control master" mode, where you can connect once, leave it running in the background, and then have other ssh instances (including scp, rsync, git, etc.) reuse that existing connection. This makes it possible to only type the password once (when setting up the control master) but execute multiple ssh commands to the same destination.
Search for ControlMaster in man ssh_config for details.
Advantages over ssh-agent:
You don't have to remember to run ssh-agent
You don't have to generate an ssh public/private key pair, which is important if the script will be run by many users (most people don't understand ssh keys, so getting a large group of people to generate them is a tiring exercise)
Depending on how it is configured, ssh-agent might time out your keys part-way through the script; this won't
Only one TCP session is started, so it is much faster if you're connecting over and over again (e.g., copying many small files one at a time)
Example usage (forgive Stack Overflow's broken syntax highlighting):
REMOTE_HOST=server
log() { printf '%s\n' "$*"; }
error() { log "ERROR: $*" >&2; }
fatal() { error "$*"; exit 1; }
try() { "$#" || fatal "'$#' failed"; }
controlmaster_start() {
CONTROLPATH=/tmp/$(basename "$0").$$.%l_%h_%p_%r
# same as CONTROLPATH but with special characters (quotes,
# spaces) escaped in a way that rsync understands
CONTROLPATH_E=$(
printf '%s\n' "${CONTROLPATH}" |
sed -e 's/'\''/"'\''"/g' -e 's/"/'\''"'\''/g' -e 's/ /" "/g'
)
log "Starting ssh control master..."
ssh -f -M -N -S "${CONTROLPATH}" "${REMOTE_HOST}" \
|| fatal "couldn't start ssh control master"
# automatically close the control master at exit, even if
# killed or interrupted with ctrl-c
trap 'controlmaster_stop' 0
trap 'exit 1' HUP INT QUIT TERM
}
controlmaster_stop() {
log "Closing ssh control master..."
ssh -O exit -S "${CONTROLPATH}" "${REMOTE_HOST}" >/dev/null \
|| fatal "couldn't close ssh control master"
}
controlmaster_start
try ssh -S "${CONTROLPATH}" "${REMOTE_HOST}" some_command
try scp -o ControlPath="${CONTROLPATH}" \
some_file "${REMOTE_HOST}":some_path
try rsync -e "ssh -S ${CONTROLPATH_E}" -avz \
some_dir "${REMOTE_HOST}":some_path
# the control master will automatically close once the script exits
I could point out an alternative solution for this. Instead of having the key stored on server A I would keep the key locally. Now I would create a local port forward to server B on port 4000.
ssh -L 4000:B:22 usernam#A
And then in a new terminal connect through the tunnel to server B.
ssh -p 4000 -i key_copied_from_a user_on_b#localhost
I don't know how feasible this is to you though.
Building up commands as a string is tricky, as you've discovered. Much more robust to use arrays:
cmd=( ssh-keygen -p -P "$1" -N "" -f "$2" )
echo "${cmd[#]}"
"${cmd[#]}"
Or even use the positional parameters
passphrase="$1"
keyfile="$2"
set -- ssh-keygen -p -P "$passphrase" -N "" -f "$keyfile"
echo "$#"
"$#"
The empty argument won't be echoed surrounded by quotes, but it's there

Resources