欢迎光临
我们一直在努力

【Linux】内网穿透 FTP 终极复现手册 (2026 版)--cpolar

【Linux】内网穿透 FTP 终极复现手册2026 版–cpolar

  • 前言
  • 一、 核心背景与原理
  • 二、 服务端:ftp_server.py (Kali 运行)
    • 如果复现过程中遇到 2个域名不一样
  • 三、访问FTP服务器
  • 四、 (非必须)客户端:ftp_client.py (Windows 运行)
  • 五、 快速复现流程总结

前言

因为有个靶场所需需要搭建一个 FTP 服务器,又因为比较懒不高兴开 VPS 服务器去搭建 FTP ,用内网穿透工具 cpolar 搭建了2条 tcp 隧道,复现了 ftp 但是花了很长时间;为避免各位踩坑特此整理了一个笔记。 安全性问题最重要说三遍: 操作过程中请注意,FTP 主目录下不要放敏感文件!!! 操作过程中请注意,FTP 主目录下不要放敏感文件!!! 操作过程中请注意,FTP 主目录下不要放敏感文件!!!

一、 核心背景与原理

本方案 cpolar 需要搭建两条 tcp 隧道1,在双重 NAT(内网环境)下,FTP 协议的** “双通道” **特性会导致连接失败。

  • 控制通道 (Port A): 负责指令:tcp://3.tcp.cpolar.top:11935 -> tcp://127.0.0.1:2121
  • 数据通道 (Port B): 负责文件内容。tcp://3.tcp.cpolar.top:11667 -> tcp://127.0.0.1:2120

穿透痛点:

  • 端口不一致: cpolar 随机分配的外网端口(如 11667)与本地监听端口(如 2120)无法对应。
  • IP 伪装失效: 服务端默认会返回内网 IP,导致公网客户端连接失败。
  • 解决方案: 通过 Python 脚本 “暴力劫持” FTP 的 PASV(被动模式)响应,强制将客户端引导至公网隧道出口。

    IP漂移以及解决方法

  • 复现过程中 cpolar 映射到过2个域名:3.tcp.cpolar.top 和 3.tcp.vip.cpolar.cn;
  • 因域名不一致导致公网 IP 也不一致,可自行 ping 命令得到真实 IP ;
  • 随着时间的推移,IP 也会变动,每次复现需要确定真实 IP。
  • 流量的一生:

  • 户连 隧道A:11935 → 转发至本地 2121 (登录成功)。
  • 用户发 ls 指令 → 服务端“撒谎”说:请连 隧道B:11667 拿数据。
  • 用户连 隧道B:11667 → 转发至本地 2120 (获取目录成功)。
  • 二、 服务端:ftp_server.py (Kali 运行)

    功能: 重写 PASV 逻辑,实现 “协议层端口重定向”。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # 运行环境: Windows / Linux / macOS (跨平台通用)
    # 依赖安装: pip install pyftpdlib
    # cpolar 两条 tcp 隧道一条映射 2121 端口,一条映射 2120 端口
    # 复现环境时 cpolar 映射公网 IP 是 8.148.71.245 有可能随着时间变动会变动,需要调整代码
    # 另外也可能 两条隧道公网 IP 不一样,

    from pyftpdlib.authorizers import DummyAuthorizer # 权限管理器
    from pyftpdlib.handlers import FTPHandler # 协议处理器
    from pyftpdlib.servers import FTPServer # 监听服务器
    import logging # 日志模块

    # 开启 DEBUG 日志,实时查看 FTP 握手过程
    logging.basicConfig(level=logging.DEBUG)

    class CpolarFTPHandler(FTPHandler):
    """
    【核心黑科技】自定义处理器,拦截并重写被动模式响应
    """

    def ftp_PASV(self, line):
    # 1. 暴力劫持响应内容:
    # 强制告诉客户端:请去连接公网地址 8.148.71.245 的 11667 端口
    # 端口计算公式:45 * 256 + 147 = 11667
    self.respond('227 Entering Passive Mode (8,148,71,245,45,147).')

    # 2. 启动本地监听:
    # 虽然告诉客户端连外网,但本地服务器必须在 2120 端口等着 cpolar 转回来的流量
    try:
    self.handle_pasv() # pyftpdlib 2.x 内部监听启动方法
    except AttributeError:
    super().ftp_PASV(line) # 兼容性兜底

    def main():
    # 实例化权限管理器
    authorizer = DummyAuthorizer()
    # 设置匿名用户路径与全权限 (elradfmw)
    authorizer.add_anonymous("/home/kali/Desktop/tmp/tmp", perm="elradfmw") # 假设 ftp 主目录在这里

    handler = CpolarFTPHandler
    handler.authorizer = authorizer

    # 【关键设置】禁用 IP 来源检查
    # 因为 cpolar 的控制流和数据流 IP 可能被识别为不一致,必须开启此项
    handler.passive_promiscuous = True

    # 设置被动模式本地监听端口范围 (对应隧道 B 的本地端)
    handler.passive_ports = range(2120, 2121)

    # 启动服务器,监听本地 2121 (对应隧道 A 的本地端)
    server = FTPServer(('0.0.0.0', 2121), handler)

    print("—————————————")
    print("💎 穿透服务端已就绪 (Python 战神版)")
    print("期待流量路径:外网 11667 -> 隧道 -> 本地 2120")
    print("—————————————")
    server.serve_forever()

    if __name__ == "__main__":
    main()

    如果复现过程中遇到 2个域名不一样

    上面代码请修改为下面的核心逻辑(因复现中又没遇到,故未做调试)

    import socket

    # 假设你的两个隧道域名不一样
    CONTROL_DOMAIN = "3.tcp.cpolar.top" # 控制通道域名
    DATA_DOMAIN = "4.tcp.vip.cpolar.cn" # 数据通道域名(假设不同)

    def get_ip(domain):
    """自动获取域名的当前公网 IP"""
    return socket.gethostbyname(domain)

    class CpolarFTPHandler(FTPHandler):
    def ftp_PASV(self, line):
    # 自动获取数据通道当前的真实公网 IP
    data_ip = get_ip(DATA_DOMAIN)
    # 将 IP 格式化为 FTP 协议需要的逗号分隔形式
    ip_parts = data_ip.replace('.', ',')

    # 动态生成响应:使用数据通道的 IP 和 11667 端口 (45*256 + 147)
    # 注意:这里的 P1, P2 需要根据 cpolar 隧道 B 实际分配的外网端口实时计算。
    pasv_resp = f'227 Entering Passive Mode ({ip_parts},45,147).'
    self.respond(pasv_resp)
    self.handle_pasv()

    三、访问FTP服务器

  • windows 直接访问 随便打开一个文件夹,输入 ftp://内网穿透真实ip:内网穿透控制通道端口 案例中是 ftp://8.148.71.245:11935/ 通过IE访问FTP

  • 下图为 FTP 跟目录中的文件,可见和访问结果一模一样。 文件已经列出

  • 但是 cmd 访问会出错 在这里插入图片描述

  • 使用 FileZilla 可以完美解决 在这里插入图片描述 FileZilla 可以读取的原因系:编辑 -> 设置 -> 连接 -> FTP -> 被动模式,已经勾选了使用服务器的外部 IP 地址来代替。 在这里插入图片描述

  • 四、 (非必须)客户端:ftp_client.py (Windows 运行)

    功能: 强制开启被动模式,避开 Windows 原生 FTP 工具的限制。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # 运行环境: Windows / Linux / macOS (跨平台通用)
    # 依赖说明: 仅使用 Python 标准库,无需额外安装依赖

    # — 配置区:需与 cpolar 公网信息一致 —
    import ftplib
    import os

    FTP_HOST = "8.148.71.245" # cpolar 公网 IP/域名
    FTP_PORT = 11935 # cpolar 控制端映射的外网端口
    FTP_USER = "anonymous" # 匿名用户
    FTP_PASS = "" # 密码留空

    def main():
    try:
    print(f"正在建立隧道连接: {FTP_HOST}:{FTP_PORT}…")
    ftp = ftplib.FTP()

    # 1. 建立控制通道连接
    ftp.connect(FTP_HOST, FTP_PORT)
    ftp.login(FTP_USER, FTP_PASS)

    # 2. 【核心步骤】强制开启被动模式
    # 解决 Windows CMD 无法列目录的根本手段:让客户端主动连服务器的数据端口
    ftp.set_pasv(True)
    print("✅ 登录成功!数据通道已就绪。")

    while True:
    # 获取用户指令
    cmd = input("\\n[远程控制] ls(列表), get(下载), quit(退出): ").strip().split()
    if not cmd: continue

    action = cmd[0].lower()

    if action == 'ls':
    print("— 目录列表 —")
    ftp.retrlines('LIST') # 接收数据流并打印

    elif action == 'get' and len(cmd) > 1:
    filename = cmd[1]
    print(f"正在通过穿透隧道提取: {filename}…")
    with open(filename, 'wb') as f:
    # 获取文件二进制流
    ftp.retrbinary(f'RETR {filename}', f.write)
    print(f"✨ 下载成功!保存在: {os.path.abspath(filename)}")

    elif action == 'quit':
    ftp.quit()
    print("👋 连接已关闭。")
    break
    except Exception as e:
    print(f"\\n⚠️ 连接中断: {e}")

    if __name__ == "__main__":
    main()

    在这里插入图片描述

    五、 快速复现流程总结

    1. 隧道配置 (cpolar)

    • 隧道 A (控制):TCP, 本地端口 2121, 获取外网端口(如 11935)。
    • 隧道 B (数据):TCP, 本地端口 2120, 获取外网随机端口(如 11667)。

    2. 参数计算如果外网随机端口变更(假设变为

    N

    N

    N):

    • 计算端口高位:

      P

      1

      =

      N

      /

      /

      256

      P1 = N // 256

      P1=N//256

    • 计算端口低位:

      P

      2

      =

      N

      %

      256

      P2 = N \\% 256

      P2=N%256

    • 修改服务端脚本:self.respond(‘227 … (IP,P1,P2).’)

    3. 为什么不再使用 vsftpd?

    传统软件很难在不修改源码的情况下实现 “伪造 227 响应” 。使用 Python pyftpdlib 可以精准控制协议握手的每一个字节,是内网穿透环境下的最优解。


  • cpolar 搭建隧道方法详见 cpolar 官网文档 ↩︎

  • 赞(0)
    未经允许不得转载:171主机测评 » 【Linux】内网穿透 FTP 终极复现手册 (2026 版)--cpolar
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址