Paramiko
Paramiko官网
从官方的介绍当中我们起码得知以下几个信息:
- 此模块用于python3.6以上,目前python3.11左右,完全满足;
- 此模块用于实现sshv2协议的客户端和服务端;
- 核心类有五种,我们常用的是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)
多次执行会直接进行覆盖的。
这里面涉及到一个新的知识点,那就是取文件的基名,要通过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)