Python Telnet script - python

Thanks to Python Library i was able to use their example to telnet to Cisco switches, I am using this for learning purposes, specifically learning python.
However, although all the code seem generally easy to read, I am a bit confused as to the following:
1- why use the if statement below
2- why use the "\n" after the username and password write method
3- why am i not getting the output on my bash terminal when the changes are infact committed and successful
HOST = "172.16.1.76"
user = raw_input("Enter your Telnet username : ")
password = getpass.getpass()
tn = telnetlib.Telnet(HOST)
tn.read_until("Username: ")
tn.write(user + '\n') <----- 2
if password: <----- 1
tn.read_until("Password: ")
tn.write(password + "\n") <------2
tn.write("show run \n")
time.sleep(5)
output = tn.read_all() <----- 3
print output
print "=" * 30
print "Configuration Complete."
I am not sure as to why using the if statement above, typically once you input in the Username, you get the password prompt right afterward. why cant we just type :
tn.read_until("Username: ")
tn.write(user + '\n')
tn.read_until("Password: ")
tn.write(password + "\n")
As for the second point, why use the '\n' after the passwords and username in the write method if we going to hit enter after we add them anyway?

1: the line
password = getpass.getpass()
asks you for you password, if you leave it empty, password will contain the empty string which, in an if statement, is the same as False
the script doesn't know ahead of time if you have a password on your server or not, it simulates knowing by asking you first and if you don't input anything, it assumes it doesn't (otherwise it would get stuck on tn.read_until("Password: ") forever.
2: the '\n' simulates you hitting the return key. when you enter your password, for example 'password<RETURN>' the variable password will not contain a trailing newline (\n), this is why it is manually appended
3: this one i dont know, possibly 5 seconds isn't enough time to wait

After execute
tn = telnetlib.Telnet(HOST)
you have created a telnet channel from your machine to HOST. But you still need to communicate with HOST to push/send your commands and receive the outputs.
To push your commands to HOST, you need to execute tn.write("your_commands_or_input \n"), \n means newline/return, which tells your current commands need to be executed now. After the execution, HOST return the result, which will be caught by your telnet object "tn" and saved in its "local cache", you can search any keywords you expected in this cache by using tn.read_until method, if the expected keyword has been found, read_until will stop(always stop on the 1st found), and you can do anything you need(It's your turn now), else the read_until will keep waiting the output from HOST(Haven't you turn yet). Finally if you want to check all output have been cached, you can execute tn.read_all().
Remember some of the HOST using different login output, i.e Username vs username or Password vs password, you better to use regular expression to match them.

There is a python library on github, specifically for telneting to cisco devices.
pip install git+https://github.com/sergeyzelyukin/cisco-telnet.git
import ciscotelnet
with ciscotelnet.CiscoTelnet(host, verbose = False) as cisco:
if cisco.login(final_mode=CiscoTelnet.MODE_ENABLE, user="john", user_pass="12345678", enable_pass="cisco"):
# if cisco.login(final_mode=CiscoTelnet.MODE_ENABLE, line_pass="abcdef", enable_pass="cisco"):
print cisco.cmd("sh int status | inc Fa0/1")
print cisco.conf(["interface fast0/1", "descr blank", "load-interval 300"])
print cisco.wr()

Related

Automating log-in action through serial communication

i am trying to automate the log-in function for a device that i communicate with through serial. In order to reach the login: prompt i got to press enter while the device boots and then after sometime the login: prompt will show up, once it the program spots the 'login:' string it enter the username(or at least that's the plan). After entering the correct username the Password: prompt will show up, if i enter the correct password i successfully log-in to the device, if i enter the wrong password i have to start over(which means to enter the username again). Also if i fail to log-in in the first try the login: prompt changes to username:.
I have made this till now, but
import serial
import re
from time import sleep
import time
ser = serial.Serial('COM3', timeout=1)
ser.baudrate = 115200
def auto_login():
while True:
output = ser.read(10000).decode('utf-8', 'ignore')
testmode_command = ser.write("\r\n".encode())
print(output)
if "1 : press [Enter] for test mode / [Esc+Enter] for plain Linux" in output:
ser.write(testmode_command)
if " login:" in output:
break
def login_repeat():
login = b"root \r\n"
output = ser.read(10000).decode('utf-8', 'ignore')
print(output)
if " login:" in output:
ser.write(login)
if "Username:" in output:
ser.write(login)
def pass_word():
password = b"p \r\n"
time.sleep(0.1)
output = ser.read(10000).decode('utf-8', 'ignore')
print(output)
if "Password:" in output:
ser.write(password)
The result i am getting is :
Login incorrect
Username:
root
System starting up, please try later
Login incorrect
Username:
root
For some reason i looks like the enter is sent first the \r\n command instead of the username and then the command. Any idea how to resolve this?
Add time.sleep(0.1), before you send a command, like this :
time.sleep(0.1)
ser.write(b"root")
time.sleep(0.1)
ser.write('\r'.encode())
Just as a hunch, are you sure, you have no buffering issues. I don't know the serial module but it might be possible that the library sends the "Enter" together with the login information.
That would result in "Enter" as user name.
Quick searching brought up this answer: https://stackoverflow.com/a/12892221/4252584
You might try to explicitly flush the buffers.
On the other hand I am wondering why you get to the login prompt without prior "Enter" key on the serial line. Are you sure, you need the "Enter" key on the line?

Why can't i receive twice from the same client

Basically i have client, ip = s.accept()
i want to send and receive send and receive, but printing out the second receive returns nothing
I'm connecting through Putty RAW Mode, i tried different programs but nothing worked.
def function(client, ip):
print(Fore.YELLOW + f"Connection from {ip} Established")
client.send("Username: ".encode("utf-8"))
username = client.recv(1024).decode("utf-8")
client.send("Password: ".encode("utf-8"))
password = client.recv(1024).decode("utf-8")
print(username + ":" + password)
I expected it to output "username:password"
but it returns "username:"
You seem to just expect the first recv to get the username and the second recv to get the password by magic or luck. If there's some protocol that allows you tell where the username ends and the password begins, you need to implement it.
Your first call to recv might get just the first letter of the username. Maybe the first call got the username and the second call got a space or newline character between the username and the password and you haven't read the password yet. Who knows?
If there's no way to know where the boundary between the username and password is, there's no way this code can possibly work. If there is some way, what is that way and where is the code that implements it?
Is your code supposed to receive the username before you send the Password: prompt? If so, where's the code to do that? You call recv, but you don't check to see if it's the username. It could just be the first character of the username. It could be more. You need to look at it and see what it is. Do not send the Password: prompt until you've received the entire username. You have to actually implement whatever protocol you are using and the client has to use that same protocol.

Python select() not waiting for terminal input after forkpty()

I am trying to write a python script that will automatically log in to a remote host via ssh and update a users password. Since ssh demands that it take its input from a terminal, I am using os.forkpty(), running ssh in the child process and using the parent process to send command input through the pseudo terminal. Here is what I have so far:
import os, sys, time, getpass, select, termios
# Time in seconds between commands sent to tty
SLEEP_TIME = 1
NETWORK_TIMEOUT = 15
#------------------------------Get Passwords------------------------------------
# get username
login = getpass.getuser()
# get current password
current_pass = getpass.getpass("Enter current password: ")
# get new password, retry if same as current password
new_pass = current_pass
first_try = True
while new_pass == current_pass:
if first_try:
first_try = False
else:
# New password equal to old password
print("New password must differ from current password.")
# Get new password
new_pass = getpass.getpass("Enter new password: ")
new_pass_confirm = getpass.getpass("Confirm new password: ")
while new_pass != new_pass_confirm:
# Passwords do not match
print("Passwords do not match")
new_pass = getpass.getpass("Enter new password: ")
new_pass_confirm = getpass.getpass("Confirm new password: ")
#------------------------------End Get Passwords--------------------------------
ssh = "/usr/bin/ssh" # ssh bin location
args = ["ssh", login + "#localhost", "-o StrictHostKeyChecking=no"]
#fork
pid, master = os.forkpty()
if pid == 0:
# Turn off echo so master does not need to read back its own input
attrs = termios.tcgetattr(sys.stdin.fileno())
attrs[3] = attrs[3] & ~termios.ECHO
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, attrs)
os.execv(ssh, args)
else:
select.select([master], [], [])
os.write(master, current_pass + "\n")
select.select([master], [], [])
os.write(master, "passwd\n")
select.select([master], [], [])
os.write(master, current_pass + "\n")
select.select([master], [], [])
os.write(master, new_pass + "\n")
select.select([master], [], [])
os.write(master, new_pass + "\n")
select.select([master], [], [])
os.write(master, "id\n")
select.select([master], [], [])
sys.stdout.write(os.read(master, 2048))
os.wait()
The script prompts the user for his/her current and new passwords, then forks and sends appropriate responses to ssh login prompt and then passwd prompts.
The problem I am having is that the select syscalls are not behaving as I would expect. They don't appear to be blocking at all. I'm thinking that I am misunderstanding something about the way select works with the master end of a pty.
If I replace them all with time.sleep(1), the script works fine, but I don't want to have to rely on that solution because I can't always guarantee the network will respond in a short time, and I don't want to make it something rediculous that will take forever (I intend to use this to programatically log into several servers to update passwords)
Is there a way to reliably poll the master side of a pty to wait for the slave's output?
Note: I realize there are better solutions to this problem with things like sshpass and chpasswd, but I am in an environment where this cannot be run as root and very few utilities are available. Thankfully python is.
select doesn't read any data; it simply blocks until data is available to be read.
Since you don't read any data after the first select, there will still be data left in the buffer for you to read, so any subsequent select will not block.
You need to read the data in the buffer before calling select again. Doing this without blocking means that you will likely have to set the file to non-blocking mode (I don't know how to do that in Python).
A better way of providing the password over SSH would be to use the --stdin flag if your passwd supports it, and to run the command directly over SSH instead of through the created shell.
handle = subprocess.Popen(["ssh", login + "#localhost", "-o StrictHostKeyChecking=no", "passwd --stdin"])
handle.communicate("\n".join([oldpass, newpass, newpass, ""]))
Have a look in man ssh at the -f option. It may be what you need when launching ssh. It will block ssh until the password is typed and then fork by itself. You could probably use this feature to achieve what you want (but you may have to slightly change your current code because it will perform by itself what you currently try to embed in your script).
This option is generally used at the command line for starting a remote graphical program: once the password is typed, you can safely close the terminal and keep interacting with the remote process with its graphical interface. But I think using this feature here would lead to a much cleaner way than playing with low-level blocking features and similar things.

Changing Linux username or password with Python script

I'm writing a Python script that changes the username and password of a Linux account user - it's part of a larger internal web-gui system that queues up password change requests from apache2 (which can't run as root), and then changes the passwords itself. The python script itself obviously must run as root in order to change passwords.
The password change function is pretty straightforward:
def chpasswd(user, passwd):
if os.getuid() != 0:
syslog.syslog("Error: chpasswd.py must be run as root")
return
proc = Popen(
['/usr/sbin/chpasswd'],
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE
)
print "Changing: " + user + ':' + passwd
out, err = proc.communicate(user + ':' + passwd)
proc.wait()
print out
if proc.returncode != 0:
print "Error: Return code", proc.returncode, ", stderr: ", out, err
if out:
syslog.syslog("stdout: " + out)
if err:
syslog.syslog("stderr: " + err)
The print statements are just there for temporary debugging. This runs fine and doesn't report any errors - there's nothing on out or err; but for some reason the actual UNIX password simply isn't changed.
The script which invokes this function is listening on a locally bound TCP socket. When it receives a change password request (in the form of user:password - later to be encrypted but for now plaintext) it adds it to a queue and then invokes the chpasswd function.
So, typical usage would be:
# telnet localhost 7001
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
jsmith:mynewpassword
When the server is running in a bash window (not as a daemon) it prints out:
# python chpasswd.py
Starting Password Server
New connection from: 127.0.0.1
Changing: jsmith:mynewpassword
The last statement, you can see, is the print statement in my chpasswd function.
But after doing the above, when I actually try to login as a user using the new password, I get:
$ su jsmith
Password:
su: Authentication failure
Is there some obvious thing I'm doing wrong here? My suspicion was that somehow the connection with Popen is not actually closing, or perhaps the single line user:password text is not being transmitted. So I tried doing something like:
out, err = proc.communicate(user + ':' + passwd + '\x04')
Notice the extra \x04 character at the end, indicating End Of Transmission. Adding this in still didn't get it to work however - the password remained unchanged.
I'm running this on Debian Wheezy, in case it makes any difference.
Update:
Investigating further, I can see that my chpasswd function actually is changing the password - if I cat the /etc/shadow file before and after connecting to my password server, I see there is a different hash.
It's just that when I try to authenticate using the plaintext password, it doesn't work. Therefore, my suspicion is that somehow, the communication with Popen is either adding additional characters, or losing characters somehow. Of course, since /etc/shadow is a hash, I can't figure out exactly what's going on here.
The problem in this particular instance was that telnet adds "\r\n" after you press return on entering text. Since your server was not stripping the data of whitespace this was preserved when changing the password.
It is possible to get telnet to not send the carriage return and newline characters by ending a line with the end-of-transmission character (EOT). You can do this by pressing Ctrl-D.
eg
$ telnet localhost 7001
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
jsmith:mynewpassword^DChanging: jsmith:mynewpassword
Alternatively you can pipe the line into telnet
echo -n jsmith:mynewpassword | telnet localhost 7001
Obviously, you'll only want to do this for testing or the new password will end up in your shell history. (The -n argument suppresses the printing of newline characters by echo)
Or you might want to do away with telnet altogether and use netcat instead.
echo -n jsmith:mynewpassword | netcat localhost 7001

How to open and search a file in a telnet session with Python

I'm using the following code to log into a server and go to a particular directory (where the logfile I want to search for a string resides). I have accomplished this with the Paramiko module (ssh), fairly straightforward. But the telnetlib module does not have many functions that I see to accomplish this. Does anyone know how I would open the file and search through it for a particular string (The server in question only allows Telnet logins - no ssh) ... Thanks:
import sys
import telnetlib
HOST = "10.28.46.14"
user = raw_input("Enter your username: ")
password = ""
tn = telnetlib.Telnet(HOST)
tn.read_until("login: ")
tn.write(user + "\n")
if password == "":
tn.read_until("Password: ")
tn.write(password + "\n")
#print "Login successful!"
else:
print "Your password is incorrect."
tn.write("cd /var/opt/mylog/log\n")
tn.write("ls\n")
tn.read_until("\n")
#tn.write("exit\n")
my_ls = tn.read_until("my.log")
print my_ls
Did you check with the owner of the machine about ssh vs telnet? There aren't many operating systems that ship with telnet out of the box anymore, because telnet is subject to replay attacks.
What if you tell tn to do a grep? You might append an "echo $?" after the grep, to get an exit code - 0 means there was one or more matches, anything else means there weren't any.

Categories

Resources