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

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.

Related

Can someone explain to me what stty does?

I have a shell script inside Docker. This is not mine, unfortunately I get the following error when running it. But only under Ubuntu 20.04 and Docker 19.03.
stty: 'standard input': Inappropriate ioctl for device
Here a cutout of line 126 from the script. What makes the stty echo and stty -echo? And why does the script run without problems on an system without docker?
askNoEcho() {
PROMPT=$1
DEFAULT=$2
stty -echo
ask "$PROMPT" "$DEFAULT"
stty echo
echo ""
}
askNonBlankNoEcho() {
PROMPT=$1
DEFAULT=$2
while true; do
stty -echo
ask "$PROMPT" "$DEFAULT"
stty echo
echo ""
if [[ -n "$response" ]]; then
break
fi
echo "A non-blank answer is required"
done
}
stty sets the terminal modes on the terminal connected to stdin. If stdin is not a terminal (eg, it's been redirected from a file when running your script), then it will display the error you show
stty -echo turns off echoing of input keystrokes. Normally when you type in a terminal, the characters you type are echoed back so you can see what you typed. stty echo turns echoing back on. The net effect is to disable echoing for the line entered in response to the prompt -- so it will print a prompt and wait for input, (and return the input), but that input will not be visible on the screen. This is commonly done for entering a password or passphrase.
There are many other terminal modes and settings that can be changed or queried with stty with various arguments. The manual page (man stty) will give you lots of additional info.
Try
$ stty echo < ~/tmp/some.file
stty: standard input: Inappropriate ioctl for device
so, stty expects its standard input to be a terminal.
Therefore, if you run on your terminal
$ stty echo
you will receive no errors.

how to prevent scp/ssh from trying to read standard input

I am passing a file from one server to another using authorized keys.
However, in the event keys are no longer valid, the script is being asked for the password on and on.
I have tried
scp ${user}#$host}:/tmp ./file1 </dev/null
but I still get the prompt.
The only time when this works is if I run it off schedule like this:
echo "scp ${user}#$host}:/tmp ./file1" | at now
In this case it will correctly error out if keys are no longer valid.
But how can I create a blank input stream, that will not be prompting the user if the script is run interactively?
#David:
echo "" | scp ${user}#${host}:/tmp ./file1 </dev/null
didn't help, same response, so it may need to have an stty command to zero tty input, I'm guessing now, per Kenster's note.
But how can I create a blank input stream, that will not be prompting the user if the script is run interactively?
Instead of that disable password authentication.
scp -o BatchMode=yes -o PasswordAuthentication=no -o PubkeyAuthentication=yes ...
This might work as well
scp ${user}#${host}:/tmp ./file1 /dev/tty0 > /dev/null

Can't get bash script to answer prompt with expect

My ssh access is restricted to a Google Authenticator verification code prompt. I'd like to have a script that programmatically answers that prompt.
Context:
The variable
($1) passes correctly to the script - it's the verification code.
The sshfs command works in terminal.
The prompt Verification code: comes with space and a key symbol at the end.
[EDIT] Just to make sure we don't switch to security discussions here, please note that of course I also use SSH keys, additionally to this Google Authenticator. As the Authenticator verification code expires every x seconds it does not matter that others could intercept it.
Result:
The disk mounts (I can see it with df -h), but is empty... Kind of same behavior as when the Verification code is wrong, or maybe it doesn't have the time to execute?
Shell script:
#!/bin/bash
expect_sh=$(expect -c "
spawn /usr/local/bin/sshfs username#123.123.1.123:/path/to/folder/RAID1 /Users/username/Desktop/RAID1 -o defer_permissions -o volname=RAID1
expect \"Verification code:\"
send \"$1\r\";
")
echo "$expect_sh"
Thanks
I'm afraid, I have to answer no.
There are some issues:
Having password has argument could reveal your password to other users with a simple
ps axw
Having password stored into a variable could reveal your password to other users with a simple
ps axeww
Having passord transmited via STDIN could be easy to trace.
For this and a lot of other reason, ssh (and sftp) refuse to trasnsmit secrets via arguments, variables or STDIO.
Before asking for password, there is a lot of verification, then the use of a secured dialog (working with direct TTY or with some boxes on X DISPLAY).
So using expect or passing secret as arguments is not directly possible with ssh.
But.
You could connect ssh server by using secret key:
ssh-keygen -b 4096
Enter file in which to save the key (/home/user/.ssh/id_rsa):
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/user/.ssh/id_rsa.
Your public key has been saved in /home/user/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:q/2fX/Hello/World/Lorem/Ipsum/Dolor/Sit/Amet user#localhst
The key's randomart image is:
+---[RSA 4096]----+
| .=o=E.o|
| .. o= o |
| o+ +=... |
| .o+ o+o. |
| . +.oS.oo |
| . *.= . ... |
| o =. oo. |
| ... +o. |
| .ooo oooo.|
+----[SHA256]-----+
Then now, you have to send your /home/user/.ssh/id_rsa.pub to be stored in authorized_keys file in the server you try to connect to (this files is located in $HOME/.ssh/ or in /etc, but could be located elsewhere, depending on sshd.conf in server).
The reason this isn't working is that you are running expect in a subshell inside the command substitution.
This would be a regular harmless useless use of echo if it weren't for the fact that you hope and expect the process to remain alive.
Just take out the variable capture and run expect as a direct descendant of your current script. If you really require your output to be available as a variable when it's done, maybe try something like
#!/bin/bash
t=$(mktemp -t gauthssh.XXXXXXXXXX) || exit
trap 'rm -f "$t"' EXIT ERROR INT HUP TERM # clean up temp file when done
expect -c "
spawn /usr/local/bin/sshfs username#123.123.1.123:/path/to/folder/RAID1 /Users/username/Desktop/RAID1 -o defer_permissions -o volname=RAID1
expect \"Verification code:\"
send \"$1\r\";
" | tee "$t"
expect_sh=$(<"$t")
You can construct a solution with screen(1)
I tested the script below. It's not especially robust, and
you'll need to make some changes according to your enviroment.
#!/bin/sh
screen -d -m -S sshtest sh -c "ssh -l postgres localhost id > output"
pass="77c94046"
ret="$(printf '\n')"
while true; do
screen -S sshtest -X hardcopy
grep -q 'password:' hardcopy.0 && break
sleep 1
done
grep -v '^$' hardcopy.0
echo -n "$passenter" | xxd
screen -S sshtest -X stuff "$pass"
screen -S sshtest -X stuff "$(printf '\r')"
sleep 1
cat output
The idea is to set up a screen running your command that redirects
its output to a local file. Then you take screen grabs in a loop and
look for your expected prompt with grep. Once you find it, use the
'stuff' command in screen to push your password into the terminal
input (i.e. screen's pty). Then you wait a bit and collect your
output if needed. This is just proof of concept code, a robust
solution would do more error checking and cleanup, and wait for
the screen to actually exit.

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..."

Password hiding in shell script

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"

Resources