I'm trying to get a SSH banner from a bunch of systems. Unfortunately, I need to enter the password before the script can move on to the next system.
user#pc:~$ for i in {1..10}; do ssh 192.168.0.$i; done
WARNING: Unauthorized access to this system is forbidden and will be
prosecuted by law. By accessing this system, you agree that your actions
may be monitored if unauthorized usage is suspected.
user#192.168.0.1's password:
Is there a way to ignore the password prompt and proceed to the next system in order to get the banner alone?
Disable password authentication, that way ssh will not try to get the password from you.
ssh example.com -o PasswordAuthentication=no
You can explicitly disable all authentication methods to make sure, the ssh doesn't accidentally open a shell (thus block).
Or you can just timeout 10 ssh to make it exit after a specified amount of time.
Related
I was wondering how openssh gets the password when login, cause I got stuck in automating entering passwords to the similar tools in linux which requires getting password from tty like ssh.
Tried to understand sshpass and found that sshpass forks a child process with the same pid then enters the password under the child process.
Don't know if my guess was right that ssh needs to check the right pid since I cannot stdin to the current tty using another process to enter the ssh password.
For security reasons, many programs requires a password interactively from users.
Quite many programs uses the following kind of check before reading a password from stdin:
if (isatty(STDIN_FILENO) == 0)
{
exit(EXIT_FAILURE);
}
So the program allows password only from a terminal. That way it try to prevent non-interactive password entering.
sshpass is just a tool for:
fooling ssh into thinking it is getting the password from an interactive user. [from man page of sshpass]
For fooling ssh, sshpass creates and open a pseudo terminal, and gives that for stdin of ssh. fork() is needed because sshpass must write a password to ssh via the pseudo terminal.
This way stdin of ssh process is a terminal, and isatty test will be passed.
o's!
Maybe you can help me with this. I can't find an answer to my specific questions, because there is an obvious solution which I'm not allowed to use. But first things first, the context:
In my company, which is a service provider, we administrate a bunch of
Linux servers. Some of my colleagues has for a long time been running
a BASH script from a source server, that then performs some tasks over
SSH on a number of remote Linux servers. The tasks it performs has to
be executed as root, so what the script does is it authorizes the
source server as root on the remote Linux servers via SSH (the remote
servers has the source servers public SSH key). Then what happened is
a new security policy was enforced and now root login over SSH is
denied. So the mentioned method no longer works.
The solution I keep finding, which we are by policy not allowed to do, is to create an entry in the sudoers file allowing sudo to root without password for the specific user.
This is the terms and they have to obey that. The only procedure that is allowed is to log on to the target server with your personal user, and then sudo su - to root WITH password.
Cocky as I apparently was, I said, "It should be possible to have the script do that automatically", and the management was like "Cool, you do it then!" and now I'm here at Stack Overflow,
because I know this is where bright minds are.
So this is exactly what I want to do with a BASH script, and I do not know if it's possible or how it's done, I really hope you can help me out:
Imagine Bob, he's logged into the source server, and he wants to
execute the script against a target server. Knowing that root over SSH
doesn't work, the authorization part of the script has been upgraded.
When Bob runs the script, it prompts him for his password. The
password is then stored in a variable (encrypted would be amazing) and
the script then logs on the target server as his user (which is
allowed) and then automatically elevates him to root on the target
server using the password he entered on the source server. Now the
script is root and it runs its tasks as usual.
Can it be done with BASH? and how?
UPDATE:
The Script:
## define code to be run on the remote system
remote_script='sudo -S hostname'
## local system
# on the local machine: prompt the user for the password
read -r -p "Enter password for $host: " password
# ...and write the password, followed by a NUL delimiter, to stdin of ssh
ssh -t 10.0.1.40 "$remote_script" < <(printf '%s\0' "$password")
The error:
[worker#source ~]$ sh elevate.sh
Enter password for : abc123
elevate.sh: line 10: syntax error near unexpected token `<'
elevate.sh: line 10: `ssh -t 10.0.1.40 "$remote_script" < <(printf '%s\0' "$password")'
First: Because it exposes plaintext passwords to the remote system (where they can be read by an attacker using diagnostic tools such as strace or sysdig), this is less secure than correctly using the NOPASSWD: flag in sudoers. If your security team aren't absolute idiots, they'll approve a policy exemption (perhaps with some appropriate controls, such as having a dedicated account with access to a setuid binary specific to the command being run, with authentication to that account being performed via public key authentication w/ the private key stored encrypted) rather than approving use of this hack.
Second: Here's your hack.
## define code to be run on the remote system
remote_script='sudo -S remote_command_here'
## local system
# on the local machine: prompt the user for the password
read -r -p "Enter password for $host: " password
# ...and write the password, followed by a NUL delimiter, to stdin of ssh
ssh "$host" "$remote_script" < <(printf '%s\0' "$password")
Allright, this is not the final answer, but I think I'm getting close, with the great help of CharlesDuffy.
So far I can run the script without errors on a remote server, that already has the publickey of my source server. However the command I execute doesn't create a file as I tell it to on the remote system.
However the script seems to run and the password seems to be accepted by the remote system.
Also I have to change in the sudoers on the remote host the line "Defaults requiretty" to "Defaults !requiretty", else it will tell me that I need a TTY to run sudo.
#!/bin/bash
## define code to be run on the remote system
remote_script='sudo -S touch /elevatedfile'
## local system
# on the local machine: prompt the user for the password
read -r -p "Enter password for $host: " password
# ...and write the password, followed by a NUL delimiter, to stdin of ssh
ssh -T 10.0.1.40 "$remote_script" < <(printf '%s\0' "$password")
UPDATE: When I tail /var/log/secure on the remote host I get the following after executing the script, which seems like the password is not being accepted.
May 11 20:15:20 target sudo: pam_unix(sudo:auth): conversation failed
May 11 20:15:20 target sudo: pam_unix(sudo:auth): auth could not identify password for [worker]
May 11 20:15:20 target sshd[3634]: Received disconnect from 10.0.1.39: 11: disconnected by user
May 11 20:15:20 target sshd[3631]: pam_unix(sshd:session): session closed for user worker
What I see on the source server, from where I launch the script:
[worker#source ~]$ bash elevate.sh
Enter password for : abc123
[sudo] password for worker:
[worker#source ~]$
Just make a daemon or cron script running as root, that in turn will check for any new scripts in specified secure location (ie. DB that it only has READ access to), and if they exist, it will download and execute them.
I want to use SSH inside a script, but this script is not going to be executed on my machine.
In my implementation there are two limitations.
I can not work outside shell's standards,therefore i can not use expect because i do not know if it will be available on this machine.
I can not expect that this machine will have public keys for the SSH.
What are the possible options-solutions ?
How can i provide ssh with the requested password with an automated and secure way without adding extra dependencies?
Will it be possible to provide the password inside the script?
Thank you all in advance :)
Install sshpass, then launch the command:
sshpass -p "yourpassword" ssh -o StrictHostKeyChecking=no yourusername#hostname
For security reasons you must avoid providing password on a command line otherwise anyone running ps command can see your password. Better to use sshpass utility like this:
#!/bin/bash
export SSHPASS="your-password"
sshpass -e ssh -oBatchMode=no sshUser#remoteHost
You might be interested in How to run the sftp command with a password from Bash script?
First of all: Don't put secrets in clear text unless you know why it is a safe thing to do (i.e. you have assessed what damage can be done by an attacker knowing the secret).
If you are ok with putting secrets in your script, you could ship an ssh key with it and execute in an ssh-agent shell:
#!/usr/bin/env ssh-agent /usr/bin/env bash
KEYFILE=`mktemp`
cat << EOF > ${KEYFILE}
-----BEGIN RSA PRIVATE KEY-----
[.......]
EOF
ssh-add ${KEYFILE}
# do your ssh things here...
# Remove the key file.
rm -f ${KEYFILE}
A benefit of using ssh keys is that you can easily use forced commands to limit what the keyholder can do on the server.
A more secure approach would be to let the script run ssh-keygen -f ~/.ssh/my-script-key to create a private key specific for this purpose, but then you would also need a routine for adding the public key to the server.
AFAIK there is no possibility beside from using keys or expect if you are using the command line version ssh. But there are library bindings for the most programming languages like C, python, php, ... . You could write a program in such a language. This way it would be possible to pass the password automatically. But note this is of course a security problem as the password will be stored in plain text in that program
I completely agree with everybody who says this is almost certainly a terrible idea. It is extremely likely to allow others to attack your computers.
USE AT YOUR OWN RISK AFTER EVALUATING THE SECURITY HAZARDS
Answer
Make a program /path/to/saypass which outputs the password, such as
#!/bin/sh
echo 'secret'
Make it executable with
chmod +x /path/to/saypass
Then this is the main command:
SSH_ASKPASS="/path/to/saypass" DISPLAY=anything setsid ssh username#hostname [farcommand]
This
sets the two environment variables SSH_ASKPASS and DISPLAY
and then runs setsid
which then runs ssh without a controlling terminal
which connects to the far hostname
... runs saypass locally to get the password
... tells it to the far server
... and assuming it's correct
which then runs farcommand (if given), or an interactive shell.
I normally test with date or hostname for the optional farcommand.
There are lots of places for this to go wrong.
Explanation
The trick to this is that standard Linux command line ssh has a couple of environment variables you can use to choose a program which gets executed to supply the password.
ssh(1) manual page says:
SSH_ASKPASS If ssh needs a passphrase, it will read the passphrase from the current terminal if it was run from a terminal. If ssh does not have a terminal associated with it but DISPLAY and SSH_ASKPASS are set, it will execute the program specified by SSH_ASKPASS and open an X11 window to read the passphrase.
So: you need a program (shell script or any other kind) which will output the password. Then you need to convince ssh to use it:
With SSH_ASKPASS set to /path/to/saypass
With DISPLAY set to something silly
With no controlling terminal (this is what setsid does)
Which you put together in the following sh command:
SSH_ASKPASS="/path/to/saypass" DISPLAY=anything setsid ssh username#hostname [command]
ssh will execute
/path/to/saypass "username#hostname's password:"
Fingerprint check
If the fingerprint is needed, where you'd normally see the message
The authenticity of host '*hostname* (*ipaddress*)' can't be established.
ECDSA key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
Are you sure you want to continue connecting (yes/no)?
Then ssh will run your command like this:
/path/to/saypass "Please type 'yes' or 'no':"
All-in-one script
The following is a single script for creating, using, and removing a saypass within the main script. Everyone will tell you do not put plaintext passwords in files and also never hardcode a password. They tell you this for good reason: it will cause you a lot of trouble. Use at your own risk.
#!/bin/sh
echo "#!/bin/sh\necho 'secret';rm -f /tmp/saypass.$$" > /tmp/saypass.$$
chmod 775 /tmp/saypass.$$
SSH_ASKPASS="/tmp/saypass.$$" DISPLAY=anything setsid ssh "$#"
SCP
This also works for scp, the copy program on top of ssh:
SSH_ASKPASS=/path/to/saypas DISPLAY=anything setsid scp username#hostname:/path/to/farfile .
Caveat
Really don't use this except in dire, dire, circumstances, such as where you have hundreds of computers and you can't install anything like ssh keys, sshpass even expect.
If you do use it, please don't tell anyone I told you how to do it. It really is terrible.
I don't know what the man page means about "open an X11 window", no such thing happens in my testing.
Tested on
OpenSSH_6.6.1p1 Ubuntu-2ubuntu2, OpenSSL 1.0.1f 6 Jan 2014 on Ubuntu 14.04.1 LTS,
OpenSSH_7.2p2 Ubuntu-4ubuntu2.1, OpenSSL 1.0.2g 1 Mar 2016 on Ubuntu 16.04.2 LTS
OpenSSH_7.6p1 Ubuntu-4ubuntu0.3, OpenSSL 1.0.2n 7 Dec 2017 on Ubuntu 18.04.5 LTS
In order to improve security a bit, I'd like to run the keychain agent as a different user. This shall prevent users who hijack my system from gaining the actual private key, while maintaining the ability to use it to authenticate ssh and scp connections.
What have I tried?
I created a user called agent who should store the private key and run the ssh-agent process. I created a script file to set up the right permissions for the socket:
#!/bin/sh
export EVAL=$(keychain --eval -q)
eval $EVAL
chmod 770 $(dirname $SSH_AUTH_SOCK) $(dirname $GPG_AGENT_INFO)
chmod 660 $SSH_AUTH_SOCK $(echo $GPG_AGENT_INFO | sed 's/:.*//')
echo $EVAL
And call that one in my .bashrc, eval'ing it.
But when I now connect to a server via ssh, I get
$ ssh server
Error reading response length from authentication socket.
Any hints?
keychain seems to use either an already running ssh-agent or gpg-agent, and start one if needed.
ssh-agent checks if the user id of the running process matches the id of the user connecting through the unix domain socket (with the exception of root). If you run the agent in debug mode you'll see the corresponding error message. In this case the socket is immediately closed, so you'll get the error message you mention above - you're probably using ssh-agent on your system. That means what you try to do won't be possible using ssh-agent (unless you don't modify it).
It should work if you use gpg-agent with the --enable-ssh-support option as replacement for ssh-agent, but you should be aware that this setup doesn't really increase security. With the permissions you're trying to set, it would allow every user that has access rights to the socket the to authenticate as you using the added key once it has been unlocked, so it's actually less secure.
I know it is possible to write a shell script which passes your hard-coded password to a ssh connection authentication (using expect). However what I need is slightly more complicated.
At my university I have a desktop computer appointed to me. I can connect remotely to this computer by first making a ssh connection with some server, then making another ssh connection from that server to my appointed desktop computer. This goes like:
localuser#localcomputer:~$ ssh -X username#serveraddress
username#serveradress password:
server$ ssh -X username#remotecomputeraddress
username#remotecomputeraddress password:
username#remotecomputer:~>
Is there a way to write a script which could automate the above (i.e. performing two consecutive ssh connections)?
Thanks in advance!
ps: Both the local and the remote computers are running on Linux.
You can do this interactively with:
ssh -t -X username#serveraddress ssh -t -X username#remotecomputeraddress
Note that is not a pipe - the second ssh is the command to run on the connection created by the first ssh. The -t options are necessary to allocate the pseudo-ttys necessary for interaction (password gathering as well as the ultimate goal - an interactive session on the remote system). Wrapping it up with expect left as an exercise for the reader.... ;-)
Bonus points for setting up proper private/public key pairs and ssh-agent so that the passwords aren't necessary (unless, of course, that is disallowed for security reasons).
Yes, you can do this.
Presuming you have your except script in the expect_script:
cat expect_script | ssh -X username#serveraddress sh -s
In this expect_script you must run ssh -X username#remotecomputeraddress.
And of course you can install public keys on the both hosts and use passwordless authentication.
I wrote something to do this with bang paths a while back:
http://stromberg.dnsalias.org/~strombrg/deep-ssh.html
So you'd set up passwordless, passphraseless authentication (or use an agent for the passphrase), like:
http://stromberg.dnsalias.org/~strombrg/ssh-keys.html
And then:
deep-ssh username#serveraddress!username#remotecomputeraddress command
If bash complains about the !, you can just escape it with a backslash.
The old timers will recognize that this is how UUCP paths were specified.