Paramiko_Linux

发布时间 2023-03-22 21:09:35作者: 张贺贺呀

Paramiko

Paramiko官网

image-20230321084638027

image-20230321084921247

从官方的介绍当中我们起码得知以下几个信息:

  1. 此模块用于python3.6以上,目前python3.11左右,完全满足;
  2. 此模块用于实现sshv2协议的客户端和服务端;
  3. 核心类有五种,我们常用的是Client和SFTP两个类

SSH

接下来,我们通过paramiko实现通过sshv2连接服务器,并执行ip addr show然后从结果当中把IP地址通过python字符串切割的方式获取到。

实验环境:

  • 服务端:192.168.80.130:kali linux
  • 客户端:windows pycharm
# 服务端基本配置
┌──(root㉿kali)-[~/Desktop]
└─# systemctl enable --now  ssh
root㉿kali)-[~/Desktop]
└─# ss -tnlp | grep 22         
LISTEN 0      128          0.0.0.0:22        0.0.0.0:*    users:(("sshd",pid=1938,fd=3))
LISTEN 0      128             [::]:22           [::]:*    users:(("sshd",pid=1938,fd=4))
┌──(root㉿kali)-[~/Desktop]
└─# grep -i "permitrootlogin" /etc/ssh/sshd_config 
PermitRootLogin yes
┌──(root㉿kali)-[~/Desktop]
└─# systemctl restart ssh    
┌──(root㉿kali)-[~/Desktop]
└─# passwd        
New password: cba-123
Retype new password: cba-123
passwd: password updated successfully
┌──(root㉿kali)-[~/Desktop]
└─# ip addr show eth0 | sed -n 3p | awk -F" " '{print $2}' | awk -F"/" '{print $1}'
192.168.3.19
                                                                                                                  
┌──(root㉿kali)-[~/Desktop]
└─# hostname -I
192.168.3.19 

在使用paramiko的过程当中我们可以选择通过shell命令进行切割字符串,也可以通过python进行切割,由于shelle命令只能在LINUX上执行,如果我们操作是网络设备那shell的命令不再好用了,所以我们打算使用python关于字符串的切割逻辑。

第一版

要求1:要求向linux发送一个df命令,并取回df的其结果。

要求2:要求向linux发送四个命令,并取会其结果

# 要求1
import paramiko,time

port_number = 22
username = "root"
password = "cba-123"
ip= "192.168.80.130"

# 调用paramiko的sshclient类进行实例化
ssh = paramiko.SSHClient()

# 斩断ssh进程与文件系统之间的联系
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

# 这一步如果抓包去看的话,其实就是sshv2的连接过程
# tcp三次握手、协商版本、交换密钥……、完事后直接四次挥手并不保持连接直接挥手
ssh.connect(
    port=port_number,
    username=username,
    password=password,
    hostname=ip
)

# 向ssh发送一个df命令,然后通过标准的输入、输出和错误接收
# 打印标准输出
stdin, stdout, stderr = ssh.exec_command("df")
out = stdout.read()
print(out.decode())

# 如果不带close最后的中断直接是reset,而使用了close之后最后中断就是平滑的三次握手
ssh.close()

"C:\Program Files\Python311\python.exe" G:\python2\第十章\test.py 
Filesystem     1K-blocks     Used Available Use% Mounted on
udev             1955888        0   1955888   0% /dev
tmpfs             398824     1380    397444   1% /run
/dev/sda1      101639152 12773768  83656196  14% /
tmpfs            1994112        0   1994112   0% /dev/shm
tmpfs               5120        0      5120   0% /run/lock
tmpfs             398820       88    398732   1% /run/user/0
# 要求2的完成可以采取一个非常取巧的办法,把四个命令写到一块即可,如下所示:
stdin, stdout, stderr = ssh.exec_command("hostname;df;ps aux|grep ssh;cat /etc/issue")

第二版

要求把上述代码写成函数,并连续执行四个命令,如下所示:

import paramiko, time

port_number = 22
username = "root"
password = "cba-123"
ip = "192.168.3.19"

def ssh(cmd):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(
        port=port_number,
        username=username,
        password=password,
        hostname=ip
    )


    stdin, stdout, stderr = ssh.exec_command(cmd)
    out = stdout.read()
    print(out.decode())
    ssh.close()

ssh("df | head -1")
ssh("hostname")
ssh("cat /etc/issue")
ssh("cat /etc/passwd | head -1")

其实像上图这么做是一个循环的过程,是完全的单线程操作,每一个命令都需要完成ssh的过程然后执行命令最后断开连接,这个过程要重复四次,这是特别浪费资源的,但我们在这里不去讨论多线程之类的,也不去关注性能,反正这么就完成任务;

其实代码还可以更简单一点,那就是把命令写在列表,然后循环列表,执行函数;

cmds = ["df|head -1","hostname","whoami","echo $PATH"]
for cmd in cmds:
    ssh(cmd)

上述代码还有改进的地方,那就是在标准输入和输出那个地方,咱们可以使用三元运算:

stdin, stdout, stderr = ssh.exec_command(cmd)
res,err = stdout.read(),stderr.read()
result = res if res else err
print(result.decode())
ssh.close()

最终版

import paramiko, time

port_number = 22
username = "root"
password = "cba-123"
ip = "192.168.3.19"

def ssh(cmd):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(
        port=port_number,
        username=username,
        password=password,
        hostname=ip
    )


    stdin, stdout, stderr = ssh.exec_command(cmd)
    res,err = stdout.read(),stderr.read()
    result = res if res else err
    print(result.decode())
    ssh.close()

cmds = ["df|head -1","hostname","whoami","ecdho $PATH"]
for cmd in cmds:
    ssh(cmd)
    
    
"C:\Program Files\Python311\python.exe" G:\python2\第十章\test.py 
Filesystem     1K-blocks     Used Available Use% Mounted on

kali

root

zsh:1: command not found: ecdho

SFTP

SFTP是在ssh建立连接的基础上,所以上述代码建立连接的部分,可以直接直接抄下来

第一版

将服务器的/etc/passwd文件下载到本地,命名为backup_hostname

# 将服务器的/etc/passwd文件下载到本地,命名为backup_hostname
import paramiko, time

port_number = 22
username = "root"
password = "cba-123"
ip = "192.168.3.19"


ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(
    port=port_number,
    username=username,
    password=password,
    hostname=ip
)

# 在ssh的基础上附加或打开sftp
sftp = ssh.open_sftp()
sftp.get("/etc/hostname","backup_hostname")
ssh.close()

第二版

将服务器的/etc下的passwd文件、fstab文件、issue文件全都下载下来;

# 下载
import os.path
import paramiko

def sftp_get(file_path):
    port_number = 22
    username = "root"
    password = "cba-123"
    ip = "192.168.3.19"

    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(
        port=port_number,
        username=username,
        password=password,
        hostname=ip
    )

    # 在ssh的基础上附加或打开sftp
    sftp = ssh.open_sftp()
    file_in = file_path
    file_out = os.path.basename(file_in)
    sftp.get(file_in, file_out)
    ssh.close()


files = ["/etc/passwd","/etc/issue","/etc/hostname"]
for file in files:
    sftp_get(file)

image-20230321125143881

多次执行会直接进行覆盖的。

这里面涉及到一个新的知识点,那就是取文件的基名,要通过os模块,非常容易理解。

# 上传
import os.path

import paramiko

def sftp_put(put_file):
    port_number = 22
    username = "root"
    password = "cba-123"
    ip = "192.168.3.19"

    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(
        port=port_number,
        username=username,
        password=password,
        hostname=ip
    )

    # 在ssh的基础上附加或打开sftp
    sftp = ssh.open_sftp()
    file_in = put_file
    file_out = f'/tmp/{file_in}.back'
    sftp.put(file_in,file_out)
    ssh.close()

files = ["passwd","issue","hostname"]
for file in files:
    sftp_put(file)