Expect Script - If SSH fails, use telnet - expect

I am iterating over a list of IP addresses and loging into them with SSH. But, some of them do not have SSH, but rather, only telnet. How do I tell Expect that: if SSH fails, try again but using telnet?
I know that a message "connection refused" is outputted when SSH fails, but when I try to use that as a condition, expect doesn't work with it...after the SSH attempt is refused, the script breaks.
Here is a code snippet - this is not working:
foreach line $MACHINES {
spawn ssh -q $USER#$line
expect {
-ex "ssh: connect to host $line port 22: Connection refused" {
spawn telnet -l $USER#$line; continue
}
}
}
Thank you so much in advance,
D

Have timeout handle the failed connection after your expect. Some systems may not even be kind enough to spit out connection refused. The following was tested on a system that does not inform the user it's spawn ssh timed out.
#!/usr/bin/env expect
# invoke example ./telnet.exp 127.0.0.1 username password
set IP [lindex $argv 0]
set User [lindex $argv 1]
set Password [lindex $argv 2]
# Set timeout from default 10 seconds
set timeout 5
# connect without asking yes/no for known_hosts
spawn ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $User#$IP
# I'm not being cheeky, I left out the p/P because I avoid regex where possible
expect {
-nocase "refused" { spawn telnet $IP }
timeout { spawn telnet $IP }
"assword:" { send "$Password\r" }
}
interact
Here is my suggested change to your snippet (this is untested)
foreach line $MACHINES {
spawn ssh -q $USER#$line
expect {
-ex "ssh: connect to host $line port 22: Connection refused" {
spawn telnet -l $USER#$line; continue
}
timeout {
spawn telnet -l $USER#$line; continue
}
}
}

Please pardon me for my belated response; I appreciate everyone's contribution.
I ended up using this little block successfully. I believe the "wait" command functions to remove the SSH process from the process list.
expect {
eof {wait;spawn telnet -l $USER $line}
}

Had to clean up some users on cisco routers (mixed bag ssh/telnet). Found this thread. Thanks for pointing me in a direction.
#!/usr/bin/expect
set arg1 [lindex $argv 0]
set arg2 [lindex $argv 1]
set timeout 120
set username "router_username"
set password "router_password"
spawn telnet $arg1
expect {
-re "Connection refused" { spawn ssh -o StrictHostKeyChecking=no $arg1 -l $username }
"Username" { send "$username\n"; }
}
expect "Password" { send "$password\n"; }
expect "#$" { send "conf t\n" ; }
expect "config)#$" { send "no username $arg2\n"; }
expect {
-re "This operation will remove all username*" { send "\n"; exp_continue }
"config)#$" { send "exit\n"; }
}
expect "#$" { send "wr\n" ; }
expect "#$" { send "end\n" ; }

Don't do that. If an attacker is able to bring down the ssh service on that box, or man in the middle that box, he could force unencrypted telnet and get credentials to log into that server.
Don't try to use telnet as fallback for ssh. A script which is doing so can be easily exploited from remote.

Related

How to write a conditional expect script to accept different set of passwords based on servers?

My requirement is to write an expect script that will expect different set of passwords for different set of jump servers. Trouble is that I cannot generalize the
expect 'Password:'
as it is common between different servers. So my only way to identify the server is to read the banner before hand and check for a specific string and send the password accordingly.
I do not know how to read the banner in an expect command for spawn ssh
My code is as below
#!/usr/bin/expect
set host [lindex $argv 0]
set pass1 'xxx'
set pass2 'yy'
spawn ssh $host
expect "Password:*"
send "$pass1\r"
expect {
"Password:*" {
send "$pass2\r"
exp_continue
}
}
It seems strange that you would need to identify the server based on some string in a banner it returns when you already have its identity in the $host variable. Unless the specified hostname resolves to multiple servers that do not share a common password.
Assuming that situation and a banner returned by each host saying "Welcome to servername", you can do something like this:
#!/usr/bin/expect
set host [lindex $argv 0]
spawn ssh $host
expect {
-ex {Welcome to server1} {
set pass "xxx"
}
-ex {Welcome to server2} {
set pass "yy"
}
default {
# Something unexpected happened
exit 1
}
}
expect {
-ex "Password: " {
send "$pass\r"
}
}
This first looks for the welcome message and sets the pass variable to the password for the specific server. Then it sends the selected password to the server after it finds the Password prompt.

Run command on cisco switch

I made the below script to login to a switch and execute a command. as soon as i execute the script it logins to switch and exits without running the command.
#!/usr/bin/expect -f
set timeout 3000
spawn bash -c "ssh ananair#10.60.255.100"
expect "*assword:"
send "pass#123\n"
expect "*#"
send "show interfaces status"
I suspect the problem is the missing \n in your final send. If you simply send "string", then expect never hits Enter at the end of the command, And since there's no final expect, it figures its work is done, and exits, probably even before it has a chance to echo the command it sent but never executed.
Consider the following:
#!/usr/bin/expect -f
set timeout 3000
spawn ssh switchhostname # no need to run this inside a shell
expect "ssword:"
send "1ns3cur3\n" # it would be better to use key based auth...
expect "#"
send "term len 0\n" # tell the switch to avoid "More" prompts
expect "#"
send "show interfaces status\n" # note the final "\n"
expect "#" # don't quit until the "show" command finishes
You might also consider getting access to this information via SNMP.
try \r instead of \n
I have my expect script that I use to login to my cisco switches for me. and then I use interact from the expect script that leaves me at the cisco prompt.
I would need to rework it to not show passwords but I can definitely help you out.
I didn't need timeouts in mine.
#!/usr/bin/expect
proc enable {} {
expect "assword:" {
send "<enable password>\r"
expect "#" {
}
}
}
proc login {} {
send "<login password>\r"
expect {
"failed" {
send "<username>\r"
enable
}
">" {
send "en\r"
enable
}
"#" {
}}
}
set user_computer_attached [lindex $argv 0]
set user_computer [split $user_computer_attached "#"]
spawn ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 \
-oGlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null \
-oStrictHostKeyChecking=no $user_computer_attached
expect_after eof {
wait
spawn telnet [lindex $user_computer 1]
expect "name:"
send [lindex $user_computer 0]
send "\r"
expect "assword:" {
login
}
}
expect "assword:" {
login
}
interact

Expect script issue with if statement

I trying different approach to update existing expect script that I have inherit...
What I trying to achieve is to check Patch Level and depending on that, execute a set of commands.
But I can't make it work. :(
I tried as to below example and also tried with 'if' statement but I am clearly to fresh to solve this task.
I would be very grateful for some feedback what I am doing wrong.
#!/usr/bin/expect -f
# Destination: user#host
set destination [lindex $argv 0]
# Destination password
set pass [lindex $argv 1]
# Expect timeout
set timeout 1000
spawn ssh $destination
expect {
"hostname nor servname provided, or not known" {
exp_close
}
"yes/no)?" {
send "yes\n"
exp_continue
}
"assword:" {
send "$pass\n"
exp_continue
}
"#" {
log_file "./tmp/$destination.log"
send "cat /etc/*release | grep PATCHLEVEL\n"
expect {
"PATCHLEVEL = 3" {
send "zypper ar http://xxx.xxx.xxx.xxx/up2date/SLES11-SP3-LTSS-Updates/sle-11-x86_64/ SLES11-SP3-LTSS-Updates && zypper mr -r SLES11-SP3-LTSS-Update;zyppe
r ref -s;zypper --non-interactive update\n"
log_file
send "exit\n"}
"PATCHLEVEL =4 " {
send "zypper --non-interactive update\n"
log_file
send "exit\n"}
exp_close
}
}
}

Is it possible to access an expect-spawned process from other expect scripts, called in bash

I have an expect script that opens a telnet session and performs the authorization. After the authorization has been performed, I want to be able to call from bash other expect scripts, that perform various actions in the same session.
Is it possible?
Not exactly getting what you want because if you provide some code or script then batter but anyway i just having bash script which only open telnet session which is first part what you want now let me know what next part while gone through script so i can modify my script as you want.
#!/bin/bash
testUserAction()
{
expect<<EOF
set timeout 200
spawn telnet $1
expect "login:"
send "testuser\r"
expect "Password"
send "12345678\r"
;;;;;;;;;;;;;#rest of expect which you want
EOF
}
testUserAction 171.172.12.1
It is possible see below example:
#!/usr/local/bin/expect
set user "user"
set host "host"
set pass "password"
spawn telnet $host
set timeout 10
expect {
timeout {
puts "Unable to connect to $host"
exit 1
}
"login: " {
send -- "$user\r"
exp_continue
}
"assword: " {
send -- "$pass\r"
}
}
#Call the other expect script on host
send -- "./test.exp $user\r"
expect {
"($user)" {
puts "OK got it!"
}
}
And then the test.exp script which should be in users home directory on host
#!/usr/local/bin/expect
set name [lindex $argv 0]
puts "Welcome ($name)"

Linux expect send weird constructions

I am trying to figure out how expect working. AFAIK the expect script consists of "expect" and "send" statements. So for each approporiate "expect" statement which appears on screen the "send" statement is called. Also the command "interact" means that controlling is passed back to user and he is able to interact with the terminal. Correct me if I am wrong. Those two statements works like a charm.
1st:
#!/usr/bin/expect
spawn ssh -q localhost;
# Handles following message: "Are you sure you want to continue connecting (yes/no)?"
expect "yes";
send "yes\r";
interact;
2nd:
#!/usr/bin/expect
spawn ssh -q localhost;
# Handles following message: "pista#localhost's password:"
expect "assword";
send "password\r";
interact;
I've found on internet that something like following code should combine those two examples into one:
#!/usr/bin/expect
spawn ssh -q localhost "uname -a";
expect {
"*yes/no*" { send "yes\r" ; exp_continue }
"*assword:" { send "password\r"; interact }
}
But this example exits immediatelly after sucesfull login (seems like "interact" is not workig here, see output bellow)
[pista#HP-PC .ssh]$ ./fin.exp
spawn ssh -q localhost uname -a
pista#localhost's password:
Linux HP-PC 3.6.6-1.fc16.x86_64 #1 SMP Mon Nov 5 16:56:43 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
[pista#HP-PC .ssh]$ set | grep SHLV
SHLVL=2
Three questions:
What those weird expect syntax mean, only possible explaination to me is that there is no emphasis on patterns order in this "big" expect ?
Can you please clarify what is exactly exp_continue doing, seems to me like "goto" statement to expect which invoked this ?
Why is not interact working here ?
Many thanks
1. This syntax means that you use consecutive expect statements, it's easier. For example, this will try SSH or telnet if SSH fails
#!/usr/bin/expect
set remote_server [lrange $argv 0 0]
set timeout 10
spawn ssh -M username#$remote_server
while 1 {
expect {
"no)?" {send "yes\r"}
"denied" {
log_file /var/log/expect_msg.log
send_log "Can't login to $remote_server. Check username and password\n";
exit 1
}
"telnet:" {
log_file /var/log/expect_msg.log
send_log "Can't connect to $remote_server via SSH or Telnet. Something went definitely wrong\n";
exit 2
}
"failed" {
log_file /var/log/expect_msg.log
send_log "Host $remote_server exists. Check ssh_hosts file\n";
exit 3
}
timeout {
log_file /var/log/expect_msg.log
send_log "Timeout problem. Host $remote_server doesn't respond\n";
exit 4
}
"refused" {
log_file /var/log/expect_msg.log
send_log "Host $remote_server refused to SSH. That is insecure.\n"
log_file
spawn telnet $remote_server
}
"sername:" {send "username\r"}
"assword:" {send "password\r"}
"#" {break}
}
}
2. exp_continue is telling expect to "continue to expect", that is to continue with the event processing. Without this instruction your expect { ... } block will stop. In the example above it is the line:
"#" {break}
First it breaks from the while loop, and then without exp_continue it stops executing expect { ... } block and going for the next instructions (not shown in the example above).
3. You have an error in your code. I've slightly modified your code, to make it work.
#!/usr/bin/expect
spawn ssh -q localhost
expect {
"*yes/no*" { send "yes\r" ; exp_continue }
"*assword:" { send "password\r"; exp_continue;}
"~" {send "uname -a\r"; interact;}
}

Resources