Python script for SSH through PuTTY

2020-03-31 04:24发布

问题:

I am able to give the following command in the command-line

C:\>cd "C:\Program Files\ExtraPuTTY\Bin"

C:\Program Files\ExtraPuTTY\Bin>putty.exe -ssh root@172.20.0.102 22

This helps me open the SSH session through PuTTY.

Whereas I am not able to reproduce them in the Python script.

cwd="C://Program Files//ExtraPuTTY//Bin"
COMMAND="ls"
ssh = Popen(['putty.exe -ssh','%s'%HOST, COMMAND,cwd],shell=True,stdout=f,stderr=f)

The error that I see is

"putty.exe -ssh"' is not recognized as an internal or external command,operable program or batch file

回答1:

In the putty download page, download and install plink, and make sure its in the windows path ($PATH variable)

Then, this python snippet should work:

import subprocess
cmd='plink -ssh {}@{} -pw {}'.format(user,server,password)
sp = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=subprocess.PIPE, shell=True)
sp.stdin.write(stdin)
sp.stdin.close()
stdout= sp.stdout.read()
stderr=sp.stderr.read()
sp.wait()

stdin is the commands typed by the user in the terminal, stdout and stderr are the server output.

Fill in your credentials in the user="root", server="172.20.0.102 22" and maybe password for the ssh connection



回答2:

You have to pass the cwd as the cwd parameter of the Popen:

Popen(['putty.exe -ssh'...], shell=True, stdout=f, stderr=f, cwd=cwd)

And you should use Plink, not PuTTY, for automating the remote command execution. The Plink accepts the command on its command-line (PuTTY does not):

Popen(['plink.exe -ssh root@172.20.0.102 ls'], shell=True, stdout=f, stderr=f, cwd=cwd)


回答3:

I know it' a bit beside the question, but that the most closed topic I found (I'd like to found that code a week ago on that post)

I was looking for a code to massively check if a password is active, and change it if possible

Putty have several cli tool like plink and pscp whch are useful for a lot of stuff.

Here is python 3 function to connect to a ssh server and accept ssh key. using pscp allow a auto accept key... can be useful for first time

def TestSSHConnection(IP_Addr,User,Password,verbosity=0, force_plink=False):
#Some infos about returned code
# 0 = Error Or crash
# 1 = Connection ok
# 2 = No connect Password Error
# 3 = SSH key trouble (shit append)
# 4 = Timeout
# 5 = Host Unreachable
# 6 = Connection Crash

out=""
err=""
try:
    if force_plink:
        print("echo y | plink -l "+str(User)+" -pw "+str(Password)+" -batch "+str(IP_Addr)+" exit",)
        ssh=subprocess.Popen("echo y | plink -l "+str(User)+" -pw "+str(Password)+" -batch "+str(IP_Addr)+" exit",shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,encoding='utf8')#,stdin=subprocess.PIPE)
    else:
        print("echo y | pscp -l "+str(User)+" -pw "+str(Password)+" -ls "+str(IP_Addr)+":/",)
        ssh=subprocess.Popen("echo y | pscp -l "+str(User)+" -pw "+str(Password)+" -ls "+str(IP_Addr)+":/",shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    out,err = ssh.communicate()
    try:
        out = out.decode('utf-8')
    except AttributeError as inst:
        pass
    except Exception as inst:
        if verbosity>1:
            print("While decoding stdout: "+str(type(inst)))
    try:
        err = err.decode('utf-8')
    except AttributeError as inst:
        pass
    except Exception as inst:
       print("While decoding stderr: "+str(type(inst)))
    ssh.kill()
    del ssh
except Exception as inst:
    print("Crash"+str(inst))
    return 0

if len(err)>0:
    if 'Unable to open connection' in err or 'Host does not exist' in err:
        if verbosity>0: print("Unreachable")
        result = 5
        if verbosity>1:
            print()
            print("-"*30)
            print(err)
            print("-"*30)

    elif 'Connection timed out' in err:
        result = 4

    elif 'The server\'s host key is not cached in the registry' in err:
        result = 3
        if verbosity>1:
            print()
            print("SSH key Err".center(30,"-"))
            print(err)
            print("-"*30)

    elif 'Access denied' in err:
        result = 2
        if verbosity>2:
            print()
            print("Denied".center(30,"-"))
            print(err)
            print("-"*30)
    else:
        result = 6
        if verbosity>0: print("ConnCrash")
        print("Oups".center(30,"-"))
        print(err)
        print("-"*30)
else:
    if verbosity>0: print("Conn ok")
    result = 1

del out,err
return result

Of cource, this juste Check connection (and accept ssh key)

So here is a code to run a script on the host (precisely a password change). To do so, you can't use one line syntax (even it must work, it won't, i tried) You have to pass through a script file and push it with plink.

def ChangeMyPassword(IP_Addr,User,Old_Password,New_Password,verbosity=0):
#Some infos about returned code
# 0 = Error Or crash
# 1 = Change Ok
# 2 = Old Password Error
# 3 = SSH key trouble (shit append)
# 4 = Timeout
# 5 = Host Unreachable
# 6 = Connection Crash

out=""
err=""

try:
    path_script = "."+os.sep+"Script_chmdp_"+str(IP_Addr)+".sh"
    if os.path.exists(path_script):os.remove(path_script)
    fic_script = codecs.open(path_script,'w', encoding='utf8')
    fic_script.write('echo -e \"'+str(Old_Password)+'\\n'+str(New_Password)+'\\n'+str(New_Password)+'\" | passwd')
    fic_script.flush()
    fic_script.close()

    cmd = "plink -l "+str(User)+" -pw "+str(Old_Password)+" -batch "+str(IP_Addr)+ " "
    cmd += "-m "+path_script

    print(str(cmd))

    ssh=subprocess.Popen(cmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,encoding='utf8')#,stdin=subprocess.PIPE)

    out,err = ssh.communicate()
    try:
        out = out.decode('utf-8')
    except AttributeError as inst:
        pass
    except Exception as inst:
        if verbosity>1:
            print("While decoding stdout: "+str(type(inst)))
    try:
        err = err.decode('utf-8')
    except AttributeError as inst:
        pass
    except Exception as inst:
       print("While decoding stderr: "+str(type(inst)))
    ssh.kill()
    del ssh
except Exception as inst:
    print("Crash"+str(inst))
    return 0

if 'all authentication tokens updated successfully' in out:
    if verbosity>0: print("Conn ok")
    result = 1
else:
    if verbosity>0: print("Something goes wrong, hope we do not crash your server :)")
    result = 0
del out,err
return result

So now you have two function to massively change password on your systems.

Bonus: a function to get /etc/passwd and /etc/shadow. Why? for educationnal use on your IT admin like 'hey you f*** up and use the same password everywhere, and now all this account can be Bruteforced. So clean up your mess

def GetPass(IP_Addr,User,Password,verbosity=0, force_plink=False):
#Some infos about returned code
# 0 = Error Or crash
# 1 = Connection ok
# 2 = No connect Password Error
# 3 = SSH key trouble (shit append)
# 4 = Timeout
# 5 = Host Unreachable
# 6 = Connection Crash

out=""
err=""
try:

    ssh=subprocess.Popen("echo "+str(Password)+" | plink -l "+str(User)+" -pw "+str(Password)+" -batch "+str(IP_Addr)+" sudo cat /etc/passwd;sudo cat /etc/shadow",shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,encoding='utf8')#,stdin=subprocess.PIPE)

    out,err = ssh.communicate()
    try:
        out = out.decode('utf-8')
    except AttributeError as inst:
        pass
    except Exception as inst:
        if verbosity>1:
            print("While decoding stdout: "+str(type(inst)))
    try:
        err = err.decode('utf-8')
    except AttributeError as inst:
        pass
    except Exception as inst:
       print("While decoding stderr: "+str(type(inst)))
    ssh.kill()
    del ssh
except Exception as inst:
    print("Crash"+str(inst))
    return 0

if len(err)>0:
    if 'Unable to open connection' in err or 'Host does not exist' in err:
        if verbosity>0: print("Unreachable")
        result = 5
        if verbosity>1:
            print()
            print("-"*30)
            print(err)
            print("-"*30)

    elif 'Connection timed out' in err:
        result = 4

    elif 'The server\'s host key is not cached in the registry' in err:
        result = 3
        if verbosity>1:
            print()
            print("SSH key Err".center(30,"-"))
            print(err)
            print("-"*30)

    elif 'Access denied' in err:
        result = 2
        if verbosity>2:
            print()
            print("Denied".center(30,"-"))
            print(err)
            print("-"*30)
    else:
        result = 6
        if verbosity>0: print("ConnCrash")
        print("Oups".center(30,"-"))
        print(err)
        print("-"*30)
else:
    if verbosity>0: print("Conn ok")
    result = out

del out,err
return result

Some more notes:

if you don't use shell=True, you don't get the output, and it does not work, I don't know why.

I also tried a asynchronous communication to send comand line by line, it does not work.

I also tried the ssh command (yes it now exist on windows \o/) but it does not work for my purpose.

Hope this help someone one day, it would have helped me a lot a week ago :)