I am running the following simple telnet script which just logs into a machine and exits.
The same script works fine (goes through 1000 iterations) from one Linux server but fails (consistently) from another Linux server (fails after say 200 attempts).
In failure case, the number of iterations it takes to fail varies but failure is persistent.
#!/usr/bin/perl
use Net::Telnet;
my $loop = 0;
my $dumpfile = "dump.log";
my $inputfile = "input.log";
for ($loop =1; $loop <=1000; $loop++) {
print "===============Loop: $loop =====================\n";
$telnet = new Net::Telnet ( Timeout=>20, Errmode=>'die');
print "$telnet\n";
$telnet->open('sys-007');
$telnet->dump_log($dumpfile);
$telnet->input_log($inputfile);
$telnet->waitfor('/login: $/i');
$telnet->print('root');
$telnet->waitfor('/Password:$/i');
$telnet->print('007');
sleep 2;
$telnet->print('exit');
print "=================================================\n";
}
Script exits with:
pattern match read eof at ./telnettest.pl line 15 (i.e, waitfor('/login: $/i'); Line)
I tried the following to see what is going wrong:
In the client machine: (sys-007)
tcpdump -nvvv -w test.txt host <Server IP>
strings test.txt has:
Successful attempt Log:
sys-007 (ttyp0)
^Fl$4
^!^Fl$
login:
^Fl$4
^Fl$4
root
^(^Fl*
root
bP"u
^Fl*4
bP5u
^.^Fl*
Password:
^Fl*4
^Fl*4
007
^7^Fl6
^Fl64
^9^Fl6
Terminal type? [xterm]
^Fl64
^Fl64
exit
^Fl<4
^Fl<
exit
^Fl<
Failed Attempt:
sys-007 (ttyp0)
*^hn
+^hn
No login: prompt!
In server machine: (Linux Server)
[telnet#server]~% netstat --inet -a | grep telnet | grep sys-007
There are no TIME_WAIT or CLOSE_WAIT sockets.
Please tell me what should I look for to find out what is going wrong.
Are you able to login normally after this? I'm guessing this other server is cutting you off because you've made too many connections.
What I suspect is that the telnet program is waiting for the prompt. I usually use the below format to define the prompt, so that the script usually is able to find the prompt from the below given options.
test = new Net::Telnet (Timeout => 3000 , Prompt => '/[%#\$>?:] $/' );
$test->open($IPAddress1);
$test->login($Login,$Password1);
my #input=$test->cmd("uname -a\n");
print "Connected to : #input";
#input=$cmd->cmd("pwd\n");
Please let us know if you still face the problem.
Related
I'm using netcat to connect to a server.
the problem is that i want to send somme non printable to the server caracters.
I wanted to achieve this with a command redirection in linux.
lets say this is the command: nc hostname port
so when i checked the file descriptors of the command nc in the folder: cd /proc/$(pidof nc)/fd is saw the there was another fd with number 3 that conserns the socket. 3 -> socket:[1675643]
the problem is that i wanted to redirect the output of let's say echo -ne '\xff\x0f\xab\xde' > ./3 to the socket.
I couldn't do so and the ouput is: bash: ./3: No such device or address
One cannot output something to a socket which is opened only by another process.
In order to first use interactive input/output and afterwards send the echo string, you can do:
(cat; echo -ne '\xff\x0f\xab\xde')|nc hostname port
(press the EOF character Ctrl-D to end your input and start the echo).
I'm trying to get the list of processes running on my Windows machine from Linux, but I don't get any output when I do it in a script. If I use telnet manually and use the command pslist I get the complete list of processes, but not in my script.
Here is the bash script (minus the variables):
( echo open ${host}
sleep 1
echo ${user}
sleep 3
echo ${pass}
sleep 1
echo pslist
sleep 2
) | telnet
and I simply call it with bash pslist.sh and the output is something like that:
telnet> Trying ip_address...
Connected to ip_address.
Escape character is '^]'.
Welcome to Microsoft Telnet Service
login: my_loginmy_passwordpslistConnection closed by foreign host.
What am I doing wrong ?
telnet is notoriously tricky to script. You may be able to succeed more often if you add a longer still sleep between the commands.
A better approach is to switch to a properly scriptable client, viz. netcat (aka nc). Better still would be to install an SSH server on your Windows box (perhaps for security only make it accessible from inside your network) and set it up with passwordless authentication. Then you can simply ssh user#ipaddress pslist
Terminate each echo with \r character, like this: echo -e "${user}\r"
I am having some issues trying to connect through telnet to a mail server.The main problem that I am having is that I need to create a script that logs me to the destination and I can't find a way to echo the password.
What I tried:
telnet host -l username ; echo 'password'
And still it asks for my password.Is there any way to fix this or I am doing something wrong?
First of all, you can use eval:
eval "{ echo user_name; sleep 1; echo pass; sleep 1; echo '?'; sleep 5; }" | telnet host_address
Make sure to replace user_name, pass, ? which is the command you want to run and host_address where your telnet host is listening; for me it is a local IP.
It’s surprisingly easy to script a set of command and pipe them into the telnet application. All you need to do is something like this:
(echo commandname;echo anothercommand) | telnet host_address
The only problem is the nagging login that you have to get through… it doesn’t show up right away. So if you pipe in an “echo admin” and then “echo password,” it will happen too quickly and won’t be sent to the server. The solution? Use the sleep command!
Adding in a couple of sleep 3 commands, to wait three seconds, solves the problem. First we’ll echo the username and password, and then we’ll echo the reboot command, and each time we’ll wait three seconds between. The final command will reboot the server immediately:
(sleep 3;echo admin;sleep 3;echo mypassword;sleep 3;echo system reboot;sleep 3;) | telnet host_address
You can put this into a shell script and run it whenever you want. Or you can add it to your cron like this (on OS X or Linux):
crontab -e
Add this line somewhere:
1 7 * * * (sleep 3;echo admin;sleep 3;echo mypassword;sleep 3;echo system reboot;sleep 3;) | telnet host_address
This will reboot your router at 7:01 AM each morning.
AFAIK, you won't be able to automate telnet that way. But it is still possible - even if it is a very bad idea (I'll elaborate on that later).
First why does your try fail :
you launched a telnet command reading from stdin (I suppose terminal) and writing to stdout and stderr (I suppose also a terminal)
if your telnet is reasonably recent, it tries to protect your authentication and asks your password from /dev/tty (for security reasons)
when that command has ended you write password on your own terminal
What should you do instead :
launch telnet with automatic authentication disable (on mine it is telnet -X SRA)
feed its input with the commands you want to pass
wait some delay before entering input, at least for login and password, because if you don't telnet clear input before reading and discards your inputs
Here is an example that allowed me to telnet to my own machine :
sh << EOF | telnet -X SRA localhost
sleep 2
echo my_user_name
sleep 1
echo my_password
# sleep 1 # looks like it can be removed
echo echo foo and bar
sleep 1
EOF
It correctly logs me into my box, executes echo foo and bar (essential command :-) ) and disconnects
Now why you should never do that :
you write a password in clear text in a script file which is poor security practice
you use telnet to do batch processing when it is not intended to be used that way : the script may not be portable to another telnet version
If you really want to pass command in a batch way to a remote server, you should instead try to use ssh which :
has options to process authentication securely (no password in script, nothing in clear text)
is intended to be used in batch mode as well as interactively
If you cannot use ssh (some sysadmin do not like to have uncontrolled input ssh connections) you could also try to use rsh. It is older, far less secure, but at least was designed for batch usage.
Thanks to Harvix answer, I got knew that there is also expect alternative native for shell, called sexpect. Get it from here. Then create this script (I call it telnetpass):
#!/bin/bash
# This script is used for automatically pass authentication by username and password in telnet prompt
# Its goal is similar as sshpass, but for telnet, so I call it telnetpass
. ~/.private/cisco_pw # should contain PASSWORD variable
export SEXPECT_SOCKFILE=/tmp/sexpect-telnetpass-$$.sock
sexpect spawn telnet $1
sexpect expect -cstring 'Username:'
sexpect send -enter $USER
sexpect expect -cstring 'Password:'
sexpect send -enter $PASSWORD
sexpect interact
Then you can run: telnetpass Host125 and got pass the authentication automatically
Trying 198.51.100.78 ...
Connected to Host125.
Escape character is '^]'.
User Access Verification
Username: ashark
Password:
host-125>
I like this solution more than using sleep commands as suggested in another answers, because sleep solutions sometimes fail.
Have you tried using the expect command ?? You will have to create a script where you identify the 'expected' response from the server e.g. 'Password:' and then supply the password in the script. The following will explain: https://en.wikipedia.org/wiki/Expect - A good example is also shown here: http://en.kioskea.net/faq/4736-shell-script-for-telnet-and-run-commands
Try eval:
eval "{ echo;
sleep 3;
echo $user;
sleep 1;
echo $pass;
sleep 1;
echo '?';
sleep 1; }" | telnet your_host
In this example, my remote command is '?' (help).
The sleeps (maybe not all of them nor these times; trial-error...) are needed to avoid telnet misses some inputs.
The user and password are passed as variables ($user and $pass). Take into account security recommendations to store the password if you are scripting.
I want to remotely excute a program tcp_sender with root priviledge
,the following function is for making a ssh connection
def connect(hostname):
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname, username='usr', pkey=paramiko.RSAKey.from_private_key(open('id_rsa'), 'psw'), timeout = 240.0)
return ssh
then I have 3 solutions:
solution A)
ssh = connect(hostname)
chan = ssh.invoke_shell()
chan.send('sudo ./tcp_sender\n')
with this solution, the remote tcp_sender is not executed, I checked using ps -ef|grep "tcp_sender", there is no process
I tried chan.send('sudo ./tcp_sender > log 2>&1\n')
and in the log, it says:
sudo: no tty present and no askpass program specified
solution B)
ssh = connect(hostname)
(stdin, stdout, stderr) = ssh.exec_command("[ -f tcp_sender ] && echo 1 || echo 0")
res = stdout.readlines()
print hostname,res[0]
if res[0] == '0\n':
UnusedHostFile.write(hostname+'no tcp_sender exists\n')
else:
chan = ssh.invoke_shell()
chan.send("sudo chmod 777 tcp_sender\n")
# if a tcp_sender is runnning, kill it
chan.send('x=`ps -ef|grep "tcp_sender"|grep -v "grep"|awk \'{print $2}\'`; [ -n "${x}" ] && sudo kill -9 $x\n')
time.sleep(4)
while not chan.recv_ready():
time.sleep(1)
buf = ''
buf +=chan.recv(9999)
print buf
chan.send('sudo ./tcp_sender\n')
with this solution, I just add some un-relevant lines, then the remote tcp_sender is running, something like:
bash-4.0# ps -ef|grep "sender"
root 9348 9325 0 Apr07 ? 00:00:00 sudo ./tcp_sender
root 9349 9348 0 Apr07 ? 00:00:00 ./tcp_sender
however, it can't run normally(as expected). In the tcp_sender, there is a fork(), maybe it is due to this?
I tried chan.send('sudo ./tcp_sender > log 2>&1\n')
and in the log, it is empty. Because I have many error-checking related printf in my tcp_sender program, I think there should be printf results in the log, but it is empty.
In addition, I noticed a phenomenon, if I kill -9 9348, all these two processes are ended.
But for the next solution C, the process 9349 will be handed over to system init process 1.
Solution C):
with this solution, I can run the remote tcp_sender correctly. But the python script will be blocked by the remote program until it exits. I don't want my script to wait that the remote exits.
log = open('log','a+')
ssh = connect(hostname)
(stdin, stdout, stderr) = ssh.exec_command("[ -f tcp_sender ] && echo 1 || echo 0")
res = stdout.readlines()
print hostname,res[0]
if res[0] == '0\n':
UnusedHostFile.write(hostname+"tcp_sender doesn't exists\n")
else:
chan = ssh.invoke_shell()
chan.send("sudo chmod 777 tcp_sender\n")
chan.send('x=`ps -ef|grep "tcp_sender"|grep -v "grep"|awk \'{print $2}\'`; [ -n "${x}" ] && sudo kill -9 $x\n')
time.sleep(4)
while not chan.recv_ready():
time.sleep(1)
buf = ''
buf +=chan.recv(9999)
print buf
chan.send('sudo ./tcp_sender\n')
#chan.send('sudo whoami\n')
time.sleep(2)
(stdin, stdout, stderr) = ssh.exec_command("ps -ef|grep 'tcp_sender'|grep -v 'grep'|wc -l")
res = stdout.readlines()
while res[0].strip() != '0':
time.sleep(3)
(stdin, stdout, stderr) = ssh.exec_command("ps -ef|grep 'tcp_sender'|grep -v 'grep'|wc -l")
res = stdout.readlines()
print res[0].strip()
while not chan.recv_ready():
time.slepp(1)
buf = ''
buf += chan.recv(9999)
log.write(hostname+': '+''.join(str(elem) for elem in buf)+'\n\n')
log.close()
so what are potential reasons for this phenomenon?
can anyone give some advice? thanks!
You're mixing things that you should probably keep separate.
First, write a script on the remote side that usr (= username that you give paramiko) can execute and which can correctly start tcp_sender using sudo without asking for a password, etc.
In the script, start sudo as background process using nohup:
nohup sudo ./tcp_sender
nohup makes sure that the new child process is properly detached so it stays alive when the connection is lost/cut.
When this script works, start the new script using ssh.exec_command('script')
Reasoning: It's probably possible to do what you want using a shell and clever Python code that drives the shell as if you were typing the commands. But it will always be brittle, hard to test - it's a variant of the God object.
Instead, split your problem into small, distinct problems that you can develop and test independently. You have three problems to solve:
tcp_sender itself.
Starting tcp_sender
Starting it remotely
so use three distinct tools to solve them.
Agreed with other comment - you are mixing issues which can be solved separately. Many people (including myself) have made this error.
Paramiko is powerful, and smart: it can navigate and respond to SSH username and password prompts. But this is a special case. Most of the time when you need to respond to a PROMPT in Paramiko, you basically wait then shoot text at the assumed prompt. This is messy.
This also has absolutely nothing to do with your real problem.
What you want to do is edit /etc/sudoers file so that your automation test user or group is able to run the precise command you want using NOPASSWD.
Let's say I want to remotely grep /var/log/auth.log on host "ServerB". While grep can be run by any user, it's known that auth.log on this system is only readable by user root. SO:
1) My test user is "scott", and a member of group "adm. See /etc/groups and /etc/passwd. Basic stuff.
2) /etc/sudoers:
%adm ALL=(ALL)NOPASSWD:/bin/grep
3) From a remote system, I run:
$ ssh scott#ServerB "sudo grep Accepted /var/log/auth.log"
2013-10-14T21:17:54+00:00 proc4-01-us1 sshd[28873]: Accepted publickey for scott from x.x.x.x port 56799 ssh2
2013-10-14T21:19:16+00:00 proc4-01-us1 sshd[29367]: Accepted publickey for scott from x.x.x.x port 56804 ssh2
2013-10-14T21:19:21+00:00 proc4-01-us1 sshd[29519]: Accepted publickey for scott from x.x.x.x port 56805 ssh2
Bang, you're done.
NOTES
DO use an absolute filesystem path to the script you specify in sudoers.
DO use SSH keys. You can use keys +passphrase if you like. (Remember, Paramiko can answer login prompts) But this also means storing your passphrase in the script...
DO consider security. You're not really lessoning security here if you attach this permission to a special user. Certainly the method I describe is more secure than hardcoding a sudo password into the script.
DON'T use NOPASSWD:ALL except in testing. Specify what is allowed explicitly.
DO consider adding restrictions to what these users can run. For example, if I'm always running this test from an EC2 box, I'd only allow that user to connect from that EC2 IP. Conversationally, I could restrict what commands can be run by a user by adding prefixes in "authorized"keys" (ie, restricting that user to only being able to run rsync command, if I wanted to avoid running an rsync server full-time for example).
I have a perl script which, when destilled a bit, looks like this:
my $randport = int(10000 + rand(1000)); # Random port as other scripts like this run at the same time
my $localip = '192.168.100.' . ($port - 4000); # Don't ask... backwards compatibility
system("ssh -NL $randport:$localip:23 root\#$ip -o ConnectTimeout=60 -i somekey &"); # create the tunnel in the background
sleep 10; # Give the tunnel some time to come up
# Create the telnet object
my $telnet = new Net::Telnet(
Timeout => 10,
Host => 'localhost',
Port => $randport,
Telnetmode => 0,
Errmode => \&fail,
);
# SNIPPED... a bunch of parsing data from $telnet
The thing is that the target $ip is on a link with very unpredictable bandwidth, so the tunnel might come up right away, it might take a while, it might not come up at all. So a sleep is necessary to give the tunnel some time to get up and running.
So the question is: How can i test if the tunnel is up and running? 10 seconds is a really undesirable delay if the tunnel comes up straight away. Ideally, i would like to check if it's up and continue with creating the telnet object once it is, to a maximum of, say, 30 seconds.
Edit: Ping doesn't help me mouch, as the remote end of the tunnel is generally up, but with a very high amount of packetloss
Solved: Extrapolating from the tip suggested by mikebabcock, sleep 10 has been replaced with this block which works like a charm:
my $starttime = time();
while (1)
{
# Check for success
if (system("nc -dzw10 localhost $randport > /dev/null") == 0) { last }
# Check for timeout
if (time() > $starttime + 30) { &fail() }
# 250ms delay before recheck
select (undef, undef, undef, 0.25);
}
Use netcat -- often nc on Linux systems:
nc -dvzw10 ${HOSTNAME} 23
Works for me, with a response like:
Connection to ${HOSTNAME} 23 port [tcp/telnet] succeeded!
It also returns 0 on success, and is happy with a simple connection after which it goes away.
-d means not to read anything from the keyboard side
-v means to be verbose (turn this off in a script)
-z means to disconnect after making the connection
-w10 means to wait up to 10 seconds, otherwise give up
You can integrate a ping to your ssh server and if it works fine the ssh tunnel is up
# only a ping sample :-D
if ! ping -c 1 192.168.101.9; then
echo "ping does not succeed"
else
echo "ping succeed"
fi
I think fping might be better then the usual ping, more script friendly.
fping -t 60000 [your server]
should try to connect to the server 60seconds before giving up
Something like
if(fping -t 60000 [your server]) {
execute desired code;
} else {
execute this script again to rerun;;
}
I think you get the idea even if the coding isn't real.